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