1 /* $OpenBSD: xdr_rec.c,v 1.11 2005/08/08 08:05:36 espie 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
xdrrec_create(XDR * xdrs,u_int sendsize,u_int recvsize,caddr_t tcp_handle,int (* readit)(caddr_t,caddr_t,int),int (* writeit)(caddr_t,caddr_t,int))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
xdrrec_getlong(XDR * xdrs,long int * lp)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
xdrrec_putlong(XDR * xdrs,long int * lp)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 */
xdrrec_getbytes(XDR * xdrs,caddr_t addr,u_int len)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
xdrrec_putbytes(XDR * xdrs,caddr_t addr,u_int len)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
xdrrec_getpos(XDR * xdrs)285 xdrrec_getpos(XDR *xdrs)
286 {
287 RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
288 long pos;
289
290 pos = lseek((int)(long)rstrm->tcp_handle, (off_t)0, 1);
291 if (pos != -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
xdrrec_setpos(XDR * xdrs,u_int pos)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 *
xdrrec_inline(XDR * xdrs,u_int len)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
xdrrec_destroy(XDR * xdrs)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
xdrrec_skiprecord(XDR * xdrs)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
xdrrec_eof(XDR * xdrs)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
xdrrec_endofrecord(XDR * xdrs,int32_t sendnow)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
flush_out(RECSTREAM * rstrm,int32_t eor)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 */
fill_input_buf(RECSTREAM * rstrm)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 */
get_input_bytes(RECSTREAM * rstrm,caddr_t addr,int len)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 */
set_input_fragment(RECSTREAM * rstrm)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! */
skip_input_bytes(RECSTREAM * rstrm,long int cnt)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
fix_buf_size(u_int s)549 fix_buf_size(u_int s)
550 {
551
552 if (s < 100)
553 s = 4000;
554 return (RNDUP(s));
555 }
556