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