xref: /freebsd/lib/libc/xdr/xdr_rec.c (revision 17d6c636720d00f77e5d098daf4c278f89d84f7b)
1 /*	$NetBSD: xdr_rec.c,v 1.18 2000/07/06 03:10:35 christos Exp $	*/
2 
3 /*
4  * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
5  * unrestricted use provided that this legend is included on all tape
6  * media and as a part of the software program in whole or part.  Users
7  * may copy or modify Sun RPC without charge, but are not authorized
8  * to license or distribute it to anyone else except as part of a product or
9  * program developed by the user.
10  *
11  * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
12  * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
13  * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
14  *
15  * Sun RPC is provided with no support and without any obligation on the
16  * part of Sun Microsystems, Inc. to assist in its use, correction,
17  * modification or enhancement.
18  *
19  * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
20  * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
21  * OR ANY PART THEREOF.
22  *
23  * In no event will Sun Microsystems, Inc. be liable for any lost revenue
24  * or profits or other special, indirect and consequential damages, even if
25  * Sun has been advised of the possibility of such damages.
26  *
27  * Sun Microsystems, Inc.
28  * 2550 Garcia Avenue
29  * Mountain View, California  94043
30  */
31 
32 #include <sys/cdefs.h>
33 #if defined(LIBC_SCCS) && !defined(lint)
34 /*static char *sccsid = "from: @(#)xdr_rec.c 1.21 87/08/11 Copyr 1984 Sun Micro";*/
35 /*static char *sccsid = "from: @(#)xdr_rec.c	2.2 88/08/01 4.0 RPCSRC";*/
36 static char *rcsid = "$FreeBSD$";
37 #endif
38 
39 /*
40  * xdr_rec.c, Implements TCP/IP based XDR streams with a "record marking"
41  * layer above tcp (for rpc's use).
42  *
43  * Copyright (C) 1984, Sun Microsystems, Inc.
44  *
45  * These routines interface XDRSTREAMS to a tcp/ip connection.
46  * There is a record marking layer between the xdr stream
47  * and the tcp transport level.  A record is composed on one or more
48  * record fragments.  A record fragment is a thirty-two bit header followed
49  * by n bytes of data, where n is contained in the header.  The header
50  * is represented as a htonl(u_long).  Thegh order bit encodes
51  * whether or not the fragment is the last fragment of the record
52  * (1 => fragment is last, 0 => more fragments to follow.
53  * The other 31 bits encode the byte length of the fragment.
54  */
55 
56 #include "namespace.h"
57 #include <sys/types.h>
58 
59 #include <netinet/in.h>
60 
61 #include <err.h>
62 #include <stdio.h>
63 #include <stdlib.h>
64 #include <string.h>
65 
66 #include <rpc/types.h>
67 #include <rpc/xdr.h>
68 #include "un-namespace.h"
69 
70 static bool_t	xdrrec_getlong __P((XDR *, long *));
71 static bool_t	xdrrec_putlong __P((XDR *, const long *));
72 static bool_t	xdrrec_getbytes __P((XDR *, char *, u_int));
73 
74 static bool_t	xdrrec_putbytes __P((XDR *, const char *, u_int));
75 static u_int	xdrrec_getpos __P((XDR *));
76 static bool_t	xdrrec_setpos __P((XDR *, u_int));
77 static int32_t *xdrrec_inline __P((XDR *, u_int));
78 static void	xdrrec_destroy __P((XDR *));
79 
80 static const struct  xdr_ops xdrrec_ops = {
81 	xdrrec_getlong,
82 	xdrrec_putlong,
83 	xdrrec_getbytes,
84 	xdrrec_putbytes,
85 	xdrrec_getpos,
86 	xdrrec_setpos,
87 	xdrrec_inline,
88 	xdrrec_destroy
89 };
90 
91 /*
92  * A record is composed of one or more record fragments.
93  * A record fragment is a two-byte header followed by zero to
94  * 2**32-1 bytes.  The header is treated as a long unsigned and is
95  * encode/decoded to the network via htonl/ntohl.  The low order 31 bits
96  * are a byte count of the fragment.  The highest order bit is a boolean:
97  * 1 => this fragment is the last fragment of the record,
98  * 0 => this fragment is followed by more fragment(s).
99  *
100  * The fragment/record machinery is not general;  it is constructed to
101  * meet the needs of xdr and rpc based on tcp.
102  */
103 
104 #define LAST_FRAG ((u_int32_t)(1 << 31))
105 
106 typedef struct rec_strm {
107 	char *tcp_handle;
108 	char *the_buffer;
109 	/*
110 	 * out-goung bits
111 	 */
112 	int (*writeit) __P((char *, char *, int));
113 	char *out_base;	/* output buffer (points to frag header) */
114 	char *out_finger;	/* next output position */
115 	char *out_boundry;	/* data cannot up to this address */
116 	u_int32_t *frag_header;	/* beginning of curren fragment */
117 	bool_t frag_sent;	/* true if buffer sent in middle of record */
118 	/*
119 	 * in-coming bits
120 	 */
121 	int (*readit) __P((char *, char *, int));
122 	u_long in_size;	/* fixed size of the input buffer */
123 	char *in_base;
124 	char *in_finger;	/* location of next byte to be had */
125 	char *in_boundry;	/* can read up to this location */
126 	long fbtbc;		/* fragment bytes to be consumed */
127 	bool_t last_frag;
128 	u_int sendsize;
129 	u_int recvsize;
130 } RECSTREAM;
131 
132 static u_int	fix_buf_size __P((u_int));
133 static bool_t	flush_out __P((RECSTREAM *, bool_t));
134 static bool_t	fill_input_buf __P((RECSTREAM *));
135 static bool_t	get_input_bytes __P((RECSTREAM *, char *, int));
136 static bool_t	set_input_fragment __P((RECSTREAM *));
137 static bool_t	skip_input_bytes __P((RECSTREAM *, long));
138 
139 
140 /*
141  * Create an xdr handle for xdrrec
142  * xdrrec_create fills in xdrs.  Sendsize and recvsize are
143  * send and recv buffer sizes (0 => use default).
144  * tcp_handle is an opaque handle that is passed as the first parameter to
145  * the procedures readit and writeit.  Readit and writeit are read and
146  * write respectively.   They are like the system
147  * calls expect that they take an opaque handle rather than an fd.
148  */
149 void
150 xdrrec_create(xdrs, sendsize, recvsize, tcp_handle, readit, writeit)
151 	XDR *xdrs;
152 	u_int sendsize;
153 	u_int recvsize;
154 	char *tcp_handle;
155 	/* like read, but pass it a tcp_handle, not sock */
156 	int (*readit) __P((char *, char *, int));
157 	/* like write, but pass it a tcp_handle, not sock */
158 	int (*writeit) __P((char *, char *, int));
159 {
160 	RECSTREAM *rstrm = mem_alloc(sizeof(RECSTREAM));
161 
162 	if (rstrm == NULL) {
163 		warnx("xdrrec_create: out of memory");
164 		/*
165 		 *  This is bad.  Should rework xdrrec_create to
166 		 *  return a handle, and in this case return NULL
167 		 */
168 		return;
169 	}
170 	/*
171 	 * adjust sizes and allocate buffer quad byte aligned
172 	 */
173 	rstrm->sendsize = sendsize = fix_buf_size(sendsize);
174 	rstrm->recvsize = recvsize = fix_buf_size(recvsize);
175 	rstrm->the_buffer = mem_alloc(sendsize + recvsize + BYTES_PER_XDR_UNIT);
176 	if (rstrm->the_buffer == NULL) {
177 		warnx("xdrrec_create: out of memory");
178 		return;
179 	}
180 	for (rstrm->out_base = rstrm->the_buffer;
181 		(u_long)rstrm->out_base % BYTES_PER_XDR_UNIT != 0;
182 		rstrm->out_base++);
183 	rstrm->in_base = rstrm->out_base + sendsize;
184 	/*
185 	 * now the rest ...
186 	 */
187 	xdrs->x_ops = &xdrrec_ops;
188 	xdrs->x_private = rstrm;
189 	rstrm->tcp_handle = tcp_handle;
190 	rstrm->readit = readit;
191 	rstrm->writeit = writeit;
192 	rstrm->out_finger = rstrm->out_boundry = rstrm->out_base;
193 	rstrm->frag_header = (u_int32_t *)(void *)rstrm->out_base;
194 	rstrm->out_finger += sizeof(u_int32_t);
195 	rstrm->out_boundry += sendsize;
196 	rstrm->frag_sent = FALSE;
197 	rstrm->in_size = recvsize;
198 	rstrm->in_boundry = rstrm->in_base;
199 	rstrm->in_finger = (rstrm->in_boundry += recvsize);
200 	rstrm->fbtbc = 0;
201 	rstrm->last_frag = TRUE;
202 }
203 
204 
205 /*
206  * The reoutines defined below are the xdr ops which will go into the
207  * xdr handle filled in by xdrrec_create.
208  */
209 
210 static bool_t
211 xdrrec_getlong(xdrs, lp)
212 	XDR *xdrs;
213 	long *lp;
214 {
215 	RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
216 	int32_t *buflp = (int32_t *)(void *)(rstrm->in_finger);
217 	int32_t mylong;
218 
219 	/* first try the inline, fast case */
220 	if ((rstrm->fbtbc >= sizeof(int32_t)) &&
221 		(((long)rstrm->in_boundry - (long)buflp) >= sizeof(int32_t))) {
222 		*lp = (long)ntohl((u_int32_t)(*buflp));
223 		rstrm->fbtbc -= sizeof(int32_t);
224 		rstrm->in_finger += sizeof(int32_t);
225 	} else {
226 		if (! xdrrec_getbytes(xdrs, (char *)(void *)&mylong,
227 		    sizeof(int32_t)))
228 			return (FALSE);
229 		*lp = (long)ntohl((u_int32_t)mylong);
230 	}
231 	return (TRUE);
232 }
233 
234 static bool_t
235 xdrrec_putlong(xdrs, lp)
236 	XDR *xdrs;
237 	const long *lp;
238 {
239 	RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
240 	int32_t *dest_lp = ((int32_t *)(void *)(rstrm->out_finger));
241 
242 	if ((rstrm->out_finger += sizeof(int32_t)) > rstrm->out_boundry) {
243 		/*
244 		 * this case should almost never happen so the code is
245 		 * inefficient
246 		 */
247 		rstrm->out_finger -= sizeof(int32_t);
248 		rstrm->frag_sent = TRUE;
249 		if (! flush_out(rstrm, FALSE))
250 			return (FALSE);
251 		dest_lp = ((int32_t *)(void *)(rstrm->out_finger));
252 		rstrm->out_finger += sizeof(int32_t);
253 	}
254 	*dest_lp = (int32_t)htonl((u_int32_t)(*lp));
255 	return (TRUE);
256 }
257 
258 static bool_t  /* must manage buffers, fragments, and records */
259 xdrrec_getbytes(xdrs, addr, len)
260 	XDR *xdrs;
261 	char *addr;
262 	u_int len;
263 {
264 	RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
265 	int current;
266 
267 	while (len > 0) {
268 		current = (int)rstrm->fbtbc;
269 		if (current == 0) {
270 			if (rstrm->last_frag)
271 				return (FALSE);
272 			if (! set_input_fragment(rstrm))
273 				return (FALSE);
274 			continue;
275 		}
276 		current = (len < current) ? len : current;
277 		if (! get_input_bytes(rstrm, addr, current))
278 			return (FALSE);
279 		addr += current;
280 		rstrm->fbtbc -= current;
281 		len -= current;
282 	}
283 	return (TRUE);
284 }
285 
286 static bool_t
287 xdrrec_putbytes(xdrs, addr, len)
288 	XDR *xdrs;
289 	const char *addr;
290 	u_int len;
291 {
292 	RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
293 	size_t current;
294 
295 	while (len > 0) {
296 		current = (size_t)((u_long)rstrm->out_boundry -
297 		    (u_long)rstrm->out_finger);
298 		current = (len < current) ? len : current;
299 		memmove(rstrm->out_finger, addr, current);
300 		rstrm->out_finger += current;
301 		addr += current;
302 		len -= current;
303 		if (rstrm->out_finger == rstrm->out_boundry) {
304 			rstrm->frag_sent = TRUE;
305 			if (! flush_out(rstrm, FALSE))
306 				return (FALSE);
307 		}
308 	}
309 	return (TRUE);
310 }
311 
312 static u_int
313 xdrrec_getpos(xdrs)
314 	XDR *xdrs;
315 {
316 	RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
317 	off_t pos;
318 
319 	pos = lseek((int)(u_long)rstrm->tcp_handle, (off_t)0, 1);
320 	if (pos != -1)
321 		switch (xdrs->x_op) {
322 
323 		case XDR_ENCODE:
324 			pos += rstrm->out_finger - rstrm->out_base;
325 			break;
326 
327 		case XDR_DECODE:
328 			pos -= rstrm->in_boundry - rstrm->in_finger;
329 			break;
330 
331 		default:
332 			pos = (off_t) -1;
333 			break;
334 		}
335 	return ((u_int) pos);
336 }
337 
338 static bool_t
339 xdrrec_setpos(xdrs, pos)
340 	XDR *xdrs;
341 	u_int pos;
342 {
343 	RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
344 	u_int currpos = xdrrec_getpos(xdrs);
345 	int delta = currpos - pos;
346 	char *newpos;
347 
348 	if ((int)currpos != -1)
349 		switch (xdrs->x_op) {
350 
351 		case XDR_ENCODE:
352 			newpos = rstrm->out_finger - delta;
353 			if ((newpos > (char *)(void *)(rstrm->frag_header)) &&
354 				(newpos < rstrm->out_boundry)) {
355 				rstrm->out_finger = newpos;
356 				return (TRUE);
357 			}
358 			break;
359 
360 		case XDR_DECODE:
361 			newpos = rstrm->in_finger - delta;
362 			if ((delta < (int)(rstrm->fbtbc)) &&
363 				(newpos <= rstrm->in_boundry) &&
364 				(newpos >= rstrm->in_base)) {
365 				rstrm->in_finger = newpos;
366 				rstrm->fbtbc -= delta;
367 				return (TRUE);
368 			}
369 			break;
370 
371 		case XDR_FREE:
372 			break;
373 		}
374 	return (FALSE);
375 }
376 
377 static int32_t *
378 xdrrec_inline(xdrs, len)
379 	XDR *xdrs;
380 	u_int len;
381 {
382 	RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
383 	int32_t *buf = NULL;
384 
385 	switch (xdrs->x_op) {
386 
387 	case XDR_ENCODE:
388 		if ((rstrm->out_finger + len) <= rstrm->out_boundry) {
389 			buf = (int32_t *)(void *)rstrm->out_finger;
390 			rstrm->out_finger += len;
391 		}
392 		break;
393 
394 	case XDR_DECODE:
395 		if ((len <= rstrm->fbtbc) &&
396 			((rstrm->in_finger + len) <= rstrm->in_boundry)) {
397 			buf = (int32_t *)(void *)rstrm->in_finger;
398 			rstrm->fbtbc -= len;
399 			rstrm->in_finger += len;
400 		}
401 		break;
402 
403 	case XDR_FREE:
404 		break;
405 	}
406 	return (buf);
407 }
408 
409 static void
410 xdrrec_destroy(xdrs)
411 	XDR *xdrs;
412 {
413 	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(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 	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 function.
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 	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 	RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
481 	u_long len;  /* fragment length */
482 
483 	if (sendnow || rstrm->frag_sent ||
484 		((u_long)rstrm->out_finger + sizeof(u_int32_t) >=
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_int32_t);
491 	*(rstrm->frag_header) = htonl((u_int32_t)len | LAST_FRAG);
492 	rstrm->frag_header = (u_int32_t *)(void *)rstrm->out_finger;
493 	rstrm->out_finger += sizeof(u_int32_t);
494 	return (TRUE);
495 }
496 
497 
498 /*
499  * Internal useful routines
500  */
501 static bool_t
502 flush_out(rstrm, eor)
503 	RECSTREAM *rstrm;
504 	bool_t eor;
505 {
506 	u_int32_t eormask = (eor == TRUE) ? LAST_FRAG : 0;
507 	u_int32_t len = (u_int32_t)((u_long)(rstrm->out_finger) -
508 		(u_long)(rstrm->frag_header) - sizeof(u_int32_t));
509 
510 	*(rstrm->frag_header) = htonl(len | eormask);
511 	len = (u_int32_t)((u_long)(rstrm->out_finger) -
512 	    (u_long)(rstrm->out_base));
513 	if ((*(rstrm->writeit))(rstrm->tcp_handle, rstrm->out_base, (int)len)
514 		!= (int)len)
515 		return (FALSE);
516 	rstrm->frag_header = (u_int32_t *)(void *)rstrm->out_base;
517 	rstrm->out_finger = (char *)rstrm->out_base + sizeof(u_int32_t);
518 	return (TRUE);
519 }
520 
521 static bool_t  /* knows nothing about records!  Only about input buffers */
522 fill_input_buf(rstrm)
523 	RECSTREAM *rstrm;
524 {
525 	char *where;
526 	u_int32_t i;
527 	int len;
528 
529 	where = rstrm->in_base;
530 	i = (u_int32_t)((u_long)rstrm->in_boundry % BYTES_PER_XDR_UNIT);
531 	where += i;
532 	len = (u_int32_t)(rstrm->in_size - i);
533 	if ((len = (*(rstrm->readit))(rstrm->tcp_handle, where, len)) == -1)
534 		return (FALSE);
535 	rstrm->in_finger = where;
536 	where += len;
537 	rstrm->in_boundry = where;
538 	return (TRUE);
539 }
540 
541 static bool_t  /* knows nothing about records!  Only about input buffers */
542 get_input_bytes(rstrm, addr, len)
543 	RECSTREAM *rstrm;
544 	char *addr;
545 	int len;
546 {
547 	size_t current;
548 
549 	while (len > 0) {
550 		current = (size_t)((long)rstrm->in_boundry -
551 		    (long)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 		memmove(addr, rstrm->in_finger, 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 	RECSTREAM *rstrm;
569 {
570 	u_int32_t header;
571 
572 	if (! get_input_bytes(rstrm, (char *)(void *)&header, sizeof(header)))
573 		return (FALSE);
574 	header = ntohl(header);
575 	rstrm->last_frag = ((header & LAST_FRAG) == 0) ? FALSE : TRUE;
576 	/*
577 	 * Sanity check. Try not to accept wildly incorrect
578 	 * record sizes. Unfortunately, the only record size
579 	 * we can positively identify as being 'wildly incorrect'
580 	 * is zero. Ridiculously large record sizes may look wrong,
581 	 * but we don't have any way to be certain that they aren't
582 	 * what the client actually intended to send us.
583 	 */
584 	if (header == 0)
585 		return(FALSE);
586 	rstrm->fbtbc = header & (~LAST_FRAG);
587 	return (TRUE);
588 }
589 
590 static bool_t  /* consumes input bytes; knows nothing about records! */
591 skip_input_bytes(rstrm, cnt)
592 	RECSTREAM *rstrm;
593 	long cnt;
594 {
595 	u_int32_t current;
596 
597 	while (cnt > 0) {
598 		current = (size_t)((long)rstrm->in_boundry -
599 		    (long)rstrm->in_finger);
600 		if (current == 0) {
601 			if (! fill_input_buf(rstrm))
602 				return (FALSE);
603 			continue;
604 		}
605 		current = (u_int32_t)((cnt < current) ? cnt : current);
606 		rstrm->in_finger += current;
607 		cnt -= current;
608 	}
609 	return (TRUE);
610 }
611 
612 static u_int
613 fix_buf_size(s)
614 	u_int s;
615 {
616 
617 	if (s < 100)
618 		s = 4000;
619 	return (RNDUP(s));
620 }
621