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