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