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