xref: /freebsd/crypto/krb5/src/lib/rpc/xdr_rec.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
1 /* @(#)xdr_rec.c	2.2 88/08/01 4.0 RPCSRC */
2 /*
3  * Copyright (c) 2010, Oracle America, Inc.
4  *
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  *
10  *     * Redistributions of source code must retain the above copyright
11  *       notice, this list of conditions and the following disclaimer.
12  *
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *
18  *     * Neither the name of the "Oracle America, Inc." nor the names of
19  *       its contributors may be used to endorse or promote products
20  *       derived from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
23  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
24  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
25  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
28  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
29  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
30  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  */
34 #if !defined(lint) && defined(SCCSIDS)
35 static char sccsid[] = "@(#)xdr_rec.c 1.21 87/08/11 Copyr 1984 Sun Micro";
36 #endif
37 
38 /*
39  * xdr_rec.c, Implements TCP/IP based XDR streams with a "record marking"
40  * layer above tcp (for rpc's use).
41  *
42  * These routines interface XDRSTREAMS to a tcp/ip connection.
43  * There is a record marking layer between the xdr stream
44  * and the tcp transport level.  A record is composed on one or more
45  * record fragments.  A record fragment is a thirty-two bit header followed
46  * by n bytes of data, where n is contained in the header.  The header
47  * is represented as a htonl(uint32_t).  Thegh order bit encodes
48  * whether or not the fragment is the last fragment of the record
49  * (1 => fragment is last, 0 => more fragments to follow.
50  * The other 31 bits encode the byte length of the fragment.
51  */
52 
53 #include <stdio.h>
54 #include <gssrpc/types.h>
55 #include <gssrpc/xdr.h>
56 #include <netinet/in.h>
57 
58 #include <unistd.h>
59 #include <string.h>
60 
61 static bool_t	xdrrec_getlong(XDR *, long *);
62 static bool_t	xdrrec_putlong(XDR *, long *);
63 static bool_t	xdrrec_getbytes(XDR *, caddr_t, u_int);
64 static bool_t	xdrrec_putbytes(XDR *, caddr_t, u_int);
65 static u_int	xdrrec_getpos(XDR *);
66 static bool_t	xdrrec_setpos(XDR *, u_int);
67 static rpc_inline_t *	xdrrec_inline(XDR *, int);
68 static void	xdrrec_destroy(XDR *);
69 
70 static struct  xdr_ops xdrrec_ops = {
71 	xdrrec_getlong,
72 	xdrrec_putlong,
73 	xdrrec_getbytes,
74 	xdrrec_putbytes,
75 	xdrrec_getpos,
76 	xdrrec_setpos,
77 	xdrrec_inline,
78 	xdrrec_destroy
79 };
80 
81 /*
82  * A record is composed of one or more record fragments.
83  * A record fragment is a four-byte header followed by zero to
84  * 2**31-1 bytes.  The header is treated as an unsigned 32 bit integer and is
85  * encode/decoded to the network via htonl/ntohl.  The low order 31 bits
86  * are a byte count of the fragment.  The highest order bit is a boolean:
87  * 1 => this fragment is the last fragment of the record,
88  * 0 => this fragment is followed by more fragment(s).
89  *
90  * The fragment/record machinery is not general;  it is constructed to
91  * meet the needs of xdr and rpc based on tcp.
92  */
93 
94 #define LAST_FRAG ((uint32_t)(1UL << 31))
95 
96 typedef struct rec_strm {
97 	caddr_t tcp_handle;
98 	caddr_t the_buffer;
99 	/*
100 	 * out-goung bits
101 	 */
102 	int (*writeit)();
103 	caddr_t out_base;	/* output buffer (points to frag header) */
104 	caddr_t out_finger;	/* next output position */
105 	caddr_t out_boundry;	/* data cannot up to this address */
106 	uint32_t *frag_header;	/* beginning of curren fragment */
107 	bool_t frag_sent;	/* true if buffer sent in middle of record */
108 	/*
109 	 * in-coming bits
110 	 */
111 	int (*readit)();
112 	uint32_t in_size;	/* fixed size of the input buffer */
113 	caddr_t in_base;
114 	caddr_t in_finger;	/* location of next byte to be had */
115 	caddr_t in_boundry;	/* can read up to this location */
116 	int32_t fbtbc;		/* fragment bytes to be consumed */
117 	bool_t last_frag;
118 	u_int sendsize;
119 	u_int recvsize;
120 } RECSTREAM;
121 
122 static u_int	fix_buf_size(u_int);
123 static bool_t   flush_out(RECSTREAM *, bool_t);
124 static bool_t   get_input_bytes(RECSTREAM *, caddr_t, int);
125 static bool_t   set_input_fragment(RECSTREAM *);
126 static bool_t   skip_input_bytes(RECSTREAM *, int32_t);
127 
128 /*
129  * Create an xdr handle for xdrrec
130  * xdrrec_create fills in xdrs.  Sendsize and recvsize are
131  * send and recv buffer sizes (0 => use default).
132  * tcp_handle is an opaque handle that is passed as the first parameter to
133  * the procedures readit and writeit.  Readit and writeit are read and
134  * write respectively.   They are like the system
135  * calls expect that they take an opaque handle rather than an fd.
136  */
137 void
xdrrec_create(XDR * xdrs,u_int sendsize,u_int recvsize,caddr_t tcp_handle,int (* readit)(),int (* writeit)())138 xdrrec_create(
139 	XDR *xdrs,
140 	u_int sendsize,
141 	u_int recvsize,
142 	caddr_t tcp_handle,
143 	int (*readit)(), /* like read, but pass it a tcp_handle, not sock */
144 	int (*writeit)() /* like write, but pass it a tcp_handle, not sock */
145 	)
146 {
147 	RECSTREAM *rstrm = mem_alloc(sizeof(RECSTREAM));
148 
149 	if (rstrm == NULL) {
150 		(void)fprintf(stderr, "xdrrec_create: out of memory\n");
151 		/*
152 		 *  This is bad.  Should rework xdrrec_create to
153 		 *  return a handle, and in this case return NULL
154 		 */
155 		return;
156 	}
157 	/*
158 	 * adjust sizes and allocate buffer quad byte aligned
159 	 */
160 	rstrm->sendsize = sendsize = fix_buf_size(sendsize);
161 	rstrm->recvsize = recvsize = fix_buf_size(recvsize);
162 	rstrm->the_buffer = mem_alloc(sendsize + recvsize + BYTES_PER_XDR_UNIT);
163 	if (rstrm->the_buffer == NULL) {
164 		(void)fprintf(stderr, "xdrrec_create: out of memory\n");
165 		return;
166 	}
167 	for (rstrm->out_base = rstrm->the_buffer;
168 	     /* Pointer arithmetic - long cast allowed... */
169 		(u_long)rstrm->out_base % BYTES_PER_XDR_UNIT != 0;
170 		rstrm->out_base++);
171 	rstrm->in_base = rstrm->out_base + sendsize;
172 	/*
173 	 * now the rest ...
174 	 */
175 	xdrs->x_ops = &xdrrec_ops;
176 	xdrs->x_private = (caddr_t)rstrm;
177 	rstrm->tcp_handle = tcp_handle;
178 	rstrm->readit = readit;
179 	rstrm->writeit = writeit;
180 	rstrm->out_finger = rstrm->out_boundry = rstrm->out_base;
181 	rstrm->frag_header = (uint32_t *)(void *)rstrm->out_base;
182 	rstrm->out_finger += BYTES_PER_XDR_UNIT;
183 	rstrm->out_boundry += sendsize;
184 	rstrm->frag_sent = FALSE;
185 	rstrm->in_size = recvsize;
186 	rstrm->in_boundry = rstrm->in_base;
187 	rstrm->in_finger = (rstrm->in_boundry += recvsize);
188 	rstrm->fbtbc = 0;
189 	rstrm->last_frag = TRUE;
190 }
191 
192 
193 /*
194  * The reoutines defined below are the xdr ops which will go into the
195  * xdr handle filled in by xdrrec_create.
196  */
197 
198 static bool_t
xdrrec_getlong(XDR * xdrs,long * lp)199 xdrrec_getlong(XDR *xdrs, long *lp)
200 {
201 	RECSTREAM *rstrm = xdrs->x_private;
202 	int32_t *buflp = (void *)(rstrm->in_finger);
203 	uint32_t mylong;
204 
205 	/* first try the inline, fast case */
206 	if ((rstrm->fbtbc >= BYTES_PER_XDR_UNIT) &&
207 		(((long)rstrm->in_boundry - (long)buflp) >=
208 		 BYTES_PER_XDR_UNIT)) {
209 		*lp = (long)ntohl((uint32_t)(*buflp));
210 		rstrm->fbtbc -= BYTES_PER_XDR_UNIT;
211 		rstrm->in_finger += BYTES_PER_XDR_UNIT;
212 	} else {
213 		if (! xdrrec_getbytes(xdrs, (caddr_t)&mylong,
214 				      BYTES_PER_XDR_UNIT))
215 			return (FALSE);
216 		*lp = (long)(int32_t)ntohl(mylong);
217 	}
218 	return (TRUE);
219 }
220 
221 static bool_t
xdrrec_putlong(XDR * xdrs,long * lp)222 xdrrec_putlong(XDR *xdrs, long *lp)
223 {
224 	RECSTREAM *rstrm = xdrs->x_private;
225 	int32_t *dest_lp = (void *)(rstrm->out_finger);
226 
227 	if (rstrm->out_boundry - rstrm->out_finger < BYTES_PER_XDR_UNIT) {
228 		/*
229 		 * this case should almost never happen so the code is
230 		 * inefficient
231 		 */
232 		rstrm->frag_sent = TRUE;
233 		if (! flush_out(rstrm, FALSE))
234 			return (FALSE);
235 		dest_lp = ((int32_t *)(void *)(rstrm->out_finger));
236 	}
237 	rstrm->out_finger += BYTES_PER_XDR_UNIT;
238 	*dest_lp = (int32_t)htonl((uint32_t)(*lp));
239 	return (TRUE);
240 }
241 
242 static bool_t  /* must manage buffers, fragments, and records */
xdrrec_getbytes(XDR * xdrs,caddr_t addr,u_int len)243 xdrrec_getbytes(XDR *xdrs, caddr_t addr, u_int len)
244 {
245 	RECSTREAM *rstrm = xdrs->x_private;
246 	u_int current;
247 
248 	while (len > 0) {
249 		current = rstrm->fbtbc;
250 		if (current == 0) {
251 			if (rstrm->last_frag)
252 				return (FALSE);
253 			if (! set_input_fragment(rstrm))
254 				return (FALSE);
255 			continue;
256 		}
257 		current = (len < current) ? len : current;
258 		if (! get_input_bytes(rstrm, addr, current))
259 			return (FALSE);
260 		addr += current;
261 		rstrm->fbtbc -= current;
262 		len -= current;
263 	}
264 	return (TRUE);
265 }
266 
267 static bool_t
xdrrec_putbytes(XDR * xdrs,caddr_t addr,u_int len)268 xdrrec_putbytes(XDR *xdrs, caddr_t addr, u_int len)
269 {
270 	RECSTREAM *rstrm = xdrs->x_private;
271 	size_t current;
272 
273 	while (len > 0) {
274 		current = (size_t) ((long)rstrm->out_boundry -
275 				 (long)rstrm->out_finger);
276 		current = (len < current) ? len : current;
277 		memmove(rstrm->out_finger, addr, current);
278 		rstrm->out_finger += current;
279 		addr += current;
280 		len -= current;
281 		if (rstrm->out_finger == rstrm->out_boundry) {
282 			rstrm->frag_sent = TRUE;
283 			if (! flush_out(rstrm, FALSE))
284 				return (FALSE);
285 		}
286 	}
287 	return (TRUE);
288 }
289 
290 static u_int
xdrrec_getpos(XDR * xdrs)291 xdrrec_getpos(XDR *xdrs)
292 {
293 	RECSTREAM *rstrm = xdrs->x_private;
294 	int pos;
295 
296 	switch (xdrs->x_op) {
297 
298 	case XDR_ENCODE:
299 		pos = rstrm->out_finger - rstrm->out_base
300 			- BYTES_PER_XDR_UNIT;
301 		break;
302 
303 	case XDR_DECODE:
304 		pos = rstrm->in_boundry - rstrm->in_finger
305 			- BYTES_PER_XDR_UNIT;
306 		break;
307 
308 	default:
309 		pos = -1;
310 		break;
311 	}
312 	return ((u_int) pos);
313 }
314 
315 static bool_t
xdrrec_setpos(XDR * xdrs,u_int pos)316 xdrrec_setpos(XDR *xdrs, u_int pos)
317 {
318 	RECSTREAM *rstrm = xdrs->x_private;
319 	u_int currpos = xdrrec_getpos(xdrs);
320 	int delta = currpos - pos;
321 	caddr_t newpos;
322 
323 	if ((int)currpos != -1)
324 		switch (xdrs->x_op) {
325 
326 		case XDR_ENCODE:
327 			newpos = rstrm->out_finger - delta;
328 			if ((newpos > (caddr_t)(rstrm->frag_header)) &&
329 				(newpos < rstrm->out_boundry)) {
330 				rstrm->out_finger = newpos;
331 				return (TRUE);
332 			}
333 			break;
334 
335 		case XDR_DECODE:
336 			newpos = rstrm->in_finger - delta;
337 			if ((delta < (int)(rstrm->fbtbc)) &&
338 				(newpos <= rstrm->in_boundry) &&
339 				(newpos >= rstrm->in_base)) {
340 				rstrm->in_finger = newpos;
341 				rstrm->fbtbc -= delta;
342 				return (TRUE);
343 			}
344 			break;
345 
346 		case XDR_FREE:
347 			break;
348 		}
349 	return (FALSE);
350 }
351 
352 static rpc_inline_t *
xdrrec_inline(XDR * xdrs,int len)353 xdrrec_inline(XDR *xdrs, int len)
354 {
355 	RECSTREAM *rstrm = xdrs->x_private;
356 	rpc_inline_t * buf = NULL;
357 
358 	if (len < 0)
359 		return (FALSE);
360 
361 	switch (xdrs->x_op) {
362 
363 	case XDR_ENCODE:
364 		if (len <= (rstrm->out_boundry - rstrm->out_finger)) {
365 			buf = (rpc_inline_t *)(void *) rstrm->out_finger;
366 			rstrm->out_finger += len;
367 		}
368 		break;
369 
370 	case XDR_DECODE:
371 		if ((len <= rstrm->fbtbc) &&
372 			(len <= (rstrm->in_boundry - rstrm->in_finger))) {
373 			buf = (rpc_inline_t *)(void *) rstrm->in_finger;
374 			rstrm->fbtbc -= len;
375 			rstrm->in_finger += len;
376 		}
377 		break;
378 
379 	case XDR_FREE:
380 	        break;
381 	}
382 	return (buf);
383 }
384 
385 static void
xdrrec_destroy(XDR * xdrs)386 xdrrec_destroy(XDR *xdrs)
387 {
388 	RECSTREAM *rstrm = xdrs->x_private;
389 
390 	mem_free(rstrm->the_buffer,
391 		rstrm->sendsize + rstrm->recvsize + BYTES_PER_XDR_UNIT);
392 	mem_free((caddr_t)rstrm, sizeof(RECSTREAM));
393 }
394 
395 
396 /*
397  * Exported routines to manage xdr records
398  */
399 
400 /*
401  * Before reading (deserializing from the stream, one should always call
402  * this procedure to guarantee proper record alignment.
403  */
404 bool_t
xdrrec_skiprecord(XDR * xdrs)405 xdrrec_skiprecord(XDR *xdrs)
406 {
407 	RECSTREAM *rstrm = xdrs->x_private;
408 
409 	while (rstrm->fbtbc > 0 || (! rstrm->last_frag)) {
410 		if (! skip_input_bytes(rstrm, rstrm->fbtbc))
411 			return (FALSE);
412 		rstrm->fbtbc = 0;
413 		if ((! rstrm->last_frag) && (! set_input_fragment(rstrm)))
414 			return (FALSE);
415 	}
416 	rstrm->last_frag = FALSE;
417 	return (TRUE);
418 }
419 
420 /*
421  * Look ahead function.
422  * Returns TRUE iff there is no more input in the buffer
423  * after consuming the rest of the current record.
424  */
425 bool_t
xdrrec_eof(XDR * xdrs)426 xdrrec_eof(XDR *xdrs)
427 {
428 	RECSTREAM *rstrm = 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 lets the package support batched or
446  * pipelined procedure calls.)  TRUE => immediate flush to tcp connection.
447  */
448 bool_t
xdrrec_endofrecord(XDR * xdrs,bool_t sendnow)449 xdrrec_endofrecord(XDR *xdrs, bool_t sendnow)
450 {
451 	RECSTREAM *rstrm = xdrs->x_private;
452 	uint32_t len;  /* fragment length */
453 
454 	if (sendnow || rstrm->frag_sent ||
455 		((long)rstrm->out_finger + BYTES_PER_XDR_UNIT >=
456 		(long)rstrm->out_boundry)) {
457 		rstrm->frag_sent = FALSE;
458 		return (flush_out(rstrm, TRUE));
459 	}
460 	len = (long)(rstrm->out_finger) - (long)(rstrm->frag_header) -
461 	   BYTES_PER_XDR_UNIT;
462 	*(rstrm->frag_header) = htonl((uint32_t)len | LAST_FRAG);
463 	rstrm->frag_header = (uint32_t *)(void *)rstrm->out_finger;
464 	rstrm->out_finger += BYTES_PER_XDR_UNIT;
465 	return (TRUE);
466 }
467 
468 
469 /*
470  * Internal useful routines
471  */
472 static bool_t
flush_out(RECSTREAM * rstrm,bool_t eor)473 flush_out(RECSTREAM *rstrm, bool_t eor)
474 {
475 	uint32_t eormask = (eor == TRUE) ? LAST_FRAG : 0;
476 	uint32_t len = (u_long)(rstrm->out_finger) -
477 		(u_long)(rstrm->frag_header) - BYTES_PER_XDR_UNIT;
478 
479 	*(rstrm->frag_header) = htonl(len | eormask);
480 	len = (u_long)(rstrm->out_finger) - (u_long)(rstrm->out_base);
481 	if ((*(rstrm->writeit))(rstrm->tcp_handle, rstrm->out_base, (int)len)
482 		!= (int)len)
483 		return (FALSE);
484 	rstrm->frag_header = (uint32_t *)(void *)rstrm->out_base;
485 	rstrm->out_finger = (caddr_t)rstrm->out_base + BYTES_PER_XDR_UNIT;
486 	return (TRUE);
487 }
488 
489 static bool_t  /* knows nothing about records!  Only about input buffers */
fill_input_buf(RECSTREAM * rstrm)490 fill_input_buf(RECSTREAM *rstrm)
491 {
492 	caddr_t where;
493 	u_int i;
494 	int len;
495 
496 	where = rstrm->in_base;
497 	i = (u_int)((u_long)rstrm->in_boundry % BYTES_PER_XDR_UNIT);
498 	where += i;
499 	len = rstrm->in_size - i;
500 	if ((len = (*(rstrm->readit))(rstrm->tcp_handle, where, len)) == -1)
501 		return (FALSE);
502 	rstrm->in_finger = where;
503 	where += len;
504 	rstrm->in_boundry = where;
505 	return (TRUE);
506 }
507 
508 static bool_t  /* knows nothing about records!  Only about input buffers */
get_input_bytes(RECSTREAM * rstrm,caddr_t addr,int len)509 get_input_bytes(RECSTREAM *rstrm, caddr_t addr, int len)
510 {
511 	size_t current;
512 
513 	while (len > 0) {
514 		current = (size_t)((long)rstrm->in_boundry -
515 				(long)rstrm->in_finger);
516 		if (current == 0) {
517 			if (! fill_input_buf(rstrm))
518 				return (FALSE);
519 			continue;
520 		}
521 		current = ((size_t)len < current) ? (size_t)len : current;
522 		memmove(addr, rstrm->in_finger, current);
523 		rstrm->in_finger += current;
524 		addr += current;
525 		len -= current;
526 	}
527 	return (TRUE);
528 }
529 
530 static bool_t  /* next four bytes of input stream are treated as a header */
set_input_fragment(rstrm)531 set_input_fragment(rstrm)
532 	RECSTREAM *rstrm;
533 {
534 	uint32_t header;
535 
536 	if (! get_input_bytes(rstrm, (caddr_t)&header, sizeof(header)))
537 		return (FALSE);
538 	header = ntohl(header);
539 	rstrm->last_frag = ((header & LAST_FRAG) == 0) ? FALSE : TRUE;
540 	rstrm->fbtbc = header & (~LAST_FRAG);
541 	return (TRUE);
542 }
543 
544 static bool_t  /* consumes input bytes; knows nothing about records! */
skip_input_bytes(RECSTREAM * rstrm,int32_t cnt)545 skip_input_bytes(RECSTREAM *rstrm, int32_t cnt)
546 {
547 	int current;
548 
549 	while (cnt > 0) {
550 		current = (int)((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 = (cnt < current) ? cnt : current;
558 		rstrm->in_finger += current;
559 		cnt -= current;
560 	}
561 	return (TRUE);
562 }
563 
564 static u_int
fix_buf_size(u_int s)565 fix_buf_size(u_int s)
566 {
567 
568 	if (s < 100)
569 		s = 4000;
570 	return (RNDUP(s));
571 }
572