1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 27 /* All Rights Reserved */ 28 /* 29 * Portions of this source code were derived from Berkeley 30 * 4.3 BSD under license from the Regents of the University of 31 * California. 32 */ 33 34 #pragma ident "%Z%%M% %I% %E% SMI" 35 36 /* 37 * xdr_rec.c, Implements (TCP/IP based) XDR streams with a "record marking" 38 * layer above connection oriented transport layer (e.g. tcp) (for rpc's use). 39 * 40 * 41 * These routines interface XDRSTREAMS to a (tcp/ip) connection transport. 42 * There is a record marking layer between the xdr stream 43 * and the (tcp) cv transport level. A record is composed on one or more 44 * record fragments. A record fragment is a thirty-two bit header followed 45 * by n bytes of data, where n is contained in the header. The header 46 * is represented as a htonl(ulong_t). The order bit encodes 47 * whether or not the fragment is the last fragment of the record 48 * (1 => fragment is last, 0 => more fragments to follow. 49 * The other 31 bits encode the byte length of the fragment. 50 */ 51 52 #include "mt.h" 53 #include "rpc_mt.h" 54 #include <stdio.h> 55 #include <rpc/types.h> 56 #include <rpc/rpc.h> 57 #include <sys/types.h> 58 #include <rpc/trace.h> 59 #include <syslog.h> 60 #include <memory.h> 61 #include <stdlib.h> 62 #include <unistd.h> 63 #include <inttypes.h> 64 #include <string.h> 65 66 static uint_t fix_buf_size(); 67 static struct xdr_ops *xdrrec_ops(); 68 static bool_t xdrrec_getbytes(); 69 static bool_t flush_out(); 70 static bool_t get_input_bytes(); 71 static bool_t set_input_fragment(); 72 static bool_t skip_input_bytes(); 73 74 bool_t __xdrrec_getbytes_nonblock(); 75 76 /* 77 * A record is composed of one or more record fragments. 78 * A record fragment is a four-byte header followed by zero to 79 * 2**32-1 bytes. The header is treated as a long unsigned and is 80 * encode/decoded to the network via htonl/ntohl. The low order 31 bits 81 * are a byte count of the fragment. The highest order bit is a boolean: 82 * 1 => this fragment is the last fragment of the record, 83 * 0 => this fragment is followed by more fragment(s). 84 * 85 * The fragment/record machinery is not general; it is constructed to 86 * meet the needs of xdr and rpc based on tcp. 87 */ 88 89 #define LAST_FRAG (((uint32_t)1 << 31)) 90 91 /* 92 * Minimum fragment size is size of rpc callmsg over TCP: 93 * xid direction vers prog vers proc 94 * cred flavor, cred length, cred 95 * verf flavor, verf length, verf 96 * (with no cred or verf allocated) 97 */ 98 #define MIN_FRAG (10 * BYTES_PER_XDR_UNIT) 99 100 typedef struct rec_strm { 101 caddr_t tcp_handle; 102 /* 103 * out-going bits 104 */ 105 int (*writeit)(); 106 caddr_t out_base; /* output buffer (points to frag header) */ 107 caddr_t out_finger; /* next output position */ 108 caddr_t out_boundry; /* data cannot up to this address */ 109 uint32_t *frag_header; /* beginning of current fragment */ 110 bool_t frag_sent; /* true if buffer sent in middle of record */ 111 /* 112 * in-coming bits 113 */ 114 int (*readit)(); 115 caddr_t in_base; /* input buffer */ 116 caddr_t in_finger; /* location of next byte to be had */ 117 caddr_t in_boundry; /* can read up to this location */ 118 int fbtbc; /* fragment bytes to be consumed */ 119 bool_t last_frag; 120 uint_t sendsize; 121 uint_t recvsize; 122 /* 123 * Is this the first time that the 124 * getbytes routine has been called ? 125 */ 126 uint_t firsttime; 127 /* 128 * Is this non-blocked? 129 */ 130 uint_t in_nonblock; /* non-blocked input */ 131 uint_t in_needpoll; /* need to poll to get more data ? */ 132 uint32_t in_maxrecsz; /* maximum record size */ 133 caddr_t in_nextrec; /* start of next record */ 134 uint32_t in_nextrecsz; /* part of next record in buffer */ 135 } RECSTREAM; 136 137 /* 138 * Create an xdr handle for xdrrec 139 * xdrrec_create fills in xdrs. Sendsize and recvsize are 140 * send and recv buffer sizes (0 => use default). 141 * vc_handle is an opaque handle that is passed as the first parameter to 142 * the procedures readit and writeit. Readit and writeit are read and 143 * write respectively. They are like the system calls expect that they 144 * take an opaque handle rather than an fd. 145 */ 146 147 static const char mem_err_msg_rec[] = "xdrrec_create: out of memory"; 148 149 void 150 xdrrec_create(XDR *xdrs, uint_t sendsize, uint_t recvsize, caddr_t tcp_handle, 151 int (*readit)(), int (*writeit)()) 152 { 153 RECSTREAM *rstrm = 154 (RECSTREAM *)mem_alloc(sizeof (RECSTREAM)); 155 156 /* 157 * XXX: Should still rework xdrrec_create to return a handle, 158 * and in any malloc-failure case return NULL. 159 */ 160 trace3(TR_xdrrec_create, 0, sendsize, recvsize); 161 if (rstrm == NULL) { 162 (void) syslog(LOG_ERR, mem_err_msg_rec); 163 trace1(TR_xdrrec_create, 1); 164 return; 165 } 166 /* 167 * Adjust sizes and allocate buffers; malloc(3C), for which mem_alloc 168 * is a wrapper, provides a buffer suitably aligned for any use, so 169 * there's no need for us to mess around with alignment. 170 * 171 * Since non-blocking connections may need to reallocate the input 172 * buffer, we use separate mem_alloc()s for input and output. 173 */ 174 rstrm->sendsize = sendsize = fix_buf_size(sendsize); 175 rstrm->recvsize = recvsize = fix_buf_size(recvsize); 176 rstrm->out_base = (caddr_t)mem_alloc(rstrm->sendsize); 177 if (rstrm->out_base == NULL) { 178 (void) syslog(LOG_ERR, mem_err_msg_rec); 179 (void) mem_free((char *)rstrm, sizeof (RECSTREAM)); 180 trace1(TR_xdrrec_create, 1); 181 return; 182 } 183 rstrm->in_base = (caddr_t)mem_alloc(rstrm->recvsize); 184 if (rstrm->in_base == NULL) { 185 (void) syslog(LOG_ERR, mem_err_msg_rec); 186 (void) mem_free(rstrm->out_base, rstrm->sendsize); 187 (void) mem_free(rstrm, sizeof (RECSTREAM)); 188 trace1(TR_xdrrec_create, 1); 189 return; 190 } 191 192 /* 193 * now the rest ... 194 */ 195 196 xdrs->x_ops = xdrrec_ops(); 197 xdrs->x_private = (caddr_t)rstrm; 198 rstrm->tcp_handle = tcp_handle; 199 rstrm->readit = readit; 200 rstrm->writeit = writeit; 201 rstrm->out_finger = rstrm->out_boundry = rstrm->out_base; 202 rstrm->frag_header = (uint32_t *)rstrm->out_base; 203 rstrm->out_finger += sizeof (uint_t); 204 rstrm->out_boundry += sendsize; 205 rstrm->frag_sent = FALSE; 206 rstrm->in_boundry = rstrm->in_base; 207 rstrm->in_finger = (rstrm->in_boundry += recvsize); 208 rstrm->fbtbc = 0; 209 rstrm->last_frag = TRUE; 210 rstrm->firsttime = 0; 211 rstrm->in_nonblock = 0; 212 rstrm->in_needpoll = 1; 213 rstrm->in_maxrecsz = 0; 214 rstrm->in_nextrec = rstrm->in_base; 215 rstrm->in_nextrecsz = 0; 216 217 trace1(TR_xdrrec_create, 1); 218 } 219 220 /* 221 * Align input stream. If all applications behaved correctly, this 222 * defensive procedure will not be necessary, since received data will be 223 * aligned correctly. 224 */ 225 static void 226 align_instream(RECSTREAM *rstrm) 227 { 228 int current = rstrm->in_boundry - rstrm->in_finger; 229 230 (void) memcpy(rstrm->in_base, rstrm->in_finger, current); 231 rstrm->in_finger = rstrm->in_base; 232 rstrm->in_boundry = rstrm->in_finger + current; 233 } 234 235 /* 236 * The routines defined below are the xdr ops which will go into the 237 * xdr handle filled in by xdrrec_create. 238 */ 239 static bool_t 240 xdrrec_getint32(XDR *xdrs, int32_t *ip) 241 { 242 RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private); 243 int32_t *buflp = (int32_t *)(rstrm->in_finger); 244 int32_t mylong; 245 246 trace1(TR_xdrrec_getint32, 0); 247 /* first try the inline, fast case */ 248 if ((rstrm->fbtbc >= (int)sizeof (int32_t)) && 249 ((uint_t)(rstrm->in_boundry - (caddr_t)buflp) >= 250 (uint_t)sizeof (int32_t))) { 251 /* 252 * Check if buflp is longword aligned. If not, align it. 253 */ 254 if (((uintptr_t)buflp) & ((int)sizeof (int32_t) - 1)) { 255 align_instream(rstrm); 256 buflp = (int32_t *)(rstrm->in_finger); 257 } 258 *ip = (int32_t)ntohl((uint32_t)(*buflp)); 259 rstrm->fbtbc -= (int)sizeof (int32_t); 260 rstrm->in_finger += sizeof (int32_t); 261 } else { 262 if (!xdrrec_getbytes(xdrs, &mylong, sizeof (int32_t))) { 263 trace1(TR_xdrrec_getint32_t, 1); 264 return (FALSE); 265 } 266 *ip = (int32_t)ntohl((uint32_t)mylong); 267 } 268 trace1(TR_xdrrec_getint32, 1); 269 return (TRUE); 270 } 271 272 static bool_t 273 xdrrec_putint32(XDR *xdrs, int32_t *ip) 274 { 275 RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private); 276 int32_t *dest_lp = ((int32_t *)(rstrm->out_finger)); 277 278 trace1(TR_xdrrec_putint32, 0); 279 if ((rstrm->out_finger += sizeof (int32_t)) > rstrm->out_boundry) { 280 /* 281 * this case should almost never happen so the code is 282 * inefficient 283 */ 284 rstrm->out_finger -= sizeof (int32_t); 285 rstrm->frag_sent = TRUE; 286 if (! flush_out(rstrm, FALSE)) { 287 trace1(TR_xdrrec_putint32_t, 1); 288 return (FALSE); 289 } 290 dest_lp = ((int32_t *)(rstrm->out_finger)); 291 rstrm->out_finger += sizeof (int32_t); 292 } 293 *dest_lp = (int32_t)htonl((uint32_t)(*ip)); 294 trace1(TR_xdrrec_putint32, 1); 295 return (TRUE); 296 } 297 298 static bool_t 299 xdrrec_getlong(XDR *xdrs, long *lp) 300 { 301 int32_t i; 302 303 if (!xdrrec_getint32(xdrs, &i)) 304 return (FALSE); 305 306 *lp = (long)i; 307 308 return (TRUE); 309 } 310 311 static bool_t 312 xdrrec_putlong(XDR *xdrs, long *lp) 313 { 314 int32_t i; 315 316 #if defined(_LP64) 317 if ((*lp > INT32_MAX) || (*lp < INT32_MIN)) { 318 return (FALSE); 319 } 320 #endif 321 322 i = (int32_t)*lp; 323 324 return (xdrrec_putint32(xdrs, &i)); 325 } 326 327 static bool_t /* must manage buffers, fragments, and records */ 328 xdrrec_getbytes(XDR *xdrs, caddr_t addr, int len) 329 { 330 RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private); 331 int current; 332 333 trace2(TR_xdrrec_getbytes, 0, len); 334 while (len > 0) { 335 current = rstrm->fbtbc; 336 if (current == 0) { 337 if (rstrm->last_frag) { 338 trace1(TR_xdrrec_getbytes, 1); 339 return (FALSE); 340 } 341 if (! set_input_fragment(rstrm)) { 342 trace1(TR_xdrrec_getbytes, 1); 343 return (FALSE); 344 } 345 continue; 346 } 347 current = (len < current) ? len : current; 348 if (! get_input_bytes(rstrm, addr, current, FALSE)) { 349 trace1(TR_xdrrec_getbytes, 1); 350 return (FALSE); 351 } 352 addr += current; 353 rstrm->fbtbc -= current; 354 len -= current; 355 } 356 trace1(TR_xdrrec_getbytes, 1); 357 return (TRUE); 358 } 359 360 static bool_t 361 xdrrec_putbytes(XDR *xdrs, caddr_t addr, int len) 362 { 363 RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private); 364 int current; 365 366 trace2(TR_xdrrec_putbytes, 0, len); 367 while (len > 0) { 368 369 current = (uintptr_t)rstrm->out_boundry - 370 (uintptr_t)rstrm->out_finger; 371 current = (len < current) ? len : current; 372 (void) memcpy(rstrm->out_finger, addr, current); 373 rstrm->out_finger += current; 374 addr += current; 375 len -= current; 376 if (rstrm->out_finger == rstrm->out_boundry) { 377 rstrm->frag_sent = TRUE; 378 if (! flush_out(rstrm, FALSE)) { 379 trace1(TR_xdrrec_putbytes, 1); 380 return (FALSE); 381 } 382 } 383 } 384 trace1(TR_xdrrec_putbytes, 1); 385 return (TRUE); 386 } 387 /* 388 * This is just like the ops vector x_getbytes(), except that 389 * instead of returning success or failure on getting a certain number 390 * of bytes, it behaves much more like the read() system call against a 391 * pipe -- it returns up to the number of bytes requested and a return of 392 * zero indicates end-of-record. A -1 means something very bad happened. 393 */ 394 uint_t /* must manage buffers, fragments, and records */ 395 xdrrec_readbytes(XDR *xdrs, caddr_t addr, uint_t l) 396 { 397 RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private); 398 int current, len; 399 400 len = l; 401 while (len > 0) { 402 current = rstrm->fbtbc; 403 if (current == 0) { 404 if (rstrm->last_frag) 405 return (l - len); 406 if (! set_input_fragment(rstrm)) 407 return ((uint_t)-1); 408 continue; 409 } 410 current = (len < current) ? len : current; 411 if (! get_input_bytes(rstrm, addr, current, FALSE)) 412 return ((uint_t)-1); 413 addr += current; 414 rstrm->fbtbc -= current; 415 len -= current; 416 } 417 return (l - len); 418 } 419 420 static uint_t 421 xdrrec_getpos(XDR *xdrs) 422 { 423 RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private; 424 int32_t pos; 425 426 trace1(TR_xdrrec_getpos, 0); 427 pos = lseek((intptr_t)rstrm->tcp_handle, 0, 1); 428 if (pos != -1) 429 switch (xdrs->x_op) { 430 431 case XDR_ENCODE: 432 pos += rstrm->out_finger - rstrm->out_base; 433 break; 434 435 case XDR_DECODE: 436 pos -= rstrm->in_boundry - rstrm->in_finger; 437 break; 438 439 default: 440 pos = (uint_t)-1; 441 break; 442 } 443 trace1(TR_xdrrec_getpos, 1); 444 return ((uint_t)pos); 445 } 446 447 static bool_t 448 xdrrec_setpos(XDR *xdrs, uint_t pos) 449 { 450 RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private; 451 uint_t currpos = xdrrec_getpos(xdrs); 452 int delta = currpos - pos; 453 caddr_t newpos; 454 455 trace2(TR_xdrrec_setpos, 0, pos); 456 if ((int)currpos != -1) 457 switch (xdrs->x_op) { 458 459 case XDR_ENCODE: 460 newpos = rstrm->out_finger - delta; 461 if ((newpos > (caddr_t)(rstrm->frag_header)) && 462 (newpos < rstrm->out_boundry)) { 463 rstrm->out_finger = newpos; 464 trace1(TR_xdrrec_setpos, 1); 465 return (TRUE); 466 } 467 break; 468 469 case XDR_DECODE: 470 newpos = rstrm->in_finger - delta; 471 if ((delta < (int)(rstrm->fbtbc)) && 472 (newpos <= rstrm->in_boundry) && 473 (newpos >= rstrm->in_base)) { 474 rstrm->in_finger = newpos; 475 rstrm->fbtbc -= delta; 476 trace1(TR_xdrrec_setpos, 1); 477 return (TRUE); 478 } 479 break; 480 } 481 trace1(TR_xdrrec_setpos, 1); 482 return (FALSE); 483 } 484 485 static rpc_inline_t * 486 xdrrec_inline(XDR *xdrs, int len) 487 { 488 RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private; 489 rpc_inline_t *buf = NULL; 490 491 trace2(TR_xdrrec_inline, 0, len); 492 switch (xdrs->x_op) { 493 494 case XDR_ENCODE: 495 if ((rstrm->out_finger + len) <= rstrm->out_boundry) { 496 buf = (rpc_inline_t *)rstrm->out_finger; 497 rstrm->out_finger += len; 498 } 499 break; 500 501 case XDR_DECODE: 502 if ((len <= rstrm->fbtbc) && 503 ((rstrm->in_finger + len) <= rstrm->in_boundry)) { 504 /* 505 * Check if rstrm->in_finger is longword aligned; 506 * if not, align it. 507 */ 508 if (((intptr_t)rstrm->in_finger) & 509 (sizeof (int32_t) - 1)) 510 align_instream(rstrm); 511 buf = (rpc_inline_t *)rstrm->in_finger; 512 rstrm->fbtbc -= len; 513 rstrm->in_finger += len; 514 } 515 break; 516 } 517 trace1(TR_xdrrec_inline, 1); 518 return (buf); 519 } 520 521 static void 522 xdrrec_destroy(XDR *xdrs) 523 { 524 RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private; 525 526 trace1(TR_xdrrec_destroy, 0); 527 mem_free(rstrm->out_base, rstrm->sendsize); 528 mem_free(rstrm->in_base, rstrm->recvsize); 529 mem_free((caddr_t)rstrm, sizeof (RECSTREAM)); 530 trace1(TR_xdrrec_destroy, 1); 531 } 532 533 534 /* 535 * Exported routines to manage xdr records 536 */ 537 538 /* 539 * Before reading (deserializing) from the stream, one should always call 540 * this procedure to guarantee proper record alignment. 541 */ 542 bool_t 543 xdrrec_skiprecord(XDR *xdrs) 544 { 545 RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private); 546 547 trace1(TR_xdrrec_skiprecord, 0); 548 if (rstrm->in_nonblock) { 549 enum xprt_stat pstat; 550 /* 551 * Read and discard a record from the non-blocking 552 * buffer. Return succes only if a complete record can 553 * be retrieved without blocking, or if the buffer was 554 * empty and there was no data to fetch. 555 */ 556 if (__xdrrec_getbytes_nonblock(xdrs, &pstat) || 557 (pstat == XPRT_MOREREQS && 558 rstrm->in_finger == rstrm->in_boundry)) { 559 rstrm->fbtbc = 0; 560 trace1(TR_xdrrec_skiprecord, 1); 561 return (TRUE); 562 } else { 563 trace1(TR_xdrrec_skiprecord, 1); 564 return (FALSE); 565 } 566 } 567 while (rstrm->fbtbc > 0 || (! rstrm->last_frag)) { 568 if (! skip_input_bytes(rstrm, rstrm->fbtbc)) { 569 trace1(TR_xdrrec_skiprecord, 1); 570 return (FALSE); 571 } 572 rstrm->fbtbc = 0; 573 if ((! rstrm->last_frag) && (! set_input_fragment(rstrm))) { 574 trace1(TR_xdrrec_skiprecord, 1); 575 return (FALSE); 576 } 577 } 578 rstrm->last_frag = FALSE; 579 trace1(TR_xdrrec_skiprecord, 1); 580 return (TRUE); 581 } 582 583 /* 584 * Look ahead fuction. 585 * Returns TRUE iff there is no more input in the buffer 586 * after consuming the rest of the current record. 587 */ 588 bool_t 589 xdrrec_eof(XDR *xdrs) 590 { 591 RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private); 592 593 trace1(TR_xdrrec_eof, 0); 594 if (rstrm->in_nonblock) { 595 /* 596 * If in_needpoll is true, the non-blocking XDR stream 597 * does not have a complete record. 598 */ 599 return (rstrm->in_needpoll); 600 } 601 while (rstrm->fbtbc > 0 || (! rstrm->last_frag)) { 602 if (! skip_input_bytes(rstrm, rstrm->fbtbc)) { 603 trace1(TR_xdrrec_eof, 1); 604 return (TRUE); 605 } 606 rstrm->fbtbc = 0; 607 if ((! rstrm->last_frag) && (! set_input_fragment(rstrm))) { 608 trace1(TR_xdrrec_eof, 1); 609 return (TRUE); 610 } 611 } 612 if (rstrm->in_finger == rstrm->in_boundry) { 613 trace1(TR_xdrrec_eof, 1); 614 return (TRUE); 615 } 616 trace1(TR_xdrrec_eof, 1); 617 return (FALSE); 618 } 619 620 /* 621 * The client must tell the package when an end-of-record has occurred. 622 * The second parameters tells whether the record should be flushed to the 623 * (output) tcp stream. (This let's the package support batched or 624 * pipelined procedure calls.) TRUE => immmediate flush to tcp connection. 625 */ 626 bool_t 627 xdrrec_endofrecord(XDR *xdrs, bool_t sendnow) 628 { 629 RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private); 630 uint32_t len; /* fragment length */ 631 bool_t dummy; 632 633 trace1(TR_xdrrec_endofrecord, 0); 634 if (sendnow || rstrm->frag_sent || 635 ((uintptr_t)rstrm->out_finger + sizeof (uint32_t) >= 636 (uintptr_t)rstrm->out_boundry)) { 637 rstrm->frag_sent = FALSE; 638 dummy = flush_out(rstrm, TRUE); 639 trace1(TR_xdrrec_endofrecord, 1); 640 return (dummy); 641 } 642 len = (uintptr_t)(rstrm->out_finger) - (uintptr_t)(rstrm->frag_header) - 643 sizeof (uint32_t); 644 *(rstrm->frag_header) = htonl((uint32_t)len | LAST_FRAG); 645 rstrm->frag_header = (uint32_t *)rstrm->out_finger; 646 rstrm->out_finger += sizeof (uint32_t); 647 trace1(TR_xdrrec_endofrecord, 1); 648 return (TRUE); 649 } 650 651 652 /* 653 * Internal useful routines 654 */ 655 static bool_t 656 flush_out(RECSTREAM *rstrm, bool_t eor) 657 { 658 uint32_t eormask = (eor == TRUE) ? LAST_FRAG : 0; 659 uint32_t len = (uintptr_t)(rstrm->out_finger) - 660 (uintptr_t)(rstrm->frag_header) - sizeof (uint32_t); 661 int written; 662 663 trace1(TR_flush_out, 0); 664 *(rstrm->frag_header) = htonl(len | eormask); 665 len = (uintptr_t)(rstrm->out_finger) - (uintptr_t)(rstrm->out_base); 666 667 written = (*(rstrm->writeit)) 668 (rstrm->tcp_handle, rstrm->out_base, (int)len); 669 /* 670 * Handle the specific 'CANT_STORE' error. In this case, the 671 * fragment must be cleared. 672 */ 673 if ((written != (int)len) && (written != -2)) { 674 trace1(TR_flush_out, 1); 675 return (FALSE); 676 } 677 rstrm->frag_header = (uint32_t *)rstrm->out_base; 678 rstrm->out_finger = (caddr_t)rstrm->out_base + sizeof (uint32_t); 679 680 trace1(TR_flush_out, 1); 681 return (TRUE); 682 } 683 684 /* knows nothing about records! Only about input buffers */ 685 static bool_t 686 fill_input_buf(RECSTREAM *rstrm, bool_t do_align) 687 { 688 caddr_t where; 689 int len; 690 691 trace1(TR_fill_input_buf, 0); 692 if (rstrm->in_nonblock) { 693 /* Should never get here in the non-blocking case */ 694 trace1(TR_fill_input_buf, 1); 695 return (FALSE); 696 } 697 where = rstrm->in_base; 698 if (do_align) { 699 len = rstrm->recvsize; 700 } else { 701 uint_t i = (uintptr_t)rstrm->in_boundry % BYTES_PER_XDR_UNIT; 702 703 where += i; 704 len = rstrm->recvsize - i; 705 } 706 if ((len = (*(rstrm->readit))(rstrm->tcp_handle, where, len)) == -1) { 707 trace1(TR_fill_input_buf, 1); 708 return (FALSE); 709 } 710 rstrm->in_finger = where; 711 where += len; 712 rstrm->in_boundry = where; 713 trace1(TR_fill_input_buf, 1); 714 return (TRUE); 715 } 716 717 /* knows nothing about records! Only about input buffers */ 718 static bool_t 719 get_input_bytes(RECSTREAM *rstrm, caddr_t addr, 720 int len, bool_t do_align) 721 { 722 int current; 723 724 trace2(TR_get_input_bytes, 0, len); 725 726 if (rstrm->in_nonblock) { 727 /* 728 * Data should already be in the rstrm buffer, so we just 729 * need to copy it to 'addr'. 730 */ 731 current = (int)(rstrm->in_boundry - rstrm->in_finger); 732 if (len > current) { 733 trace1(TR_get_input_bytes, 1); 734 return (FALSE); 735 } 736 (void) memcpy(addr, rstrm->in_finger, len); 737 rstrm->in_finger += len; 738 addr += len; 739 trace1(TR_get_input_bytes, 1); 740 return (TRUE); 741 } 742 743 while (len > 0) { 744 current = (intptr_t)rstrm->in_boundry - 745 (intptr_t)rstrm->in_finger; 746 if (current == 0) { 747 if (! fill_input_buf(rstrm, do_align)) { 748 trace1(TR_get_input_bytes, 1); 749 return (FALSE); 750 } 751 continue; 752 } 753 current = (len < current) ? len : current; 754 (void) memcpy(addr, rstrm->in_finger, current); 755 rstrm->in_finger += current; 756 addr += current; 757 len -= current; 758 do_align = FALSE; 759 } 760 trace1(TR_get_input_bytes, 1); 761 return (TRUE); 762 } 763 764 /* next four bytes of the input stream are treated as a header */ 765 static bool_t 766 set_input_fragment(RECSTREAM *rstrm) 767 { 768 uint32_t header; 769 770 trace1(TR_set_input_fragment, 0); 771 if (rstrm->in_nonblock) { 772 /* 773 * In the non-blocking case, the fragment headers should 774 * already have been consumed, so we should never get 775 * here. Might as well return failure right away. 776 */ 777 trace1(TR_set_input_fragment, 1); 778 return (FALSE); 779 } 780 if (! get_input_bytes(rstrm, (caddr_t)&header, (int)sizeof (header), 781 rstrm->last_frag)) { 782 trace1(TR_set_input_fragment, 1); 783 return (FALSE); 784 } 785 header = (uint32_t)ntohl(header); 786 rstrm->last_frag = ((header & LAST_FRAG) == 0) ? FALSE : TRUE; 787 rstrm->fbtbc = header & (~LAST_FRAG); 788 trace1(TR_set_input_fragment, 1); 789 return (TRUE); 790 } 791 792 /* consumes input bytes; knows nothing about records! */ 793 static bool_t 794 skip_input_bytes(RECSTREAM *rstrm, int32_t cnt) 795 { 796 int current; 797 798 trace2(TR_skip_input_bytes, 0, cnt); 799 while (cnt > 0) { 800 current = (intptr_t)rstrm->in_boundry - 801 (intptr_t)rstrm->in_finger; 802 if (current == 0) { 803 if (! fill_input_buf(rstrm, FALSE)) { 804 trace1(TR_skip_input_bytes, 1); 805 return (FALSE); 806 } 807 continue; 808 } 809 current = (cnt < current) ? cnt : current; 810 rstrm->in_finger += current; 811 cnt -= current; 812 } 813 trace1(TR_skip_input_bytes, 1); 814 return (TRUE); 815 } 816 817 818 static bool_t 819 __xdrrec_nonblock_realloc(RECSTREAM *rstrm, uint32_t newsize) 820 { 821 caddr_t newbuf = rstrm->in_base; 822 ptrdiff_t offset; 823 bool_t ret = TRUE; 824 825 if (newsize > rstrm->recvsize) { 826 newbuf = (caddr_t)realloc(newbuf, newsize); 827 if (newbuf == 0) { 828 ret = FALSE; 829 } else { 830 /* Make pointers valid for the new buffer */ 831 offset = newbuf - rstrm->in_base; 832 rstrm->in_finger += offset; 833 rstrm->in_boundry += offset; 834 rstrm->in_nextrec += offset; 835 rstrm->in_base = newbuf; 836 rstrm->recvsize = newsize; 837 } 838 } 839 840 return (ret); 841 } 842 843 /* 844 * adjust sizes and allocate buffer quad byte aligned 845 */ 846 bool_t 847 __xdrrec_set_conn_nonblock(XDR *xdrs, uint32_t tcp_maxrecsz) 848 { 849 RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private); 850 caddr_t realloc_buffer; 851 size_t newsize; 852 853 rstrm->in_nonblock = TRUE; 854 if (tcp_maxrecsz == 0) { 855 /* 856 * If maxrecsz has not been set, use the default 857 * that was set from xdrrec_create() and 858 * fix_buf_size() 859 */ 860 rstrm->in_maxrecsz = rstrm->recvsize; 861 return (TRUE); 862 } 863 rstrm->in_maxrecsz = tcp_maxrecsz; 864 if (tcp_maxrecsz <= rstrm->recvsize) 865 return (TRUE); 866 867 /* 868 * For nonblocked connection, the entire record is read into the 869 * buffer before any xdr processing. This implies that the record 870 * size must allow for the maximum expected message size of the 871 * service. However, it's inconvenient to allocate very large 872 * buffers up front, so we limit ourselves to a reasonable 873 * default size here, and reallocate (up to the maximum record 874 * size allowed for the connection) as necessary. 875 */ 876 if ((newsize = tcp_maxrecsz) > RPC_MAXDATASIZE) { 877 newsize = RPC_MAXDATASIZE; 878 } 879 if (! __xdrrec_nonblock_realloc(rstrm, newsize)) { 880 (void) syslog(LOG_ERR, mem_err_msg_rec); 881 (void) mem_free(rstrm->out_base, rstrm->sendsize); 882 (void) mem_free(rstrm->in_base, rstrm->recvsize); 883 (void) mem_free((char *)rstrm, sizeof (RECSTREAM)); 884 trace1(TR_xdrrec_create, 1); 885 return (FALSE); 886 } 887 888 return (TRUE); 889 } 890 891 /* 892 * Retrieve input data from the non-blocking connection, increase 893 * the size of the read buffer if necessary, and check that the 894 * record size stays below the allowed maximum for the connection. 895 */ 896 bool_t 897 __xdrrec_getbytes_nonblock(XDR *xdrs, enum xprt_stat *pstat) 898 { 899 RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private); 900 uint32_t prevbytes_thisrec, minreqrecsize; 901 uint32_t *header; 902 uint32_t len_received = 0, unprocessed = 0; 903 904 trace2(TR__xdrrec_getbytes_nonblock, 0, len); 905 906 /* 907 * For connection oriented protocols, there's no guarantee that 908 * we will receive the data nicely chopped into records, no 909 * matter how it was sent. We use the in_nextrec pointer to 910 * indicate where in the buffer the next record starts. If 911 * in_nextrec != in_base, there's data in the buffer from 912 * previous reads, and if in_nextrecsz > 0, we need to copy 913 * the portion of the next record already read to the start of 914 * the input buffer 915 */ 916 if (rstrm->in_nextrecsz > 0) { 917 /* Starting on new record with data already in the buffer */ 918 (void) memmove(rstrm->in_base, rstrm->in_nextrec, 919 rstrm->in_nextrecsz); 920 rstrm->in_nextrec = rstrm->in_finger = rstrm->in_base; 921 rstrm->in_boundry = rstrm->in_nextrec + rstrm->in_nextrecsz; 922 unprocessed = rstrm->in_nextrecsz; 923 rstrm->in_nextrecsz = 0; 924 } else if (rstrm->in_nextrec == rstrm->in_base) { 925 /* Starting on new record with empty buffer */ 926 rstrm->in_boundry = rstrm->in_finger = rstrm->in_base; 927 rstrm->last_frag = FALSE; 928 rstrm->in_needpoll = TRUE; 929 } 930 931 prevbytes_thisrec = (uint32_t)(rstrm->in_boundry - rstrm->in_base); 932 933 /* Do we need to retrieve data ? */ 934 if (rstrm->in_needpoll) { 935 int len_requested, len_total_received; 936 937 rstrm->in_needpoll = FALSE; 938 len_total_received = 939 (int)(rstrm->in_boundry - rstrm->in_base); 940 len_requested = rstrm->recvsize - len_total_received; 941 /* 942 * if len_requested is 0, this means that the input 943 * buffer is full and need to be increased. 944 * The minimum record size we will need is whatever's 945 * already in the buffer, plus what's yet to be 946 * consumed in the current fragment, plus space for at 947 * least one more fragment header, if this is not the 948 * last fragment. We use the RNDUP() macro to 949 * account for possible realignment of the next 950 * fragment header. 951 */ 952 if (len_requested == 0) { 953 minreqrecsize = rstrm->recvsize + 954 rstrm->fbtbc + 955 (rstrm->last_frag ? 0 : sizeof (*header)); 956 minreqrecsize = RNDUP(minreqrecsize); 957 if (minreqrecsize == rstrm->recvsize) { 958 /* 959 * no more bytes to be consumed and 960 * last fragment. We should never end up 961 * here. Might as well return failure 962 * right away. 963 */ 964 *pstat = XPRT_DIED; 965 trace1(TR__xdrrec_getbytes_nonblock, 1); 966 return (FALSE); 967 } else if (minreqrecsize > rstrm->in_maxrecsz) { 968 goto recsz_invalid; 969 } else { 970 goto needpoll; 971 } 972 } 973 if ((len_received = (*(rstrm->readit))(rstrm->tcp_handle, 974 rstrm->in_boundry, len_requested)) == -1) { 975 *pstat = XPRT_DIED; 976 trace1(TR__xdrrec_getbytes_nonblock, 1); 977 return (FALSE); 978 } 979 rstrm->in_boundry += len_received; 980 rstrm->in_nextrec = rstrm->in_boundry; 981 } 982 983 /* Account for any left over data from previous processing */ 984 len_received += unprocessed; 985 986 /* Set a lower limit on the buffer space we'll need */ 987 minreqrecsize = prevbytes_thisrec + rstrm->fbtbc; 988 989 /* 990 * Consume bytes for this record until it's either complete, 991 * rejected, or we need to poll for more bytes. 992 * 993 * If fbtbc == 0, in_finger points to the start of the fragment 994 * header. Otherwise, it points to the start of the fragment data. 995 */ 996 while (len_received > 0) { 997 if (rstrm->fbtbc == 0) { 998 uint32_t hdrlen, minfraglen = 0; 999 uint32_t len_recvd_thisfrag; 1000 bool_t last_frag; 1001 1002 len_recvd_thisfrag = (uint32_t)(rstrm->in_boundry - 1003 rstrm->in_finger); 1004 header = (uint32_t *)rstrm->in_finger; 1005 hdrlen = (len_recvd_thisfrag < sizeof (*header)) ? 1006 len_recvd_thisfrag : sizeof (*header); 1007 memcpy(&minfraglen, header, hdrlen); 1008 last_frag = (ntohl(minfraglen) & LAST_FRAG) != 0; 1009 minfraglen = ntohl(minfraglen) & (~LAST_FRAG); 1010 /* 1011 * The minimum record size we will need is whatever's 1012 * already in the buffer, plus the size of this 1013 * fragment, plus (if this isn't the last fragment) 1014 * space for at least one more fragment header. We 1015 * use the RNDUP() macro to account for possible 1016 * realignment of the next fragment header. 1017 */ 1018 minreqrecsize += minfraglen + 1019 (last_frag?0:sizeof (*header)); 1020 minreqrecsize = RNDUP(minreqrecsize); 1021 1022 if (hdrlen < sizeof (*header)) { 1023 /* 1024 * We only have a partial fragment header, 1025 * but we can still put a lower limit on the 1026 * final fragment size, and check against the 1027 * maximum allowed. 1028 */ 1029 if (len_recvd_thisfrag > 0 && 1030 (minreqrecsize > rstrm->in_maxrecsz)) { 1031 goto recsz_invalid; 1032 } 1033 /* Need more bytes to obtain fbtbc value */ 1034 goto needpoll; 1035 } 1036 /* 1037 * We've got a complete fragment header, so 1038 * 'minfraglen' is the actual fragment length, and 1039 * 'minreqrecsize' the requested record size. 1040 */ 1041 rstrm->last_frag = last_frag; 1042 rstrm->fbtbc = minfraglen; 1043 /* 1044 * Check that the sum of the total number of bytes read 1045 * so far (for the record) and the size of the incoming 1046 * fragment is less than the maximum allowed. 1047 * 1048 * If this is the last fragment, also check that the 1049 * record (message) meets the minimum length 1050 * requirement. 1051 * 1052 * If this isn't the last fragment, check for a zero 1053 * fragment length. Accepting such fragments would 1054 * leave us open to an attack where the sender keeps 1055 * the connection open indefinitely, without any 1056 * progress, by occasionally sending a zero length 1057 * fragment. 1058 */ 1059 if ((minreqrecsize > rstrm->in_maxrecsz) || 1060 (rstrm->last_frag && minreqrecsize < MIN_FRAG) || 1061 (!rstrm->last_frag && minfraglen == 0)) { 1062 recsz_invalid: 1063 rstrm->fbtbc = 0; 1064 rstrm->last_frag = 1; 1065 *pstat = XPRT_DIED; 1066 trace1(TR__xdrrec_getbytes_nonblock, 1); 1067 return (FALSE); 1068 } 1069 /* 1070 * Make this fragment abut the previous one. If it's 1071 * the first fragment, just advance in_finger past 1072 * the header. This avoids buffer copying for the 1073 * usual case where there's one fragment per record. 1074 */ 1075 if (rstrm->in_finger == rstrm->in_base) { 1076 rstrm->in_finger += sizeof (*header); 1077 } else { 1078 rstrm->in_boundry -= sizeof (*header); 1079 (void) memmove(rstrm->in_finger, 1080 rstrm->in_finger + sizeof (*header), 1081 rstrm->in_boundry - rstrm->in_finger); 1082 } 1083 /* Consume the fragment header */ 1084 if (len_received > sizeof (*header)) { 1085 len_received -= sizeof (*header); 1086 } else { 1087 len_received = 0; 1088 } 1089 } 1090 /* 1091 * Consume whatever fragment bytes we have. 1092 * If we've received all bytes for this fragment, advance 1093 * in_finger to point to the start of the next fragment 1094 * header. Otherwise, make fbtbc tell how much is left in 1095 * in this fragment and advance finger to point to end of 1096 * fragment data. 1097 */ 1098 if (len_received >= rstrm->fbtbc) { 1099 len_received -= rstrm->fbtbc; 1100 rstrm->in_finger += rstrm->fbtbc; 1101 rstrm->fbtbc = 0; 1102 } else { 1103 rstrm->fbtbc -= len_received; 1104 rstrm->in_finger += len_received; 1105 len_received = 0; 1106 } 1107 /* 1108 * If there's more data in the buffer, there are two 1109 * possibilities: 1110 * 1111 * (1) This is the last fragment, so the extra data 1112 * presumably belongs to the next record. 1113 * 1114 * (2) Not the last fragment, so we'll start over 1115 * from the top of the loop. 1116 */ 1117 if (len_received > 0 && rstrm->last_frag) { 1118 rstrm->in_nextrec = rstrm->in_finger; 1119 rstrm->in_nextrecsz = (uint32_t)(rstrm->in_boundry - 1120 rstrm->in_nextrec); 1121 len_received = 0; 1122 } 1123 } 1124 1125 /* Was this the last fragment, and have we read the entire record ? */ 1126 if (rstrm->last_frag && rstrm->fbtbc == 0) { 1127 *pstat = XPRT_MOREREQS; 1128 /* 1129 * We've been using both in_finger and fbtbc for our own 1130 * purposes. Now's the time to update them to be what 1131 * xdrrec_inline() expects. Set in_finger to point to the 1132 * start of data for this record, and fbtbc to the number 1133 * of bytes in the record. 1134 */ 1135 rstrm->fbtbc = (int)(rstrm->in_finger - 1136 rstrm->in_base - sizeof (*header)); 1137 rstrm->in_finger = rstrm->in_base + sizeof (*header); 1138 if (rstrm->in_nextrecsz == 0) 1139 rstrm->in_nextrec = rstrm->in_base; 1140 trace1(TR__xdrrec_getbytes_nonblock, 1); 1141 return (TRUE); 1142 } 1143 needpoll: 1144 /* 1145 * Need more bytes, so we set the needpoll flag, and go back to 1146 * the main RPC request loop. However, first we reallocate the 1147 * input buffer, if necessary. 1148 */ 1149 if (minreqrecsize > rstrm->recvsize) { 1150 if (! __xdrrec_nonblock_realloc(rstrm, minreqrecsize)) { 1151 rstrm->fbtbc = 0; 1152 rstrm->last_frag = 1; 1153 *pstat = XPRT_DIED; 1154 trace1(TR__xdrrec_getbytes_nonblock, 1); 1155 return (FALSE); 1156 } 1157 } 1158 1159 rstrm->in_needpoll = TRUE; 1160 *pstat = XPRT_MOREREQS; 1161 trace1(TR__xdrrec_getbytes_nonblock, 1); 1162 return (FALSE); 1163 } 1164 1165 int 1166 __is_xdrrec_first(XDR *xdrs) 1167 { 1168 RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private); 1169 return ((rstrm->firsttime == TRUE)? 1 : 0); 1170 } 1171 1172 int 1173 __xdrrec_setfirst(XDR *xdrs) 1174 { 1175 RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private); 1176 1177 /* 1178 * Set rstrm->firsttime only if the input buffer is empty. 1179 * Otherwise, the first read from the network could skip 1180 * a poll. 1181 */ 1182 if (rstrm->in_finger == rstrm->in_boundry) 1183 rstrm->firsttime = TRUE; 1184 else 1185 rstrm->firsttime = FALSE; 1186 return (1); 1187 } 1188 1189 int 1190 __xdrrec_resetfirst(XDR *xdrs) 1191 { 1192 RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private); 1193 1194 rstrm->firsttime = FALSE; 1195 return (1); 1196 } 1197 1198 1199 static uint_t 1200 fix_buf_size(uint_t s) 1201 { 1202 uint_t dummy1; 1203 1204 trace2(TR_fix_buf_size, 0, s); 1205 if (s < 100) 1206 s = 4000; 1207 dummy1 = RNDUP(s); 1208 trace1(TR_fix_buf_size, 1); 1209 return (dummy1); 1210 } 1211 1212 1213 1214 static bool_t 1215 xdrrec_control(XDR *xdrs, int request, void *info) 1216 { 1217 RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private); 1218 xdr_bytesrec *xptr; 1219 1220 switch (request) { 1221 1222 case XDR_GET_BYTES_AVAIL: 1223 /* Check if at end of fragment and not last fragment */ 1224 if ((rstrm->fbtbc == 0) && (!rstrm->last_frag)) 1225 if (!set_input_fragment(rstrm)) { 1226 return (FALSE); 1227 }; 1228 1229 xptr = (xdr_bytesrec *)info; 1230 xptr->xc_is_last_record = rstrm->last_frag; 1231 xptr->xc_num_avail = rstrm->fbtbc; 1232 1233 return (TRUE); 1234 default: 1235 return (FALSE); 1236 1237 } 1238 1239 } 1240 1241 static struct xdr_ops * 1242 xdrrec_ops() 1243 { 1244 static struct xdr_ops ops; 1245 extern mutex_t ops_lock; 1246 1247 /* VARIABLES PROTECTED BY ops_lock: ops */ 1248 1249 trace1(TR_xdrrec_ops, 0); 1250 mutex_lock(&ops_lock); 1251 if (ops.x_getlong == NULL) { 1252 ops.x_getlong = xdrrec_getlong; 1253 ops.x_putlong = xdrrec_putlong; 1254 ops.x_getbytes = xdrrec_getbytes; 1255 ops.x_putbytes = xdrrec_putbytes; 1256 ops.x_getpostn = xdrrec_getpos; 1257 ops.x_setpostn = xdrrec_setpos; 1258 ops.x_inline = xdrrec_inline; 1259 ops.x_destroy = xdrrec_destroy; 1260 ops.x_control = xdrrec_control; 1261 #if defined(_LP64) 1262 ops.x_getint32 = xdrrec_getint32; 1263 ops.x_putint32 = xdrrec_putint32; 1264 #endif 1265 } 1266 mutex_unlock(&ops_lock); 1267 trace1(TR_xdrrec_ops, 1); 1268 return (&ops); 1269 } 1270