1 |
|
|
/* $OpenBSD: clnt_tcp.c,v 1.29 2015/11/01 03:45:29 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 |
|
|
* clnt_tcp.c, Implements a TCP/IP based, client side RPC. |
36 |
|
|
* |
37 |
|
|
* TCP based RPC supports 'batched calls'. |
38 |
|
|
* A sequence of calls may be batched-up in a send buffer. The rpc call |
39 |
|
|
* return immediately to the client even though the call was not necessarily |
40 |
|
|
* sent. The batching occurs if the results' xdr routine is NULL (0) AND |
41 |
|
|
* the rpc timeout value is zero (see clnt.h, rpc). |
42 |
|
|
* |
43 |
|
|
* Clients should NOT casually batch calls that in fact return results; that is, |
44 |
|
|
* the server side should be aware that a call is batched and not produce any |
45 |
|
|
* return message. Batched calls that produce many result messages can |
46 |
|
|
* deadlock (netlock) the client and the server.... |
47 |
|
|
* |
48 |
|
|
* Now go hang yourself. |
49 |
|
|
*/ |
50 |
|
|
|
51 |
|
|
#include <stdio.h> |
52 |
|
|
#include <stdlib.h> |
53 |
|
|
#include <string.h> |
54 |
|
|
#include <unistd.h> |
55 |
|
|
#include <rpc/rpc.h> |
56 |
|
|
#include <sys/socket.h> |
57 |
|
|
#include <netdb.h> |
58 |
|
|
#include <errno.h> |
59 |
|
|
#include <rpc/pmap_clnt.h> |
60 |
|
|
|
61 |
|
|
#define MCALL_MSG_SIZE 24 |
62 |
|
|
|
63 |
|
|
static enum clnt_stat clnttcp_call(CLIENT *, u_long, xdrproc_t, caddr_t, |
64 |
|
|
xdrproc_t, caddr_t, struct timeval); |
65 |
|
|
static void clnttcp_abort(CLIENT *); |
66 |
|
|
static void clnttcp_geterr(CLIENT *, struct rpc_err *); |
67 |
|
|
static bool_t clnttcp_freeres(CLIENT *, xdrproc_t, caddr_t); |
68 |
|
|
static bool_t clnttcp_control(CLIENT *, u_int, void *); |
69 |
|
|
static void clnttcp_destroy(CLIENT *); |
70 |
|
|
|
71 |
|
|
static struct clnt_ops tcp_ops = { |
72 |
|
|
clnttcp_call, |
73 |
|
|
clnttcp_abort, |
74 |
|
|
clnttcp_geterr, |
75 |
|
|
clnttcp_freeres, |
76 |
|
|
clnttcp_destroy, |
77 |
|
|
clnttcp_control |
78 |
|
|
}; |
79 |
|
|
|
80 |
|
|
struct ct_data { |
81 |
|
|
int ct_sock; |
82 |
|
|
bool_t ct_closeit; |
83 |
|
|
struct timeval ct_wait; |
84 |
|
|
bool_t ct_waitset; /* wait set by clnt_control? */ |
85 |
|
|
struct sockaddr_in ct_addr; |
86 |
|
|
struct rpc_err ct_error; |
87 |
|
|
char ct_mcall[MCALL_MSG_SIZE]; /* marshalled callmsg */ |
88 |
|
|
u_int ct_mpos; /* pos after marshal */ |
89 |
|
|
XDR ct_xdrs; |
90 |
|
|
}; |
91 |
|
|
|
92 |
|
|
static int readtcp(struct ct_data *, caddr_t, int); |
93 |
|
|
static int writetcp(struct ct_data *, caddr_t, int); |
94 |
|
|
|
95 |
|
|
/* |
96 |
|
|
* Create a client handle for a tcp/ip connection. |
97 |
|
|
* If *sockp<0, *sockp is set to a newly created TCP socket and it is |
98 |
|
|
* connected to raddr. If *sockp non-negative then |
99 |
|
|
* raddr is ignored. The rpc/tcp package does buffering |
100 |
|
|
* similar to stdio, so the client must pick send and receive buffer sizes,]; |
101 |
|
|
* 0 => use the default. |
102 |
|
|
* If raddr->sin_port is 0, then a binder on the remote machine is |
103 |
|
|
* consulted for the right port number. |
104 |
|
|
* NB: *sockp is copied into a private area. |
105 |
|
|
* NB: It is the client's responsibility to close *sockp, unless |
106 |
|
|
* clnttcp_create() was called with *sockp = -1 (so it created |
107 |
|
|
* the socket), and CLNT_DESTROY() is used. |
108 |
|
|
* NB: The rpch->cl_auth is set null authentication. Caller may wish to set this |
109 |
|
|
* something more useful. |
110 |
|
|
*/ |
111 |
|
|
CLIENT * |
112 |
|
|
clnttcp_create(struct sockaddr_in *raddr, u_long prog, u_long vers, int *sockp, |
113 |
|
|
u_int sendsz, u_int recvsz) |
114 |
|
|
{ |
115 |
|
|
CLIENT *h; |
116 |
|
|
struct ct_data *ct = NULL; |
117 |
|
|
struct timeval now; |
118 |
|
|
struct rpc_msg call_msg; |
119 |
|
|
|
120 |
|
|
h = (CLIENT *)mem_alloc(sizeof(*h)); |
121 |
|
|
if (h == NULL) { |
122 |
|
|
rpc_createerr.cf_stat = RPC_SYSTEMERROR; |
123 |
|
|
rpc_createerr.cf_error.re_errno = errno; |
124 |
|
|
goto fooy; |
125 |
|
|
} |
126 |
|
|
ct = (struct ct_data *)mem_alloc(sizeof(*ct)); |
127 |
|
|
if (ct == NULL) { |
128 |
|
|
rpc_createerr.cf_stat = RPC_SYSTEMERROR; |
129 |
|
|
rpc_createerr.cf_error.re_errno = errno; |
130 |
|
|
goto fooy; |
131 |
|
|
} |
132 |
|
|
|
133 |
|
|
/* |
134 |
|
|
* If no port number given ask the pmap for one |
135 |
|
|
*/ |
136 |
|
|
if (raddr->sin_port == 0) { |
137 |
|
|
u_short port; |
138 |
|
|
if ((port = pmap_getport(raddr, prog, vers, IPPROTO_TCP)) == 0) { |
139 |
|
|
mem_free((caddr_t)ct, sizeof(struct ct_data)); |
140 |
|
|
mem_free((caddr_t)h, sizeof(CLIENT)); |
141 |
|
|
return (NULL); |
142 |
|
|
} |
143 |
|
|
raddr->sin_port = htons(port); |
144 |
|
|
} |
145 |
|
|
|
146 |
|
|
/* |
147 |
|
|
* If no socket given, open one |
148 |
|
|
*/ |
149 |
|
|
if (*sockp < 0) { |
150 |
|
|
*sockp = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); |
151 |
|
|
(void)bindresvport(*sockp, NULL); |
152 |
|
|
if ((*sockp < 0) |
153 |
|
|
|| (connect(*sockp, (struct sockaddr *)raddr, |
154 |
|
|
sizeof(*raddr)) < 0)) { |
155 |
|
|
rpc_createerr.cf_stat = RPC_SYSTEMERROR; |
156 |
|
|
rpc_createerr.cf_error.re_errno = errno; |
157 |
|
|
if (*sockp != -1) |
158 |
|
|
(void)close(*sockp); |
159 |
|
|
goto fooy; |
160 |
|
|
} |
161 |
|
|
ct->ct_closeit = TRUE; |
162 |
|
|
} else { |
163 |
|
|
ct->ct_closeit = FALSE; |
164 |
|
|
} |
165 |
|
|
|
166 |
|
|
/* |
167 |
|
|
* Set up private data struct |
168 |
|
|
*/ |
169 |
|
|
ct->ct_sock = *sockp; |
170 |
|
|
ct->ct_wait.tv_usec = 0; |
171 |
|
|
ct->ct_waitset = FALSE; |
172 |
|
|
ct->ct_addr = *raddr; |
173 |
|
|
|
174 |
|
|
/* |
175 |
|
|
* Initialize call message |
176 |
|
|
*/ |
177 |
|
|
(void)gettimeofday(&now, NULL); |
178 |
|
|
call_msg.rm_xid = arc4random(); |
179 |
|
|
call_msg.rm_direction = CALL; |
180 |
|
|
call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; |
181 |
|
|
call_msg.rm_call.cb_prog = prog; |
182 |
|
|
call_msg.rm_call.cb_vers = vers; |
183 |
|
|
|
184 |
|
|
/* |
185 |
|
|
* pre-serialize the static part of the call msg and stash it away |
186 |
|
|
*/ |
187 |
|
|
xdrmem_create(&(ct->ct_xdrs), ct->ct_mcall, MCALL_MSG_SIZE, |
188 |
|
|
XDR_ENCODE); |
189 |
|
|
if (! xdr_callhdr(&(ct->ct_xdrs), &call_msg)) { |
190 |
|
|
if (ct->ct_closeit) { |
191 |
|
|
(void)close(*sockp); |
192 |
|
|
} |
193 |
|
|
goto fooy; |
194 |
|
|
} |
195 |
|
|
ct->ct_mpos = XDR_GETPOS(&(ct->ct_xdrs)); |
196 |
|
|
XDR_DESTROY(&(ct->ct_xdrs)); |
197 |
|
|
|
198 |
|
|
/* |
199 |
|
|
* Create a client handle which uses xdrrec for serialization |
200 |
|
|
* and authnone for authentication. |
201 |
|
|
*/ |
202 |
|
|
xdrrec_create(&(ct->ct_xdrs), sendsz, recvsz, |
203 |
|
|
(caddr_t)ct, (int(*)(caddr_t, caddr_t, int))readtcp, |
204 |
|
|
(int(*)(caddr_t, caddr_t, int))writetcp); |
205 |
|
|
h->cl_ops = &tcp_ops; |
206 |
|
|
h->cl_private = (caddr_t) ct; |
207 |
|
|
h->cl_auth = authnone_create(); |
208 |
|
|
if (h->cl_auth == NULL) { |
209 |
|
|
rpc_createerr.cf_stat = RPC_SYSTEMERROR; |
210 |
|
|
rpc_createerr.cf_error.re_errno = errno; |
211 |
|
|
goto fooy; |
212 |
|
|
} |
213 |
|
|
return (h); |
214 |
|
|
|
215 |
|
|
fooy: |
216 |
|
|
/* |
217 |
|
|
* Something goofed, free stuff and barf |
218 |
|
|
*/ |
219 |
|
|
if (ct) |
220 |
|
|
mem_free((caddr_t)ct, sizeof(struct ct_data)); |
221 |
|
|
if (h) |
222 |
|
|
mem_free((caddr_t)h, sizeof(CLIENT)); |
223 |
|
|
return (NULL); |
224 |
|
|
} |
225 |
|
|
DEF_WEAK(clnttcp_create); |
226 |
|
|
|
227 |
|
|
static enum clnt_stat |
228 |
|
|
clnttcp_call(CLIENT *h, u_long proc, xdrproc_t xdr_args, caddr_t args_ptr, |
229 |
|
|
xdrproc_t xdr_results, caddr_t results_ptr, struct timeval timeout) |
230 |
|
|
{ |
231 |
|
|
struct ct_data *ct = (struct ct_data *) h->cl_private; |
232 |
|
|
XDR *xdrs = &(ct->ct_xdrs); |
233 |
|
|
struct rpc_msg reply_msg; |
234 |
|
|
u_long x_id; |
235 |
|
|
u_int32_t *msg_x_id = (u_int32_t *)(ct->ct_mcall); /* yuk */ |
236 |
|
|
bool_t shipnow; |
237 |
|
|
int refreshes = 2; |
238 |
|
|
|
239 |
|
|
if (!ct->ct_waitset) { |
240 |
|
|
ct->ct_wait = timeout; |
241 |
|
|
} |
242 |
|
|
|
243 |
|
|
shipnow = |
244 |
|
|
(xdr_results == NULL && timeout.tv_sec == 0 |
245 |
|
|
&& timeout.tv_usec == 0) ? FALSE : TRUE; |
246 |
|
|
|
247 |
|
|
call_again: |
248 |
|
|
xdrs->x_op = XDR_ENCODE; |
249 |
|
|
ct->ct_error.re_status = RPC_SUCCESS; |
250 |
|
|
x_id = ntohl(--(*msg_x_id)); |
251 |
|
|
if ((! XDR_PUTBYTES(xdrs, ct->ct_mcall, ct->ct_mpos)) || |
252 |
|
|
(! XDR_PUTLONG(xdrs, (long *)&proc)) || |
253 |
|
|
(! AUTH_MARSHALL(h->cl_auth, xdrs)) || |
254 |
|
|
(! (*xdr_args)(xdrs, args_ptr))) { |
255 |
|
|
if (ct->ct_error.re_status == RPC_SUCCESS) |
256 |
|
|
ct->ct_error.re_status = RPC_CANTENCODEARGS; |
257 |
|
|
(void)xdrrec_endofrecord(xdrs, TRUE); |
258 |
|
|
return (ct->ct_error.re_status); |
259 |
|
|
} |
260 |
|
|
if (! xdrrec_endofrecord(xdrs, shipnow)) |
261 |
|
|
return (ct->ct_error.re_status = RPC_CANTSEND); |
262 |
|
|
if (! shipnow) |
263 |
|
|
return (RPC_SUCCESS); |
264 |
|
|
/* |
265 |
|
|
* Hack to provide rpc-based message passing |
266 |
|
|
*/ |
267 |
|
|
if (timeout.tv_sec == 0 && timeout.tv_usec == 0) { |
268 |
|
|
return(ct->ct_error.re_status = RPC_TIMEDOUT); |
269 |
|
|
} |
270 |
|
|
|
271 |
|
|
|
272 |
|
|
/* |
273 |
|
|
* Keep receiving until we get a valid transaction id |
274 |
|
|
*/ |
275 |
|
|
xdrs->x_op = XDR_DECODE; |
276 |
|
|
while (TRUE) { |
277 |
|
|
reply_msg.acpted_rply.ar_verf = _null_auth; |
278 |
|
|
reply_msg.acpted_rply.ar_results.where = NULL; |
279 |
|
|
reply_msg.acpted_rply.ar_results.proc = xdr_void; |
280 |
|
|
if (! xdrrec_skiprecord(xdrs)) |
281 |
|
|
return (ct->ct_error.re_status); |
282 |
|
|
/* now decode and validate the response header */ |
283 |
|
|
if (! xdr_replymsg(xdrs, &reply_msg)) { |
284 |
|
|
if (ct->ct_error.re_status == RPC_SUCCESS) |
285 |
|
|
continue; |
286 |
|
|
return (ct->ct_error.re_status); |
287 |
|
|
} |
288 |
|
|
if (reply_msg.rm_xid == x_id) |
289 |
|
|
break; |
290 |
|
|
} |
291 |
|
|
|
292 |
|
|
/* |
293 |
|
|
* process header |
294 |
|
|
*/ |
295 |
|
|
_seterr_reply(&reply_msg, &(ct->ct_error)); |
296 |
|
|
if (ct->ct_error.re_status == RPC_SUCCESS) { |
297 |
|
|
if (! AUTH_VALIDATE(h->cl_auth, &reply_msg.acpted_rply.ar_verf)) { |
298 |
|
|
ct->ct_error.re_status = RPC_AUTHERROR; |
299 |
|
|
ct->ct_error.re_why = AUTH_INVALIDRESP; |
300 |
|
|
} else if (! (*xdr_results)(xdrs, results_ptr)) { |
301 |
|
|
if (ct->ct_error.re_status == RPC_SUCCESS) |
302 |
|
|
ct->ct_error.re_status = RPC_CANTDECODERES; |
303 |
|
|
} |
304 |
|
|
/* free verifier ... */ |
305 |
|
|
if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) { |
306 |
|
|
xdrs->x_op = XDR_FREE; |
307 |
|
|
(void)xdr_opaque_auth(xdrs, &(reply_msg.acpted_rply.ar_verf)); |
308 |
|
|
} |
309 |
|
|
} /* end successful completion */ |
310 |
|
|
else { |
311 |
|
|
/* maybe our credentials need to be refreshed ... */ |
312 |
|
|
if (refreshes-- && AUTH_REFRESH(h->cl_auth)) |
313 |
|
|
goto call_again; |
314 |
|
|
} /* end of unsuccessful completion */ |
315 |
|
|
return (ct->ct_error.re_status); |
316 |
|
|
} |
317 |
|
|
|
318 |
|
|
static void |
319 |
|
|
clnttcp_geterr(CLIENT *h, struct rpc_err *errp) |
320 |
|
|
{ |
321 |
|
|
struct ct_data *ct = |
322 |
|
|
(struct ct_data *) h->cl_private; |
323 |
|
|
|
324 |
|
|
*errp = ct->ct_error; |
325 |
|
|
} |
326 |
|
|
|
327 |
|
|
static bool_t |
328 |
|
|
clnttcp_freeres(CLIENT *cl, xdrproc_t xdr_res, caddr_t res_ptr) |
329 |
|
|
{ |
330 |
|
|
struct ct_data *ct = (struct ct_data *)cl->cl_private; |
331 |
|
|
XDR *xdrs = &(ct->ct_xdrs); |
332 |
|
|
|
333 |
|
|
xdrs->x_op = XDR_FREE; |
334 |
|
|
return ((*xdr_res)(xdrs, res_ptr)); |
335 |
|
|
} |
336 |
|
|
|
337 |
|
|
static void |
338 |
|
|
clnttcp_abort(CLIENT *clnt) |
339 |
|
|
{ |
340 |
|
|
} |
341 |
|
|
|
342 |
|
|
static bool_t |
343 |
|
|
clnttcp_control(CLIENT *cl, u_int request, void *info) |
344 |
|
|
{ |
345 |
|
|
struct ct_data *ct = (struct ct_data *)cl->cl_private; |
346 |
|
|
|
347 |
|
|
switch (request) { |
348 |
|
|
case CLSET_TIMEOUT: |
349 |
|
|
ct->ct_wait = *(struct timeval *)info; |
350 |
|
|
ct->ct_waitset = TRUE; |
351 |
|
|
break; |
352 |
|
|
case CLGET_TIMEOUT: |
353 |
|
|
*(struct timeval *)info = ct->ct_wait; |
354 |
|
|
break; |
355 |
|
|
case CLGET_SERVER_ADDR: |
356 |
|
|
*(struct sockaddr_in *)info = ct->ct_addr; |
357 |
|
|
break; |
358 |
|
|
default: |
359 |
|
|
return (FALSE); |
360 |
|
|
} |
361 |
|
|
return (TRUE); |
362 |
|
|
} |
363 |
|
|
|
364 |
|
|
|
365 |
|
|
static void |
366 |
|
|
clnttcp_destroy(CLIENT *h) |
367 |
|
|
{ |
368 |
|
|
struct ct_data *ct = |
369 |
|
|
(struct ct_data *) h->cl_private; |
370 |
|
|
|
371 |
|
|
if (ct->ct_closeit && ct->ct_sock != -1) { |
372 |
|
|
(void)close(ct->ct_sock); |
373 |
|
|
} |
374 |
|
|
XDR_DESTROY(&(ct->ct_xdrs)); |
375 |
|
|
mem_free((caddr_t)ct, sizeof(struct ct_data)); |
376 |
|
|
mem_free((caddr_t)h, sizeof(CLIENT)); |
377 |
|
|
} |
378 |
|
|
|
379 |
|
|
/* |
380 |
|
|
* Interface between xdr serializer and tcp connection. |
381 |
|
|
* Behaves like the system calls, read & write, but keeps some error state |
382 |
|
|
* around for the rpc level. |
383 |
|
|
*/ |
384 |
|
|
static int |
385 |
|
|
readtcp(struct ct_data *ct, caddr_t buf, int len) |
386 |
|
|
{ |
387 |
|
|
struct pollfd pfd[1]; |
388 |
|
|
struct timeval start, after, duration, tmp; |
389 |
|
|
int delta, r, save_errno; |
390 |
|
|
|
391 |
|
|
if (len == 0) |
392 |
|
|
return (0); |
393 |
|
|
|
394 |
|
|
pfd[0].fd = ct->ct_sock; |
395 |
|
|
pfd[0].events = POLLIN; |
396 |
|
|
delta = ct->ct_wait.tv_sec * 1000 + ct->ct_wait.tv_usec / 1000; |
397 |
|
|
gettimeofday(&start, NULL); |
398 |
|
|
for (;;) { |
399 |
|
|
r = poll(pfd, 1, delta); |
400 |
|
|
save_errno = errno; |
401 |
|
|
|
402 |
|
|
gettimeofday(&after, NULL); |
403 |
|
|
timersub(&start, &after, &duration); |
404 |
|
|
timersub(&ct->ct_wait, &duration, &tmp); |
405 |
|
|
delta = tmp.tv_sec * 1000 + tmp.tv_usec / 1000; |
406 |
|
|
if (delta <= 0) |
407 |
|
|
r = 0; |
408 |
|
|
|
409 |
|
|
switch (r) { |
410 |
|
|
case 0: |
411 |
|
|
ct->ct_error.re_status = RPC_TIMEDOUT; |
412 |
|
|
return (-1); |
413 |
|
|
case 1: |
414 |
|
|
if (pfd[0].revents & POLLNVAL) |
415 |
|
|
errno = EBADF; |
416 |
|
|
else if (pfd[0].revents & POLLERR) |
417 |
|
|
errno = EIO; |
418 |
|
|
else |
419 |
|
|
break; |
420 |
|
|
/* FALLTHROUGH */ |
421 |
|
|
case -1: |
422 |
|
|
if (errno == EINTR) |
423 |
|
|
continue; |
424 |
|
|
ct->ct_error.re_status = RPC_CANTRECV; |
425 |
|
|
ct->ct_error.re_errno = save_errno; |
426 |
|
|
return (-1); |
427 |
|
|
} |
428 |
|
|
break; |
429 |
|
|
} |
430 |
|
|
|
431 |
|
|
switch (len = read(ct->ct_sock, buf, len)) { |
432 |
|
|
case 0: |
433 |
|
|
/* premature eof */ |
434 |
|
|
ct->ct_error.re_errno = ECONNRESET; |
435 |
|
|
ct->ct_error.re_status = RPC_CANTRECV; |
436 |
|
|
len = -1; /* it's really an error */ |
437 |
|
|
break; |
438 |
|
|
case -1: |
439 |
|
|
ct->ct_error.re_errno = errno; |
440 |
|
|
ct->ct_error.re_status = RPC_CANTRECV; |
441 |
|
|
break; |
442 |
|
|
} |
443 |
|
|
return (len); |
444 |
|
|
} |
445 |
|
|
|
446 |
|
|
static int |
447 |
|
|
writetcp(struct ct_data *ct, caddr_t buf, int len) |
448 |
|
|
{ |
449 |
|
|
int i, cnt; |
450 |
|
|
|
451 |
|
|
for (cnt = len; cnt > 0; cnt -= i, buf += i) { |
452 |
|
|
if ((i = write(ct->ct_sock, buf, cnt)) == -1) { |
453 |
|
|
ct->ct_error.re_errno = errno; |
454 |
|
|
ct->ct_error.re_status = RPC_CANTSEND; |
455 |
|
|
return (-1); |
456 |
|
|
} |
457 |
|
|
} |
458 |
|
|
return (len); |
459 |
|
|
} |