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