xref: /original-bsd/lib/librpc/rpc/xdr_rec.c (revision 1e395994)
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 
56 extern long	lseek();
57 
58 static u_int	fix_buf_size();
59 
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();
68 
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
78 };
79 
80 /*
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).
88  *
89  * The fragment/record machinery is not general;  it is constructed to
90  * meet the needs of xdr and rpc based on tcp.
91  */
92 
93 #define LAST_FRAG ((u_long)(1 << 31))
94 
95 typedef struct rec_strm {
96 	caddr_t tcp_handle;
97 	caddr_t the_buffer;
98 	/*
99 	 * out-goung bits
100 	 */
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 */
107 	/*
108 	 * in-coming bits
109 	 */
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;
120 
121 
122 /*
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.
130  */
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 */
139 {
140 	register RECSTREAM *rstrm =
141 		(RECSTREAM *)mem_alloc(sizeof(RECSTREAM));
142 
143 	if (rstrm == NULL) {
144 		(void)fprintf(stderr, "xdrrec_create: out of memory\n");
145 		/*
146 		 *  This is bad.  Should rework xdrrec_create to
147 		 *  return a handle, and in this case return NULL
148 		 */
149 		return;
150 	}
151 	/*
152 	 * adjust sizes and allocate buffer quad byte aligned
153 	 */
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;
160 	}
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;
165 	/*
166 	 * now the rest ...
167 	 */
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;
183 }
184 
185 
186 /*
187  * The reoutines defined below are the xdr ops which will go into the
188  * xdr handle filled in by xdrrec_create.
189  */
190 
191 static bool_t
192 xdrrec_getlong(xdrs, lp)
193 	XDR *xdrs;
194 	long *lp;
195 {
196 	register RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
197 	register long *buflp = (long *)(rstrm->in_finger);
198 	long mylong;
199 
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);
210 	}
211 	return (TRUE);
212 }
213 
214 static bool_t
215 xdrrec_putlong(xdrs, lp)
216 	XDR *xdrs;
217 	long *lp;
218 {
219 	register RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
220 	register long *dest_lp = ((long *)(rstrm->out_finger));
221 
222 	if ((rstrm->out_finger += sizeof(long)) > rstrm->out_boundry) {
223 		/*
224 		 * this case should almost never happen so the code is
225 		 * inefficient
226 		 */
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);
233 	}
234 	*dest_lp = (long)htonl((u_long)(*lp));
235 	return (TRUE);
236 }
237 
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;
243 {
244 	register RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
245 	register int current;
246 
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;
255 		}
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;
262 	}
263 	return (TRUE);
264 }
265 
266 static bool_t
267 xdrrec_putbytes(xdrs, addr, len)
268 	XDR *xdrs;
269 	register caddr_t addr;
270 	register u_int len;
271 {
272 	register RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
273 	register int current;
274 
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);
286 		}
287 	}
288 	return (TRUE);
289 }
290 
291 static u_int
292 xdrrec_getpos(xdrs)
293 	register XDR *xdrs;
294 {
295 	register RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
296 	register long pos;
297 
298 	pos = lseek((int)rstrm->tcp_handle, (long) 0, 1);
299 	if (pos != -1)
300 		switch (xdrs->x_op) {
301 
302 		case XDR_ENCODE:
303 			pos += rstrm->out_finger - rstrm->out_base;
304 			break;
305 
306 		case XDR_DECODE:
307 			pos -= rstrm->in_boundry - rstrm->in_finger;
308 			break;
309 
310 		default:
311 			pos = (u_int) -1;
312 			break;
313 		}
314 	return ((u_int) pos);
315 }
316 
317 static bool_t
318 xdrrec_setpos(xdrs, pos)
319 	register XDR *xdrs;
320 	u_int pos;
321 {
322 	register RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
323 	u_int currpos = xdrrec_getpos(xdrs);
324 	int delta = currpos - pos;
325 	caddr_t newpos;
326 
327 	if ((int)currpos != -1)
328 		switch (xdrs->x_op) {
329 
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);
336 			}
337 			break;
338 
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);
347 			}
348 			break;
349 		}
350 	return (FALSE);
351 }
352 
353 static long *
354 xdrrec_inline(xdrs, len)
355 	register XDR *xdrs;
356 	int len;
357 {
358 	register RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
359 	long * buf = NULL;
360 
361 	switch (xdrs->x_op) {
362 
363 	case XDR_ENCODE:
364 		if ((rstrm->out_finger + len) <= rstrm->out_boundry) {
365 			buf = (long *) rstrm->out_finger;
366 			rstrm->out_finger += len;
367 		}
368 		break;
369 
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;
376 		}
377 		break;
378 	}
379 	return (buf);
380 }
381 
382 static void
383 xdrrec_destroy(xdrs)
384 	register XDR *xdrs;
385 {
386 	register RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
387 
388 	mem_free(rstrm->the_buffer,
389 		rstrm->sendsize + rstrm->recvsize + BYTES_PER_XDR_UNIT);
390 	mem_free((caddr_t)rstrm, sizeof(RECSTREAM));
391 }
392 
393 
394 /*
395  * Exported routines to manage xdr records
396  */
397 
398 /*
399  * Before reading (deserializing from the stream, one should always call
400  * this procedure to guarantee proper record alignment.
401  */
402 bool_t
403 xdrrec_skiprecord(xdrs)
404 	XDR *xdrs;
405 {
406 	register RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
407 
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);
414 	}
415 	rstrm->last_frag = FALSE;
416 	return (TRUE);
417 }
418 
419 /*
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.
423  */
424 bool_t
425 xdrrec_eof(xdrs)
426 	XDR *xdrs;
427 {
428 	register RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
429 
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);
436 	}
437 	if (rstrm->in_finger == rstrm->in_boundry)
438 		return (TRUE);
439 	return (FALSE);
440 }
441 
442 /*
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.
447  */
448 bool_t
449 xdrrec_endofrecord(xdrs, sendnow)
450 	XDR *xdrs;
451 	bool_t sendnow;
452 {
453 	register RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
454 	register u_long len;  /* fragment length */
455 
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));
461 	}
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);
468 }
469 
470 
471 /*
472  * Internal useful routines
473  */
474 static bool_t
475 flush_out(rstrm, eor)
476 	register RECSTREAM *rstrm;
477 	bool_t eor;
478 {
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);
482 
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);
491 }
492 
493 static bool_t  /* knows nothing about records!  Only about input buffers */
494 fill_input_buf(rstrm)
495 	register RECSTREAM *rstrm;
496 {
497 	register caddr_t where;
498 	u_int i;
499 	register int len;
500 
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);
511 }
512 
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;
518 {
519 	register int current;
520 
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;
527 		}
528 		current = (len < current) ? len : current;
529 		bcopy(rstrm->in_finger, addr, current);
530 		rstrm->in_finger += current;
531 		addr += current;
532 		len -= current;
533 	}
534 	return (TRUE);
535 }
536 
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;
540 {
541 	u_long header;
542 
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);
549 }
550 
551 static bool_t  /* consumes input bytes; knows nothing about records! */
552 skip_input_bytes(rstrm, cnt)
553 	register RECSTREAM *rstrm;
554 	long cnt;
555 {
556 	register int current;
557 
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;
564 		}
565 		current = (cnt < current) ? cnt : current;
566 		rstrm->in_finger += current;
567 		cnt -= current;
568 	}
569 	return (TRUE);
570 }
571 
572 static u_int
573 fix_buf_size(s)
574 	register u_int s;
575 {
576 
577 	if (s < 100)
578 		s = 4000;
579 	return (RNDUP(s));
580 }
581