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