1 
2 /*
3  * Copyright (c) 2009, Sun Microsystems, Inc.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  * - Redistributions of source code must retain the above copyright notice,
9  *   this list of conditions and the following disclaimer.
10  * - Redistributions in binary form must reproduce the above copyright notice,
11  *   this list of conditions and the following disclaimer in the documentation
12  *   and/or other materials provided with the distribution.
13  * - Neither the name of Sun Microsystems, Inc. nor the names of its
14  *   contributors may be used to endorse or promote products derived
15  *   from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
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).  Thegh 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 #define _DEFAULT_SOURCE
48 #include <stddef.h>
49 #include <sys/types.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <assert.h>
53 #include <unistd.h>
54 #include <errno.h>
55 #include <limits.h>
56 
57 #include <rpc/types.h>
58 #include <rpc/xdr.h>
59 
60 #include "xdr_private.h"
61 
62 #ifndef ntohl
63 # define ntohl(x) xdr_ntohl(x)
64 #endif
65 #ifndef htonl
66 # define htonl(x) xdr_htonl(x)
67 #endif
68 
69 enum xprt_stat
70 {
71   XPRT_DIED,
72   XPRT_MOREREQS,
73   XPRT_IDLE
74 };
75 
76 static bool_t xdrrec_getlong (XDR *, long *);
77 static bool_t xdrrec_putlong (XDR *, const long *);
78 static bool_t xdrrec_getbytes (XDR *, char *, u_int);
79 static bool_t xdrrec_putbytes (XDR *, const char *, u_int);
80 static u_int xdrrec_getpos (XDR *);
81 static bool_t xdrrec_setpos (XDR *, u_int);
82 static int32_t * xdrrec_inline (XDR *, u_int);
83 static void xdrrec_destroy (XDR *);
84 static bool_t xdrrec_getint32 (XDR *, int32_t *);
85 static bool_t xdrrec_putint32 (XDR *, const int32_t *);
86 
87 static const struct xdr_ops xdrrec_ops = {
88   xdrrec_getlong,
89   xdrrec_putlong,
90   xdrrec_getbytes,
91   xdrrec_putbytes,
92   xdrrec_getpos,
93   xdrrec_setpos,
94   xdrrec_inline,
95   xdrrec_destroy,
96   xdrrec_getint32,
97   xdrrec_putint32
98 };
99 
100 /*
101  * A record is composed of one or more record fragments.
102  * A record fragment is a four-byte header followed by zero to
103  * 2**32-1 bytes.  The header is treated as a long unsigned and is
104  * encode/decoded to the network via htonl/ntohl.  The low order 31 bits
105  * are a byte count of the fragment.  The highest order bit is a boolean:
106  * 1 => this fragment is the last fragment of the record,
107  * 0 => this fragment is followed by more fragment(s).
108  *
109  * The fragment/record machinery is not general;  it is constructed to
110  * meet the needs of xdr and rpc based on tcp.
111  */
112 
113 #define LAST_FRAG ((u_int32_t)(UINT32_C(1) << 31))
114 
115 typedef struct rec_strm
116 {
117   caddr_t tcp_handle;
118   /*
119    * out-goung bits
120    */
121   caddr_t out_buffer;           /* buffer as allocated; may not be aligned */
122   int (*writeit) (void *, void *, int);
123   caddr_t out_base;             /* output buffer (points to frag header) */
124   caddr_t out_finger;           /* next output position */
125   caddr_t out_boundry;          /* data cannot up to this address */
126   u_int32_t *frag_header;       /* beginning of curren fragment */
127   bool_t frag_sent;             /* true if buffer sent in middle of record */
128   /*
129    * in-coming bits
130    */
131   caddr_t in_buffer;            /* buffer as allocated; may not be aligned */
132   int (*readit) (void *, void *, int);
133   u_long in_size;               /* fixed size of the input buffer */
134   caddr_t in_base;
135   caddr_t in_finger;            /* location of next byte to be had */
136   caddr_t in_boundry;           /* can read up to this location */
137   long fbtbc;                   /* fragment bytes to be consumed */
138   bool_t last_frag;
139   u_int sendsize;               /* must be <= INT_MAX */
140   u_int recvsize;               /* must be <= INT_MAX */
141 
142   bool_t nonblock;
143   bool_t in_haveheader;
144   u_int32_t in_header;
145   char *in_hdrp;
146   int in_hdrlen;
147   int in_reclen;
148   int in_received;
149   int in_maxrec;
150 } RECSTREAM;
151 
152 static u_int fix_buf_size (u_int);
153 static bool_t flush_out (RECSTREAM *, bool_t);
154 static bool_t fill_input_buf (RECSTREAM *);
155 static bool_t get_input_bytes (RECSTREAM *, char *, size_t);
156 static bool_t set_input_fragment (RECSTREAM *);
157 static bool_t skip_input_bytes (RECSTREAM *, long);
158 static bool_t realloc_stream (RECSTREAM *, int);
159 
160 bool_t __xdrrec_getrec (XDR *, enum xprt_stat *, bool_t);
161 bool_t __xdrrec_setnonblock (XDR *, int);
162 
163 /*
164  * Create an xdr handle for xdrrec
165  * xdrrec_create fills in xdrs.  Sendsize and recvsize are
166  * send and recv buffer sizes (0 => use default), and must be <= INT_MAX.
167  * tcp_handle is an opaque handle that is passed as the first parameter to
168  * the procedures readit and writeit.  Readit and writeit are read and
169  * write respectively.   They are like the system
170  * calls except that they take an opaque handle rather than an fd.
171  */
172 void
xdrrec_create(XDR * xdrs,u_int sendsize,u_int recvsize,void * tcp_handle,int (* readit)(void *,void *,int),int (* writeit)(void *,void *,int))173 xdrrec_create (XDR * xdrs,
174 	u_int sendsize,
175 	u_int recvsize,
176 	void *tcp_handle,
177         int (*readit) (void *, void *, int),
178         int (*writeit) (void *, void *, int))
179 {
180   RECSTREAM *rstrm;
181   /* Although sendsize and recvsize are u_int, we require
182    * that they be less than INT_MAX, because often we need
183    * to compare against values held in (signed) integers.
184    * Please don't try to use send/recv buffers > 2GB...
185    */
186   assert (sendsize < (u_int)INT_MAX);
187   assert (recvsize < (u_int)INT_MAX);
188 
189   rstrm = (RECSTREAM *) mem_alloc (sizeof (RECSTREAM));
190   if (rstrm == NULL)
191     {
192       xdr_warnx ("xdrrec_create: out of memory");
193       /*
194        *  This is bad.  Should rework xdrrec_create to
195        *  return a handle, and in this case return NULL
196        */
197       errno = ENOMEM;
198       return;
199     }
200 
201 
202   /* allocate send buffer; insure BYTES_PER_UNIT alignment */
203   rstrm->sendsize = sendsize = fix_buf_size (sendsize);
204   rstrm->out_buffer = mem_alloc (rstrm->sendsize + BYTES_PER_XDR_UNIT);
205   if (rstrm->out_buffer == NULL)
206     {
207       xdr_warnx ("xdrrec_create: out of memory");
208       mem_free (rstrm, sizeof (RECSTREAM));
209       errno = ENOMEM;
210       return;
211     }
212   for (rstrm->out_base = rstrm->out_buffer;
213        (intptr_t) rstrm->out_base % BYTES_PER_XDR_UNIT != 0; rstrm->out_base++)
214     ;
215 
216   /* allocate recv buffer; insure BYTES_PER_UNIT alignment */
217   rstrm->recvsize = recvsize = fix_buf_size (recvsize);
218   rstrm->in_buffer = mem_alloc (recvsize + BYTES_PER_XDR_UNIT);
219   if (rstrm->in_buffer == NULL)
220     {
221       xdr_warnx ("xdrrec_create: out of memory");
222       mem_free (rstrm->out_buffer, sendsize + BYTES_PER_XDR_UNIT);
223       mem_free (rstrm, sizeof (RECSTREAM));
224       errno = ENOMEM;
225       return;
226     }
227   for (rstrm->in_base = rstrm->in_buffer;
228        (intptr_t) rstrm->in_base % BYTES_PER_XDR_UNIT != 0; rstrm->in_base++)
229     ;
230 
231   /*
232    * now the rest ...
233    */
234   xdrs->x_ops = &xdrrec_ops;
235   xdrs->x_private = rstrm;
236   rstrm->tcp_handle = tcp_handle;
237   rstrm->readit = readit;
238   rstrm->writeit = writeit;
239   rstrm->out_finger = rstrm->out_boundry = rstrm->out_base;
240   rstrm->frag_header = (u_int32_t *) (void *) rstrm->out_base;
241   rstrm->out_finger += sizeof (u_int32_t);
242   rstrm->out_boundry += sendsize;
243   rstrm->frag_sent = FALSE;
244   rstrm->in_size = recvsize;
245   rstrm->in_boundry = rstrm->in_base;
246   rstrm->in_finger = (rstrm->in_boundry += recvsize);
247   rstrm->fbtbc = 0;
248   rstrm->last_frag = TRUE;
249   rstrm->in_haveheader = FALSE;
250   rstrm->in_hdrlen = 0;
251   rstrm->in_hdrp = (char *) (void *) &rstrm->in_header;
252   rstrm->nonblock = FALSE;
253   rstrm->in_reclen = 0;
254   rstrm->in_received = 0;
255 }
256 
257 
258 /*
259  * The reoutines defined below are the xdr ops which will go into the
260  * xdr handle filled in by xdrrec_create.
261  */
262 
263 static bool_t
xdrrec_getlong(XDR * xdrs,long * lp)264 xdrrec_getlong (XDR * xdrs,
265 	long *lp)
266 {
267   RECSTREAM *rstrm = (RECSTREAM *) (xdrs->x_private);
268   int32_t *buflp = (int32_t *) (void *) (rstrm->in_finger);
269   int32_t mylong;
270 
271   /* first try the inline, fast case */
272   if ((rstrm->fbtbc >= (long) sizeof (int32_t)) &&
273       (((intptr_t) rstrm->in_boundry - (intptr_t) buflp) >= (long) sizeof (int32_t)))
274     {
275       *lp = (long) ntohl ((u_int32_t) (*buflp));
276       rstrm->fbtbc -= sizeof (int32_t);
277       rstrm->in_finger += sizeof (int32_t);
278     }
279   else
280     {
281       if (!xdrrec_getbytes (xdrs, (char *) (void *) &mylong,
282                             sizeof (int32_t)))
283         return FALSE;
284       *lp = (long) ntohl ((u_int32_t) mylong);
285     }
286   return TRUE;
287 }
288 
289 static bool_t
xdrrec_putlong(XDR * xdrs,const long * lp)290 xdrrec_putlong (XDR * xdrs,
291 	const long *lp)
292 {
293   RECSTREAM *rstrm = (RECSTREAM *) (xdrs->x_private);
294   int32_t *dest_lp = ((int32_t *) (void *) (rstrm->out_finger));
295 
296   if ((rstrm->out_finger += sizeof (int32_t)) > rstrm->out_boundry)
297     {
298       /*
299        * this case should almost never happen so the code is
300        * inefficient
301        */
302       rstrm->out_finger -= sizeof (int32_t);
303       rstrm->frag_sent = TRUE;
304       if (!flush_out (rstrm, FALSE))
305         return FALSE;
306       dest_lp = ((int32_t *) (void *) (rstrm->out_finger));
307       rstrm->out_finger += sizeof (int32_t);
308     }
309   *dest_lp = (int32_t) htonl ((u_int32_t) (*lp));
310   return TRUE;
311 }
312 
313 static bool_t                   /* must manage buffers, fragments, and records */
xdrrec_getbytes(XDR * xdrs,char * addr,u_int len)314 xdrrec_getbytes (XDR * xdrs,
315 	char *addr,
316 	u_int len)
317 {
318   RECSTREAM *rstrm = (RECSTREAM *) (xdrs->x_private);
319   size_t current;
320 
321   while (len > 0)
322     {
323       current = (int) rstrm->fbtbc;
324       if (current == 0)
325         {
326           if (rstrm->last_frag)
327             return FALSE;
328           if (!set_input_fragment (rstrm))
329             return FALSE;
330           continue;
331         }
332       current = (len < current) ? len : current;
333       if (!get_input_bytes (rstrm, addr, current))
334         return FALSE;
335       addr += current;
336       rstrm->fbtbc -= current;
337       len -= current;
338     }
339   return TRUE;
340 }
341 
342 static bool_t
xdrrec_putbytes(XDR * xdrs,const char * addr,u_int len)343 xdrrec_putbytes (XDR * xdrs,
344         const char *addr,
345 	u_int len)
346 {
347   RECSTREAM *rstrm = (RECSTREAM *) (xdrs->x_private);
348   size_t current;
349 
350   while (len > 0)
351     {
352       current = (size_t) ((uintptr_t) rstrm->out_boundry -
353                           (uintptr_t) rstrm->out_finger);
354       current = (len < current) ? len : current;
355       memmove (rstrm->out_finger, addr, current);
356       rstrm->out_finger += current;
357       addr += current;
358       len -= current;
359       if (rstrm->out_finger == rstrm->out_boundry)
360         {
361           rstrm->frag_sent = TRUE;
362           if (!flush_out (rstrm, FALSE))
363             return FALSE;
364         }
365     }
366   return TRUE;
367 }
368 
369 static u_int
xdrrec_getpos(XDR * xdrs)370 xdrrec_getpos (XDR * xdrs)
371 {
372   RECSTREAM *rstrm = (RECSTREAM *) xdrs->x_private;
373   off_t pos;
374 
375   pos = lseek ((int) (uintptr_t) rstrm->tcp_handle, (off_t) 0, 1);
376   if (pos != -1)
377     switch (xdrs->x_op)
378       {
379 
380       case XDR_ENCODE:
381         pos += rstrm->out_finger - rstrm->out_base;
382         break;
383 
384       case XDR_DECODE:
385         pos -= rstrm->in_boundry - rstrm->in_finger;
386         break;
387 
388       default:
389         pos = (off_t) - 1;
390         break;
391       }
392   return ((u_int) pos);
393 }
394 
395 static bool_t
xdrrec_setpos(XDR * xdrs,u_int pos)396 xdrrec_setpos (XDR * xdrs,
397 	u_int pos)
398 {
399   RECSTREAM *rstrm = (RECSTREAM *) xdrs->x_private;
400   u_int currpos = xdrrec_getpos (xdrs);
401   int delta = currpos - pos;
402   char *newpos;
403 
404   if ((int) currpos != -1)
405     switch (xdrs->x_op)
406       {
407 
408       case XDR_ENCODE:
409         newpos = rstrm->out_finger - delta;
410         if ((newpos > (char *) (void *) (rstrm->frag_header)) &&
411             (newpos < rstrm->out_boundry))
412           {
413             rstrm->out_finger = newpos;
414             return TRUE;
415           }
416         break;
417 
418       case XDR_DECODE:
419         newpos = rstrm->in_finger - delta;
420         if ((delta < (int) (rstrm->fbtbc)) &&
421             (newpos <= rstrm->in_boundry) && (newpos >= rstrm->in_base))
422           {
423             rstrm->in_finger = newpos;
424             rstrm->fbtbc -= delta;
425             return TRUE;
426           }
427         break;
428 
429       case XDR_FREE:
430         break;
431       }
432   return FALSE;
433 }
434 
435 static int32_t *
xdrrec_inline(XDR * xdrs,u_int len)436 xdrrec_inline (XDR * xdrs,
437 	u_int len)
438 {
439   RECSTREAM *rstrm = (RECSTREAM *) xdrs->x_private;
440   int32_t *buf = NULL;
441   /* len represents the number of bytes to extract
442    * from the buffer. The number of bytes remaining
443    * in the buffer is rstrm->fbtbc, which is a long.
444    * Thus, the buffer size maximum is 2GB (!), and
445    * we require that no one ever try to read more
446    * than than number of bytes at once.
447    */
448   assert (len < (u_int)LONG_MAX);
449 
450   switch (xdrs->x_op)
451     {
452 
453     case XDR_ENCODE:
454       if ((rstrm->out_finger + len) <= rstrm->out_boundry)
455         {
456           buf = (int32_t *) (void *) rstrm->out_finger;
457           rstrm->out_finger += len;
458         }
459       break;
460 
461     case XDR_DECODE:
462       if (((long)len <= rstrm->fbtbc) &&
463           ((rstrm->in_finger + len) <= rstrm->in_boundry))
464         {
465           buf = (int32_t *) (void *) rstrm->in_finger;
466           rstrm->fbtbc -= len;
467           rstrm->in_finger += len;
468         }
469       break;
470 
471     case XDR_FREE:
472       break;
473     }
474   return (buf);
475 }
476 
477 static void
xdrrec_destroy(XDR * xdrs)478 xdrrec_destroy (XDR * xdrs)
479 {
480   RECSTREAM *rstrm = (RECSTREAM *) xdrs->x_private;
481 
482   mem_free (rstrm->out_buffer, rstrm->sendsize + BYTES_PER_XDR_UNIT);
483   mem_free (rstrm->in_buffer, rstrm->recvsize + BYTES_PER_XDR_UNIT);
484   mem_free (rstrm, sizeof (RECSTREAM));
485 }
486 
487 static bool_t
xdrrec_getint32(XDR * xdrs,int32_t * ip)488 xdrrec_getint32 (XDR *xdrs,
489 	int32_t *ip)
490 {
491   RECSTREAM *rstrm = (RECSTREAM *) (xdrs->x_private);
492   int32_t *bufip = (int32_t *) (void *) (rstrm->in_finger);
493   int32_t mylong;
494 
495   /* first try the inline, fast case */
496   if ((rstrm->fbtbc >= (long) sizeof (int32_t)) &&
497       (( rstrm->in_boundry - (char *) bufip) >= (ssize_t) sizeof (int32_t)))
498     {
499       *ip = (int32_t) ntohl (*bufip);
500       rstrm->fbtbc -= sizeof (int32_t);
501       rstrm->in_finger += sizeof (int32_t);
502     }
503   else
504     {
505       if (!xdrrec_getbytes (xdrs, (char *) (void *) &mylong,
506                             sizeof (int32_t)))
507         return FALSE;
508       *ip = (int32_t) ntohl (mylong);
509     }
510   return TRUE;
511 }
512 
513 static bool_t
xdrrec_putint32(XDR * xdrs,const int32_t * ip)514 xdrrec_putint32 (XDR *xdrs,
515 	const int32_t *ip)
516 {
517   RECSTREAM *rstrm = (RECSTREAM *) (xdrs->x_private);
518   int32_t *dest_ip = ((int32_t *) (void *) (rstrm->out_finger));
519 
520   if ((rstrm->out_finger += sizeof (int32_t)) > rstrm->out_boundry)
521     {
522       /*
523        * this case should almost never happen so the code is
524        * inefficient
525        */
526       rstrm->out_finger -= sizeof (int32_t);
527       rstrm->frag_sent = TRUE;
528       if (!flush_out (rstrm, FALSE))
529         return FALSE;
530       dest_ip = ((int32_t *) (void *) (rstrm->out_finger));
531       rstrm->out_finger += sizeof (int32_t);
532     }
533   *dest_ip = (int32_t) htonl (*ip);
534   return TRUE;
535 }
536 
537 /*
538  * Exported routines to manage xdr records
539  */
540 
541 /*
542  * Before reading (deserializing from the stream, one should always call
543  * this procedure to guarantee proper record alignment.
544  */
545 bool_t
xdrrec_skiprecord(XDR * xdrs)546 xdrrec_skiprecord (XDR * xdrs)
547 {
548   RECSTREAM *rstrm = (RECSTREAM *) (xdrs->x_private);
549   enum xprt_stat xstat;
550 
551   if (rstrm->nonblock)
552     {
553       if (__xdrrec_getrec (xdrs, &xstat, FALSE))
554         {
555           rstrm->fbtbc = 0;
556           return TRUE;
557         }
558       if (rstrm->in_finger == rstrm->in_boundry && xstat == XPRT_MOREREQS)
559         {
560           rstrm->fbtbc = 0;
561           return TRUE;
562         }
563       return FALSE;
564     }
565 
566   while (rstrm->fbtbc > 0 || (!rstrm->last_frag))
567     {
568       if (!skip_input_bytes (rstrm, rstrm->fbtbc))
569         return FALSE;
570       rstrm->fbtbc = 0;
571       if ((!rstrm->last_frag) && (!set_input_fragment (rstrm)))
572         return FALSE;
573     }
574   rstrm->last_frag = FALSE;
575   return TRUE;
576 }
577 
578 /*
579  * Look ahead function.
580  * Returns TRUE iff there is no more input in the buffer
581  * after consuming the rest of the current record.
582  */
583 bool_t
xdrrec_eof(XDR * xdrs)584 xdrrec_eof (XDR * xdrs)
585 {
586   RECSTREAM *rstrm = (RECSTREAM *) (xdrs->x_private);
587 
588   while (rstrm->fbtbc > 0 || (!rstrm->last_frag))
589     {
590       if (!skip_input_bytes (rstrm, rstrm->fbtbc))
591         return TRUE;
592       rstrm->fbtbc = 0;
593       if ((!rstrm->last_frag) && (!set_input_fragment (rstrm)))
594         return TRUE;
595     }
596   if (rstrm->in_finger == rstrm->in_boundry)
597     return TRUE;
598   return FALSE;
599 }
600 
601 /*
602  * The client must tell the package when an end-of-record has occurred.
603  * The second paraemters tells whether the record should be flushed to the
604  * (output) tcp stream.  (This let's the package support batched or
605  * pipelined procedure calls.)  TRUE => immmediate flush to tcp connection.
606  */
607 bool_t
xdrrec_endofrecord(XDR * xdrs,bool_t sendnow)608 xdrrec_endofrecord (XDR * xdrs,
609 	bool_t sendnow)
610 {
611   RECSTREAM *rstrm = (RECSTREAM *) (xdrs->x_private);
612   u_long len;                   /* fragment length */
613 
614   if (sendnow || rstrm->frag_sent ||
615       ((uintptr_t) rstrm->out_finger + sizeof (u_int32_t) >=
616        (uintptr_t) rstrm->out_boundry))
617     {
618       rstrm->frag_sent = FALSE;
619       return (flush_out (rstrm, TRUE));
620     }
621   len = (uintptr_t) (rstrm->out_finger) - (uintptr_t) (rstrm->frag_header) -
622     sizeof (u_int32_t);
623   *(rstrm->frag_header) = htonl ((u_int32_t) len | LAST_FRAG);
624   rstrm->frag_header = (u_int32_t *) (void *) rstrm->out_finger;
625   rstrm->out_finger += sizeof (u_int32_t);
626   return TRUE;
627 }
628 
629 /*
630  * Fill the stream buffer with a record for a non-blocking connection.
631  * Return true if a record is available in the buffer, false if not.
632  */
633 bool_t
__xdrrec_getrec(XDR * xdrs,enum xprt_stat * statp,bool_t expectdata)634 __xdrrec_getrec (XDR * xdrs,
635 	enum xprt_stat * statp,
636 	bool_t expectdata)
637 {
638   RECSTREAM *rstrm = (RECSTREAM *) (xdrs->x_private);
639   ssize_t n;
640   int fraglen;
641 
642   if (!rstrm->in_haveheader)
643     {
644       n = rstrm->readit (rstrm->tcp_handle, rstrm->in_hdrp,
645                          (int) sizeof (rstrm->in_header) - rstrm->in_hdrlen);
646       if (n == 0)
647         {
648           *statp = expectdata ? XPRT_DIED : XPRT_IDLE;
649           return FALSE;
650         }
651       if (n < 0)
652         {
653           *statp = XPRT_DIED;
654           return FALSE;
655         }
656       rstrm->in_hdrp += n;
657       rstrm->in_hdrlen += n;
658       if (rstrm->in_hdrlen < (int) sizeof (rstrm->in_header))
659         {
660           *statp = XPRT_MOREREQS;
661           return FALSE;
662         }
663       rstrm->in_header = ntohl (rstrm->in_header);
664       fraglen = (int) (rstrm->in_header & ~LAST_FRAG);
665       if (fraglen == 0 || fraglen > rstrm->in_maxrec ||
666           (rstrm->in_reclen + fraglen) > rstrm->in_maxrec)
667         {
668           *statp = XPRT_DIED;
669           return FALSE;
670         }
671       rstrm->in_reclen += fraglen;
672       if (rstrm->in_reclen > (int)rstrm->recvsize) /* guaranteed recvsize < INT_MAX */
673         realloc_stream (rstrm, rstrm->in_reclen);
674       if (rstrm->in_header & LAST_FRAG)
675         {
676           rstrm->in_header &= ~LAST_FRAG;
677           rstrm->last_frag = TRUE;
678         }
679       /*
680        * We can only reasonably expect to read once from a
681        * non-blocking stream. Reading the fragment header
682        * may have drained the stream.
683        */
684       expectdata = FALSE;
685     }
686 
687   n = rstrm->readit (rstrm->tcp_handle,
688                      rstrm->in_base + rstrm->in_received,
689                      (rstrm->in_reclen - rstrm->in_received));
690 
691   if (n < 0)
692     {
693       *statp = XPRT_DIED;
694       return FALSE;
695     }
696 
697   if (n == 0)
698     {
699       *statp = expectdata ? XPRT_DIED : XPRT_IDLE;
700       return FALSE;
701     }
702 
703   rstrm->in_received += n;
704 
705   if (rstrm->in_received == rstrm->in_reclen)
706     {
707       rstrm->in_haveheader = FALSE;
708       rstrm->in_hdrp = (char *) (void *) &rstrm->in_header;
709       rstrm->in_hdrlen = 0;
710       if (rstrm->last_frag)
711         {
712           rstrm->fbtbc = rstrm->in_reclen;
713           rstrm->in_boundry = rstrm->in_base + rstrm->in_reclen;
714           rstrm->in_finger = rstrm->in_base;
715           rstrm->in_reclen = rstrm->in_received = 0;
716           *statp = XPRT_MOREREQS;
717           return TRUE;
718         }
719     }
720 
721   *statp = XPRT_MOREREQS;
722   return FALSE;
723 }
724 
725 bool_t
__xdrrec_setnonblock(XDR * xdrs,int maxrec)726 __xdrrec_setnonblock (XDR * xdrs,
727 	int maxrec)
728 {
729   RECSTREAM *rstrm = (RECSTREAM *) (xdrs->x_private);
730 
731   rstrm->nonblock = TRUE;
732   if (maxrec == 0)
733     maxrec = rstrm->recvsize;
734   rstrm->in_maxrec = maxrec;
735   return TRUE;
736 }
737 
738 /*
739  * Internal useful routines
740  */
741 static bool_t
flush_out(RECSTREAM * rstrm,bool_t eor)742 flush_out (RECSTREAM * rstrm,
743 	bool_t eor)
744 {
745   u_int32_t eormask = (eor == TRUE) ? LAST_FRAG : 0;
746   u_int32_t len = (u_int32_t) ((uintptr_t) (rstrm->out_finger) -
747                                (uintptr_t) (rstrm->frag_header) -
748                                sizeof (u_int32_t));
749 
750   *(rstrm->frag_header) = htonl (len | eormask);
751   len = (u_int32_t) ((uintptr_t) (rstrm->out_finger) -
752                      (uintptr_t) (rstrm->out_base));
753   if ((*(rstrm->writeit)) (rstrm->tcp_handle, rstrm->out_base, (int) len)
754       != (int) len)
755     return FALSE;
756   rstrm->frag_header = (u_int32_t *) (void *) rstrm->out_base;
757   rstrm->out_finger = (char *) rstrm->out_base + sizeof (u_int32_t);
758   return TRUE;
759 }
760 
761 static bool_t                   /* knows nothing about records!  Only about input buffers */
fill_input_buf(RECSTREAM * rstrm)762 fill_input_buf (RECSTREAM * rstrm)
763 {
764   char *where;
765   u_int32_t i;
766   int len;
767 
768   if (rstrm->nonblock)
769     return FALSE;
770 
771   where = rstrm->in_base;
772   i = (u_int32_t) ((uintptr_t) rstrm->in_boundry % BYTES_PER_XDR_UNIT);
773   where += i;
774   len = (u_int32_t) (rstrm->in_size - i);
775   if ((len = (*(rstrm->readit)) (rstrm->tcp_handle, where, len)) == -1)
776     return FALSE;
777   rstrm->in_finger = where;
778   where += len;
779   rstrm->in_boundry = where;
780   return TRUE;
781 }
782 
783 static bool_t                   /* knows nothing about records!  Only about input buffers */
get_input_bytes(RECSTREAM * rstrm,char * addr,size_t len)784 get_input_bytes (RECSTREAM * rstrm,
785 	char *addr,
786 	size_t len)
787 {
788   size_t current;
789 
790   if (rstrm->nonblock)
791     {
792       if ((rstrm->in_boundry < rstrm->in_finger) ||               /* <-- should never happen, but avoids... */
793          (len > (size_t) (rstrm->in_boundry - rstrm->in_finger))) /* <-- signed/unsigned comparison */
794         return FALSE;
795       memcpy (addr, rstrm->in_finger, (size_t) len);
796       rstrm->in_finger += len;
797       return TRUE;
798     }
799 
800   while (len > 0)
801     {
802       current = (size_t) ((intptr_t) rstrm->in_boundry - (intptr_t) rstrm->in_finger);
803       if (current == 0)
804         {
805           if (!fill_input_buf (rstrm))
806             return FALSE;
807           continue;
808         }
809       current = (len < current) ? len : current;
810       memmove (addr, rstrm->in_finger, current);
811       rstrm->in_finger += current;
812       addr += current;
813       len -= current;
814     }
815   return TRUE;
816 }
817 
818 static bool_t                   /* next two bytes of the input stream are treated as a header */
set_input_fragment(RECSTREAM * rstrm)819 set_input_fragment (RECSTREAM * rstrm)
820 {
821   u_int32_t header;
822 
823   if (rstrm->nonblock)
824     return FALSE;
825   if (!get_input_bytes (rstrm, (char *) (void *) &header, sizeof (header)))
826     return FALSE;
827   header = ntohl (header);
828   rstrm->last_frag = ((header & LAST_FRAG) == 0) ? FALSE : TRUE;
829   /*
830    * Sanity check. Try not to accept wildly incorrect
831    * record sizes. Unfortunately, the only record size
832    * we can positively identify as being 'wildly incorrect'
833    * is zero. Ridiculously large record sizes may look wrong,
834    * but we don't have any way to be certain that they aren't
835    * what the client actually intended to send us.
836    */
837   if (header == 0)
838     return FALSE;
839   rstrm->fbtbc = header & (~LAST_FRAG);
840   return TRUE;
841 }
842 
843 static bool_t                   /* consumes input bytes; knows nothing about records! */
skip_input_bytes(RECSTREAM * rstrm,long cnt)844 skip_input_bytes (RECSTREAM * rstrm,
845 	long cnt)
846 {
847   size_t current;
848 
849   while (cnt > 0)
850     {
851       current = (size_t) ((intptr_t) rstrm->in_boundry - (intptr_t) rstrm->in_finger);
852       if (current == 0)
853         {
854           if (!fill_input_buf (rstrm))
855             return FALSE;
856           continue;
857         }
858       /* in this loop (prior to last line), cnt > 0 so size_t cast is safe*/
859       current = (size_t) (((size_t)cnt < current) ? (size_t)cnt : current);
860       rstrm->in_finger += current;
861       cnt -= current;
862     }
863   return TRUE;
864 }
865 
866 static u_int
fix_buf_size(u_int s)867 fix_buf_size (u_int s)
868 {
869 
870   if (s < 100)
871     s = 4000;
872   return (RNDUP (s));
873 }
874 
875 /*
876  * Reallocate the input buffer for a non-block stream.
877  */
878 static bool_t
realloc_stream(RECSTREAM * rstrm,int size)879 realloc_stream (RECSTREAM * rstrm,
880 	int size)
881 {
882   ptrdiff_t diff;
883   char *buf;
884   char *buf_algn;
885 
886   if (size > (int)rstrm->recvsize) /* recvsize guaranteed < INT_MAX */
887     {
888       buf = realloc (rstrm->in_buffer, (size_t) (size + BYTES_PER_XDR_UNIT));
889       if (buf == NULL)
890         return FALSE;
891       for (buf_algn = buf;
892            (intptr_t) buf_algn % BYTES_PER_XDR_UNIT != 0; buf_algn++)
893         ;
894       diff = buf_algn - rstrm->in_base;
895       rstrm->in_finger += diff;
896       rstrm->in_base = buf_algn;
897       rstrm->in_buffer = buf;
898       rstrm->in_boundry = buf_algn + size;
899       rstrm->recvsize = size;
900       rstrm->in_size = size;
901     }
902 
903   return TRUE;
904 }
905 
906