Mon Nov 20 16:19:15 1995 Roland McGrath <roland@churchy.gnu.ai.mit.edu>
[glibc.git] / sunrpc / xdr_rec.c
blob4d0d4ecfb3ee457241fee5a64ba35a8b3bc5d573
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.
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.
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.
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.
26 * Sun Microsystems, Inc.
27 * 2550 Garcia Avenue
28 * Mountain View, California 94043
30 #if !defined(lint) && defined(SCCSIDS)
31 static char sccsid[] = "@(#)xdr_rec.c 1.21 87/08/11 Copyr 1984 Sun Micro";
32 #endif
35 * xdr_rec.c, Implements TCP/IP based XDR streams with a "record marking"
36 * layer above tcp (for rpc's use).
38 * Copyright (C) 1984, Sun Microsystems, Inc.
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.
51 #include <stdio.h>
52 #include <rpc/types.h>
53 #include <rpc/xdr.h>
54 #include <netinet/in.h>
56 extern long lseek();
58 static u_int fix_buf_size();
60 static bool_t xdrrec_getlong();
61 static bool_t xdrrec_putlong();
62 static bool_t xdrrec_getbytes();
63 static bool_t xdrrec_putbytes();
64 static u_int xdrrec_getpos();
65 static bool_t xdrrec_setpos();
66 static long * xdrrec_inline();
67 static void xdrrec_destroy();
69 static struct xdr_ops xdrrec_ops = {
70 xdrrec_getlong,
71 xdrrec_putlong,
72 xdrrec_getbytes,
73 xdrrec_putbytes,
74 xdrrec_getpos,
75 xdrrec_setpos,
76 xdrrec_inline,
77 xdrrec_destroy
81 * A record is composed of one or more record fragments.
82 * A record fragment is a two-byte header followed by zero to
83 * 2**32-1 bytes. The header is treated as a long unsigned and is
84 * encode/decoded to the network via htonl/ntohl. The low order 31 bits
85 * are a byte count of the fragment. The highest order bit is a boolean:
86 * 1 => this fragment is the last fragment of the record,
87 * 0 => this fragment is followed by more fragment(s).
89 * The fragment/record machinery is not general; it is constructed to
90 * meet the needs of xdr and rpc based on tcp.
93 #define LAST_FRAG ((u_long)(1 << 31))
95 typedef struct rec_strm {
96 caddr_t tcp_handle;
97 caddr_t the_buffer;
99 * out-goung bits
101 int (*writeit)();
102 caddr_t out_base; /* output buffer (points to frag header) */
103 caddr_t out_finger; /* next output position */
104 caddr_t out_boundry; /* data cannot up to this address */
105 u_long *frag_header; /* beginning of curren fragment */
106 bool_t frag_sent; /* true if buffer sent in middle of record */
108 * in-coming bits
110 int (*readit)();
111 u_long in_size; /* fixed size of the input buffer */
112 caddr_t in_base;
113 caddr_t in_finger; /* location of next byte to be had */
114 caddr_t in_boundry; /* can read up to this location */
115 long fbtbc; /* fragment bytes to be consumed */
116 bool_t last_frag;
117 u_int sendsize;
118 u_int recvsize;
119 } RECSTREAM;
123 * Create an xdr handle for xdrrec
124 * xdrrec_create fills in xdrs. Sendsize and recvsize are
125 * send and recv buffer sizes (0 => use default).
126 * tcp_handle is an opaque handle that is passed as the first parameter to
127 * the procedures readit and writeit. Readit and writeit are read and
128 * write respectively. They are like the system
129 * calls expect that they take an opaque handle rather than an fd.
131 void
132 xdrrec_create(xdrs, sendsize, recvsize, tcp_handle, readit, writeit)
133 register XDR *xdrs;
134 register u_int sendsize;
135 register u_int recvsize;
136 caddr_t tcp_handle;
137 int (*readit)(); /* like read, but pass it a tcp_handle, not sock */
138 int (*writeit)(); /* like write, but pass it a tcp_handle, not sock */
140 register RECSTREAM *rstrm =
141 (RECSTREAM *)mem_alloc(sizeof(RECSTREAM));
143 if (rstrm == NULL) {
144 (void)fprintf(stderr, "xdrrec_create: out of memory\n");
146 * This is bad. Should rework xdrrec_create to
147 * return a handle, and in this case return NULL
149 return;
152 * adjust sizes and allocate buffer quad byte aligned
154 rstrm->sendsize = sendsize = fix_buf_size(sendsize);
155 rstrm->recvsize = recvsize = fix_buf_size(recvsize);
156 rstrm->the_buffer = mem_alloc(sendsize + recvsize + BYTES_PER_XDR_UNIT);
157 if (rstrm->the_buffer == NULL) {
158 (void)fprintf(stderr, "xdrrec_create: out of memory\n");
159 return;
161 for (rstrm->out_base = rstrm->the_buffer;
162 (u_int)rstrm->out_base % BYTES_PER_XDR_UNIT != 0;
163 rstrm->out_base++);
164 rstrm->in_base = rstrm->out_base + sendsize;
166 * now the rest ...
168 xdrs->x_ops = &xdrrec_ops;
169 xdrs->x_private = (caddr_t)rstrm;
170 rstrm->tcp_handle = tcp_handle;
171 rstrm->readit = readit;
172 rstrm->writeit = writeit;
173 rstrm->out_finger = rstrm->out_boundry = rstrm->out_base;
174 rstrm->frag_header = (u_long *)rstrm->out_base;
175 rstrm->out_finger += sizeof(u_long);
176 rstrm->out_boundry += sendsize;
177 rstrm->frag_sent = FALSE;
178 rstrm->in_size = recvsize;
179 rstrm->in_boundry = rstrm->in_base;
180 rstrm->in_finger = (rstrm->in_boundry += recvsize);
181 rstrm->fbtbc = 0;
182 rstrm->last_frag = TRUE;
187 * The reoutines defined below are the xdr ops which will go into the
188 * xdr handle filled in by xdrrec_create.
191 static bool_t
192 xdrrec_getlong(xdrs, lp)
193 XDR *xdrs;
194 long *lp;
196 register RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
197 register long *buflp = (long *)(rstrm->in_finger);
198 long mylong;
200 /* first try the inline, fast case */
201 if ((rstrm->fbtbc >= sizeof(long)) &&
202 (((int)rstrm->in_boundry - (int)buflp) >= sizeof(long))) {
203 *lp = (long)ntohl((u_long)(*buflp));
204 rstrm->fbtbc -= sizeof(long);
205 rstrm->in_finger += sizeof(long);
206 } else {
207 if (! xdrrec_getbytes(xdrs, (caddr_t)&mylong, sizeof(long)))
208 return (FALSE);
209 *lp = (long)ntohl((u_long)mylong);
211 return (TRUE);
214 static bool_t
215 xdrrec_putlong(xdrs, lp)
216 XDR *xdrs;
217 long *lp;
219 register RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
220 register long *dest_lp = ((long *)(rstrm->out_finger));
222 if ((rstrm->out_finger += sizeof(long)) > rstrm->out_boundry) {
224 * this case should almost never happen so the code is
225 * inefficient
227 rstrm->out_finger -= sizeof(long);
228 rstrm->frag_sent = TRUE;
229 if (! flush_out(rstrm, FALSE))
230 return (FALSE);
231 dest_lp = ((long *)(rstrm->out_finger));
232 rstrm->out_finger += sizeof(long);
234 *dest_lp = (long)htonl((u_long)(*lp));
235 return (TRUE);
238 static bool_t /* must manage buffers, fragments, and records */
239 xdrrec_getbytes(xdrs, addr, len)
240 XDR *xdrs;
241 register caddr_t addr;
242 register u_int len;
244 register RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
245 register int current;
247 while (len > 0) {
248 current = rstrm->fbtbc;
249 if (current == 0) {
250 if (rstrm->last_frag)
251 return (FALSE);
252 if (! set_input_fragment(rstrm))
253 return (FALSE);
254 continue;
256 current = (len < current) ? len : current;
257 if (! get_input_bytes(rstrm, addr, current))
258 return (FALSE);
259 addr += current;
260 rstrm->fbtbc -= current;
261 len -= current;
263 return (TRUE);
266 static bool_t
267 xdrrec_putbytes(xdrs, addr, len)
268 XDR *xdrs;
269 register caddr_t addr;
270 register u_int len;
272 register RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
273 register int current;
275 while (len > 0) {
276 current = (u_int)rstrm->out_boundry - (u_int)rstrm->out_finger;
277 current = (len < current) ? len : current;
278 bcopy(addr, rstrm->out_finger, current);
279 rstrm->out_finger += current;
280 addr += current;
281 len -= current;
282 if (rstrm->out_finger == rstrm->out_boundry) {
283 rstrm->frag_sent = TRUE;
284 if (! flush_out(rstrm, FALSE))
285 return (FALSE);
288 return (TRUE);
291 static u_int
292 xdrrec_getpos(xdrs)
293 register XDR *xdrs;
295 register RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
296 register long pos;
298 pos = lseek((int)rstrm->tcp_handle, (long) 0, 1);
299 if (pos != -1)
300 switch (xdrs->x_op) {
302 case XDR_ENCODE:
303 pos += rstrm->out_finger - rstrm->out_base;
304 break;
306 case XDR_DECODE:
307 pos -= rstrm->in_boundry - rstrm->in_finger;
308 break;
310 default:
311 pos = (u_int) -1;
312 break;
314 return ((u_int) pos);
317 static bool_t
318 xdrrec_setpos(xdrs, pos)
319 register XDR *xdrs;
320 u_int pos;
322 register RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
323 u_int currpos = xdrrec_getpos(xdrs);
324 int delta = currpos - pos;
325 caddr_t newpos;
327 if ((int)currpos != -1)
328 switch (xdrs->x_op) {
330 case XDR_ENCODE:
331 newpos = rstrm->out_finger - delta;
332 if ((newpos > (caddr_t)(rstrm->frag_header)) &&
333 (newpos < rstrm->out_boundry)) {
334 rstrm->out_finger = newpos;
335 return (TRUE);
337 break;
339 case XDR_DECODE:
340 newpos = rstrm->in_finger - delta;
341 if ((delta < (int)(rstrm->fbtbc)) &&
342 (newpos <= rstrm->in_boundry) &&
343 (newpos >= rstrm->in_base)) {
344 rstrm->in_finger = newpos;
345 rstrm->fbtbc -= delta;
346 return (TRUE);
348 break;
350 return (FALSE);
353 static long *
354 xdrrec_inline(xdrs, len)
355 register XDR *xdrs;
356 int len;
358 register RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
359 long * buf = NULL;
361 switch (xdrs->x_op) {
363 case XDR_ENCODE:
364 if ((rstrm->out_finger + len) <= rstrm->out_boundry) {
365 buf = (long *) rstrm->out_finger;
366 rstrm->out_finger += len;
368 break;
370 case XDR_DECODE:
371 if ((len <= rstrm->fbtbc) &&
372 ((rstrm->in_finger + len) <= rstrm->in_boundry)) {
373 buf = (long *) rstrm->in_finger;
374 rstrm->fbtbc -= len;
375 rstrm->in_finger += len;
377 break;
379 return (buf);
382 static void
383 xdrrec_destroy(xdrs)
384 register XDR *xdrs;
386 register RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
388 mem_free(rstrm->the_buffer,
389 rstrm->sendsize + rstrm->recvsize + BYTES_PER_XDR_UNIT);
390 mem_free((caddr_t)rstrm, sizeof(RECSTREAM));
395 * Exported routines to manage xdr records
399 * Before reading (deserializing from the stream, one should always call
400 * this procedure to guarantee proper record alignment.
402 bool_t
403 xdrrec_skiprecord(xdrs)
404 XDR *xdrs;
406 register RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
408 while (rstrm->fbtbc > 0 || (! rstrm->last_frag)) {
409 if (! skip_input_bytes(rstrm, rstrm->fbtbc))
410 return (FALSE);
411 rstrm->fbtbc = 0;
412 if ((! rstrm->last_frag) && (! set_input_fragment(rstrm)))
413 return (FALSE);
415 rstrm->last_frag = FALSE;
416 return (TRUE);
420 * Look ahead fuction.
421 * Returns TRUE iff there is no more input in the buffer
422 * after consuming the rest of the current record.
424 bool_t
425 xdrrec_eof(xdrs)
426 XDR *xdrs;
428 register RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
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);
437 if (rstrm->in_finger == rstrm->in_boundry)
438 return (TRUE);
439 return (FALSE);
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 let's the package support batched or
446 * pipelined procedure calls.) TRUE => immmediate flush to tcp connection.
448 bool_t
449 xdrrec_endofrecord(xdrs, sendnow)
450 XDR *xdrs;
451 bool_t sendnow;
453 register RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
454 register u_long len; /* fragment length */
456 if (sendnow || rstrm->frag_sent ||
457 ((u_long)rstrm->out_finger + sizeof(u_long) >=
458 (u_long)rstrm->out_boundry)) {
459 rstrm->frag_sent = FALSE;
460 return (flush_out(rstrm, TRUE));
462 len = (u_long)(rstrm->out_finger) - (u_long)(rstrm->frag_header) -
463 sizeof(u_long);
464 *(rstrm->frag_header) = htonl((u_long)len | LAST_FRAG);
465 rstrm->frag_header = (u_long *)rstrm->out_finger;
466 rstrm->out_finger += sizeof(u_long);
467 return (TRUE);
472 * Internal useful routines
474 static bool_t
475 flush_out(rstrm, eor)
476 register RECSTREAM *rstrm;
477 bool_t eor;
479 register u_long eormask = (eor == TRUE) ? LAST_FRAG : 0;
480 register u_long len = (u_long)(rstrm->out_finger) -
481 (u_long)(rstrm->frag_header) - sizeof(u_long);
483 *(rstrm->frag_header) = htonl(len | eormask);
484 len = (u_long)(rstrm->out_finger) - (u_long)(rstrm->out_base);
485 if ((*(rstrm->writeit))(rstrm->tcp_handle, rstrm->out_base, (int)len)
486 != (int)len)
487 return (FALSE);
488 rstrm->frag_header = (u_long *)rstrm->out_base;
489 rstrm->out_finger = (caddr_t)rstrm->out_base + sizeof(u_long);
490 return (TRUE);
493 static bool_t /* knows nothing about records! Only about input buffers */
494 fill_input_buf(rstrm)
495 register RECSTREAM *rstrm;
497 register caddr_t where;
498 u_int i;
499 register int len;
501 where = rstrm->in_base;
502 i = (u_int)rstrm->in_boundry % BYTES_PER_XDR_UNIT;
503 where += i;
504 len = rstrm->in_size - i;
505 if ((len = (*(rstrm->readit))(rstrm->tcp_handle, where, len)) == -1)
506 return (FALSE);
507 rstrm->in_finger = where;
508 where += len;
509 rstrm->in_boundry = where;
510 return (TRUE);
513 static bool_t /* knows nothing about records! Only about input buffers */
514 get_input_bytes(rstrm, addr, len)
515 register RECSTREAM *rstrm;
516 register caddr_t addr;
517 register int len;
519 register int current;
521 while (len > 0) {
522 current = (int)rstrm->in_boundry - (int)rstrm->in_finger;
523 if (current == 0) {
524 if (! fill_input_buf(rstrm))
525 return (FALSE);
526 continue;
528 current = (len < current) ? len : current;
529 bcopy(rstrm->in_finger, addr, current);
530 rstrm->in_finger += current;
531 addr += current;
532 len -= current;
534 return (TRUE);
537 static bool_t /* next two bytes of the input stream are treated as a header */
538 set_input_fragment(rstrm)
539 register RECSTREAM *rstrm;
541 u_long header;
543 if (! get_input_bytes(rstrm, (caddr_t)&header, sizeof(header)))
544 return (FALSE);
545 header = (long)ntohl(header);
546 rstrm->last_frag = ((header & LAST_FRAG) == 0) ? FALSE : TRUE;
547 rstrm->fbtbc = header & (~LAST_FRAG);
548 return (TRUE);
551 static bool_t /* consumes input bytes; knows nothing about records! */
552 skip_input_bytes(rstrm, cnt)
553 register RECSTREAM *rstrm;
554 long cnt;
556 register int current;
558 while (cnt > 0) {
559 current = (int)rstrm->in_boundry - (int)rstrm->in_finger;
560 if (current == 0) {
561 if (! fill_input_buf(rstrm))
562 return (FALSE);
563 continue;
565 current = (cnt < current) ? cnt : current;
566 rstrm->in_finger += current;
567 cnt -= current;
569 return (TRUE);
572 static u_int
573 fix_buf_size(s)
574 register u_int s;
577 if (s < 100)
578 s = 4000;
579 return (RNDUP(s));