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