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