Libinfo-78.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.2 1999/10/14 21:56:55 wsanchez 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 <rpc/types.h>
78 #include <rpc/xdr.h>
79 #include <netinet/in.h>
80
81 static u_int fix_buf_size();
82 static bool_t flush_out();
83 static bool_t get_input_bytes();
84 static bool_t set_input_fragment();
85 static bool_t skip_input_bytes();
86
87 static bool_t xdrrec_getlong();
88 static bool_t xdrrec_putlong();
89 static bool_t xdrrec_getbytes();
90 static bool_t xdrrec_putbytes();
91 static u_int xdrrec_getpos();
92 static bool_t xdrrec_setpos();
93 static long * xdrrec_inline();
94 static void xdrrec_destroy();
95
96 static struct xdr_ops xdrrec_ops = {
97 xdrrec_getlong,
98 xdrrec_putlong,
99 xdrrec_getbytes,
100 xdrrec_putbytes,
101 xdrrec_getpos,
102 xdrrec_setpos,
103 xdrrec_inline,
104 xdrrec_destroy
105 };
106
107 /*
108 * A record is composed of one or more record fragments.
109 * A record fragment is a two-byte header followed by zero to
110 * 2**32-1 bytes. The header is treated as a long unsigned and is
111 * encode/decoded to the network via htonl/ntohl. The low order 31 bits
112 * are a byte count of the fragment. The highest order bit is a boolean:
113 * 1 => this fragment is the last fragment of the record,
114 * 0 => this fragment is followed by more fragment(s).
115 *
116 * The fragment/record machinery is not general; it is constructed to
117 * meet the needs of xdr and rpc based on tcp.
118 */
119
120 #define LAST_FRAG ((u_long)(1 << 31))
121
122 typedef struct rec_strm {
123 caddr_t tcp_handle;
124 caddr_t the_buffer;
125 /*
126 * out-goung bits
127 */
128 int (*writeit)();
129 caddr_t out_base; /* output buffer (points to frag header) */
130 caddr_t out_finger; /* next output position */
131 caddr_t out_boundry; /* data cannot up to this address */
132 u_long *frag_header; /* beginning of curren fragment */
133 bool_t frag_sent; /* true if buffer sent in middle of record */
134 /*
135 * in-coming bits
136 */
137 int (*readit)();
138 u_long in_size; /* fixed size of the input buffer */
139 caddr_t in_base;
140 caddr_t in_finger; /* location of next byte to be had */
141 caddr_t in_boundry; /* can read up to this location */
142 long fbtbc; /* fragment bytes to be consumed */
143 bool_t last_frag;
144 u_int sendsize;
145 u_int recvsize;
146 } RECSTREAM;
147
148
149 /*
150 * Create an xdr handle for xdrrec
151 * xdrrec_create fills in xdrs. Sendsize and recvsize are
152 * send and recv buffer sizes (0 => use default).
153 * tcp_handle is an opaque handle that is passed as the first parameter to
154 * the procedures readit and writeit. Readit and writeit are read and
155 * write respectively. They are like the system
156 * calls expect that they take an opaque handle rather than an fd.
157 */
158 void
159 xdrrec_create(xdrs, sendsize, recvsize, tcp_handle, readit, writeit)
160 register XDR *xdrs;
161 register u_int sendsize;
162 register u_int recvsize;
163 caddr_t tcp_handle;
164 int (*readit)(); /* like read, but pass it a tcp_handle, not sock */
165 int (*writeit)(); /* like write, but pass it a tcp_handle, not sock */
166 {
167 register RECSTREAM *rstrm =
168 (RECSTREAM *)mem_alloc(sizeof(RECSTREAM));
169
170 if (rstrm == NULL) {
171 (void)fprintf(stderr, "xdrrec_create: out of memory\n");
172 /*
173 * This is bad. Should rework xdrrec_create to
174 * return a handle, and in this case return NULL
175 */
176 return;
177 }
178 /*
179 * adjust sizes and allocate buffer quad byte aligned
180 */
181 rstrm->sendsize = sendsize = fix_buf_size(sendsize);
182 rstrm->recvsize = recvsize = fix_buf_size(recvsize);
183 rstrm->the_buffer = mem_alloc(sendsize + recvsize + BYTES_PER_XDR_UNIT);
184 if (rstrm->the_buffer == NULL) {
185 (void)fprintf(stderr, "xdrrec_create: out of memory\n");
186 return;
187 }
188 for (rstrm->out_base = rstrm->the_buffer;
189 (u_int)rstrm->out_base % BYTES_PER_XDR_UNIT != 0;
190 rstrm->out_base++);
191 rstrm->in_base = rstrm->out_base + sendsize;
192 /*
193 * now the rest ...
194 */
195 xdrs->x_ops = &xdrrec_ops;
196 xdrs->x_private = (caddr_t)rstrm;
197 rstrm->tcp_handle = tcp_handle;
198 rstrm->readit = readit;
199 rstrm->writeit = writeit;
200 rstrm->out_finger = rstrm->out_boundry = rstrm->out_base;
201 rstrm->frag_header = (u_long *)rstrm->out_base;
202 rstrm->out_finger += sizeof(u_long);
203 rstrm->out_boundry += sendsize;
204 rstrm->frag_sent = FALSE;
205 rstrm->in_size = recvsize;
206 rstrm->in_boundry = rstrm->in_base;
207 rstrm->in_finger = (rstrm->in_boundry += recvsize);
208 rstrm->fbtbc = 0;
209 rstrm->last_frag = TRUE;
210 }
211
212
213 /*
214 * The reoutines defined below are the xdr ops which will go into the
215 * xdr handle filled in by xdrrec_create.
216 */
217
218 static bool_t
219 xdrrec_getlong(xdrs, lp)
220 XDR *xdrs;
221 long *lp;
222 {
223 register RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
224 register long *buflp = (long *)(rstrm->in_finger);
225 long mylong;
226
227 /* first try the inline, fast case */
228 if ((rstrm->fbtbc >= sizeof(long)) &&
229 (((int)rstrm->in_boundry - (int)buflp) >= sizeof(long))) {
230 *lp = (long)ntohl((u_long)(*buflp));
231 rstrm->fbtbc -= sizeof(long);
232 rstrm->in_finger += sizeof(long);
233 } else {
234 if (! xdrrec_getbytes(xdrs, (caddr_t)&mylong, sizeof(long)))
235 return (FALSE);
236 *lp = (long)ntohl((u_long)mylong);
237 }
238 return (TRUE);
239 }
240
241 static bool_t
242 xdrrec_putlong(xdrs, lp)
243 XDR *xdrs;
244 long *lp;
245 {
246 register RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
247 register long *dest_lp = ((long *)(rstrm->out_finger));
248
249 if ((rstrm->out_finger += sizeof(long)) > rstrm->out_boundry) {
250 /*
251 * this case should almost never happen so the code is
252 * inefficient
253 */
254 rstrm->out_finger -= sizeof(long);
255 rstrm->frag_sent = TRUE;
256 if (! flush_out(rstrm, FALSE))
257 return (FALSE);
258 dest_lp = ((long *)(rstrm->out_finger));
259 rstrm->out_finger += sizeof(long);
260 }
261 *dest_lp = (long)htonl((u_long)(*lp));
262 return (TRUE);
263 }
264
265 static bool_t /* must manage buffers, fragments, and records */
266 xdrrec_getbytes(xdrs, addr, len)
267 XDR *xdrs;
268 register caddr_t addr;
269 register u_int len;
270 {
271 register RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
272 register int current;
273
274 while (len > 0) {
275 current = rstrm->fbtbc;
276 if (current == 0) {
277 if (rstrm->last_frag)
278 return (FALSE);
279 if (! set_input_fragment(rstrm))
280 return (FALSE);
281 continue;
282 }
283 current = (len < current) ? len : current;
284 if (! get_input_bytes(rstrm, addr, current))
285 return (FALSE);
286 addr += current;
287 rstrm->fbtbc -= current;
288 len -= current;
289 }
290 return (TRUE);
291 }
292
293 static bool_t
294 xdrrec_putbytes(xdrs, addr, len)
295 XDR *xdrs;
296 register caddr_t addr;
297 register u_int len;
298 {
299 register RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
300 register int current;
301
302 while (len > 0) {
303 current = (u_int)rstrm->out_boundry - (u_int)rstrm->out_finger;
304 current = (len < current) ? len : current;
305 bcopy(addr, rstrm->out_finger, current);
306 rstrm->out_finger += current;
307 addr += current;
308 len -= current;
309 if (rstrm->out_finger == rstrm->out_boundry) {
310 rstrm->frag_sent = TRUE;
311 if (! flush_out(rstrm, FALSE))
312 return (FALSE);
313 }
314 }
315 return (TRUE);
316 }
317
318 static u_int
319 xdrrec_getpos(xdrs)
320 register XDR *xdrs;
321 {
322 register RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
323 register long pos;
324
325 pos = lseek((int)rstrm->tcp_handle, 0, 1);
326 if (pos != -1)
327 switch (xdrs->x_op) {
328
329 case XDR_ENCODE:
330 pos += rstrm->out_finger - rstrm->out_base;
331 break;
332
333 case XDR_DECODE:
334 pos -= rstrm->in_boundry - rstrm->in_finger;
335 break;
336
337 default:
338 pos = (u_int) -1;
339 break;
340 }
341 return ((u_int) pos);
342 }
343
344 static bool_t
345 xdrrec_setpos(xdrs, pos)
346 register XDR *xdrs;
347 u_int pos;
348 {
349 register RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
350 u_int currpos = xdrrec_getpos(xdrs);
351 int delta = currpos - pos;
352 caddr_t newpos;
353
354 if ((int)currpos != -1)
355 switch (xdrs->x_op) {
356
357 case XDR_ENCODE:
358 newpos = rstrm->out_finger - delta;
359 if ((newpos > (caddr_t)(rstrm->frag_header)) &&
360 (newpos < rstrm->out_boundry)) {
361 rstrm->out_finger = newpos;
362 return (TRUE);
363 }
364 break;
365
366 case XDR_DECODE:
367 newpos = rstrm->in_finger - delta;
368 if ((delta < (int)(rstrm->fbtbc)) &&
369 (newpos <= rstrm->in_boundry) &&
370 (newpos >= rstrm->in_base)) {
371 rstrm->in_finger = newpos;
372 rstrm->fbtbc -= delta;
373 return (TRUE);
374 }
375 break;
376 }
377 return (FALSE);
378 }
379
380 static long *
381 xdrrec_inline(xdrs, len)
382 register XDR *xdrs;
383 int len;
384 {
385 register RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
386 long * buf = NULL;
387
388 switch (xdrs->x_op) {
389
390 case XDR_ENCODE:
391 if ((rstrm->out_finger + len) <= rstrm->out_boundry) {
392 buf = (long *) rstrm->out_finger;
393 rstrm->out_finger += len;
394 }
395 break;
396
397 case XDR_DECODE:
398 if ((len <= rstrm->fbtbc) &&
399 ((rstrm->in_finger + len) <= rstrm->in_boundry)) {
400 buf = (long *) rstrm->in_finger;
401 rstrm->fbtbc -= len;
402 rstrm->in_finger += len;
403 }
404 break;
405 }
406 return (buf);
407 }
408
409 static void
410 xdrrec_destroy(xdrs)
411 register XDR *xdrs;
412 {
413 register RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
414
415 mem_free(rstrm->the_buffer,
416 rstrm->sendsize + rstrm->recvsize + BYTES_PER_XDR_UNIT);
417 mem_free((caddr_t)rstrm, sizeof(RECSTREAM));
418 }
419
420
421 /*
422 * Exported routines to manage xdr records
423 */
424
425 /*
426 * Before reading (deserializing from the stream, one should always call
427 * this procedure to guarantee proper record alignment.
428 */
429 bool_t
430 xdrrec_skiprecord(xdrs)
431 XDR *xdrs;
432 {
433 register RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
434
435 while (rstrm->fbtbc > 0 || (! rstrm->last_frag)) {
436 if (! skip_input_bytes(rstrm, rstrm->fbtbc))
437 return (FALSE);
438 rstrm->fbtbc = 0;
439 if ((! rstrm->last_frag) && (! set_input_fragment(rstrm)))
440 return (FALSE);
441 }
442 rstrm->last_frag = FALSE;
443 return (TRUE);
444 }
445
446 /*
447 * Look ahead fuction.
448 * Returns TRUE iff there is no more input in the buffer
449 * after consuming the rest of the current record.
450 */
451 bool_t
452 xdrrec_eof(xdrs)
453 XDR *xdrs;
454 {
455 register RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
456
457 while (rstrm->fbtbc > 0 || (! rstrm->last_frag)) {
458 if (! skip_input_bytes(rstrm, rstrm->fbtbc))
459 return (TRUE);
460 rstrm->fbtbc = 0;
461 if ((! rstrm->last_frag) && (! set_input_fragment(rstrm)))
462 return (TRUE);
463 }
464 if (rstrm->in_finger == rstrm->in_boundry)
465 return (TRUE);
466 return (FALSE);
467 }
468
469 /*
470 * The client must tell the package when an end-of-record has occurred.
471 * The second paraemters tells whether the record should be flushed to the
472 * (output) tcp stream. (This let's the package support batched or
473 * pipelined procedure calls.) TRUE => immmediate flush to tcp connection.
474 */
475 bool_t
476 xdrrec_endofrecord(xdrs, sendnow)
477 XDR *xdrs;
478 bool_t sendnow;
479 {
480 register RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
481 register u_long len; /* fragment length */
482
483 if (sendnow || rstrm->frag_sent ||
484 ((u_long)rstrm->out_finger + sizeof(u_long) >=
485 (u_long)rstrm->out_boundry)) {
486 rstrm->frag_sent = FALSE;
487 return (flush_out(rstrm, TRUE));
488 }
489 len = (u_long)(rstrm->out_finger) - (u_long)(rstrm->frag_header) -
490 sizeof(u_long);
491 *(rstrm->frag_header) = htonl((u_long)len | LAST_FRAG);
492 rstrm->frag_header = (u_long *)rstrm->out_finger;
493 rstrm->out_finger += sizeof(u_long);
494 return (TRUE);
495 }
496
497
498 /*
499 * Internal useful routines
500 */
501 static bool_t
502 flush_out(rstrm, eor)
503 register RECSTREAM *rstrm;
504 bool_t eor;
505 {
506 register u_long eormask = (eor == TRUE) ? LAST_FRAG : 0;
507 register u_long len = (u_long)(rstrm->out_finger) -
508 (u_long)(rstrm->frag_header) - sizeof(u_long);
509
510 *(rstrm->frag_header) = htonl(len | eormask);
511 len = (u_long)(rstrm->out_finger) - (u_long)(rstrm->out_base);
512 if ((*(rstrm->writeit))(rstrm->tcp_handle, rstrm->out_base, (int)len)
513 != (int)len)
514 return (FALSE);
515 rstrm->frag_header = (u_long *)rstrm->out_base;
516 rstrm->out_finger = (caddr_t)rstrm->out_base + sizeof(u_long);
517 return (TRUE);
518 }
519
520 static bool_t /* knows nothing about records! Only about input buffers */
521 fill_input_buf(rstrm)
522 register RECSTREAM *rstrm;
523 {
524 register caddr_t where;
525 u_int i;
526 register int len;
527
528 where = rstrm->in_base;
529 i = (u_int)rstrm->in_boundry % BYTES_PER_XDR_UNIT;
530 where += i;
531 len = rstrm->in_size - i;
532 if ((len = (*(rstrm->readit))(rstrm->tcp_handle, where, len)) == -1)
533 return (FALSE);
534 rstrm->in_finger = where;
535 where += len;
536 rstrm->in_boundry = where;
537 return (TRUE);
538 }
539
540 static bool_t /* knows nothing about records! Only about input buffers */
541 get_input_bytes(rstrm, addr, len)
542 register RECSTREAM *rstrm;
543 register caddr_t addr;
544 register int len;
545 {
546 register int current;
547
548 while (len > 0) {
549 current = (int)rstrm->in_boundry - (int)rstrm->in_finger;
550 if (current == 0) {
551 if (! fill_input_buf(rstrm))
552 return (FALSE);
553 continue;
554 }
555 current = (len < current) ? len : current;
556 bcopy(rstrm->in_finger, addr, current);
557 rstrm->in_finger += current;
558 addr += current;
559 len -= current;
560 }
561 return (TRUE);
562 }
563
564 static bool_t /* next two bytes of the input stream are treated as a header */
565 set_input_fragment(rstrm)
566 register RECSTREAM *rstrm;
567 {
568 u_long header;
569
570 if (! get_input_bytes(rstrm, (caddr_t)&header, sizeof(header)))
571 return (FALSE);
572 header = (long)ntohl(header);
573 rstrm->last_frag = ((header & LAST_FRAG) == 0) ? FALSE : TRUE;
574 rstrm->fbtbc = header & (~LAST_FRAG);
575 return (TRUE);
576 }
577
578 static bool_t /* consumes input bytes; knows nothing about records! */
579 skip_input_bytes(rstrm, cnt)
580 register RECSTREAM *rstrm;
581 long cnt;
582 {
583 register int current;
584
585 while (cnt > 0) {
586 current = (int)rstrm->in_boundry - (int)rstrm->in_finger;
587 if (current == 0) {
588 if (! fill_input_buf(rstrm))
589 return (FALSE);
590 continue;
591 }
592 current = (cnt < current) ? cnt : current;
593 rstrm->in_finger += current;
594 cnt -= current;
595 }
596 return (TRUE);
597 }
598
599 static u_int
600 fix_buf_size(s)
601 register u_int s;
602 {
603
604 if (s < 100)
605 s = 4000;
606 return (RNDUP(s));
607 }