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