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