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