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