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