1 1.41 andvar /* $NetBSD: xdr_rec.c,v 1.41 2024/03/22 19:45:22 andvar Exp $ */ 2 1.5 cgd 3 1.1 cgd /* 4 1.35 tron * Copyright (c) 2010, Oracle America, Inc. 5 1.35 tron * 6 1.35 tron * Redistribution and use in source and binary forms, with or without 7 1.35 tron * modification, are permitted provided that the following conditions are 8 1.35 tron * met: 9 1.35 tron * 10 1.35 tron * * Redistributions of source code must retain the above copyright 11 1.35 tron * notice, this list of conditions and the following disclaimer. 12 1.35 tron * * Redistributions in binary form must reproduce the above 13 1.35 tron * copyright notice, this list of conditions and the following 14 1.35 tron * disclaimer in the documentation and/or other materials 15 1.35 tron * provided with the distribution. 16 1.35 tron * * Neither the name of the "Oracle America, Inc." nor the names of its 17 1.35 tron * contributors may be used to endorse or promote products derived 18 1.35 tron * from this software without specific prior written permission. 19 1.35 tron * 20 1.35 tron * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 1.35 tron * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 1.35 tron * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 1.35 tron * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 1.35 tron * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 25 1.35 tron * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 1.35 tron * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 27 1.35 tron * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 1.35 tron * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 29 1.35 tron * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 30 1.35 tron * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 1.35 tron * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 1.1 cgd */ 33 1.7 christos 34 1.7 christos #include <sys/cdefs.h> 35 1.1 cgd #if defined(LIBC_SCCS) && !defined(lint) 36 1.7 christos #if 0 37 1.7 christos static char *sccsid = "@(#)xdr_rec.c 1.21 87/08/11 Copyr 1984 Sun Micro"; 38 1.7 christos static char *sccsid = "@(#)xdr_rec.c 2.2 88/08/01 4.0 RPCSRC"; 39 1.7 christos #else 40 1.41 andvar __RCSID("$NetBSD: xdr_rec.c,v 1.41 2024/03/22 19:45:22 andvar Exp $"); 41 1.7 christos #endif 42 1.1 cgd #endif 43 1.1 cgd 44 1.1 cgd /* 45 1.1 cgd * xdr_rec.c, Implements TCP/IP based XDR streams with a "record marking" 46 1.1 cgd * layer above tcp (for rpc's use). 47 1.1 cgd * 48 1.1 cgd * Copyright (C) 1984, Sun Microsystems, Inc. 49 1.1 cgd * 50 1.1 cgd * These routines interface XDRSTREAMS to a tcp/ip connection. 51 1.1 cgd * There is a record marking layer between the xdr stream 52 1.1 cgd * and the tcp transport level. A record is composed on one or more 53 1.1 cgd * record fragments. A record fragment is a thirty-two bit header followed 54 1.1 cgd * by n bytes of data, where n is contained in the header. The header 55 1.11 lukem * is represented as a htonl(u_long). Thegh order bit encodes 56 1.1 cgd * whether or not the fragment is the last fragment of the record 57 1.1 cgd * (1 => fragment is last, 0 => more fragments to follow. 58 1.1 cgd * The other 31 bits encode the byte length of the fragment. 59 1.1 cgd */ 60 1.1 cgd 61 1.8 jtc #include "namespace.h" 62 1.40 christos #include "reentrant.h" 63 1.12 lukem 64 1.12 lukem #include <sys/types.h> 65 1.12 lukem 66 1.12 lukem #include <netinet/in.h> 67 1.12 lukem 68 1.32 christos #include <assert.h> 69 1.12 lukem #include <err.h> 70 1.19 fvdl #include <stddef.h> 71 1.1 cgd #include <stdio.h> 72 1.1 cgd #include <stdlib.h> 73 1.6 cgd #include <string.h> 74 1.12 lukem 75 1.1 cgd #include <rpc/types.h> 76 1.1 cgd #include <rpc/xdr.h> 77 1.19 fvdl #include <rpc/auth.h> 78 1.19 fvdl #include <rpc/svc.h> 79 1.19 fvdl #include <rpc/clnt.h> 80 1.19 fvdl 81 1.19 fvdl #include "rpc_internal.h" 82 1.8 jtc 83 1.8 jtc #ifdef __weak_alias 84 1.17 mycroft __weak_alias(xdrrec_create,_xdrrec_create) 85 1.17 mycroft __weak_alias(xdrrec_endofrecord,_xdrrec_endofrecord) 86 1.17 mycroft __weak_alias(xdrrec_eof,_xdrrec_eof) 87 1.17 mycroft __weak_alias(xdrrec_skiprecord,_xdrrec_skiprecord) 88 1.8 jtc #endif 89 1.1 cgd 90 1.33 matt static bool_t xdrrec_getlong(XDR *, long *); 91 1.33 matt static bool_t xdrrec_putlong(XDR *, const long *); 92 1.33 matt static bool_t xdrrec_getbytes(XDR *, char *, u_int); 93 1.33 matt 94 1.33 matt static bool_t xdrrec_putbytes(XDR *, const char *, u_int); 95 1.33 matt static u_int xdrrec_getpos(XDR *); 96 1.33 matt static bool_t xdrrec_setpos(XDR *, u_int); 97 1.33 matt static int32_t *xdrrec_inline(XDR *, u_int); 98 1.33 matt static void xdrrec_destroy(XDR *); 99 1.1 cgd 100 1.13 mycroft static const struct xdr_ops xdrrec_ops = { 101 1.1 cgd xdrrec_getlong, 102 1.1 cgd xdrrec_putlong, 103 1.1 cgd xdrrec_getbytes, 104 1.1 cgd xdrrec_putbytes, 105 1.1 cgd xdrrec_getpos, 106 1.1 cgd xdrrec_setpos, 107 1.1 cgd xdrrec_inline, 108 1.27 christos xdrrec_destroy, 109 1.27 christos NULL, /* xdrrec_control */ 110 1.1 cgd }; 111 1.1 cgd 112 1.1 cgd /* 113 1.1 cgd * A record is composed of one or more record fragments. 114 1.19 fvdl * A record fragment is a four-byte header followed by zero to 115 1.1 cgd * 2**32-1 bytes. The header is treated as a long unsigned and is 116 1.1 cgd * encode/decoded to the network via htonl/ntohl. The low order 31 bits 117 1.1 cgd * are a byte count of the fragment. The highest order bit is a boolean: 118 1.1 cgd * 1 => this fragment is the last fragment of the record, 119 1.1 cgd * 0 => this fragment is followed by more fragment(s). 120 1.1 cgd * 121 1.1 cgd * The fragment/record machinery is not general; it is constructed to 122 1.1 cgd * meet the needs of xdr and rpc based on tcp. 123 1.1 cgd */ 124 1.1 cgd 125 1.37 kamil #define LAST_FRAG ((uint32_t)(1U << 31)) 126 1.1 cgd 127 1.1 cgd typedef struct rec_strm { 128 1.14 mycroft char *tcp_handle; 129 1.1 cgd /* 130 1.1 cgd * out-goung bits 131 1.1 cgd */ 132 1.33 matt int (*writeit)(char *, char *, int); 133 1.14 mycroft char *out_base; /* output buffer (points to frag header) */ 134 1.14 mycroft char *out_finger; /* next output position */ 135 1.14 mycroft char *out_boundry; /* data cannot up to this address */ 136 1.39 andvar uint32_t *frag_header; /* beginning of current fragment */ 137 1.1 cgd bool_t frag_sent; /* true if buffer sent in middle of record */ 138 1.1 cgd /* 139 1.1 cgd * in-coming bits 140 1.1 cgd */ 141 1.33 matt int (*readit)(char *, char *, int); 142 1.11 lukem u_long in_size; /* fixed size of the input buffer */ 143 1.14 mycroft char *in_base; 144 1.14 mycroft char *in_finger; /* location of next byte to be had */ 145 1.14 mycroft char *in_boundry; /* can read up to this location */ 146 1.11 lukem long fbtbc; /* fragment bytes to be consumed */ 147 1.1 cgd bool_t last_frag; 148 1.11 lukem u_int sendsize; 149 1.11 lukem u_int recvsize; 150 1.19 fvdl 151 1.19 fvdl bool_t nonblock; 152 1.19 fvdl bool_t in_haveheader; 153 1.33 matt uint32_t in_header; 154 1.19 fvdl char *in_hdrp; 155 1.19 fvdl int in_hdrlen; 156 1.19 fvdl int in_reclen; 157 1.19 fvdl int in_received; 158 1.19 fvdl int in_maxrec; 159 1.1 cgd } RECSTREAM; 160 1.1 cgd 161 1.33 matt static u_int fix_buf_size(u_int); 162 1.33 matt static bool_t flush_out(RECSTREAM *, bool_t); 163 1.33 matt static bool_t fill_input_buf(RECSTREAM *); 164 1.33 matt static bool_t get_input_bytes(RECSTREAM *, char *, u_int); 165 1.33 matt static bool_t set_input_fragment(RECSTREAM *); 166 1.33 matt static bool_t skip_input_bytes(RECSTREAM *, long); 167 1.33 matt static bool_t realloc_stream(RECSTREAM *, int); 168 1.6 cgd 169 1.1 cgd 170 1.1 cgd /* 171 1.1 cgd * Create an xdr handle for xdrrec 172 1.1 cgd * xdrrec_create fills in xdrs. Sendsize and recvsize are 173 1.1 cgd * send and recv buffer sizes (0 => use default). 174 1.1 cgd * tcp_handle is an opaque handle that is passed as the first parameter to 175 1.1 cgd * the procedures readit and writeit. Readit and writeit are read and 176 1.1 cgd * write respectively. They are like the system 177 1.1 cgd * calls expect that they take an opaque handle rather than an fd. 178 1.1 cgd */ 179 1.1 cgd void 180 1.33 matt xdrrec_create( 181 1.33 matt XDR *xdrs, 182 1.33 matt u_int sendsize, 183 1.33 matt u_int recvsize, 184 1.33 matt char *tcp_handle, 185 1.11 lukem /* like read, but pass it a tcp_handle, not sock */ 186 1.33 matt int (*readit)(char *, char *, int), 187 1.11 lukem /* like write, but pass it a tcp_handle, not sock */ 188 1.33 matt int (*writeit)(char *, char *, int)) 189 1.1 cgd { 190 1.18 christos RECSTREAM *rstrm = mem_alloc(sizeof(RECSTREAM)); 191 1.1 cgd 192 1.1 cgd if (rstrm == NULL) { 193 1.34 christos warn("%s: out of memory", __func__); 194 1.1 cgd /* 195 1.1 cgd * This is bad. Should rework xdrrec_create to 196 1.1 cgd * return a handle, and in this case return NULL 197 1.1 cgd */ 198 1.1 cgd return; 199 1.1 cgd } 200 1.19 fvdl 201 1.1 cgd rstrm->sendsize = sendsize = fix_buf_size(sendsize); 202 1.25 yamt rstrm->out_base = malloc(rstrm->sendsize); 203 1.19 fvdl if (rstrm->out_base == NULL) { 204 1.34 christos warn("%s: out of memory", __func__); 205 1.19 fvdl mem_free(rstrm, sizeof(RECSTREAM)); 206 1.19 fvdl return; 207 1.19 fvdl } 208 1.19 fvdl 209 1.1 cgd rstrm->recvsize = recvsize = fix_buf_size(recvsize); 210 1.25 yamt rstrm->in_base = malloc(recvsize); 211 1.19 fvdl if (rstrm->in_base == NULL) { 212 1.34 christos warn("%s: out of memory", __func__); 213 1.19 fvdl mem_free(rstrm->out_base, sendsize); 214 1.19 fvdl mem_free(rstrm, sizeof(RECSTREAM)); 215 1.1 cgd return; 216 1.1 cgd } 217 1.1 cgd /* 218 1.1 cgd * now the rest ... 219 1.1 cgd */ 220 1.1 cgd xdrs->x_ops = &xdrrec_ops; 221 1.15 christos xdrs->x_private = rstrm; 222 1.1 cgd rstrm->tcp_handle = tcp_handle; 223 1.1 cgd rstrm->readit = readit; 224 1.1 cgd rstrm->writeit = writeit; 225 1.1 cgd rstrm->out_finger = rstrm->out_boundry = rstrm->out_base; 226 1.33 matt rstrm->frag_header = (uint32_t *)(void *)rstrm->out_base; 227 1.33 matt rstrm->out_finger += sizeof(uint32_t); 228 1.1 cgd rstrm->out_boundry += sendsize; 229 1.1 cgd rstrm->frag_sent = FALSE; 230 1.1 cgd rstrm->in_size = recvsize; 231 1.1 cgd rstrm->in_boundry = rstrm->in_base; 232 1.1 cgd rstrm->in_finger = (rstrm->in_boundry += recvsize); 233 1.1 cgd rstrm->fbtbc = 0; 234 1.1 cgd rstrm->last_frag = TRUE; 235 1.19 fvdl rstrm->in_haveheader = FALSE; 236 1.19 fvdl rstrm->in_hdrlen = 0; 237 1.19 fvdl rstrm->in_hdrp = (char *)(void *)&rstrm->in_header; 238 1.19 fvdl rstrm->nonblock = FALSE; 239 1.19 fvdl rstrm->in_reclen = 0; 240 1.19 fvdl rstrm->in_received = 0; 241 1.1 cgd } 242 1.1 cgd 243 1.1 cgd 244 1.1 cgd /* 245 1.41 andvar * The routines defined below are the xdr ops which will go into the 246 1.1 cgd * xdr handle filled in by xdrrec_create. 247 1.1 cgd */ 248 1.1 cgd 249 1.1 cgd static bool_t 250 1.33 matt xdrrec_getlong(XDR *xdrs, long *lp) 251 1.1 cgd { 252 1.12 lukem RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private); 253 1.15 christos int32_t *buflp = (int32_t *)(void *)(rstrm->in_finger); 254 1.4 cgd int32_t mylong; 255 1.1 cgd 256 1.1 cgd /* first try the inline, fast case */ 257 1.30 lukem if ((rstrm->fbtbc >= (long)sizeof(int32_t)) && 258 1.30 lukem (((uintptr_t)rstrm->in_boundry - (uintptr_t)buflp) >= sizeof(int32_t))) { 259 1.33 matt *lp = (long)ntohl((uint32_t)(*buflp)); 260 1.4 cgd rstrm->fbtbc -= sizeof(int32_t); 261 1.4 cgd rstrm->in_finger += sizeof(int32_t); 262 1.1 cgd } else { 263 1.15 christos if (! xdrrec_getbytes(xdrs, (char *)(void *)&mylong, 264 1.32 christos (u_int)sizeof(int32_t))) 265 1.1 cgd return (FALSE); 266 1.33 matt *lp = (long)ntohl((uint32_t)mylong); 267 1.1 cgd } 268 1.1 cgd return (TRUE); 269 1.1 cgd } 270 1.1 cgd 271 1.1 cgd static bool_t 272 1.33 matt xdrrec_putlong(XDR *xdrs, const long *lp) 273 1.1 cgd { 274 1.12 lukem RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private); 275 1.15 christos int32_t *dest_lp = ((int32_t *)(void *)(rstrm->out_finger)); 276 1.1 cgd 277 1.4 cgd if ((rstrm->out_finger += sizeof(int32_t)) > rstrm->out_boundry) { 278 1.1 cgd /* 279 1.1 cgd * this case should almost never happen so the code is 280 1.1 cgd * inefficient 281 1.1 cgd */ 282 1.4 cgd rstrm->out_finger -= sizeof(int32_t); 283 1.1 cgd rstrm->frag_sent = TRUE; 284 1.1 cgd if (! flush_out(rstrm, FALSE)) 285 1.1 cgd return (FALSE); 286 1.15 christos dest_lp = ((int32_t *)(void *)(rstrm->out_finger)); 287 1.4 cgd rstrm->out_finger += sizeof(int32_t); 288 1.1 cgd } 289 1.33 matt *dest_lp = (int32_t)htonl((uint32_t)(*lp)); 290 1.1 cgd return (TRUE); 291 1.1 cgd } 292 1.1 cgd 293 1.1 cgd static bool_t /* must manage buffers, fragments, and records */ 294 1.33 matt xdrrec_getbytes(XDR *xdrs, char *addr, u_int len) 295 1.1 cgd { 296 1.12 lukem RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private); 297 1.30 lukem u_int current; 298 1.1 cgd 299 1.1 cgd while (len > 0) { 300 1.30 lukem current = (u_int)rstrm->fbtbc; 301 1.1 cgd if (current == 0) { 302 1.1 cgd if (rstrm->last_frag) 303 1.1 cgd return (FALSE); 304 1.1 cgd if (! set_input_fragment(rstrm)) 305 1.1 cgd return (FALSE); 306 1.1 cgd continue; 307 1.1 cgd } 308 1.1 cgd current = (len < current) ? len : current; 309 1.1 cgd if (! get_input_bytes(rstrm, addr, current)) 310 1.1 cgd return (FALSE); 311 1.1 cgd addr += current; 312 1.1 cgd rstrm->fbtbc -= current; 313 1.1 cgd len -= current; 314 1.1 cgd } 315 1.1 cgd return (TRUE); 316 1.1 cgd } 317 1.1 cgd 318 1.1 cgd static bool_t 319 1.33 matt xdrrec_putbytes(XDR *xdrs, const char *addr, u_int len) 320 1.1 cgd { 321 1.12 lukem RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private); 322 1.15 christos size_t current; 323 1.1 cgd 324 1.1 cgd while (len > 0) { 325 1.15 christos current = (size_t)((u_long)rstrm->out_boundry - 326 1.15 christos (u_long)rstrm->out_finger); 327 1.1 cgd current = (len < current) ? len : current; 328 1.12 lukem memmove(rstrm->out_finger, addr, current); 329 1.1 cgd rstrm->out_finger += current; 330 1.1 cgd addr += current; 331 1.32 christos _DIAGASSERT(__type_fit(u_int, current)); 332 1.32 christos len -= (u_int)current; 333 1.1 cgd if (rstrm->out_finger == rstrm->out_boundry) { 334 1.1 cgd rstrm->frag_sent = TRUE; 335 1.1 cgd if (! flush_out(rstrm, FALSE)) 336 1.1 cgd return (FALSE); 337 1.1 cgd } 338 1.1 cgd } 339 1.1 cgd return (TRUE); 340 1.1 cgd } 341 1.1 cgd 342 1.11 lukem static u_int 343 1.33 matt xdrrec_getpos(XDR *xdrs) 344 1.1 cgd { 345 1.12 lukem RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private; 346 1.15 christos off_t pos; 347 1.1 cgd 348 1.15 christos pos = lseek((int)(u_long)rstrm->tcp_handle, (off_t)0, 1); 349 1.1 cgd if (pos != -1) 350 1.1 cgd switch (xdrs->x_op) { 351 1.1 cgd 352 1.1 cgd case XDR_ENCODE: 353 1.1 cgd pos += rstrm->out_finger - rstrm->out_base; 354 1.1 cgd break; 355 1.1 cgd 356 1.1 cgd case XDR_DECODE: 357 1.1 cgd pos -= rstrm->in_boundry - rstrm->in_finger; 358 1.1 cgd break; 359 1.1 cgd 360 1.1 cgd default: 361 1.15 christos pos = (off_t) -1; 362 1.1 cgd break; 363 1.1 cgd } 364 1.11 lukem return ((u_int) pos); 365 1.1 cgd } 366 1.1 cgd 367 1.1 cgd static bool_t 368 1.33 matt xdrrec_setpos(XDR *xdrs, u_int pos) 369 1.1 cgd { 370 1.12 lukem RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private; 371 1.11 lukem u_int currpos = xdrrec_getpos(xdrs); 372 1.1 cgd int delta = currpos - pos; 373 1.14 mycroft char *newpos; 374 1.1 cgd 375 1.1 cgd if ((int)currpos != -1) 376 1.1 cgd switch (xdrs->x_op) { 377 1.1 cgd 378 1.1 cgd case XDR_ENCODE: 379 1.1 cgd newpos = rstrm->out_finger - delta; 380 1.15 christos if ((newpos > (char *)(void *)(rstrm->frag_header)) && 381 1.1 cgd (newpos < rstrm->out_boundry)) { 382 1.1 cgd rstrm->out_finger = newpos; 383 1.1 cgd return (TRUE); 384 1.1 cgd } 385 1.1 cgd break; 386 1.1 cgd 387 1.1 cgd case XDR_DECODE: 388 1.1 cgd newpos = rstrm->in_finger - delta; 389 1.1 cgd if ((delta < (int)(rstrm->fbtbc)) && 390 1.1 cgd (newpos <= rstrm->in_boundry) && 391 1.1 cgd (newpos >= rstrm->in_base)) { 392 1.1 cgd rstrm->in_finger = newpos; 393 1.1 cgd rstrm->fbtbc -= delta; 394 1.1 cgd return (TRUE); 395 1.1 cgd } 396 1.1 cgd break; 397 1.7 christos 398 1.7 christos case XDR_FREE: 399 1.7 christos break; 400 1.1 cgd } 401 1.1 cgd return (FALSE); 402 1.1 cgd } 403 1.1 cgd 404 1.4 cgd static int32_t * 405 1.33 matt xdrrec_inline(XDR *xdrs, u_int len) 406 1.1 cgd { 407 1.12 lukem RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private; 408 1.4 cgd int32_t *buf = NULL; 409 1.1 cgd 410 1.1 cgd switch (xdrs->x_op) { 411 1.1 cgd 412 1.1 cgd case XDR_ENCODE: 413 1.1 cgd if ((rstrm->out_finger + len) <= rstrm->out_boundry) { 414 1.15 christos buf = (int32_t *)(void *)rstrm->out_finger; 415 1.1 cgd rstrm->out_finger += len; 416 1.1 cgd } 417 1.1 cgd break; 418 1.1 cgd 419 1.1 cgd case XDR_DECODE: 420 1.30 lukem if ((len <= (u_int)rstrm->fbtbc) && 421 1.1 cgd ((rstrm->in_finger + len) <= rstrm->in_boundry)) { 422 1.15 christos buf = (int32_t *)(void *)rstrm->in_finger; 423 1.1 cgd rstrm->fbtbc -= len; 424 1.1 cgd rstrm->in_finger += len; 425 1.1 cgd } 426 1.7 christos break; 427 1.7 christos 428 1.7 christos case XDR_FREE: 429 1.1 cgd break; 430 1.1 cgd } 431 1.1 cgd return (buf); 432 1.1 cgd } 433 1.1 cgd 434 1.1 cgd static void 435 1.33 matt xdrrec_destroy(XDR *xdrs) 436 1.1 cgd { 437 1.12 lukem RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private; 438 1.1 cgd 439 1.19 fvdl mem_free(rstrm->out_base, rstrm->sendsize); 440 1.19 fvdl mem_free(rstrm->in_base, rstrm->recvsize); 441 1.15 christos mem_free(rstrm, sizeof(RECSTREAM)); 442 1.1 cgd } 443 1.1 cgd 444 1.1 cgd 445 1.1 cgd /* 446 1.1 cgd * Exported routines to manage xdr records 447 1.1 cgd */ 448 1.1 cgd 449 1.1 cgd /* 450 1.1 cgd * Before reading (deserializing from the stream, one should always call 451 1.1 cgd * this procedure to guarantee proper record alignment. 452 1.1 cgd */ 453 1.1 cgd bool_t 454 1.33 matt xdrrec_skiprecord(XDR *xdrs) 455 1.1 cgd { 456 1.12 lukem RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private); 457 1.19 fvdl enum xprt_stat xstat; 458 1.1 cgd 459 1.23 fvdl if (rstrm->nonblock) { 460 1.23 fvdl if (__xdrrec_getrec(xdrs, &xstat, FALSE)) { 461 1.23 fvdl rstrm->fbtbc = 0; 462 1.23 fvdl return TRUE; 463 1.23 fvdl } 464 1.23 fvdl if (rstrm->in_finger == rstrm->in_boundry && 465 1.23 fvdl xstat == XPRT_MOREREQS) { 466 1.23 fvdl rstrm->fbtbc = 0; 467 1.23 fvdl return TRUE; 468 1.23 fvdl } 469 1.23 fvdl return FALSE; 470 1.23 fvdl } 471 1.1 cgd while (rstrm->fbtbc > 0 || (! rstrm->last_frag)) { 472 1.1 cgd if (! skip_input_bytes(rstrm, rstrm->fbtbc)) 473 1.1 cgd return (FALSE); 474 1.1 cgd rstrm->fbtbc = 0; 475 1.1 cgd if ((! rstrm->last_frag) && (! set_input_fragment(rstrm))) 476 1.1 cgd return (FALSE); 477 1.1 cgd } 478 1.1 cgd rstrm->last_frag = FALSE; 479 1.1 cgd return (TRUE); 480 1.1 cgd } 481 1.1 cgd 482 1.1 cgd /* 483 1.38 andvar * Look ahead function. 484 1.29 rtr * Returns TRUE iff there is no more input in the buffer 485 1.1 cgd * after consuming the rest of the current record. 486 1.1 cgd */ 487 1.1 cgd bool_t 488 1.33 matt xdrrec_eof(XDR *xdrs) 489 1.1 cgd { 490 1.12 lukem RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private); 491 1.1 cgd 492 1.1 cgd while (rstrm->fbtbc > 0 || (! rstrm->last_frag)) { 493 1.19 fvdl if (!skip_input_bytes(rstrm, rstrm->fbtbc)) 494 1.1 cgd return (TRUE); 495 1.1 cgd rstrm->fbtbc = 0; 496 1.19 fvdl if ((!rstrm->last_frag) && (!set_input_fragment(rstrm))) 497 1.1 cgd return (TRUE); 498 1.1 cgd } 499 1.1 cgd if (rstrm->in_finger == rstrm->in_boundry) 500 1.1 cgd return (TRUE); 501 1.1 cgd return (FALSE); 502 1.1 cgd } 503 1.1 cgd 504 1.1 cgd /* 505 1.1 cgd * The client must tell the package when an end-of-record has occurred. 506 1.41 andvar * The second parameters tells whether the record should be flushed to the 507 1.1 cgd * (output) tcp stream. (This let's the package support batched or 508 1.41 andvar * pipelined procedure calls.) TRUE => immediate flush to tcp connection. 509 1.1 cgd */ 510 1.1 cgd bool_t 511 1.36 justin xdrrec_endofrecord(XDR *xdrs, int sendnow) 512 1.1 cgd { 513 1.12 lukem RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private); 514 1.12 lukem u_long len; /* fragment length */ 515 1.1 cgd 516 1.1 cgd if (sendnow || rstrm->frag_sent || 517 1.33 matt ((u_long)rstrm->out_finger + sizeof(uint32_t) >= 518 1.11 lukem (u_long)rstrm->out_boundry)) { 519 1.1 cgd rstrm->frag_sent = FALSE; 520 1.1 cgd return (flush_out(rstrm, TRUE)); 521 1.1 cgd } 522 1.11 lukem len = (u_long)(rstrm->out_finger) - (u_long)(rstrm->frag_header) - 523 1.33 matt sizeof(uint32_t); 524 1.33 matt *(rstrm->frag_header) = htonl((uint32_t)len | LAST_FRAG); 525 1.33 matt rstrm->frag_header = (uint32_t *)(void *)rstrm->out_finger; 526 1.33 matt rstrm->out_finger += sizeof(uint32_t); 527 1.1 cgd return (TRUE); 528 1.1 cgd } 529 1.1 cgd 530 1.19 fvdl /* 531 1.19 fvdl * Fill the stream buffer with a record for a non-blocking connection. 532 1.19 fvdl * Return true if a record is available in the buffer, false if not. 533 1.19 fvdl */ 534 1.19 fvdl bool_t 535 1.33 matt __xdrrec_getrec(XDR *xdrs, enum xprt_stat *statp, bool_t expectdata) 536 1.19 fvdl { 537 1.19 fvdl RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private); 538 1.19 fvdl ssize_t n; 539 1.19 fvdl int fraglen; 540 1.19 fvdl 541 1.19 fvdl if (!rstrm->in_haveheader) { 542 1.19 fvdl n = rstrm->readit(rstrm->tcp_handle, rstrm->in_hdrp, 543 1.19 fvdl (int)sizeof (rstrm->in_header) - rstrm->in_hdrlen); 544 1.19 fvdl if (n == 0) { 545 1.19 fvdl *statp = expectdata ? XPRT_DIED : XPRT_IDLE; 546 1.19 fvdl return FALSE; 547 1.19 fvdl } 548 1.19 fvdl if (n < 0) { 549 1.19 fvdl *statp = XPRT_DIED; 550 1.19 fvdl return FALSE; 551 1.19 fvdl } 552 1.19 fvdl rstrm->in_hdrp += n; 553 1.32 christos _DIAGASSERT(__type_fit(int, n)); 554 1.32 christos rstrm->in_hdrlen += (int)n; 555 1.30 lukem if (rstrm->in_hdrlen < (int)sizeof(rstrm->in_header)) { 556 1.19 fvdl *statp = XPRT_MOREREQS; 557 1.19 fvdl return FALSE; 558 1.19 fvdl } 559 1.19 fvdl rstrm->in_header = ntohl(rstrm->in_header); 560 1.19 fvdl fraglen = (int)(rstrm->in_header & ~LAST_FRAG); 561 1.19 fvdl if (fraglen == 0 || fraglen > rstrm->in_maxrec || 562 1.19 fvdl (rstrm->in_reclen + fraglen) > rstrm->in_maxrec) { 563 1.19 fvdl *statp = XPRT_DIED; 564 1.19 fvdl return FALSE; 565 1.19 fvdl } 566 1.19 fvdl rstrm->in_reclen += fraglen; 567 1.31 christos if ((u_int)rstrm->in_reclen > rstrm->recvsize) { 568 1.31 christos if (!realloc_stream(rstrm, rstrm->in_reclen)) { 569 1.31 christos *statp = XPRT_DIED; 570 1.31 christos return FALSE; 571 1.31 christos } 572 1.31 christos } 573 1.19 fvdl if (rstrm->in_header & LAST_FRAG) { 574 1.19 fvdl rstrm->in_header &= ~LAST_FRAG; 575 1.19 fvdl rstrm->last_frag = TRUE; 576 1.19 fvdl } 577 1.19 fvdl } 578 1.19 fvdl 579 1.19 fvdl n = rstrm->readit(rstrm->tcp_handle, 580 1.19 fvdl rstrm->in_base + rstrm->in_received, 581 1.19 fvdl (rstrm->in_reclen - rstrm->in_received)); 582 1.19 fvdl 583 1.19 fvdl if (n < 0) { 584 1.19 fvdl *statp = XPRT_DIED; 585 1.19 fvdl return FALSE; 586 1.19 fvdl } 587 1.19 fvdl 588 1.19 fvdl if (n == 0) { 589 1.19 fvdl *statp = expectdata ? XPRT_DIED : XPRT_IDLE; 590 1.19 fvdl return FALSE; 591 1.19 fvdl } 592 1.19 fvdl 593 1.32 christos _DIAGASSERT(__type_fit(int, n)); 594 1.32 christos rstrm->in_received += (int)n; 595 1.19 fvdl 596 1.19 fvdl if (rstrm->in_received == rstrm->in_reclen) { 597 1.19 fvdl rstrm->in_haveheader = FALSE; 598 1.19 fvdl rstrm->in_hdrp = (char *)(void *)&rstrm->in_header; 599 1.19 fvdl rstrm->in_hdrlen = 0; 600 1.19 fvdl if (rstrm->last_frag) { 601 1.19 fvdl rstrm->fbtbc = rstrm->in_reclen; 602 1.19 fvdl rstrm->in_boundry = rstrm->in_base + rstrm->in_reclen; 603 1.19 fvdl rstrm->in_finger = rstrm->in_base; 604 1.21 fvdl rstrm->in_reclen = rstrm->in_received = 0; 605 1.19 fvdl *statp = XPRT_MOREREQS; 606 1.19 fvdl return TRUE; 607 1.19 fvdl } 608 1.19 fvdl } 609 1.19 fvdl 610 1.19 fvdl *statp = XPRT_MOREREQS; 611 1.19 fvdl return FALSE; 612 1.19 fvdl } 613 1.19 fvdl 614 1.19 fvdl bool_t 615 1.33 matt __xdrrec_setnonblock(XDR *xdrs, int maxrec) 616 1.19 fvdl { 617 1.19 fvdl RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private); 618 1.19 fvdl 619 1.19 fvdl rstrm->nonblock = TRUE; 620 1.19 fvdl if (maxrec == 0) 621 1.19 fvdl maxrec = rstrm->recvsize; 622 1.19 fvdl rstrm->in_maxrec = maxrec; 623 1.19 fvdl return TRUE; 624 1.19 fvdl } 625 1.19 fvdl 626 1.1 cgd 627 1.1 cgd /* 628 1.1 cgd * Internal useful routines 629 1.1 cgd */ 630 1.1 cgd static bool_t 631 1.33 matt flush_out(RECSTREAM *rstrm, bool_t eor) 632 1.33 matt { 633 1.33 matt uint32_t eormask = (eor == TRUE) ? LAST_FRAG : 0; 634 1.33 matt uint32_t len = (uint32_t)((u_long)(rstrm->out_finger) - 635 1.33 matt (u_long)(rstrm->frag_header) - sizeof(uint32_t)); 636 1.1 cgd 637 1.1 cgd *(rstrm->frag_header) = htonl(len | eormask); 638 1.33 matt len = (uint32_t)((u_long)(rstrm->out_finger) - 639 1.15 christos (u_long)(rstrm->out_base)); 640 1.1 cgd if ((*(rstrm->writeit))(rstrm->tcp_handle, rstrm->out_base, (int)len) 641 1.1 cgd != (int)len) 642 1.1 cgd return (FALSE); 643 1.33 matt rstrm->frag_header = (uint32_t *)(void *)rstrm->out_base; 644 1.33 matt rstrm->out_finger = (char *)rstrm->out_base + sizeof(uint32_t); 645 1.1 cgd return (TRUE); 646 1.1 cgd } 647 1.1 cgd 648 1.1 cgd static bool_t /* knows nothing about records! Only about input buffers */ 649 1.33 matt fill_input_buf(RECSTREAM *rstrm) 650 1.1 cgd { 651 1.14 mycroft char *where; 652 1.33 matt uint32_t i; 653 1.15 christos int len; 654 1.1 cgd 655 1.19 fvdl if (rstrm->nonblock) 656 1.19 fvdl return FALSE; 657 1.1 cgd where = rstrm->in_base; 658 1.33 matt i = (uint32_t)((u_long)rstrm->in_boundry % BYTES_PER_XDR_UNIT); 659 1.1 cgd where += i; 660 1.33 matt len = (uint32_t)(rstrm->in_size - i); 661 1.1 cgd if ((len = (*(rstrm->readit))(rstrm->tcp_handle, where, len)) == -1) 662 1.1 cgd return (FALSE); 663 1.1 cgd rstrm->in_finger = where; 664 1.1 cgd where += len; 665 1.1 cgd rstrm->in_boundry = where; 666 1.1 cgd return (TRUE); 667 1.1 cgd } 668 1.1 cgd 669 1.1 cgd static bool_t /* knows nothing about records! Only about input buffers */ 670 1.33 matt get_input_bytes(RECSTREAM *rstrm, char *addr, u_int len) 671 1.1 cgd { 672 1.30 lukem u_int current; 673 1.1 cgd 674 1.19 fvdl if (rstrm->nonblock) { 675 1.30 lukem if (len > ((uintptr_t)rstrm->in_boundry - (uintptr_t)rstrm->in_finger)) 676 1.19 fvdl return FALSE; 677 1.30 lukem memcpy(addr, rstrm->in_finger, len); 678 1.19 fvdl rstrm->in_finger += len; 679 1.19 fvdl return TRUE; 680 1.19 fvdl } 681 1.19 fvdl 682 1.1 cgd while (len > 0) { 683 1.32 christos uintptr_t d = ((uintptr_t)rstrm->in_boundry - 684 1.30 lukem (uintptr_t)rstrm->in_finger); 685 1.32 christos _DIAGASSERT(__type_fit(u_int, d)); 686 1.32 christos current = (u_int)d; 687 1.1 cgd if (current == 0) { 688 1.1 cgd if (! fill_input_buf(rstrm)) 689 1.1 cgd return (FALSE); 690 1.1 cgd continue; 691 1.1 cgd } 692 1.1 cgd current = (len < current) ? len : current; 693 1.12 lukem memmove(addr, rstrm->in_finger, current); 694 1.1 cgd rstrm->in_finger += current; 695 1.1 cgd addr += current; 696 1.1 cgd len -= current; 697 1.1 cgd } 698 1.1 cgd return (TRUE); 699 1.1 cgd } 700 1.1 cgd 701 1.1 cgd static bool_t /* next two bytes of the input stream are treated as a header */ 702 1.33 matt set_input_fragment(RECSTREAM *rstrm) 703 1.1 cgd { 704 1.33 matt uint32_t header; 705 1.1 cgd 706 1.19 fvdl if (rstrm->nonblock) 707 1.19 fvdl return FALSE; 708 1.32 christos if (! get_input_bytes(rstrm, (char *)(void *)&header, 709 1.32 christos (u_int)sizeof(header))) 710 1.1 cgd return (FALSE); 711 1.15 christos header = ntohl(header); 712 1.1 cgd rstrm->last_frag = ((header & LAST_FRAG) == 0) ? FALSE : TRUE; 713 1.16 lukem /* 714 1.16 lukem * Sanity check. Try not to accept wildly incorrect 715 1.16 lukem * record sizes. Unfortunately, the only record size 716 1.16 lukem * we can positively identify as being 'wildly incorrect' 717 1.16 lukem * is zero. Ridiculously large record sizes may look wrong, 718 1.16 lukem * but we don't have any way to be certain that they aren't 719 1.16 lukem * what the client actually intended to send us. 720 1.16 lukem */ 721 1.26 christos if (header == 0) 722 1.16 lukem return(FALSE); 723 1.1 cgd rstrm->fbtbc = header & (~LAST_FRAG); 724 1.1 cgd return (TRUE); 725 1.1 cgd } 726 1.1 cgd 727 1.1 cgd static bool_t /* consumes input bytes; knows nothing about records! */ 728 1.33 matt skip_input_bytes(RECSTREAM *rstrm, long cnt) 729 1.1 cgd { 730 1.33 matt uint32_t current; 731 1.1 cgd 732 1.1 cgd while (cnt > 0) { 733 1.32 christos current = (uint32_t)((long)rstrm->in_boundry - 734 1.15 christos (long)rstrm->in_finger); 735 1.1 cgd if (current == 0) { 736 1.1 cgd if (! fill_input_buf(rstrm)) 737 1.1 cgd return (FALSE); 738 1.1 cgd continue; 739 1.1 cgd } 740 1.33 matt current = ((uint32_t)cnt < current) ? (uint32_t)cnt : current; 741 1.1 cgd rstrm->in_finger += current; 742 1.1 cgd cnt -= current; 743 1.1 cgd } 744 1.1 cgd return (TRUE); 745 1.1 cgd } 746 1.1 cgd 747 1.11 lukem static u_int 748 1.33 matt fix_buf_size(u_int s) 749 1.1 cgd { 750 1.1 cgd 751 1.1 cgd if (s < 100) 752 1.1 cgd s = 4000; 753 1.1 cgd return (RNDUP(s)); 754 1.19 fvdl } 755 1.19 fvdl 756 1.19 fvdl /* 757 1.19 fvdl * Reallocate the input buffer for a non-block stream. 758 1.19 fvdl */ 759 1.19 fvdl static bool_t 760 1.33 matt realloc_stream(RECSTREAM *rstrm, int size) 761 1.19 fvdl { 762 1.19 fvdl ptrdiff_t diff; 763 1.19 fvdl char *buf; 764 1.19 fvdl 765 1.30 lukem if ((u_int)size > rstrm->recvsize) { 766 1.19 fvdl buf = realloc(rstrm->in_base, (size_t)size); 767 1.19 fvdl if (buf == NULL) 768 1.19 fvdl return FALSE; 769 1.19 fvdl diff = buf - rstrm->in_base; 770 1.19 fvdl rstrm->in_finger += diff; 771 1.19 fvdl rstrm->in_base = buf; 772 1.19 fvdl rstrm->in_boundry = buf + size; 773 1.19 fvdl rstrm->recvsize = size; 774 1.19 fvdl rstrm->in_size = size; 775 1.19 fvdl } 776 1.19 fvdl 777 1.19 fvdl return TRUE; 778 1.1 cgd } 779