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: #ifndef lint 30: static char sccsid[] = "@(#)xdr_rec.c 1.5 85/03/14 Copyr 1984 Sun Micro"; 31: #endif 32: 33: /* 34: * xdr_rec.c, Implements TCP/IP based XDR streams with a "record marking" 35: * layer above tcp (for rpc's use). 36: * 37: * Copyright (C) 1984, Sun Microsystems, Inc. 38: * 39: * These routines interface XDRSTREAMS to a tcp/ip connection. 40: * There is a record marking layer between the xdr stream 41: * and the tcp transport level. A record is composed on one or more 42: * record fragments. A record fragment is a thirty-two bit header followed 43: * by n bytes of data, where n is contained in the header. The header 44: * is represented as a htonl(u_long). Thegh order bit encodes 45: * whether or not the fragment is the last fragment of the record 46: * (1 => fragment is last, 0 => more fragments to follow. 47: * The other 31 bits encode the byte length of the fragment. 48: */ 49: 50: #include <stdio.h> 51: #include "types.h" 52: #include "xdr.h" 53: #include <sys/time.h> 54: #include <netinet/in.h> 55: 56: char *mem_alloc(); 57: 58: static u_int fix_buf_size(); 59: 60: static bool_t xdrrec_getlong(); 61: static bool_t xdrrec_putlong(); 62: static bool_t xdrrec_getbytes(); 63: static bool_t xdrrec_putbytes(); 64: static u_int xdrrec_getpos(); 65: static bool_t xdrrec_setpos(); 66: static long * xdrrec_inline(); 67: static void xdrrec_destroy(); 68: 69: static struct xdr_ops xdrrec_ops = { 70: xdrrec_getlong, 71: xdrrec_putlong, 72: xdrrec_getbytes, 73: xdrrec_putbytes, 74: xdrrec_getpos, 75: xdrrec_setpos, 76: xdrrec_inline, 77: xdrrec_destroy 78: }; 79: 80: /* 81: * A record is composed of one or more record fragments. 82: * A record fragment is a two-byte header followed by zero to 83: * 2**32-1 bytes. The header is treated as a long unsigned and is 84: * encode/decoded to the network via htonl/ntohl. The low order 31 bits 85: * are a byte count of the fragment. The highest order bit is a boolean: 86: * 1 => this fragment is the last fragment of the record, 87: * 0 => this fragment is followed by more fragment(s). 88: * 89: * The fragment/record machinery is not general; it is constructed to 90: * meet the needs of xdr and rpc based on tcp. 91: */ 92: 93: #define LAST_FRAG ((u_long)(1 << 31)) 94: 95: typedef struct rec_strm { 96: caddr_t tcp_handle; 97: /* 98: * out-goung bits 99: */ 100: int (*writeit)(); 101: caddr_t out_base; /* output buffer (points to frag header) */ 102: caddr_t out_finger; /* next output position */ 103: caddr_t out_boundry; /* data cannot up to this address */ 104: u_long *frag_header; /* beginning of curren fragment */ 105: bool_t frag_sent; /* true if buffer sent in middle of record */ 106: /* 107: * in-coming bits 108: */ 109: int (*readit)(); 110: u_long in_size; /* fixed size of the input buffer */ 111: caddr_t in_base; 112: caddr_t in_finger; /* location of next byte to be had */ 113: caddr_t in_boundry; /* can read up to this location */ 114: long fbtbc; /* fragment bytes to be consumed */ 115: bool_t last_frag; 116: u_int sendsize; 117: u_int recvsize; 118: } RECSTREAM; 119: 120: 121: /* 122: * Create an xdr handle for xdrrec 123: * xdrrec_create fills in xdrs. Sendsize and recvsize are 124: * send and recv buffer sizes (0 => use default). 125: * tcp_handle is an opaque handle that is passed as the first parameter to 126: * the procedures readit and writeit. Readit and writeit are read and 127: * write respectively. They are like the system 128: * calls expect that they take an opaque handle rather than an fd. 129: */ 130: void 131: xdrrec_create(xdrs, sendsize, recvsize, tcp_handle, readit, writeit) 132: register XDR *xdrs; 133: u_int sendsize; 134: u_int recvsize; 135: caddr_t tcp_handle; 136: int (*readit)(); /* like read, but pass it a tcp_handle, not sock */ 137: int (*writeit)(); /* like write, but pass it a tcp_handle, not sock */ 138: { 139: register RECSTREAM *rstrm = 140: (RECSTREAM *)mem_alloc(sizeof(RECSTREAM)); 141: 142: if (rstrm == NULL) { 143: fprintf(stderr, "xdrrec_create: out of memory\n"); 144: /* 145: * This is bad. Should rework xdrrec_create to 146: * return a handle, and in this case return NULL 147: */ 148: return; 149: } 150: xdrs->x_ops = &xdrrec_ops; 151: xdrs->x_private = (caddr_t)rstrm; 152: rstrm->tcp_handle = tcp_handle; 153: rstrm->readit = readit; 154: rstrm->writeit = writeit; 155: sendsize = fix_buf_size(sendsize); 156: if ((rstrm->out_base = rstrm->out_finger = rstrm->out_boundry = 157: mem_alloc(sendsize)) == NULL) { 158: fprintf(stderr, "xdrrec_create: out of memory\n"); 159: return; 160: } 161: rstrm->frag_header = (u_long *)rstrm->out_base; 162: rstrm->out_finger += sizeof(u_long); 163: rstrm->out_boundry += sendsize; 164: rstrm->frag_sent = FALSE; 165: rstrm->in_size = recvsize = fix_buf_size(recvsize); 166: if ((rstrm->in_base = rstrm->in_boundry=mem_alloc(recvsize)) == NULL) { 167: fprintf(stderr, "xdrrec_create: out of memory\n"); 168: return; 169: } 170: rstrm->in_finger = (rstrm->in_boundry += recvsize); 171: rstrm->fbtbc = 0; 172: rstrm->last_frag = TRUE; 173: rstrm->sendsize = sendsize; 174: rstrm->recvsize = recvsize; 175: } 176: 177: 178: /* 179: * The reoutines defined below are the xdr ops which will go into the 180: * xdr handle filled in by xdrrec_create. 181: */ 182: 183: static bool_t 184: xdrrec_getlong(xdrs, lp) 185: XDR *xdrs; 186: long *lp; 187: { 188: register RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private); 189: register long *buflp = (long *)(rstrm->in_finger); 190: long mylong; 191: 192: /* first try the inline, fast case */ 193: if ((rstrm->fbtbc >= sizeof(long)) && 194: (((int)rstrm->in_boundry - (int)buflp) >= sizeof(long))) { 195: *lp = ntohl(*buflp); 196: rstrm->fbtbc -= sizeof(long); 197: rstrm->in_finger += sizeof(long); 198: } else { 199: if (! xdrrec_getbytes(xdrs, (caddr_t)&mylong, sizeof(long))) 200: return (FALSE); 201: *lp = ntohl(mylong); 202: } 203: return (TRUE); 204: } 205: 206: static bool_t 207: xdrrec_putlong(xdrs, lp) 208: XDR *xdrs; 209: long *lp; 210: { 211: register RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private); 212: register long *dest_lp = ((long *)(rstrm->out_finger)); 213: 214: if ((rstrm->out_finger += sizeof(long)) > rstrm->out_boundry) { 215: /* 216: * this case should almost never happen so the code is 217: * inefficient 218: */ 219: rstrm->out_finger -= sizeof(long); 220: rstrm->frag_sent = TRUE; 221: if (! flush_out(rstrm, FALSE)) 222: return (FALSE); 223: dest_lp = ((long *)(rstrm->out_finger)); 224: rstrm->out_finger += sizeof(long); 225: } 226: *dest_lp = htonl(*lp); 227: return (TRUE); 228: } 229: 230: static bool_t /* must manage buffers, fragments, and records */ 231: xdrrec_getbytes(xdrs, addr, len) 232: XDR *xdrs; 233: register caddr_t addr; 234: register u_int len; 235: { 236: register RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private); 237: register int current; 238: 239: while (len > 0) { 240: current = rstrm->fbtbc; 241: if (current == 0) { 242: if (rstrm->last_frag) 243: return (FALSE); 244: if (! set_input_fragment(rstrm)) 245: return (FALSE); 246: continue; 247: } 248: current = (len < current) ? len : current; 249: if (! get_input_bytes(rstrm, addr, current)) 250: return (FALSE); 251: addr += current; 252: rstrm->fbtbc -= current; 253: len -= current; 254: } 255: return (TRUE); 256: } 257: 258: static bool_t 259: xdrrec_putbytes(xdrs, addr, len) 260: XDR *xdrs; 261: register caddr_t addr; 262: register u_int len; 263: { 264: register RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private); 265: register int current; 266: 267: while (len > 0) { 268: current = (u_int)rstrm->out_boundry - (u_int)rstrm->out_finger; 269: current = (len < current) ? len : current; 270: bcopy(addr, rstrm->out_finger, current); 271: rstrm->out_finger += current; 272: addr += current; 273: len -= current; 274: if (rstrm->out_finger == rstrm->out_boundry) { 275: rstrm->frag_sent = TRUE; 276: if (! flush_out(rstrm, FALSE)) 277: return (FALSE); 278: } 279: } 280: return (TRUE); 281: } 282: 283: static u_int 284: xdrrec_getpos(xdrs) 285: register XDR *xdrs; 286: { 287: register RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private; 288: register u_int pos; 289: 290: pos = lseek((int)rstrm->tcp_handle, 0, 1); 291: if ((int)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 = (u_int) -1; 304: break; 305: } 306: return (pos); 307: } 308: 309: static bool_t 310: xdrrec_setpos(xdrs, pos) 311: register XDR *xdrs; 312: u_int pos; 313: { 314: register RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private; 315: u_int currpos = xdrrec_getpos(xdrs); 316: int delta = currpos - pos; 317: caddr_t newpos; 318: 319: if ((int)currpos != -1) 320: switch (xdrs->x_op) { 321: 322: case XDR_ENCODE: 323: newpos = rstrm->out_finger - delta; 324: if ((newpos > (caddr_t)(rstrm->frag_header)) && 325: (newpos < rstrm->out_boundry)) { 326: rstrm->out_finger = newpos; 327: return (TRUE); 328: } 329: break; 330: 331: case XDR_DECODE: 332: newpos = rstrm->in_finger - delta; 333: if ((delta < (int)(rstrm->fbtbc)) && 334: (newpos <= rstrm->in_boundry) && 335: (newpos >= rstrm->in_base)) { 336: rstrm->in_finger = newpos; 337: rstrm->fbtbc -= delta; 338: return (TRUE); 339: } 340: break; 341: } 342: return (FALSE); 343: } 344: 345: static long * 346: xdrrec_inline(xdrs, len) 347: register XDR *xdrs; 348: int len; 349: { 350: register RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private; 351: long * buf = NULL; 352: 353: switch (xdrs->x_op) { 354: 355: case XDR_ENCODE: 356: if ((rstrm->out_finger + len) <= rstrm->out_boundry) { 357: buf = (long *) rstrm->out_finger; 358: rstrm->out_finger += len; 359: } 360: break; 361: 362: case XDR_DECODE: 363: if ((len <= rstrm->fbtbc) && 364: ((rstrm->in_finger + len) <= rstrm->in_boundry)) { 365: buf = (long *) rstrm->in_finger; 366: rstrm->fbtbc -= len; 367: rstrm->in_finger += len; 368: } 369: break; 370: } 371: return (buf); 372: } 373: 374: static void 375: xdrrec_destroy(xdrs) 376: register XDR *xdrs; 377: { 378: register RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private; 379: 380: mem_free(rstrm->out_base, rstrm->sendsize); 381: mem_free(rstrm->in_base, rstrm->recvsize); 382: mem_free((caddr_t)rstrm, sizeof(RECSTREAM)); 383: } 384: 385: 386: /* 387: * Exported routines to manage xdr records 388: */ 389: 390: /* 391: * Before reading (deserializing from the stream, one should always call 392: * this procedure to guarantee proper record alignment. 393: */ 394: bool_t 395: xdrrec_skiprecord(xdrs) 396: XDR *xdrs; 397: { 398: register RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private); 399: 400: while (rstrm->fbtbc > 0 || (! rstrm->last_frag)) { 401: if (! skip_input_bytes(rstrm, rstrm->fbtbc)) 402: return (FALSE); 403: rstrm->fbtbc = 0; 404: if ((! rstrm->last_frag) && (! set_input_fragment(rstrm))) 405: return (FALSE); 406: } 407: rstrm->last_frag = FALSE; 408: return (TRUE); 409: } 410: 411: /* 412: * Look ahead fuction. 413: * Returns TRUE iff there is no more input in the buffer 414: * after consuming the rest of the current record. 415: */ 416: bool_t 417: xdrrec_eof(xdrs) 418: XDR *xdrs; 419: { 420: register RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private); 421: 422: while (rstrm->fbtbc > 0 || (! rstrm->last_frag)) { 423: if (! skip_input_bytes(rstrm, rstrm->fbtbc)) 424: return (TRUE); 425: rstrm->fbtbc = 0; 426: if ((! rstrm->last_frag) && (! set_input_fragment(rstrm))) 427: return (TRUE); 428: } 429: if (rstrm->in_finger == rstrm->in_boundry) 430: return (TRUE); 431: return (FALSE); 432: } 433: 434: /* 435: * The client must tell the package when an end-of-record has occurred. 436: * The second paraemters tells whether the record should be flushed to the 437: * (output) tcp stream. (This let's the package support batched or 438: * pipelined procedure calls.) TRUE => immmediate flush to tcp connection. 439: */ 440: bool_t 441: xdrrec_endofrecord(xdrs, sendnow) 442: XDR *xdrs; 443: bool_t sendnow; 444: { 445: register RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private); 446: register u_long len; /* fragment length */ 447: 448: if (sendnow || rstrm->frag_sent || 449: ((u_long)rstrm->out_finger + sizeof(u_long) >= 450: (u_long)rstrm->out_boundry)) { 451: rstrm->frag_sent = FALSE; 452: return (flush_out(rstrm, TRUE)); 453: } 454: len = (u_long)(rstrm->out_finger) - (u_long)(rstrm->frag_header) - 455: sizeof(u_long); 456: *(rstrm->frag_header) = htonl(len | LAST_FRAG); 457: rstrm->frag_header = (u_long *)rstrm->out_finger; 458: rstrm->out_finger += sizeof(u_long); 459: return (TRUE); 460: } 461: 462: 463: /* 464: * Internal useful routines 465: */ 466: static bool_t 467: flush_out(rstrm, eor) 468: register RECSTREAM *rstrm; 469: bool_t eor; 470: { 471: register u_long eormask = (eor == TRUE) ? LAST_FRAG : 0; 472: register u_long len = (u_long)(rstrm->out_finger) - 473: (u_long)(rstrm->frag_header) - sizeof(u_long); 474: 475: *(rstrm->frag_header) = htonl(len | eormask); 476: len = (u_long)(rstrm->out_finger) - (u_long)(rstrm->out_base); 477: if ((*(rstrm->writeit))(rstrm->tcp_handle, rstrm->out_base, (int)len) 478: != (int)len) 479: return (FALSE); 480: rstrm->frag_header = (u_long *)rstrm->out_base; 481: rstrm->out_finger = (caddr_t)rstrm->out_base + sizeof(u_long); 482: return (TRUE); 483: } 484: 485: static bool_t /* knows nothing about records! Only about input buffers */ 486: fill_input_buf(rstrm) 487: register RECSTREAM *rstrm; 488: { 489: register caddr_t where = rstrm->in_base; 490: register int len = rstrm->in_size; 491: 492: if (((int)rstrm->in_boundry % 2) != 0) { 493: /* keep stream odd bytes aligned with memory odd bytes */ 494: where++; 495: len--; 496: } 497: if ((len = (*(rstrm->readit))(rstrm->tcp_handle, where, len)) == -1) 498: return (FALSE); 499: rstrm->in_finger = where; 500: where += len; 501: rstrm->in_boundry = where; 502: return (TRUE); 503: } 504: 505: static bool_t /* knows nothing about records! Only about input buffers */ 506: get_input_bytes(rstrm, addr, len) 507: register RECSTREAM *rstrm; 508: register caddr_t addr; 509: register int len; 510: { 511: register int current; 512: 513: while (len > 0) { 514: current = (int)rstrm->in_boundry - (int)rstrm->in_finger; 515: if (current == 0) { 516: if (! fill_input_buf(rstrm)) 517: return (FALSE); 518: continue; 519: } 520: current = (len < current) ? len : current; 521: bcopy(rstrm->in_finger, addr, current); 522: rstrm->in_finger += current; 523: addr += current; 524: len -= current; 525: } 526: return (TRUE); 527: } 528: 529: static bool_t /* next two bytes of the input stream are treated as a header */ 530: set_input_fragment(rstrm) 531: register RECSTREAM *rstrm; 532: { 533: u_long header; 534: 535: if (! get_input_bytes(rstrm, (caddr_t)&header, sizeof(header))) 536: return (FALSE); 537: header = ntohl(header); 538: rstrm->last_frag = ((header & LAST_FRAG) == 0) ? FALSE : TRUE; 539: rstrm->fbtbc = header & (~LAST_FRAG); 540: return (TRUE); 541: } 542: 543: static bool_t /* consumes input bytes; knows nothing about records! */ 544: skip_input_bytes(rstrm, cnt) 545: register RECSTREAM *rstrm; 546: int cnt; 547: { 548: register int current; 549: 550: while (cnt > 0) { 551: current = (int)rstrm->in_boundry - (int)rstrm->in_finger; 552: if (current == 0) { 553: if (! fill_input_buf(rstrm)) 554: return (FALSE); 555: continue; 556: } 557: current = (cnt < current) ? cnt : current; 558: rstrm->in_finger += current; 559: cnt -= current; 560: } 561: return (TRUE); 562: } 563: 564: static u_int 565: fix_buf_size(s) 566: register u_int s; 567: { 568: 569: if (s < 100) 570: return (3998); 571: for (; (s % 4) != 2; --s); 572: return (s); 573: }