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