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