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