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