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