]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libc/xdr/xdr_rec.c
Merge release 1.14 of bsnmp.
[FreeBSD/FreeBSD.git] / lib / libc / xdr / xdr_rec.c
1 /*      $NetBSD: xdr_rec.c,v 1.18 2000/07/06 03:10:35 christos Exp $    */
2
3 /*-
4  * SPDX-License-Identifier: BSD-3-Clause
5  *
6  * Copyright (c) 2010, Oracle America, Inc.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are
10  * met:
11  *
12  *     * Redistributions of source code must retain the above copyright
13  *       notice, this list of conditions and the following disclaimer.
14  *     * Redistributions in binary form must reproduce the above
15  *       copyright notice, this list of conditions and the following
16  *       disclaimer in the documentation and/or other materials
17  *       provided with the distribution.
18  *     * Neither the name of the "Oracle America, Inc." nor the names of its
19  *       contributors may be used to endorse or promote products derived
20  *       from this software without specific prior written permission.
21  *
22  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  *   FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26  *   COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
27  *   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  *   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
29  *   GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30  *   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
31  *   WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32  *   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34  */
35
36 #if defined(LIBC_SCCS) && !defined(lint) 
37 static char *sccsid2 = "@(#)xdr_rec.c 1.21 87/08/11 Copyr 1984 Sun Micro";
38 static char *sccsid = "@(#)xdr_rec.c    2.2 88/08/01 4.0 RPCSRC";
39 #endif
40 #include <sys/cdefs.h>
41 __FBSDID("$FreeBSD$");
42
43 /*
44  * xdr_rec.c, Implements TCP/IP based XDR streams with a "record marking"
45  * layer above tcp (for rpc's use).
46  *
47  * These routines interface XDRSTREAMS to a tcp/ip connection.
48  * There is a record marking layer between the xdr stream
49  * and the tcp transport level.  A record is composed on one or more
50  * record fragments.  A record fragment is a thirty-two bit header followed
51  * by n bytes of data, where n is contained in the header.  The header
52  * is represented as a htonl(u_long).  Thegh order bit encodes
53  * whether or not the fragment is the last fragment of the record
54  * (1 => fragment is last, 0 => more fragments to follow. 
55  * The other 31 bits encode the byte length of the fragment.
56  */
57
58 #include "namespace.h"
59 #include <sys/types.h>
60
61 #include <netinet/in.h>
62
63 #include <err.h>
64 #include <stdio.h>
65 #include <stdlib.h>
66 #include <string.h>
67
68 #include <rpc/types.h>
69 #include <rpc/xdr.h>
70 #include <rpc/auth.h>
71 #include <rpc/svc.h>
72 #include <rpc/clnt.h>
73 #include <sys/stddef.h>
74 #include "un-namespace.h"
75 #include "rpc_com.h"
76
77 static bool_t   xdrrec_getlong(XDR *, long *);
78 static bool_t   xdrrec_putlong(XDR *, const long *);
79 static bool_t   xdrrec_getbytes(XDR *, char *, u_int);
80
81 static bool_t   xdrrec_putbytes(XDR *, const char *, u_int);
82 static u_int    xdrrec_getpos(XDR *);
83 static bool_t   xdrrec_setpos(XDR *, u_int);
84 static int32_t *xdrrec_inline(XDR *, u_int);
85 static void     xdrrec_destroy(XDR *);
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 };
97
98 /*
99  * A record is composed of one or more record fragments.
100  * A record fragment is a four-byte header followed by zero to
101  * 2**32-1 bytes.  The header is treated as a long unsigned and is
102  * encode/decoded to the network via htonl/ntohl.  The low order 31 bits
103  * are a byte count of the fragment.  The highest order bit is a boolean:
104  * 1 => this fragment is the last fragment of the record,
105  * 0 => this fragment is followed by more fragment(s).
106  *
107  * The fragment/record machinery is not general;  it is constructed to
108  * meet the needs of xdr and rpc based on tcp.
109  */
110
111 #define LAST_FRAG ((u_int32_t)(1U << 31))
112
113 typedef struct rec_strm {
114         char *tcp_handle;
115         /*
116          * out-goung bits
117          */
118         int (*writeit)(void *, void *, int);
119         char *out_base; /* output buffer (points to frag header) */
120         char *out_finger;       /* next output position */
121         char *out_boundry;      /* data cannot up to this address */
122         u_int32_t *frag_header; /* beginning of curren fragment */
123         bool_t frag_sent;       /* true if buffer sent in middle of record */
124         /*
125          * in-coming bits
126          */
127         int (*readit)(void *, void *, int);
128         u_long in_size; /* fixed size of the input buffer */
129         char *in_base;
130         char *in_finger;        /* location of next byte to be had */
131         char *in_boundry;       /* can read up to this location */
132         long fbtbc;             /* fragment bytes to be consumed */
133         bool_t last_frag;
134         u_int sendsize;
135         u_int recvsize;
136
137         bool_t nonblock;
138         bool_t in_haveheader;
139         u_int32_t in_header;
140         char *in_hdrp;
141         int in_hdrlen;
142         int in_reclen;
143         int in_received;
144         int in_maxrec;
145 } RECSTREAM;
146
147 static u_int    fix_buf_size(u_int);
148 static bool_t   flush_out(RECSTREAM *, bool_t);
149 static bool_t   fill_input_buf(RECSTREAM *);
150 static bool_t   get_input_bytes(RECSTREAM *, char *, int);
151 static bool_t   set_input_fragment(RECSTREAM *);
152 static bool_t   skip_input_bytes(RECSTREAM *, long);
153 static bool_t   realloc_stream(RECSTREAM *, int);
154
155
156 /*
157  * Create an xdr handle for xdrrec
158  * xdrrec_create fills in xdrs.  Sendsize and recvsize are
159  * send and recv buffer sizes (0 => use default).
160  * tcp_handle is an opaque handle that is passed as the first parameter to
161  * the procedures readit and writeit.  Readit and writeit are read and
162  * write respectively.   They are like the system
163  * calls expect that they take an opaque handle rather than an fd.
164  */
165 void
166 xdrrec_create(XDR *xdrs, u_int sendsize, u_int recvsize, void *tcp_handle,
167     int (*readit)(void *, void *, int), int (*writeit)(void *, void *, int))
168 /*
169  *      XDR *xdrs;
170  *      u_int sendsize;
171  *      u_int recvsize;
172  *      void *tcp_handle;
173  *      // like read, but pass it a tcp_handle, not sock
174  *      int (*readit)(void *, void *, int);
175  *      // like write, but pass it a tcp_handle, not sock
176  *      int (*writeit)(void *, void *, int);
177  */
178 {
179         RECSTREAM *rstrm = mem_alloc(sizeof(RECSTREAM));
180
181         if (rstrm == NULL) {
182                 warnx("xdrrec_create: out of memory");
183                 /* 
184                  *  This is bad.  Should rework xdrrec_create to 
185                  *  return a handle, and in this case return NULL
186                  */
187                 return;
188         }
189         rstrm->sendsize = sendsize = fix_buf_size(sendsize);
190         rstrm->out_base = mem_alloc(rstrm->sendsize);
191         if (rstrm->out_base == NULL) {
192                 warnx("xdrrec_create: out of memory");
193                 mem_free(rstrm, sizeof(RECSTREAM));
194                 return;
195         }
196         rstrm->recvsize = recvsize = fix_buf_size(recvsize);
197         rstrm->in_base = mem_alloc(recvsize);
198         if (rstrm->in_base == NULL) {
199                 warnx("xdrrec_create: out of memory");
200                 mem_free(rstrm->out_base, sendsize);
201                 mem_free(rstrm, sizeof(RECSTREAM));
202                 return;
203         }
204         /*
205          * now the rest ...
206          */
207         xdrs->x_ops = &xdrrec_ops;
208         xdrs->x_private = rstrm;
209         rstrm->tcp_handle = tcp_handle;
210         rstrm->readit = readit;
211         rstrm->writeit = writeit;
212         rstrm->out_finger = rstrm->out_boundry = rstrm->out_base;
213         rstrm->frag_header = (u_int32_t *)(void *)rstrm->out_base;
214         rstrm->out_finger += sizeof(u_int32_t);
215         rstrm->out_boundry += sendsize;
216         rstrm->frag_sent = FALSE;
217         rstrm->in_size = recvsize;
218         rstrm->in_boundry = rstrm->in_base;
219         rstrm->in_finger = (rstrm->in_boundry += recvsize);
220         rstrm->fbtbc = 0;
221         rstrm->last_frag = TRUE;
222         rstrm->in_haveheader = FALSE;
223         rstrm->in_hdrlen = 0;
224         rstrm->in_hdrp = (char *)(void *)&rstrm->in_header;
225         rstrm->nonblock = FALSE;
226         rstrm->in_reclen = 0;
227         rstrm->in_received = 0;
228 }
229
230
231 /*
232  * The reoutines defined below are the xdr ops which will go into the
233  * xdr handle filled in by xdrrec_create.
234  */
235
236 static bool_t
237 xdrrec_getlong(XDR *xdrs, long *lp)
238 {
239         RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
240         int32_t *buflp = (int32_t *)(void *)(rstrm->in_finger);
241         int32_t mylong;
242
243         /* first try the inline, fast case */
244         if ((rstrm->fbtbc >= sizeof(int32_t)) &&
245                 (((long)rstrm->in_boundry - (long)buflp) >= sizeof(int32_t))) {
246                 *lp = (long)ntohl((u_int32_t)(*buflp));
247                 rstrm->fbtbc -= sizeof(int32_t);
248                 rstrm->in_finger += sizeof(int32_t);
249         } else {
250                 if (! xdrrec_getbytes(xdrs, (char *)(void *)&mylong,
251                     sizeof(int32_t)))
252                         return (FALSE);
253                 *lp = (long)ntohl((u_int32_t)mylong);
254         }
255         return (TRUE);
256 }
257
258 static bool_t
259 xdrrec_putlong(XDR *xdrs, const long *lp)
260 {
261         RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
262         int32_t *dest_lp = ((int32_t *)(void *)(rstrm->out_finger));
263
264         if ((rstrm->out_finger += sizeof(int32_t)) > rstrm->out_boundry) {
265                 /*
266                  * this case should almost never happen so the code is
267                  * inefficient
268                  */
269                 rstrm->out_finger -= sizeof(int32_t);
270                 rstrm->frag_sent = TRUE;
271                 if (! flush_out(rstrm, FALSE))
272                         return (FALSE);
273                 dest_lp = ((int32_t *)(void *)(rstrm->out_finger));
274                 rstrm->out_finger += sizeof(int32_t);
275         }
276         *dest_lp = (int32_t)htonl((u_int32_t)(*lp));
277         return (TRUE);
278 }
279
280 static bool_t  /* must manage buffers, fragments, and records */
281 xdrrec_getbytes(XDR *xdrs, char *addr, u_int len)
282 {
283         RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
284         int current;
285
286         while (len > 0) {
287                 current = (int)rstrm->fbtbc;
288                 if (current == 0) {
289                         if (rstrm->last_frag)
290                                 return (FALSE);
291                         if (! set_input_fragment(rstrm))
292                                 return (FALSE);
293                         continue;
294                 }
295                 current = (len < current) ? len : current;
296                 if (! get_input_bytes(rstrm, addr, current))
297                         return (FALSE);
298                 addr += current; 
299                 rstrm->fbtbc -= current;
300                 len -= current;
301         }
302         return (TRUE);
303 }
304
305 static bool_t
306 xdrrec_putbytes(XDR *xdrs, const char *addr, u_int len)
307 {
308         RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
309         size_t current;
310
311         while (len > 0) {
312                 current = (size_t)((u_long)rstrm->out_boundry -
313                     (u_long)rstrm->out_finger);
314                 current = (len < current) ? len : current;
315                 memmove(rstrm->out_finger, addr, current);
316                 rstrm->out_finger += current;
317                 addr += current;
318                 len -= current;
319                 if (rstrm->out_finger == rstrm->out_boundry) {
320                         rstrm->frag_sent = TRUE;
321                         if (! flush_out(rstrm, FALSE))
322                                 return (FALSE);
323                 }
324         }
325         return (TRUE);
326 }
327
328 static u_int
329 xdrrec_getpos(XDR *xdrs)
330 {
331         RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
332         off_t pos;
333
334         pos = lseek((int)(u_long)rstrm->tcp_handle, (off_t)0, 1);
335         if (pos == -1)
336                 pos = 0;
337         switch (xdrs->x_op) {
338
339         case XDR_ENCODE:
340                 pos += rstrm->out_finger - rstrm->out_base;
341                 break;
342
343         case XDR_DECODE:
344                 pos -= rstrm->in_boundry - rstrm->in_finger;
345                 break;
346
347         default:
348                 pos = (off_t) -1;
349                 break;
350         }
351         return ((u_int) pos);
352 }
353
354 static bool_t
355 xdrrec_setpos(XDR *xdrs, u_int pos)
356 {
357         RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
358         u_int currpos = xdrrec_getpos(xdrs);
359         int delta = currpos - pos;
360         char *newpos;
361
362         if ((int)currpos != -1)
363                 switch (xdrs->x_op) {
364
365                 case XDR_ENCODE:
366                         newpos = rstrm->out_finger - delta;
367                         if ((newpos > (char *)(void *)(rstrm->frag_header)) &&
368                                 (newpos < rstrm->out_boundry)) {
369                                 rstrm->out_finger = newpos;
370                                 return (TRUE);
371                         }
372                         break;
373
374                 case XDR_DECODE:
375                         newpos = rstrm->in_finger - delta;
376                         if ((delta < (int)(rstrm->fbtbc)) &&
377                                 (newpos <= rstrm->in_boundry) &&
378                                 (newpos >= rstrm->in_base)) {
379                                 rstrm->in_finger = newpos;
380                                 rstrm->fbtbc -= delta;
381                                 return (TRUE);
382                         }
383                         break;
384
385                 case XDR_FREE:
386                         break;
387                 }
388         return (FALSE);
389 }
390
391 static int32_t *
392 xdrrec_inline(XDR *xdrs, u_int len)
393 {
394         RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
395         int32_t *buf = NULL;
396
397         switch (xdrs->x_op) {
398
399         case XDR_ENCODE:
400                 if ((rstrm->out_finger + len) <= rstrm->out_boundry) {
401                         buf = (int32_t *)(void *)rstrm->out_finger;
402                         rstrm->out_finger += len;
403                 }
404                 break;
405
406         case XDR_DECODE:
407                 if ((len <= rstrm->fbtbc) &&
408                         ((rstrm->in_finger + len) <= rstrm->in_boundry)) {
409                         buf = (int32_t *)(void *)rstrm->in_finger;
410                         rstrm->fbtbc -= len;
411                         rstrm->in_finger += len;
412                 }
413                 break;
414
415         case XDR_FREE:
416                 break;
417         }
418         return (buf);
419 }
420
421 static void
422 xdrrec_destroy(XDR *xdrs)
423 {
424         RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
425
426         mem_free(rstrm->out_base, rstrm->sendsize);
427         mem_free(rstrm->in_base, rstrm->recvsize);
428         mem_free(rstrm, sizeof(RECSTREAM));
429 }
430
431
432 /*
433  * Exported routines to manage xdr records
434  */
435
436 /*
437  * Before reading (deserializing from the stream, one should always call
438  * this procedure to guarantee proper record alignment.
439  */
440 bool_t
441 xdrrec_skiprecord(XDR *xdrs)
442 {
443         RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
444         enum xprt_stat xstat;
445
446         if (rstrm->nonblock) {
447                 if (__xdrrec_getrec(xdrs, &xstat, FALSE)) {
448                         rstrm->fbtbc = 0;
449                         return TRUE;
450                 }
451                 if (rstrm->in_finger == rstrm->in_boundry &&
452                     xstat == XPRT_MOREREQS) {
453                         rstrm->fbtbc = 0;
454                         return TRUE;
455                 }
456                 return FALSE;
457         }
458
459         while (rstrm->fbtbc > 0 || (! rstrm->last_frag)) {
460                 if (! skip_input_bytes(rstrm, rstrm->fbtbc))
461                         return (FALSE);
462                 rstrm->fbtbc = 0;
463                 if ((! rstrm->last_frag) && (! set_input_fragment(rstrm)))
464                         return (FALSE);
465         }
466         rstrm->last_frag = FALSE;
467         return (TRUE);
468 }
469
470 /*
471  * Look ahead function.
472  * Returns TRUE iff there is no more input in the buffer
473  * after consuming the rest of the current record.
474  */
475 bool_t
476 xdrrec_eof(XDR *xdrs)
477 {
478         RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
479
480         while (rstrm->fbtbc > 0 || (! rstrm->last_frag)) {
481                 if (! skip_input_bytes(rstrm, rstrm->fbtbc))
482                         return (TRUE);
483                 rstrm->fbtbc = 0;
484                 if ((! rstrm->last_frag) && (! set_input_fragment(rstrm)))
485                         return (TRUE);
486         }
487         if (rstrm->in_finger == rstrm->in_boundry)
488                 return (TRUE);
489         return (FALSE);
490 }
491
492 /*
493  * The client must tell the package when an end-of-record has occurred.
494  * The second paraemters tells whether the record should be flushed to the
495  * (output) tcp stream.  (This let's the package support batched or
496  * pipelined procedure calls.)  TRUE => immmediate flush to tcp connection.
497  */
498 bool_t
499 xdrrec_endofrecord(XDR *xdrs, bool_t sendnow)
500 {
501         RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
502         u_long len;  /* fragment length */
503
504         if (sendnow || rstrm->frag_sent ||
505                 ((u_long)rstrm->out_finger + sizeof(u_int32_t) >=
506                 (u_long)rstrm->out_boundry)) {
507                 rstrm->frag_sent = FALSE;
508                 return (flush_out(rstrm, TRUE));
509         }
510         len = (u_long)(rstrm->out_finger) - (u_long)(rstrm->frag_header) -
511            sizeof(u_int32_t);
512         *(rstrm->frag_header) = htonl((u_int32_t)len | LAST_FRAG);
513         rstrm->frag_header = (u_int32_t *)(void *)rstrm->out_finger;
514         rstrm->out_finger += sizeof(u_int32_t);
515         return (TRUE);
516 }
517
518 /*
519  * Fill the stream buffer with a record for a non-blocking connection.
520  * Return true if a record is available in the buffer, false if not.
521  */
522 bool_t
523 __xdrrec_getrec(XDR *xdrs, enum xprt_stat *statp, bool_t expectdata)
524 {
525         RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
526         ssize_t n;
527         int fraglen;
528
529         if (!rstrm->in_haveheader) {
530                 n = rstrm->readit(rstrm->tcp_handle, rstrm->in_hdrp,
531                     (int)sizeof (rstrm->in_header) - rstrm->in_hdrlen);
532                 if (n == 0) {
533                         *statp = expectdata ? XPRT_DIED : XPRT_IDLE;
534                         return FALSE;
535                 }
536                 if (n < 0) {
537                         *statp = XPRT_DIED;
538                         return FALSE;
539                 }
540                 rstrm->in_hdrp += n;
541                 rstrm->in_hdrlen += n;
542                 if (rstrm->in_hdrlen < sizeof (rstrm->in_header)) {
543                         *statp = XPRT_MOREREQS;
544                         return FALSE;
545                 }
546                 rstrm->in_header = ntohl(rstrm->in_header);
547                 fraglen = (int)(rstrm->in_header & ~LAST_FRAG);
548                 if (fraglen == 0 || fraglen > rstrm->in_maxrec ||
549                     (rstrm->in_reclen + fraglen) > rstrm->in_maxrec) {
550                         *statp = XPRT_DIED;
551                         return FALSE;
552                 }
553                 rstrm->in_reclen += fraglen;
554                 if (rstrm->in_reclen > rstrm->recvsize)
555                         realloc_stream(rstrm, rstrm->in_reclen);
556                 if (rstrm->in_header & LAST_FRAG) {
557                         rstrm->in_header &= ~LAST_FRAG;
558                         rstrm->last_frag = TRUE;
559                 }
560                 /*
561                  * We can only reasonably expect to read once from a
562                  * non-blocking stream. Reading the fragment header
563                  * may have drained the stream.
564                  */
565                 expectdata = FALSE;
566         }
567
568         n =  rstrm->readit(rstrm->tcp_handle,
569             rstrm->in_base + rstrm->in_received,
570             (rstrm->in_reclen - rstrm->in_received));
571
572         if (n < 0) {
573                 *statp = XPRT_DIED;
574                 return FALSE;
575         }
576
577         if (n == 0) {
578                 *statp = expectdata ? XPRT_DIED : XPRT_IDLE;
579                 return FALSE;
580         }
581
582         rstrm->in_received += n;
583
584         if (rstrm->in_received == rstrm->in_reclen) {
585                 rstrm->in_haveheader = FALSE;
586                 rstrm->in_hdrp = (char *)(void *)&rstrm->in_header;
587                 rstrm->in_hdrlen = 0;
588                 if (rstrm->last_frag) {
589                         rstrm->fbtbc = rstrm->in_reclen;
590                         rstrm->in_boundry = rstrm->in_base + rstrm->in_reclen;
591                         rstrm->in_finger = rstrm->in_base;
592                         rstrm->in_reclen = rstrm->in_received = 0;
593                         *statp = XPRT_MOREREQS;
594                         return TRUE;
595                 }
596         }
597
598         *statp = XPRT_MOREREQS;
599         return FALSE;
600 }
601
602 bool_t
603 __xdrrec_setnonblock(XDR *xdrs, int maxrec)
604 {
605         RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
606
607         rstrm->nonblock = TRUE;
608         if (maxrec == 0)
609                 maxrec = rstrm->recvsize;
610         rstrm->in_maxrec = maxrec;
611         return TRUE;
612 }
613
614 /*
615  * Internal useful routines
616  */
617 static bool_t
618 flush_out(RECSTREAM *rstrm, bool_t eor)
619 {
620         u_int32_t eormask = (eor == TRUE) ? LAST_FRAG : 0;
621         u_int32_t len = (u_int32_t)((u_long)(rstrm->out_finger) - 
622                 (u_long)(rstrm->frag_header) - sizeof(u_int32_t));
623
624         *(rstrm->frag_header) = htonl(len | eormask);
625         len = (u_int32_t)((u_long)(rstrm->out_finger) - 
626             (u_long)(rstrm->out_base));
627         if ((*(rstrm->writeit))(rstrm->tcp_handle, rstrm->out_base, (int)len)
628                 != (int)len)
629                 return (FALSE);
630         rstrm->frag_header = (u_int32_t *)(void *)rstrm->out_base;
631         rstrm->out_finger = (char *)rstrm->out_base + sizeof(u_int32_t);
632         return (TRUE);
633 }
634
635 static bool_t  /* knows nothing about records!  Only about input buffers */
636 fill_input_buf(RECSTREAM *rstrm)
637 {
638         char *where;
639         u_int32_t i;
640         int len;
641
642         if (rstrm->nonblock)
643                 return FALSE;
644
645         where = rstrm->in_base;
646         i = (u_int32_t)((u_long)rstrm->in_boundry % BYTES_PER_XDR_UNIT);
647         where += i;
648         len = (u_int32_t)(rstrm->in_size - i);
649         if ((len = (*(rstrm->readit))(rstrm->tcp_handle, where, len)) == -1)
650                 return (FALSE);
651         rstrm->in_finger = where;
652         where += len;
653         rstrm->in_boundry = where;
654         return (TRUE);
655 }
656
657 static bool_t  /* knows nothing about records!  Only about input buffers */
658 get_input_bytes(RECSTREAM *rstrm, char *addr, int len)
659 {
660         size_t current;
661
662         if (rstrm->nonblock) {
663                 if (len > (int)(rstrm->in_boundry - rstrm->in_finger))
664                         return FALSE;
665                 memcpy(addr, rstrm->in_finger, (size_t)len);
666                 rstrm->in_finger += len;
667                 return TRUE;
668         }
669
670         while (len > 0) {
671                 current = (size_t)((long)rstrm->in_boundry -
672                     (long)rstrm->in_finger);
673                 if (current == 0) {
674                         if (! fill_input_buf(rstrm))
675                                 return (FALSE);
676                         continue;
677                 }
678                 current = (len < current) ? len : current;
679                 memmove(addr, rstrm->in_finger, current);
680                 rstrm->in_finger += current;
681                 addr += current;
682                 len -= current;
683         }
684         return (TRUE);
685 }
686
687 static bool_t  /* next two bytes of the input stream are treated as a header */
688 set_input_fragment(RECSTREAM *rstrm)
689 {
690         u_int32_t header;
691
692         if (rstrm->nonblock)
693                 return FALSE;
694         if (! get_input_bytes(rstrm, (char *)(void *)&header, sizeof(header)))
695                 return (FALSE);
696         header = ntohl(header);
697         rstrm->last_frag = ((header & LAST_FRAG) == 0) ? FALSE : TRUE;
698         /*
699          * Sanity check. Try not to accept wildly incorrect
700          * record sizes. Unfortunately, the only record size
701          * we can positively identify as being 'wildly incorrect'
702          * is zero. Ridiculously large record sizes may look wrong,
703          * but we don't have any way to be certain that they aren't
704          * what the client actually intended to send us.
705          */
706         if (header == 0)
707                 return(FALSE);
708         rstrm->fbtbc = header & (~LAST_FRAG);
709         return (TRUE);
710 }
711
712 static bool_t  /* consumes input bytes; knows nothing about records! */
713 skip_input_bytes(RECSTREAM *rstrm, long cnt)
714 {
715         u_int32_t current;
716
717         while (cnt > 0) {
718                 current = (size_t)((long)rstrm->in_boundry - 
719                     (long)rstrm->in_finger);
720                 if (current == 0) {
721                         if (! fill_input_buf(rstrm))
722                                 return (FALSE);
723                         continue;
724                 }
725                 current = (u_int32_t)((cnt < current) ? cnt : current);
726                 rstrm->in_finger += current;
727                 cnt -= current;
728         }
729         return (TRUE);
730 }
731
732 static u_int
733 fix_buf_size(u_int s)
734 {
735
736         if (s < 100)
737                 s = 4000;
738         return (RNDUP(s));
739 }
740
741 /*
742  * Reallocate the input buffer for a non-block stream.
743  */
744 static bool_t
745 realloc_stream(RECSTREAM *rstrm, int size)
746 {
747         ptrdiff_t diff;
748         char *buf;
749
750         if (size > rstrm->recvsize) {
751                 buf = realloc(rstrm->in_base, (size_t)size);
752                 if (buf == NULL)
753                         return FALSE;
754                 diff = buf - rstrm->in_base;
755                 rstrm->in_finger += diff;
756                 rstrm->in_base = buf;
757                 rstrm->in_boundry = buf + size;
758                 rstrm->recvsize = size;
759                 rstrm->in_size = size;
760         }
761
762         return TRUE;
763 }