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