xref: /freebsd/crypto/krb5/src/lib/rpc/xdr_rec.c (revision cb2887746f8b9dd4ad6b1e757cdc053a08b25a2e)
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)(caddr_t, caddr_t, int);
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)(caddr_t, caddr_t, int);
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
138 xdrrec_create(
139 	XDR *xdrs,
140 	u_int sendsize,
141 	u_int recvsize,
142 	caddr_t tcp_handle,
143 	/* like read, but pass it a tcp_handle, not sock */
144 	int (*readit)(caddr_t, caddr_t, int),
145 	 /* like write, but pass it a tcp_handle, not sock */
146 	int (*writeit)(caddr_t, caddr_t, int)
147 	)
148 {
149 	RECSTREAM *rstrm = mem_alloc(sizeof(RECSTREAM));
150 
151 	if (rstrm == NULL) {
152 		(void)fprintf(stderr, "xdrrec_create: out of memory\n");
153 		/*
154 		 *  This is bad.  Should rework xdrrec_create to
155 		 *  return a handle, and in this case return NULL
156 		 */
157 		return;
158 	}
159 	/*
160 	 * adjust sizes and allocate buffer quad byte aligned
161 	 */
162 	rstrm->sendsize = sendsize = fix_buf_size(sendsize);
163 	rstrm->recvsize = recvsize = fix_buf_size(recvsize);
164 	rstrm->the_buffer = mem_alloc(sendsize + recvsize + BYTES_PER_XDR_UNIT);
165 	if (rstrm->the_buffer == NULL) {
166 		(void)fprintf(stderr, "xdrrec_create: out of memory\n");
167 		return;
168 	}
169 	for (rstrm->out_base = rstrm->the_buffer;
170 	     /* Pointer arithmetic - long cast allowed... */
171 		(u_long)rstrm->out_base % BYTES_PER_XDR_UNIT != 0;
172 		rstrm->out_base++);
173 	rstrm->in_base = rstrm->out_base + sendsize;
174 	/*
175 	 * now the rest ...
176 	 */
177 	xdrs->x_ops = &xdrrec_ops;
178 	xdrs->x_private = (caddr_t)rstrm;
179 	rstrm->tcp_handle = tcp_handle;
180 	rstrm->readit = readit;
181 	rstrm->writeit = writeit;
182 	rstrm->out_finger = rstrm->out_boundry = rstrm->out_base;
183 	rstrm->frag_header = (uint32_t *)(void *)rstrm->out_base;
184 	rstrm->out_finger += BYTES_PER_XDR_UNIT;
185 	rstrm->out_boundry += sendsize;
186 	rstrm->frag_sent = FALSE;
187 	rstrm->in_size = recvsize;
188 	rstrm->in_boundry = rstrm->in_base;
189 	rstrm->in_finger = (rstrm->in_boundry += recvsize);
190 	rstrm->fbtbc = 0;
191 	rstrm->last_frag = TRUE;
192 }
193 
194 
195 /*
196  * The reoutines defined below are the xdr ops which will go into the
197  * xdr handle filled in by xdrrec_create.
198  */
199 
200 static bool_t
201 xdrrec_getlong(XDR *xdrs, long *lp)
202 {
203 	RECSTREAM *rstrm = xdrs->x_private;
204 	int32_t *buflp = (void *)(rstrm->in_finger);
205 	uint32_t mylong;
206 
207 	/* first try the inline, fast case */
208 	if ((rstrm->fbtbc >= BYTES_PER_XDR_UNIT) &&
209 		(((long)rstrm->in_boundry - (long)buflp) >=
210 		 BYTES_PER_XDR_UNIT)) {
211 		*lp = (long)ntohl((uint32_t)(*buflp));
212 		rstrm->fbtbc -= BYTES_PER_XDR_UNIT;
213 		rstrm->in_finger += BYTES_PER_XDR_UNIT;
214 	} else {
215 		if (! xdrrec_getbytes(xdrs, (caddr_t)&mylong,
216 				      BYTES_PER_XDR_UNIT))
217 			return (FALSE);
218 		*lp = (long)(int32_t)ntohl(mylong);
219 	}
220 	return (TRUE);
221 }
222 
223 static bool_t
224 xdrrec_putlong(XDR *xdrs, long *lp)
225 {
226 	RECSTREAM *rstrm = xdrs->x_private;
227 	int32_t *dest_lp = (void *)(rstrm->out_finger);
228 
229 	if (rstrm->out_boundry - rstrm->out_finger < BYTES_PER_XDR_UNIT) {
230 		/*
231 		 * this case should almost never happen so the code is
232 		 * inefficient
233 		 */
234 		rstrm->frag_sent = TRUE;
235 		if (! flush_out(rstrm, FALSE))
236 			return (FALSE);
237 		dest_lp = ((int32_t *)(void *)(rstrm->out_finger));
238 	}
239 	rstrm->out_finger += BYTES_PER_XDR_UNIT;
240 	*dest_lp = (int32_t)htonl((uint32_t)(*lp));
241 	return (TRUE);
242 }
243 
244 static bool_t  /* must manage buffers, fragments, and records */
245 xdrrec_getbytes(XDR *xdrs, caddr_t addr, u_int len)
246 {
247 	RECSTREAM *rstrm = xdrs->x_private;
248 	u_int current;
249 
250 	while (len > 0) {
251 		current = rstrm->fbtbc;
252 		if (current == 0) {
253 			if (rstrm->last_frag)
254 				return (FALSE);
255 			if (! set_input_fragment(rstrm))
256 				return (FALSE);
257 			continue;
258 		}
259 		current = (len < current) ? len : current;
260 		if (! get_input_bytes(rstrm, addr, current))
261 			return (FALSE);
262 		addr += current;
263 		rstrm->fbtbc -= current;
264 		len -= current;
265 	}
266 	return (TRUE);
267 }
268 
269 static bool_t
270 xdrrec_putbytes(XDR *xdrs, caddr_t addr, u_int len)
271 {
272 	RECSTREAM *rstrm = xdrs->x_private;
273 	size_t current;
274 
275 	while (len > 0) {
276 		current = (size_t) ((long)rstrm->out_boundry -
277 				 (long)rstrm->out_finger);
278 		current = (len < current) ? len : current;
279 		memmove(rstrm->out_finger, addr, current);
280 		rstrm->out_finger += current;
281 		addr += current;
282 		len -= current;
283 		if (rstrm->out_finger == rstrm->out_boundry) {
284 			rstrm->frag_sent = TRUE;
285 			if (! flush_out(rstrm, FALSE))
286 				return (FALSE);
287 		}
288 	}
289 	return (TRUE);
290 }
291 
292 static u_int
293 xdrrec_getpos(XDR *xdrs)
294 {
295 	RECSTREAM *rstrm = xdrs->x_private;
296 	int pos;
297 
298 	switch (xdrs->x_op) {
299 
300 	case XDR_ENCODE:
301 		pos = rstrm->out_finger - rstrm->out_base
302 			- BYTES_PER_XDR_UNIT;
303 		break;
304 
305 	case XDR_DECODE:
306 		pos = rstrm->in_boundry - rstrm->in_finger
307 			- BYTES_PER_XDR_UNIT;
308 		break;
309 
310 	default:
311 		pos = -1;
312 		break;
313 	}
314 	return ((u_int) pos);
315 }
316 
317 static bool_t
318 xdrrec_setpos(XDR *xdrs, u_int pos)
319 {
320 	RECSTREAM *rstrm = xdrs->x_private;
321 	u_int currpos = xdrrec_getpos(xdrs);
322 	int delta = currpos - pos;
323 	caddr_t newpos;
324 
325 	if ((int)currpos != -1)
326 		switch (xdrs->x_op) {
327 
328 		case XDR_ENCODE:
329 			newpos = rstrm->out_finger - delta;
330 			if ((newpos > (caddr_t)(rstrm->frag_header)) &&
331 				(newpos < rstrm->out_boundry)) {
332 				rstrm->out_finger = newpos;
333 				return (TRUE);
334 			}
335 			break;
336 
337 		case XDR_DECODE:
338 			newpos = rstrm->in_finger - delta;
339 			if ((delta < (int)(rstrm->fbtbc)) &&
340 				(newpos <= rstrm->in_boundry) &&
341 				(newpos >= rstrm->in_base)) {
342 				rstrm->in_finger = newpos;
343 				rstrm->fbtbc -= delta;
344 				return (TRUE);
345 			}
346 			break;
347 
348 		case XDR_FREE:
349 			break;
350 		}
351 	return (FALSE);
352 }
353 
354 static rpc_inline_t *
355 xdrrec_inline(XDR *xdrs, int len)
356 {
357 	RECSTREAM *rstrm = xdrs->x_private;
358 	rpc_inline_t * buf = NULL;
359 
360 	if (len < 0)
361 		return (FALSE);
362 
363 	switch (xdrs->x_op) {
364 
365 	case XDR_ENCODE:
366 		if (len <= (rstrm->out_boundry - rstrm->out_finger)) {
367 			buf = (rpc_inline_t *)(void *) rstrm->out_finger;
368 			rstrm->out_finger += len;
369 		}
370 		break;
371 
372 	case XDR_DECODE:
373 		if ((len <= rstrm->fbtbc) &&
374 			(len <= (rstrm->in_boundry - rstrm->in_finger))) {
375 			buf = (rpc_inline_t *)(void *) rstrm->in_finger;
376 			rstrm->fbtbc -= len;
377 			rstrm->in_finger += len;
378 		}
379 		break;
380 
381 	case XDR_FREE:
382 	        break;
383 	}
384 	return (buf);
385 }
386 
387 static void
388 xdrrec_destroy(XDR *xdrs)
389 {
390 	RECSTREAM *rstrm = xdrs->x_private;
391 
392 	mem_free(rstrm->the_buffer,
393 		rstrm->sendsize + rstrm->recvsize + BYTES_PER_XDR_UNIT);
394 	mem_free((caddr_t)rstrm, sizeof(RECSTREAM));
395 }
396 
397 
398 /*
399  * Exported routines to manage xdr records
400  */
401 
402 /*
403  * Before reading (deserializing from the stream, one should always call
404  * this procedure to guarantee proper record alignment.
405  */
406 bool_t
407 xdrrec_skiprecord(XDR *xdrs)
408 {
409 	RECSTREAM *rstrm = xdrs->x_private;
410 
411 	while (rstrm->fbtbc > 0 || (! rstrm->last_frag)) {
412 		if (! skip_input_bytes(rstrm, rstrm->fbtbc))
413 			return (FALSE);
414 		rstrm->fbtbc = 0;
415 		if ((! rstrm->last_frag) && (! set_input_fragment(rstrm)))
416 			return (FALSE);
417 	}
418 	rstrm->last_frag = FALSE;
419 	return (TRUE);
420 }
421 
422 /*
423  * Look ahead function.
424  * Returns TRUE iff there is no more input in the buffer
425  * after consuming the rest of the current record.
426  */
427 bool_t
428 xdrrec_eof(XDR *xdrs)
429 {
430 	RECSTREAM *rstrm = xdrs->x_private;
431 
432 	while (rstrm->fbtbc > 0 || (! rstrm->last_frag)) {
433 		if (! skip_input_bytes(rstrm, rstrm->fbtbc))
434 			return (TRUE);
435 		rstrm->fbtbc = 0;
436 		if ((! rstrm->last_frag) && (! set_input_fragment(rstrm)))
437 			return (TRUE);
438 	}
439 	if (rstrm->in_finger == rstrm->in_boundry)
440 		return (TRUE);
441 	return (FALSE);
442 }
443 
444 /*
445  * The client must tell the package when an end-of-record has occurred.
446  * The second paraemters tells whether the record should be flushed to the
447  * (output) tcp stream.  (This lets the package support batched or
448  * pipelined procedure calls.)  TRUE => immediate flush to tcp connection.
449  */
450 bool_t
451 xdrrec_endofrecord(XDR *xdrs, bool_t sendnow)
452 {
453 	RECSTREAM *rstrm = xdrs->x_private;
454 	uint32_t len;  /* fragment length */
455 
456 	if (sendnow || rstrm->frag_sent ||
457 		((long)rstrm->out_finger + BYTES_PER_XDR_UNIT >=
458 		(long)rstrm->out_boundry)) {
459 		rstrm->frag_sent = FALSE;
460 		return (flush_out(rstrm, TRUE));
461 	}
462 	len = (long)(rstrm->out_finger) - (long)(rstrm->frag_header) -
463 	   BYTES_PER_XDR_UNIT;
464 	*(rstrm->frag_header) = htonl((uint32_t)len | LAST_FRAG);
465 	rstrm->frag_header = (uint32_t *)(void *)rstrm->out_finger;
466 	rstrm->out_finger += BYTES_PER_XDR_UNIT;
467 	return (TRUE);
468 }
469 
470 
471 /*
472  * Internal useful routines
473  */
474 static bool_t
475 flush_out(RECSTREAM *rstrm, bool_t eor)
476 {
477 	uint32_t eormask = (eor == TRUE) ? LAST_FRAG : 0;
478 	uint32_t len = (u_long)(rstrm->out_finger) -
479 		(u_long)(rstrm->frag_header) - BYTES_PER_XDR_UNIT;
480 
481 	*(rstrm->frag_header) = htonl(len | eormask);
482 	len = (u_long)(rstrm->out_finger) - (u_long)(rstrm->out_base);
483 	if ((*(rstrm->writeit))(rstrm->tcp_handle, rstrm->out_base, (int)len)
484 		!= (int)len)
485 		return (FALSE);
486 	rstrm->frag_header = (uint32_t *)(void *)rstrm->out_base;
487 	rstrm->out_finger = (caddr_t)rstrm->out_base + BYTES_PER_XDR_UNIT;
488 	return (TRUE);
489 }
490 
491 static bool_t  /* knows nothing about records!  Only about input buffers */
492 fill_input_buf(RECSTREAM *rstrm)
493 {
494 	caddr_t where;
495 	u_int i;
496 	int len;
497 
498 	where = rstrm->in_base;
499 	i = (u_int)((u_long)rstrm->in_boundry % BYTES_PER_XDR_UNIT);
500 	where += i;
501 	len = rstrm->in_size - i;
502 	if ((len = (*(rstrm->readit))(rstrm->tcp_handle, where, len)) == -1)
503 		return (FALSE);
504 	rstrm->in_finger = where;
505 	where += len;
506 	rstrm->in_boundry = where;
507 	return (TRUE);
508 }
509 
510 static bool_t  /* knows nothing about records!  Only about input buffers */
511 get_input_bytes(RECSTREAM *rstrm, caddr_t addr, int len)
512 {
513 	size_t current;
514 
515 	while (len > 0) {
516 		current = (size_t)((long)rstrm->in_boundry -
517 				(long)rstrm->in_finger);
518 		if (current == 0) {
519 			if (! fill_input_buf(rstrm))
520 				return (FALSE);
521 			continue;
522 		}
523 		current = ((size_t)len < current) ? (size_t)len : current;
524 		memmove(addr, rstrm->in_finger, current);
525 		rstrm->in_finger += current;
526 		addr += current;
527 		len -= current;
528 	}
529 	return (TRUE);
530 }
531 
532 static bool_t  /* next four bytes of input stream are treated as a header */
533 set_input_fragment(RECSTREAM *rstrm)
534 {
535 	uint32_t header;
536 
537 	if (! get_input_bytes(rstrm, (caddr_t)&header, sizeof(header)))
538 		return (FALSE);
539 	header = ntohl(header);
540 	rstrm->last_frag = ((header & LAST_FRAG) == 0) ? FALSE : TRUE;
541 	rstrm->fbtbc = header & (~LAST_FRAG);
542 	return (TRUE);
543 }
544 
545 static bool_t  /* consumes input bytes; knows nothing about records! */
546 skip_input_bytes(RECSTREAM *rstrm, int32_t cnt)
547 {
548 	int current;
549 
550 	while (cnt > 0) {
551 		current = (int)((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 = (cnt < current) ? cnt : current;
559 		rstrm->in_finger += current;
560 		cnt -= current;
561 	}
562 	return (TRUE);
563 }
564 
565 static u_int
566 fix_buf_size(u_int s)
567 {
568 
569 	if (s < 100)
570 		s = 4000;
571 	return (RNDUP(s));
572 }
573