cleanup spec
[platform/upstream/glibc.git] / sunrpc / xdr_rec.c
1 /*
2  * xdr_rec.c, Implements TCP/IP based XDR streams with a "record marking"
3  * layer above tcp (for rpc's use).
4  *
5  * Copyright (c) 2010, Oracle America, Inc.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are
9  * met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above
14  *       copyright notice, this list of conditions and the following
15  *       disclaimer in the documentation and/or other materials
16  *       provided with the distribution.
17  *     * Neither the name of the "Oracle America, Inc." nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  *   FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  *   COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
26  *   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  *   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
28  *   GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29  *   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
30  *   WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31  *   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  *
34  * These routines interface XDRSTREAMS to a tcp/ip connection.
35  * There is a record marking layer between the xdr stream
36  * and the tcp transport level.  A record is composed on one or more
37  * record fragments.  A record fragment is a thirty-two bit header followed
38  * by n bytes of data, where n is contained in the header.  The header
39  * is represented as a htonl(u_long).  The high order bit encodes
40  * whether or not the fragment is the last fragment of the record
41  * (1 => fragment is last, 0 => more fragments to follow.
42  * The other 31 bits encode the byte length of the fragment.
43  */
44
45 #include <stdio.h>
46 #include <string.h>
47 #include <unistd.h>
48 #include <stdint.h>
49 #include <rpc/rpc.h>
50 #include <libintl.h>
51 #include <wchar.h>
52 #include <libio/iolibio.h>
53
54 static bool_t xdrrec_getlong (XDR *, long *);
55 static bool_t xdrrec_putlong (XDR *, const long *);
56 static bool_t xdrrec_getbytes (XDR *, caddr_t, u_int);
57 static bool_t xdrrec_putbytes (XDR *, const char *, u_int);
58 static u_int xdrrec_getpos (const XDR *);
59 static bool_t xdrrec_setpos (XDR *, u_int);
60 static int32_t *xdrrec_inline (XDR *, u_int);
61 static void xdrrec_destroy (XDR *);
62 static bool_t xdrrec_getint32 (XDR *, int32_t *);
63 static bool_t xdrrec_putint32 (XDR *, const int32_t *);
64
65 static const struct xdr_ops xdrrec_ops = {
66   xdrrec_getlong,
67   xdrrec_putlong,
68   xdrrec_getbytes,
69   xdrrec_putbytes,
70   xdrrec_getpos,
71   xdrrec_setpos,
72   xdrrec_inline,
73   xdrrec_destroy,
74   xdrrec_getint32,
75   xdrrec_putint32
76 };
77
78 /*
79  * A record is composed of one or more record fragments.
80  * A record fragment is a two-byte header followed by zero to
81  * 2**32-1 bytes.  The header is treated as a long unsigned and is
82  * encode/decoded to the network via htonl/ntohl.  The low order 31 bits
83  * are a byte count of the fragment.  The highest order bit is a boolean:
84  * 1 => this fragment is the last fragment of the record,
85  * 0 => this fragment is followed by more fragment(s).
86  *
87  * The fragment/record machinery is not general;  it is constructed to
88  * meet the needs of xdr and rpc based on tcp.
89  */
90
91 #define LAST_FRAG (1UL << 31)
92
93 typedef struct rec_strm
94   {
95     caddr_t tcp_handle;
96     caddr_t the_buffer;
97     /*
98      * out-going bits
99      */
100     int (*writeit) (char *, char *, int);
101     caddr_t out_base;           /* output buffer (points to frag header) */
102     caddr_t out_finger;         /* next output position */
103     caddr_t out_boundry;        /* data cannot up to this address */
104     u_int32_t *frag_header;     /* beginning of curren fragment */
105     bool_t frag_sent;           /* true if buffer sent in middle of record */
106     /*
107      * in-coming bits
108      */
109     int (*readit) (char *, char *, int);
110     u_long in_size;             /* fixed size of the input buffer */
111     caddr_t in_base;
112     caddr_t in_finger;          /* location of next byte to be had */
113     caddr_t in_boundry;         /* can read up to this location */
114     long fbtbc;                 /* fragment bytes to be consumed */
115     bool_t last_frag;
116     u_int sendsize;
117     u_int recvsize;
118   }
119 RECSTREAM;
120
121 static u_int fix_buf_size (u_int) internal_function;
122 static bool_t skip_input_bytes (RECSTREAM *, long) internal_function;
123 static bool_t flush_out (RECSTREAM *, bool_t) internal_function;
124 static bool_t set_input_fragment (RECSTREAM *) internal_function;
125 static bool_t get_input_bytes (RECSTREAM *, caddr_t, int) internal_function;
126
127 /*
128  * Create an xdr handle for xdrrec
129  * xdrrec_create fills in xdrs.  Sendsize and recvsize are
130  * send and recv buffer sizes (0 => use default).
131  * tcp_handle is an opaque handle that is passed as the first parameter to
132  * the procedures readit and writeit.  Readit and writeit are read and
133  * write respectively.   They are like the system
134  * calls expect that they take an opaque handle rather than an fd.
135  */
136 void
137 xdrrec_create (XDR *xdrs, u_int sendsize,
138                u_int recvsize, caddr_t tcp_handle,
139                int (*readit) (char *, char *, int),
140                int (*writeit) (char *, char *, int))
141 {
142   RECSTREAM *rstrm = (RECSTREAM *) mem_alloc (sizeof (RECSTREAM));
143   caddr_t tmp;
144   char *buf;
145
146   sendsize = fix_buf_size (sendsize);
147   recvsize = fix_buf_size (recvsize);
148   buf = mem_alloc (sendsize + recvsize + BYTES_PER_XDR_UNIT);
149
150   if (rstrm == NULL || buf == NULL)
151     {
152       (void) __fxprintf (NULL, "%s: %s", __func__, _("out of memory\n"));
153       mem_free (rstrm, sizeof (RECSTREAM));
154       mem_free (buf, sendsize + recvsize + BYTES_PER_XDR_UNIT);
155       /*
156        *  This is bad.  Should rework xdrrec_create to
157        *  return a handle, and in this case return NULL
158        */
159       return;
160     }
161   /*
162    * adjust sizes and allocate buffer quad byte aligned
163    */
164   rstrm->sendsize = sendsize;
165   rstrm->recvsize = recvsize;
166   rstrm->the_buffer = buf;
167   tmp = rstrm->the_buffer;
168   if ((size_t)tmp % BYTES_PER_XDR_UNIT)
169     tmp += BYTES_PER_XDR_UNIT - (size_t)tmp % BYTES_PER_XDR_UNIT;
170   rstrm->out_base = tmp;
171   rstrm->in_base = tmp + sendsize;
172   /*
173    * now the rest ...
174    */
175   /* We have to add the cast since the `struct xdr_ops' in `struct XDR'
176      is not `const'.  */
177   xdrs->x_ops = (struct xdr_ops *) &xdrrec_ops;
178   xdrs->x_private = (caddr_t) rstrm;
179   rstrm->tcp_handle = tcp_handle;
180   rstrm->readit = readit;
181   rstrm->writeit = writeit;
182   rstrm->out_finger = rstrm->out_boundry = rstrm->out_base;
183   rstrm->frag_header = (u_int32_t *) rstrm->out_base;
184   rstrm->out_finger += 4;
185   rstrm->out_boundry += sendsize;
186   rstrm->frag_sent = FALSE;
187   rstrm->in_size = recvsize;
188   rstrm->in_boundry = rstrm->in_base;
189   rstrm->in_finger = (rstrm->in_boundry += recvsize);
190   rstrm->fbtbc = 0;
191   rstrm->last_frag = TRUE;
192 }
193 libc_hidden_nolink_sunrpc (xdrrec_create, GLIBC_2_0)
194
195
196 /*
197  * The routines defined below are the xdr ops which will go into the
198  * xdr handle filled in by xdrrec_create.
199  */
200
201 static bool_t
202 xdrrec_getlong (XDR *xdrs, long *lp)
203 {
204   RECSTREAM *rstrm = (RECSTREAM *) xdrs->x_private;
205   int32_t *buflp = (int32_t *) rstrm->in_finger;
206   int32_t mylong;
207
208   /* first try the inline, fast case */
209   if (rstrm->fbtbc >= BYTES_PER_XDR_UNIT &&
210       rstrm->in_boundry - (char *) buflp >= BYTES_PER_XDR_UNIT)
211     {
212       *lp = (int32_t) ntohl (*buflp);
213       rstrm->fbtbc -= BYTES_PER_XDR_UNIT;
214       rstrm->in_finger += BYTES_PER_XDR_UNIT;
215     }
216   else
217     {
218       if (!xdrrec_getbytes (xdrs, (caddr_t) & mylong,
219                             BYTES_PER_XDR_UNIT))
220         return FALSE;
221       *lp = (int32_t) ntohl (mylong);
222     }
223   return TRUE;
224 }
225
226 static bool_t
227 xdrrec_putlong (XDR *xdrs, const long *lp)
228 {
229   RECSTREAM *rstrm = (RECSTREAM *) xdrs->x_private;
230   int32_t *dest_lp = (int32_t *) rstrm->out_finger;
231
232   if ((rstrm->out_finger += BYTES_PER_XDR_UNIT) > rstrm->out_boundry)
233     {
234       /*
235        * this case should almost never happen so the code is
236        * inefficient
237        */
238       rstrm->out_finger -= BYTES_PER_XDR_UNIT;
239       rstrm->frag_sent = TRUE;
240       if (!flush_out (rstrm, FALSE))
241         return FALSE;
242       dest_lp = (int32_t *) rstrm->out_finger;
243       rstrm->out_finger += BYTES_PER_XDR_UNIT;
244     }
245   *dest_lp = htonl (*lp);
246   return TRUE;
247 }
248
249 static bool_t      /* must manage buffers, fragments, and records */
250 xdrrec_getbytes (XDR *xdrs, caddr_t addr, u_int len)
251 {
252   RECSTREAM *rstrm = (RECSTREAM *) xdrs->x_private;
253   u_int current;
254
255   while (len > 0)
256     {
257       current = rstrm->fbtbc;
258       if (current == 0)
259         {
260           if (rstrm->last_frag)
261             return FALSE;
262           if (!set_input_fragment (rstrm))
263             return FALSE;
264           continue;
265         }
266       current = (len < current) ? len : current;
267       if (!get_input_bytes (rstrm, addr, current))
268         return FALSE;
269       addr += current;
270       rstrm->fbtbc -= current;
271       len -= current;
272     }
273   return TRUE;
274 }
275
276 static bool_t
277 xdrrec_putbytes (XDR *xdrs, const char *addr, u_int len)
278 {
279   RECSTREAM *rstrm = (RECSTREAM *) xdrs->x_private;
280   u_int current;
281
282   while (len > 0)
283     {
284       current = rstrm->out_boundry - rstrm->out_finger;
285       current = (len < current) ? len : current;
286       memcpy (rstrm->out_finger, addr, current);
287       rstrm->out_finger += current;
288       addr += current;
289       len -= current;
290       if (rstrm->out_finger == rstrm->out_boundry && len > 0)
291         {
292           rstrm->frag_sent = TRUE;
293           if (!flush_out (rstrm, FALSE))
294             return FALSE;
295         }
296     }
297   return TRUE;
298 }
299
300 static u_int
301 xdrrec_getpos (const XDR *xdrs)
302 {
303   RECSTREAM *rstrm = (RECSTREAM *) xdrs->x_private;
304   long pos;
305
306   pos = __lseek ((int) (long) rstrm->tcp_handle, (long) 0, 1);
307   if (pos != -1)
308     switch (xdrs->x_op)
309       {
310
311       case XDR_ENCODE:
312         pos += rstrm->out_finger - rstrm->out_base;
313         break;
314
315       case XDR_DECODE:
316         pos -= rstrm->in_boundry - rstrm->in_finger;
317         break;
318
319       default:
320         pos = (u_int) - 1;
321         break;
322       }
323   return (u_int) pos;
324 }
325
326 static bool_t
327 xdrrec_setpos (XDR *xdrs, u_int pos)
328 {
329   RECSTREAM *rstrm = (RECSTREAM *) xdrs->x_private;
330   u_int currpos = xdrrec_getpos (xdrs);
331   int delta = currpos - pos;
332   caddr_t newpos;
333
334   if ((int) currpos != -1)
335     switch (xdrs->x_op)
336       {
337
338       case XDR_ENCODE:
339         newpos = rstrm->out_finger - delta;
340         if (newpos > (caddr_t) rstrm->frag_header &&
341             newpos < rstrm->out_boundry)
342           {
343             rstrm->out_finger = newpos;
344             return TRUE;
345           }
346         break;
347
348       case XDR_DECODE:
349         newpos = rstrm->in_finger - delta;
350         if ((delta < (int) (rstrm->fbtbc)) &&
351             (newpos <= rstrm->in_boundry) &&
352             (newpos >= rstrm->in_base))
353           {
354             rstrm->in_finger = newpos;
355             rstrm->fbtbc -= delta;
356             return TRUE;
357           }
358         break;
359
360       default:
361         break;
362       }
363   return FALSE;
364 }
365
366 static int32_t *
367 xdrrec_inline (XDR *xdrs, u_int len)
368 {
369   RECSTREAM *rstrm = (RECSTREAM *) xdrs->x_private;
370   int32_t *buf = NULL;
371
372   switch (xdrs->x_op)
373     {
374
375     case XDR_ENCODE:
376       if ((rstrm->out_finger + len) <= rstrm->out_boundry)
377         {
378           buf = (int32_t *) rstrm->out_finger;
379           rstrm->out_finger += len;
380         }
381       break;
382
383     case XDR_DECODE:
384       if ((len <= rstrm->fbtbc) &&
385           ((rstrm->in_finger + len) <= rstrm->in_boundry))
386         {
387           buf = (int32_t *) rstrm->in_finger;
388           rstrm->fbtbc -= len;
389           rstrm->in_finger += len;
390         }
391       break;
392
393     default:
394       break;
395     }
396   return buf;
397 }
398
399 static void
400 xdrrec_destroy (XDR *xdrs)
401 {
402   RECSTREAM *rstrm = (RECSTREAM *) xdrs->x_private;
403
404   mem_free (rstrm->the_buffer,
405             rstrm->sendsize + rstrm->recvsize + BYTES_PER_XDR_UNIT);
406   mem_free ((caddr_t) rstrm, sizeof (RECSTREAM));
407 }
408
409 static bool_t
410 xdrrec_getint32 (XDR *xdrs, int32_t *ip)
411 {
412   RECSTREAM *rstrm = (RECSTREAM *) xdrs->x_private;
413   int32_t *bufip = (int32_t *) rstrm->in_finger;
414   int32_t mylong;
415
416   /* first try the inline, fast case */
417   if (rstrm->fbtbc >= BYTES_PER_XDR_UNIT &&
418       rstrm->in_boundry - (char *) bufip >= BYTES_PER_XDR_UNIT)
419     {
420       *ip = ntohl (*bufip);
421       rstrm->fbtbc -= BYTES_PER_XDR_UNIT;
422       rstrm->in_finger += BYTES_PER_XDR_UNIT;
423     }
424   else
425     {
426       if (!xdrrec_getbytes (xdrs, (caddr_t) &mylong,
427                             BYTES_PER_XDR_UNIT))
428         return FALSE;
429       *ip = ntohl (mylong);
430     }
431   return TRUE;
432 }
433
434 static bool_t
435 xdrrec_putint32 (XDR *xdrs, const int32_t *ip)
436 {
437   RECSTREAM *rstrm = (RECSTREAM *) xdrs->x_private;
438   int32_t *dest_ip = (int32_t *) rstrm->out_finger;
439
440   if ((rstrm->out_finger += BYTES_PER_XDR_UNIT) > rstrm->out_boundry)
441     {
442       /*
443        * this case should almost never happen so the code is
444        * inefficient
445        */
446       rstrm->out_finger -= BYTES_PER_XDR_UNIT;
447       rstrm->frag_sent = TRUE;
448       if (!flush_out (rstrm, FALSE))
449         return FALSE;
450       dest_ip = (int32_t *) rstrm->out_finger;
451       rstrm->out_finger += BYTES_PER_XDR_UNIT;
452     }
453   *dest_ip = htonl (*ip);
454   return TRUE;
455 }
456
457 /*
458  * Exported routines to manage xdr records
459  */
460
461 /*
462  * Before reading (deserializing from the stream, one should always call
463  * this procedure to guarantee proper record alignment.
464  */
465 bool_t
466 xdrrec_skiprecord (XDR *xdrs)
467 {
468   RECSTREAM *rstrm = (RECSTREAM *) xdrs->x_private;
469
470   while (rstrm->fbtbc > 0 || (!rstrm->last_frag))
471     {
472       if (!skip_input_bytes (rstrm, rstrm->fbtbc))
473         return FALSE;
474       rstrm->fbtbc = 0;
475       if ((!rstrm->last_frag) && (!set_input_fragment (rstrm)))
476         return FALSE;
477     }
478   rstrm->last_frag = FALSE;
479   return TRUE;
480 }
481 libc_hidden_nolink_sunrpc (xdrrec_skiprecord, GLIBC_2_0)
482
483 /*
484  * Lookahead function.
485  * Returns TRUE iff there is no more input in the buffer
486  * after consuming the rest of the current record.
487  */
488 bool_t
489 xdrrec_eof (XDR *xdrs)
490 {
491   RECSTREAM *rstrm = (RECSTREAM *) xdrs->x_private;
492
493   while (rstrm->fbtbc > 0 || (!rstrm->last_frag))
494     {
495       if (!skip_input_bytes (rstrm, rstrm->fbtbc))
496         return TRUE;
497       rstrm->fbtbc = 0;
498       if ((!rstrm->last_frag) && (!set_input_fragment (rstrm)))
499         return TRUE;
500     }
501   if (rstrm->in_finger == rstrm->in_boundry)
502     return TRUE;
503   return FALSE;
504 }
505 libc_hidden_nolink_sunrpc (xdrrec_eof, GLIBC_2_0)
506
507 /*
508  * The client must tell the package when an end-of-record has occurred.
509  * The second parameter tells whether the record should be flushed to the
510  * (output) tcp stream.  (This lets the package support batched or
511  * pipelined procedure calls.)  TRUE => immediate flush to tcp connection.
512  */
513 bool_t
514 xdrrec_endofrecord (XDR *xdrs, bool_t sendnow)
515 {
516   RECSTREAM *rstrm = (RECSTREAM *) xdrs->x_private;
517   u_long len;           /* fragment length */
518
519   if (sendnow || rstrm->frag_sent
520       || rstrm->out_finger + BYTES_PER_XDR_UNIT >= rstrm->out_boundry)
521     {
522       rstrm->frag_sent = FALSE;
523       return flush_out (rstrm, TRUE);
524     }
525   len = (rstrm->out_finger - (char *) rstrm->frag_header
526          - BYTES_PER_XDR_UNIT);
527   *rstrm->frag_header = htonl ((u_long) len | LAST_FRAG);
528   rstrm->frag_header = (u_int32_t *) rstrm->out_finger;
529   rstrm->out_finger += BYTES_PER_XDR_UNIT;
530   return TRUE;
531 }
532 libc_hidden_nolink_sunrpc (xdrrec_endofrecord, GLIBC_2_0)
533
534
535 /*
536  * Internal useful routines
537  */
538 static bool_t
539 internal_function
540 flush_out (RECSTREAM *rstrm, bool_t eor)
541 {
542   u_long eormask = (eor == TRUE) ? LAST_FRAG : 0;
543   u_long len = (rstrm->out_finger - (char *) rstrm->frag_header
544                 - BYTES_PER_XDR_UNIT);
545
546   *rstrm->frag_header = htonl (len | eormask);
547   len = rstrm->out_finger - rstrm->out_base;
548   if ((*(rstrm->writeit)) (rstrm->tcp_handle, rstrm->out_base, (int) len)
549       != (int) len)
550     return FALSE;
551   rstrm->frag_header = (u_int32_t *) rstrm->out_base;
552   rstrm->out_finger = (caddr_t) rstrm->out_base + BYTES_PER_XDR_UNIT;
553   return TRUE;
554 }
555
556 static bool_t   /* knows nothing about records!  Only about input buffers */
557 fill_input_buf (RECSTREAM *rstrm)
558 {
559   caddr_t where;
560   size_t i;
561   int len;
562
563   where = rstrm->in_base;
564   i = (size_t) rstrm->in_boundry % BYTES_PER_XDR_UNIT;
565   where += i;
566   len = rstrm->in_size - i;
567   if ((len = (*(rstrm->readit)) (rstrm->tcp_handle, where, len)) == -1)
568     return FALSE;
569   rstrm->in_finger = where;
570   where += len;
571   rstrm->in_boundry = where;
572   return TRUE;
573 }
574
575 static bool_t   /* knows nothing about records!  Only about input buffers */
576 internal_function
577 get_input_bytes (RECSTREAM *rstrm, caddr_t addr, int len)
578 {
579   int current;
580
581   while (len > 0)
582     {
583       current = rstrm->in_boundry - rstrm->in_finger;
584       if (current == 0)
585         {
586           if (!fill_input_buf (rstrm))
587             return FALSE;
588           continue;
589         }
590       current = (len < current) ? len : current;
591       memcpy (addr, rstrm->in_finger, current);
592       rstrm->in_finger += current;
593       addr += current;
594       len -= current;
595     }
596   return TRUE;
597 }
598
599 static bool_t /* next two bytes of the input stream are treated as a header */
600 internal_function
601 set_input_fragment (RECSTREAM *rstrm)
602 {
603   uint32_t header;
604
605   if (! get_input_bytes (rstrm, (caddr_t)&header, BYTES_PER_XDR_UNIT))
606     return FALSE;
607   header = ntohl (header);
608   rstrm->last_frag = ((header & LAST_FRAG) == 0) ? FALSE : TRUE;
609   /*
610    * Sanity check. Try not to accept wildly incorrect fragment
611    * sizes. Unfortunately, only a size of zero can be identified as
612    * 'wildely incorrect', and this only, if it is not the last
613    * fragment of a message. Ridiculously large fragment sizes may look
614    * wrong, but we don't have any way to be certain that they aren't
615    * what the client actually intended to send us. Many existing RPC
616    * implementations may sent a fragment of size zero as the last
617    * fragment of a message.
618    */
619   if (header == 0)
620     return FALSE;
621   rstrm->fbtbc = header & ~LAST_FRAG;
622   return TRUE;
623 }
624
625 static bool_t   /* consumes input bytes; knows nothing about records! */
626 internal_function
627 skip_input_bytes (RECSTREAM *rstrm, long cnt)
628 {
629   int current;
630
631   while (cnt > 0)
632     {
633       current = rstrm->in_boundry - rstrm->in_finger;
634       if (current == 0)
635         {
636           if (!fill_input_buf (rstrm))
637             return FALSE;
638           continue;
639         }
640       current = (cnt < current) ? cnt : current;
641       rstrm->in_finger += current;
642       cnt -= current;
643     }
644   return TRUE;
645 }
646
647 static u_int
648 internal_function
649 fix_buf_size (u_int s)
650 {
651   if (s < 100)
652     s = 4000;
653   return RNDUP (s);
654 }