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