Libinfo-173.1.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.4 2003/06/23 17:24:59 majka 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 default: break;
379 }
380 return (FALSE);
381 }
382
383 static long *
384 xdrrec_inline(xdrs, len)
385 register XDR *xdrs;
386 int len;
387 {
388 register RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
389 long * buf = NULL;
390
391 switch (xdrs->x_op) {
392
393 case XDR_ENCODE:
394 if ((rstrm->out_finger + len) <= rstrm->out_boundry) {
395 buf = (long *) rstrm->out_finger;
396 rstrm->out_finger += len;
397 }
398 break;
399
400 case XDR_DECODE:
401 if ((len <= rstrm->fbtbc) &&
402 ((rstrm->in_finger + len) <= rstrm->in_boundry)) {
403 buf = (long *) rstrm->in_finger;
404 rstrm->fbtbc -= len;
405 rstrm->in_finger += len;
406 }
407 break;
408 default: break;
409 }
410 return (buf);
411 }
412
413 static void
414 xdrrec_destroy(xdrs)
415 register XDR *xdrs;
416 {
417 register RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
418
419 mem_free(rstrm->the_buffer,
420 rstrm->sendsize + rstrm->recvsize + BYTES_PER_XDR_UNIT);
421 mem_free((caddr_t)rstrm, sizeof(RECSTREAM));
422 }
423
424
425 /*
426 * Exported routines to manage xdr records
427 */
428
429 /*
430 * Before reading (deserializing from the stream, one should always call
431 * this procedure to guarantee proper record alignment.
432 */
433 bool_t
434 xdrrec_skiprecord(xdrs)
435 XDR *xdrs;
436 {
437 register RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
438
439 while (rstrm->fbtbc > 0 || (! rstrm->last_frag)) {
440 if (! skip_input_bytes(rstrm, rstrm->fbtbc))
441 return (FALSE);
442 rstrm->fbtbc = 0;
443 if ((! rstrm->last_frag) && (! set_input_fragment(rstrm)))
444 return (FALSE);
445 }
446 rstrm->last_frag = FALSE;
447 return (TRUE);
448 }
449
450 /*
451 * Look ahead fuction.
452 * Returns TRUE iff there is no more input in the buffer
453 * after consuming the rest of the current record.
454 */
455 bool_t
456 xdrrec_eof(xdrs)
457 XDR *xdrs;
458 {
459 register RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
460
461 while (rstrm->fbtbc > 0 || (! rstrm->last_frag)) {
462 if (! skip_input_bytes(rstrm, rstrm->fbtbc))
463 return (TRUE);
464 rstrm->fbtbc = 0;
465 if ((! rstrm->last_frag) && (! set_input_fragment(rstrm)))
466 return (TRUE);
467 }
468 if (rstrm->in_finger == rstrm->in_boundry)
469 return (TRUE);
470 return (FALSE);
471 }
472
473 /*
474 * The client must tell the package when an end-of-record has occurred.
475 * The second paraemters tells whether the record should be flushed to the
476 * (output) tcp stream. (This let's the package support batched or
477 * pipelined procedure calls.) TRUE => immmediate flush to tcp connection.
478 */
479 bool_t
480 xdrrec_endofrecord(xdrs, sendnow)
481 XDR *xdrs;
482 bool_t sendnow;
483 {
484 register RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
485 register u_long len; /* fragment length */
486
487 if (sendnow || rstrm->frag_sent ||
488 ((u_long)rstrm->out_finger + sizeof(u_long) >=
489 (u_long)rstrm->out_boundry)) {
490 rstrm->frag_sent = FALSE;
491 return (flush_out(rstrm, TRUE));
492 }
493 len = (u_long)(rstrm->out_finger) - (u_long)(rstrm->frag_header) -
494 sizeof(u_long);
495 *(rstrm->frag_header) = htonl((u_long)len | LAST_FRAG);
496 rstrm->frag_header = (u_long *)rstrm->out_finger;
497 rstrm->out_finger += sizeof(u_long);
498 return (TRUE);
499 }
500
501
502 /*
503 * Internal useful routines
504 */
505 static bool_t
506 flush_out(rstrm, eor)
507 register RECSTREAM *rstrm;
508 bool_t eor;
509 {
510 register u_long eormask = (eor == TRUE) ? LAST_FRAG : 0;
511 register u_long len = (u_long)(rstrm->out_finger) -
512 (u_long)(rstrm->frag_header) - sizeof(u_long);
513
514 *(rstrm->frag_header) = htonl(len | eormask);
515 len = (u_long)(rstrm->out_finger) - (u_long)(rstrm->out_base);
516 if ((*(rstrm->writeit))(rstrm->tcp_handle, rstrm->out_base, (int)len)
517 != (int)len)
518 return (FALSE);
519 rstrm->frag_header = (u_long *)rstrm->out_base;
520 rstrm->out_finger = (caddr_t)rstrm->out_base + sizeof(u_long);
521 return (TRUE);
522 }
523
524 static bool_t /* knows nothing about records! Only about input buffers */
525 fill_input_buf(rstrm)
526 register RECSTREAM *rstrm;
527 {
528 register caddr_t where;
529 u_int i;
530 register int len;
531
532 where = rstrm->in_base;
533 i = (u_int)rstrm->in_boundry % BYTES_PER_XDR_UNIT;
534 where += i;
535 len = rstrm->in_size - i;
536 if ((len = (*(rstrm->readit))(rstrm->tcp_handle, where, len)) == -1)
537 return (FALSE);
538 rstrm->in_finger = where;
539 where += len;
540 rstrm->in_boundry = where;
541 return (TRUE);
542 }
543
544 static bool_t /* knows nothing about records! Only about input buffers */
545 get_input_bytes(rstrm, addr, len)
546 register RECSTREAM *rstrm;
547 register caddr_t addr;
548 register int len;
549 {
550 register int current;
551
552 while (len > 0) {
553 current = (int)rstrm->in_boundry - (int)rstrm->in_finger;
554 if (current == 0) {
555 if (! fill_input_buf(rstrm))
556 return (FALSE);
557 continue;
558 }
559 current = (len < current) ? len : current;
560 bcopy(rstrm->in_finger, addr, current);
561 rstrm->in_finger += current;
562 addr += current;
563 len -= current;
564 }
565 return (TRUE);
566 }
567
568 static bool_t /* next two bytes of the input stream are treated as a header */
569 set_input_fragment(rstrm)
570 register RECSTREAM *rstrm;
571 {
572 u_long header;
573
574 if (! get_input_bytes(rstrm, (caddr_t)&header, sizeof(header)))
575 return (FALSE);
576 header = (long)ntohl(header);
577 rstrm->last_frag = ((header & LAST_FRAG) == 0) ? FALSE : TRUE;
578 rstrm->fbtbc = header & (~LAST_FRAG);
579 return (TRUE);
580 }
581
582 static bool_t /* consumes input bytes; knows nothing about records! */
583 skip_input_bytes(rstrm, cnt)
584 register RECSTREAM *rstrm;
585 long cnt;
586 {
587 register int current;
588
589 while (cnt > 0) {
590 current = (int)rstrm->in_boundry - (int)rstrm->in_finger;
591 if (current == 0) {
592 if (! fill_input_buf(rstrm))
593 return (FALSE);
594 continue;
595 }
596 current = (cnt < current) ? cnt : current;
597 rstrm->in_finger += current;
598 cnt -= current;
599 }
600 return (TRUE);
601 }
602
603 static u_int
604 fix_buf_size(s)
605 register u_int s;
606 {
607
608 if (s < 100)
609 s = 4000;
610 return (RNDUP(s));
611 }