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