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