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