1 | #include <machine/rtems-bsd-user-space.h> |
---|
2 | |
---|
3 | /* $NetBSD: clnt_dg.c,v 1.4 2000/07/14 08:40:41 fvdl Exp $ */ |
---|
4 | |
---|
5 | /*- |
---|
6 | * Copyright (c) 2009, Sun Microsystems, Inc. |
---|
7 | * All rights reserved. |
---|
8 | * |
---|
9 | * Redistribution and use in source and binary forms, with or without |
---|
10 | * modification, are permitted provided that the following conditions are met: |
---|
11 | * - Redistributions of source code must retain the above copyright notice, |
---|
12 | * this list of conditions and the following disclaimer. |
---|
13 | * - Redistributions in binary form must reproduce the above copyright notice, |
---|
14 | * this list of conditions and the following disclaimer in the documentation |
---|
15 | * and/or other materials provided with the distribution. |
---|
16 | * - Neither the name of Sun Microsystems, 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 "AS IS" |
---|
21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
---|
22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
---|
23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
---|
24 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
---|
25 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
---|
26 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
---|
27 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
---|
28 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
---|
29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
---|
30 | * POSSIBILITY OF SUCH DAMAGE. |
---|
31 | */ |
---|
32 | /* |
---|
33 | * Copyright (c) 1986-1991 by Sun Microsystems Inc. |
---|
34 | */ |
---|
35 | |
---|
36 | #if defined(LIBC_SCCS) && !defined(lint) |
---|
37 | #ident "@(#)clnt_dg.c 1.23 94/04/22 SMI" |
---|
38 | static char sccsid[] = "@(#)clnt_dg.c 1.19 89/03/16 Copyr 1988 Sun Micro"; |
---|
39 | #endif |
---|
40 | #include <sys/cdefs.h> |
---|
41 | __FBSDID("$FreeBSD$"); |
---|
42 | |
---|
43 | /* |
---|
44 | * Implements a connectionless client side RPC. |
---|
45 | */ |
---|
46 | |
---|
47 | #include "namespace.h" |
---|
48 | #include "reentrant.h" |
---|
49 | #include <sys/types.h> |
---|
50 | #include <sys/event.h> |
---|
51 | #include <sys/time.h> |
---|
52 | #include <sys/socket.h> |
---|
53 | #include <sys/ioctl.h> |
---|
54 | #include <arpa/inet.h> |
---|
55 | #include <rpc/rpc.h> |
---|
56 | #include <rpc/rpcsec_gss.h> |
---|
57 | #include <errno.h> |
---|
58 | #include <stdlib.h> |
---|
59 | #include <string.h> |
---|
60 | #include <signal.h> |
---|
61 | #include <unistd.h> |
---|
62 | #include <err.h> |
---|
63 | #include "un-namespace.h" |
---|
64 | #include "rpc_com.h" |
---|
65 | #include "mt_misc.h" |
---|
66 | |
---|
67 | |
---|
68 | #ifdef _FREEFALL_CONFIG |
---|
69 | /* |
---|
70 | * Disable RPC exponential back-off for FreeBSD.org systems. |
---|
71 | */ |
---|
72 | #define RPC_MAX_BACKOFF 1 /* second */ |
---|
73 | #else |
---|
74 | #define RPC_MAX_BACKOFF 30 /* seconds */ |
---|
75 | #endif |
---|
76 | |
---|
77 | |
---|
78 | static struct clnt_ops *clnt_dg_ops(void); |
---|
79 | static bool_t time_not_ok(struct timeval *); |
---|
80 | static enum clnt_stat clnt_dg_call(CLIENT *, rpcproc_t, xdrproc_t, void *, |
---|
81 | xdrproc_t, void *, struct timeval); |
---|
82 | static void clnt_dg_geterr(CLIENT *, struct rpc_err *); |
---|
83 | static bool_t clnt_dg_freeres(CLIENT *, xdrproc_t, void *); |
---|
84 | static void clnt_dg_abort(CLIENT *); |
---|
85 | static bool_t clnt_dg_control(CLIENT *, u_int, void *); |
---|
86 | static void clnt_dg_destroy(CLIENT *); |
---|
87 | |
---|
88 | |
---|
89 | |
---|
90 | |
---|
91 | /* |
---|
92 | * This machinery implements per-fd locks for MT-safety. It is not |
---|
93 | * sufficient to do per-CLIENT handle locks for MT-safety because a |
---|
94 | * user may create more than one CLIENT handle with the same fd behind |
---|
95 | * it. Therfore, we allocate an array of flags (dg_fd_locks), protected |
---|
96 | * by the clnt_fd_lock mutex, and an array (dg_cv) of condition variables |
---|
97 | * similarly protected. Dg_fd_lock[fd] == 1 => a call is activte on some |
---|
98 | * CLIENT handle created for that fd. |
---|
99 | * The current implementation holds locks across the entire RPC and reply, |
---|
100 | * including retransmissions. Yes, this is silly, and as soon as this |
---|
101 | * code is proven to work, this should be the first thing fixed. One step |
---|
102 | * at a time. |
---|
103 | */ |
---|
104 | static int *dg_fd_locks; |
---|
105 | static cond_t *dg_cv; |
---|
106 | #define release_fd_lock(fd, mask) { \ |
---|
107 | mutex_lock(&clnt_fd_lock); \ |
---|
108 | dg_fd_locks[fd] = 0; \ |
---|
109 | mutex_unlock(&clnt_fd_lock); \ |
---|
110 | thr_sigsetmask(SIG_SETMASK, &(mask), NULL); \ |
---|
111 | cond_signal(&dg_cv[fd]); \ |
---|
112 | } |
---|
113 | |
---|
114 | static const char mem_err_clnt_dg[] = "clnt_dg_create: out of memory"; |
---|
115 | |
---|
116 | /* VARIABLES PROTECTED BY clnt_fd_lock: dg_fd_locks, dg_cv */ |
---|
117 | |
---|
118 | #define MCALL_MSG_SIZE 24 |
---|
119 | |
---|
120 | /* |
---|
121 | * Private data kept per client handle |
---|
122 | */ |
---|
123 | struct cu_data { |
---|
124 | int cu_fd; /* connections fd */ |
---|
125 | bool_t cu_closeit; /* opened by library */ |
---|
126 | struct sockaddr_storage cu_raddr; /* remote address */ |
---|
127 | int cu_rlen; |
---|
128 | struct timeval cu_wait; /* retransmit interval */ |
---|
129 | struct timeval cu_total; /* total time for the call */ |
---|
130 | struct rpc_err cu_error; |
---|
131 | XDR cu_outxdrs; |
---|
132 | u_int cu_xdrpos; |
---|
133 | u_int cu_sendsz; /* send size */ |
---|
134 | char cu_outhdr[MCALL_MSG_SIZE]; |
---|
135 | char *cu_outbuf; |
---|
136 | u_int cu_recvsz; /* recv size */ |
---|
137 | int cu_async; |
---|
138 | int cu_connect; /* Use connect(). */ |
---|
139 | int cu_connected; /* Have done connect(). */ |
---|
140 | struct kevent cu_kin; |
---|
141 | int cu_kq; |
---|
142 | char cu_inbuf[1]; |
---|
143 | }; |
---|
144 | |
---|
145 | /* |
---|
146 | * Connection less client creation returns with client handle parameters. |
---|
147 | * Default options are set, which the user can change using clnt_control(). |
---|
148 | * fd should be open and bound. |
---|
149 | * NB: The rpch->cl_auth is initialized to null authentication. |
---|
150 | * Caller may wish to set this something more useful. |
---|
151 | * |
---|
152 | * sendsz and recvsz are the maximum allowable packet sizes that can be |
---|
153 | * sent and received. Normally they are the same, but they can be |
---|
154 | * changed to improve the program efficiency and buffer allocation. |
---|
155 | * If they are 0, use the transport default. |
---|
156 | * |
---|
157 | * If svcaddr is NULL, returns NULL. |
---|
158 | */ |
---|
159 | CLIENT * |
---|
160 | clnt_dg_create(fd, svcaddr, program, version, sendsz, recvsz) |
---|
161 | int fd; /* open file descriptor */ |
---|
162 | const struct netbuf *svcaddr; /* servers address */ |
---|
163 | rpcprog_t program; /* program number */ |
---|
164 | rpcvers_t version; /* version number */ |
---|
165 | u_int sendsz; /* buffer recv size */ |
---|
166 | u_int recvsz; /* buffer send size */ |
---|
167 | { |
---|
168 | CLIENT *cl = NULL; /* client handle */ |
---|
169 | struct cu_data *cu = NULL; /* private data */ |
---|
170 | struct timeval now; |
---|
171 | struct rpc_msg call_msg; |
---|
172 | sigset_t mask; |
---|
173 | sigset_t newmask; |
---|
174 | struct __rpc_sockinfo si; |
---|
175 | int one = 1; |
---|
176 | |
---|
177 | sigfillset(&newmask); |
---|
178 | thr_sigsetmask(SIG_SETMASK, &newmask, &mask); |
---|
179 | mutex_lock(&clnt_fd_lock); |
---|
180 | if (dg_fd_locks == (int *) NULL) { |
---|
181 | int cv_allocsz; |
---|
182 | size_t fd_allocsz; |
---|
183 | int dtbsize = __rpc_dtbsize(); |
---|
184 | |
---|
185 | fd_allocsz = dtbsize * sizeof (int); |
---|
186 | dg_fd_locks = (int *) mem_alloc(fd_allocsz); |
---|
187 | if (dg_fd_locks == (int *) NULL) { |
---|
188 | mutex_unlock(&clnt_fd_lock); |
---|
189 | thr_sigsetmask(SIG_SETMASK, &(mask), NULL); |
---|
190 | goto err1; |
---|
191 | } else |
---|
192 | memset(dg_fd_locks, '\0', fd_allocsz); |
---|
193 | |
---|
194 | cv_allocsz = dtbsize * sizeof (cond_t); |
---|
195 | dg_cv = (cond_t *) mem_alloc(cv_allocsz); |
---|
196 | if (dg_cv == (cond_t *) NULL) { |
---|
197 | mem_free(dg_fd_locks, fd_allocsz); |
---|
198 | dg_fd_locks = (int *) NULL; |
---|
199 | mutex_unlock(&clnt_fd_lock); |
---|
200 | thr_sigsetmask(SIG_SETMASK, &(mask), NULL); |
---|
201 | goto err1; |
---|
202 | } else { |
---|
203 | int i; |
---|
204 | |
---|
205 | for (i = 0; i < dtbsize; i++) |
---|
206 | cond_init(&dg_cv[i], 0, (void *) 0); |
---|
207 | } |
---|
208 | } |
---|
209 | |
---|
210 | mutex_unlock(&clnt_fd_lock); |
---|
211 | thr_sigsetmask(SIG_SETMASK, &(mask), NULL); |
---|
212 | |
---|
213 | if (svcaddr == NULL) { |
---|
214 | rpc_createerr.cf_stat = RPC_UNKNOWNADDR; |
---|
215 | return (NULL); |
---|
216 | } |
---|
217 | |
---|
218 | if (!__rpc_fd2sockinfo(fd, &si)) { |
---|
219 | rpc_createerr.cf_stat = RPC_TLIERROR; |
---|
220 | rpc_createerr.cf_error.re_errno = 0; |
---|
221 | return (NULL); |
---|
222 | } |
---|
223 | /* |
---|
224 | * Find the receive and the send size |
---|
225 | */ |
---|
226 | sendsz = __rpc_get_t_size(si.si_af, si.si_proto, (int)sendsz); |
---|
227 | recvsz = __rpc_get_t_size(si.si_af, si.si_proto, (int)recvsz); |
---|
228 | if ((sendsz == 0) || (recvsz == 0)) { |
---|
229 | rpc_createerr.cf_stat = RPC_TLIERROR; /* XXX */ |
---|
230 | rpc_createerr.cf_error.re_errno = 0; |
---|
231 | return (NULL); |
---|
232 | } |
---|
233 | |
---|
234 | if ((cl = mem_alloc(sizeof (CLIENT))) == NULL) |
---|
235 | goto err1; |
---|
236 | /* |
---|
237 | * Should be multiple of 4 for XDR. |
---|
238 | */ |
---|
239 | sendsz = ((sendsz + 3) / 4) * 4; |
---|
240 | recvsz = ((recvsz + 3) / 4) * 4; |
---|
241 | cu = mem_alloc(sizeof (*cu) + sendsz + recvsz); |
---|
242 | if (cu == NULL) |
---|
243 | goto err1; |
---|
244 | (void) memcpy(&cu->cu_raddr, svcaddr->buf, (size_t)svcaddr->len); |
---|
245 | cu->cu_rlen = svcaddr->len; |
---|
246 | cu->cu_outbuf = &cu->cu_inbuf[recvsz]; |
---|
247 | /* Other values can also be set through clnt_control() */ |
---|
248 | cu->cu_wait.tv_sec = 15; /* heuristically chosen */ |
---|
249 | cu->cu_wait.tv_usec = 0; |
---|
250 | cu->cu_total.tv_sec = -1; |
---|
251 | cu->cu_total.tv_usec = -1; |
---|
252 | cu->cu_sendsz = sendsz; |
---|
253 | cu->cu_recvsz = recvsz; |
---|
254 | cu->cu_async = FALSE; |
---|
255 | cu->cu_connect = FALSE; |
---|
256 | cu->cu_connected = FALSE; |
---|
257 | (void) gettimeofday(&now, NULL); |
---|
258 | call_msg.rm_xid = __RPC_GETXID(&now); |
---|
259 | call_msg.rm_call.cb_prog = program; |
---|
260 | call_msg.rm_call.cb_vers = version; |
---|
261 | xdrmem_create(&(cu->cu_outxdrs), cu->cu_outhdr, MCALL_MSG_SIZE, |
---|
262 | XDR_ENCODE); |
---|
263 | if (! xdr_callhdr(&cu->cu_outxdrs, &call_msg)) { |
---|
264 | rpc_createerr.cf_stat = RPC_CANTENCODEARGS; /* XXX */ |
---|
265 | rpc_createerr.cf_error.re_errno = 0; |
---|
266 | goto err2; |
---|
267 | } |
---|
268 | cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs)); |
---|
269 | XDR_DESTROY(&cu->cu_outxdrs); |
---|
270 | xdrmem_create(&cu->cu_outxdrs, cu->cu_outbuf, sendsz, XDR_ENCODE); |
---|
271 | |
---|
272 | /* XXX fvdl - do we still want this? */ |
---|
273 | #if 0 |
---|
274 | (void)bindresvport_sa(fd, (struct sockaddr *)svcaddr->buf); |
---|
275 | #endif |
---|
276 | _ioctl(fd, FIONBIO, (char *)(void *)&one); |
---|
277 | |
---|
278 | /* |
---|
279 | * By default, closeit is always FALSE. It is users responsibility |
---|
280 | * to do a close on it, else the user may use clnt_control |
---|
281 | * to let clnt_destroy do it for him/her. |
---|
282 | */ |
---|
283 | cu->cu_closeit = FALSE; |
---|
284 | cu->cu_fd = fd; |
---|
285 | cl->cl_ops = clnt_dg_ops(); |
---|
286 | cl->cl_private = (caddr_t)(void *)cu; |
---|
287 | cl->cl_auth = authnone_create(); |
---|
288 | cl->cl_tp = NULL; |
---|
289 | cl->cl_netid = NULL; |
---|
290 | cu->cu_kq = -1; |
---|
291 | EV_SET(&cu->cu_kin, cu->cu_fd, EVFILT_READ, EV_ADD, 0, 0, 0); |
---|
292 | return (cl); |
---|
293 | err1: |
---|
294 | warnx(mem_err_clnt_dg); |
---|
295 | rpc_createerr.cf_stat = RPC_SYSTEMERROR; |
---|
296 | rpc_createerr.cf_error.re_errno = errno; |
---|
297 | err2: |
---|
298 | if (cl) { |
---|
299 | mem_free(cl, sizeof (CLIENT)); |
---|
300 | if (cu) |
---|
301 | mem_free(cu, sizeof (*cu) + sendsz + recvsz); |
---|
302 | } |
---|
303 | return (NULL); |
---|
304 | } |
---|
305 | |
---|
306 | static enum clnt_stat |
---|
307 | clnt_dg_call(cl, proc, xargs, argsp, xresults, resultsp, utimeout) |
---|
308 | CLIENT *cl; /* client handle */ |
---|
309 | rpcproc_t proc; /* procedure number */ |
---|
310 | xdrproc_t xargs; /* xdr routine for args */ |
---|
311 | void *argsp; /* pointer to args */ |
---|
312 | xdrproc_t xresults; /* xdr routine for results */ |
---|
313 | void *resultsp; /* pointer to results */ |
---|
314 | struct timeval utimeout; /* seconds to wait before giving up */ |
---|
315 | { |
---|
316 | struct cu_data *cu = (struct cu_data *)cl->cl_private; |
---|
317 | XDR *xdrs; |
---|
318 | size_t outlen = 0; |
---|
319 | struct rpc_msg reply_msg; |
---|
320 | XDR reply_xdrs; |
---|
321 | bool_t ok; |
---|
322 | int nrefreshes = 2; /* number of times to refresh cred */ |
---|
323 | int nretries = 0; /* number of times we retransmitted */ |
---|
324 | struct timeval timeout; |
---|
325 | struct timeval retransmit_time; |
---|
326 | struct timeval next_sendtime, starttime, time_waited, tv; |
---|
327 | struct timespec ts; |
---|
328 | struct kevent kv; |
---|
329 | struct sockaddr *sa; |
---|
330 | sigset_t mask; |
---|
331 | sigset_t newmask; |
---|
332 | socklen_t inlen, salen; |
---|
333 | ssize_t recvlen = 0; |
---|
334 | int kin_len, n, rpc_lock_value; |
---|
335 | u_int32_t xid; |
---|
336 | |
---|
337 | outlen = 0; |
---|
338 | sigfillset(&newmask); |
---|
339 | thr_sigsetmask(SIG_SETMASK, &newmask, &mask); |
---|
340 | mutex_lock(&clnt_fd_lock); |
---|
341 | while (dg_fd_locks[cu->cu_fd]) |
---|
342 | cond_wait(&dg_cv[cu->cu_fd], &clnt_fd_lock); |
---|
343 | if (__isthreaded) |
---|
344 | rpc_lock_value = 1; |
---|
345 | else |
---|
346 | rpc_lock_value = 0; |
---|
347 | dg_fd_locks[cu->cu_fd] = rpc_lock_value; |
---|
348 | mutex_unlock(&clnt_fd_lock); |
---|
349 | if (cu->cu_total.tv_usec == -1) { |
---|
350 | timeout = utimeout; /* use supplied timeout */ |
---|
351 | } else { |
---|
352 | timeout = cu->cu_total; /* use default timeout */ |
---|
353 | } |
---|
354 | |
---|
355 | if (cu->cu_connect && !cu->cu_connected) { |
---|
356 | if (_connect(cu->cu_fd, (struct sockaddr *)&cu->cu_raddr, |
---|
357 | cu->cu_rlen) < 0) { |
---|
358 | cu->cu_error.re_errno = errno; |
---|
359 | cu->cu_error.re_status = RPC_CANTSEND; |
---|
360 | goto out; |
---|
361 | } |
---|
362 | cu->cu_connected = 1; |
---|
363 | } |
---|
364 | if (cu->cu_connected) { |
---|
365 | sa = NULL; |
---|
366 | salen = 0; |
---|
367 | } else { |
---|
368 | sa = (struct sockaddr *)&cu->cu_raddr; |
---|
369 | salen = cu->cu_rlen; |
---|
370 | } |
---|
371 | time_waited.tv_sec = 0; |
---|
372 | time_waited.tv_usec = 0; |
---|
373 | retransmit_time = next_sendtime = cu->cu_wait; |
---|
374 | gettimeofday(&starttime, NULL); |
---|
375 | |
---|
376 | /* Clean up in case the last call ended in a longjmp(3) call. */ |
---|
377 | if (cu->cu_kq >= 0) |
---|
378 | _close(cu->cu_kq); |
---|
379 | if ((cu->cu_kq = kqueue()) < 0) { |
---|
380 | cu->cu_error.re_errno = errno; |
---|
381 | cu->cu_error.re_status = RPC_CANTSEND; |
---|
382 | goto out; |
---|
383 | } |
---|
384 | kin_len = 1; |
---|
385 | |
---|
386 | call_again: |
---|
387 | if (cu->cu_async == TRUE && xargs == NULL) |
---|
388 | goto get_reply; |
---|
389 | /* |
---|
390 | * the transaction is the first thing in the out buffer |
---|
391 | * XXX Yes, and it's in network byte order, so we should to |
---|
392 | * be careful when we increment it, shouldn't we. |
---|
393 | */ |
---|
394 | xid = ntohl(*(u_int32_t *)(void *)(cu->cu_outhdr)); |
---|
395 | xid++; |
---|
396 | *(u_int32_t *)(void *)(cu->cu_outhdr) = htonl(xid); |
---|
397 | call_again_same_xid: |
---|
398 | xdrs = &(cu->cu_outxdrs); |
---|
399 | xdrs->x_op = XDR_ENCODE; |
---|
400 | XDR_SETPOS(xdrs, 0); |
---|
401 | |
---|
402 | if (cl->cl_auth->ah_cred.oa_flavor != RPCSEC_GSS) { |
---|
403 | if ((! XDR_PUTBYTES(xdrs, cu->cu_outhdr, cu->cu_xdrpos)) || |
---|
404 | (! XDR_PUTINT32(xdrs, &proc)) || |
---|
405 | (! AUTH_MARSHALL(cl->cl_auth, xdrs)) || |
---|
406 | (! (*xargs)(xdrs, argsp))) { |
---|
407 | cu->cu_error.re_status = RPC_CANTENCODEARGS; |
---|
408 | goto out; |
---|
409 | } |
---|
410 | } else { |
---|
411 | *(uint32_t *) &cu->cu_outhdr[cu->cu_xdrpos] = htonl(proc); |
---|
412 | if (!__rpc_gss_wrap(cl->cl_auth, cu->cu_outhdr, |
---|
413 | cu->cu_xdrpos + sizeof(uint32_t), |
---|
414 | xdrs, xargs, argsp)) { |
---|
415 | cu->cu_error.re_status = RPC_CANTENCODEARGS; |
---|
416 | goto out; |
---|
417 | } |
---|
418 | } |
---|
419 | outlen = (size_t)XDR_GETPOS(xdrs); |
---|
420 | |
---|
421 | send_again: |
---|
422 | if (_sendto(cu->cu_fd, cu->cu_outbuf, outlen, 0, sa, salen) != outlen) { |
---|
423 | cu->cu_error.re_errno = errno; |
---|
424 | cu->cu_error.re_status = RPC_CANTSEND; |
---|
425 | goto out; |
---|
426 | } |
---|
427 | |
---|
428 | /* |
---|
429 | * Hack to provide rpc-based message passing |
---|
430 | */ |
---|
431 | if (timeout.tv_sec == 0 && timeout.tv_usec == 0) { |
---|
432 | cu->cu_error.re_status = RPC_TIMEDOUT; |
---|
433 | goto out; |
---|
434 | } |
---|
435 | |
---|
436 | get_reply: |
---|
437 | |
---|
438 | /* |
---|
439 | * sub-optimal code appears here because we have |
---|
440 | * some clock time to spare while the packets are in flight. |
---|
441 | * (We assume that this is actually only executed once.) |
---|
442 | */ |
---|
443 | reply_msg.acpted_rply.ar_verf = _null_auth; |
---|
444 | if (cl->cl_auth->ah_cred.oa_flavor != RPCSEC_GSS) { |
---|
445 | reply_msg.acpted_rply.ar_results.where = resultsp; |
---|
446 | reply_msg.acpted_rply.ar_results.proc = xresults; |
---|
447 | } else { |
---|
448 | reply_msg.acpted_rply.ar_results.where = NULL; |
---|
449 | reply_msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void; |
---|
450 | } |
---|
451 | |
---|
452 | for (;;) { |
---|
453 | /* Decide how long to wait. */ |
---|
454 | if (timercmp(&next_sendtime, &timeout, <)) |
---|
455 | timersub(&next_sendtime, &time_waited, &tv); |
---|
456 | else |
---|
457 | timersub(&timeout, &time_waited, &tv); |
---|
458 | if (tv.tv_sec < 0 || tv.tv_usec < 0) |
---|
459 | tv.tv_sec = tv.tv_usec = 0; |
---|
460 | TIMEVAL_TO_TIMESPEC(&tv, &ts); |
---|
461 | |
---|
462 | n = _kevent(cu->cu_kq, &cu->cu_kin, kin_len, &kv, 1, &ts); |
---|
463 | /* We don't need to register the event again. */ |
---|
464 | kin_len = 0; |
---|
465 | |
---|
466 | if (n == 1) { |
---|
467 | if (kv.flags & EV_ERROR) { |
---|
468 | cu->cu_error.re_errno = kv.data; |
---|
469 | cu->cu_error.re_status = RPC_CANTRECV; |
---|
470 | goto out; |
---|
471 | } |
---|
472 | /* We have some data now */ |
---|
473 | do { |
---|
474 | recvlen = _recvfrom(cu->cu_fd, cu->cu_inbuf, |
---|
475 | cu->cu_recvsz, 0, NULL, NULL); |
---|
476 | } while (recvlen < 0 && errno == EINTR); |
---|
477 | if (recvlen < 0 && errno != EWOULDBLOCK) { |
---|
478 | cu->cu_error.re_errno = errno; |
---|
479 | cu->cu_error.re_status = RPC_CANTRECV; |
---|
480 | goto out; |
---|
481 | } |
---|
482 | if (recvlen >= sizeof(u_int32_t) && |
---|
483 | (cu->cu_async == TRUE || |
---|
484 | *((u_int32_t *)(void *)(cu->cu_inbuf)) == |
---|
485 | *((u_int32_t *)(void *)(cu->cu_outbuf)))) { |
---|
486 | /* We now assume we have the proper reply. */ |
---|
487 | break; |
---|
488 | } |
---|
489 | } |
---|
490 | if (n == -1 && errno != EINTR) { |
---|
491 | cu->cu_error.re_errno = errno; |
---|
492 | cu->cu_error.re_status = RPC_CANTRECV; |
---|
493 | goto out; |
---|
494 | } |
---|
495 | gettimeofday(&tv, NULL); |
---|
496 | timersub(&tv, &starttime, &time_waited); |
---|
497 | |
---|
498 | /* Check for timeout. */ |
---|
499 | if (timercmp(&time_waited, &timeout, >)) { |
---|
500 | cu->cu_error.re_status = RPC_TIMEDOUT; |
---|
501 | goto out; |
---|
502 | } |
---|
503 | |
---|
504 | /* Retransmit if necessary. */ |
---|
505 | if (timercmp(&time_waited, &next_sendtime, >)) { |
---|
506 | /* update retransmit_time */ |
---|
507 | if (retransmit_time.tv_sec < RPC_MAX_BACKOFF) |
---|
508 | timeradd(&retransmit_time, &retransmit_time, |
---|
509 | &retransmit_time); |
---|
510 | timeradd(&next_sendtime, &retransmit_time, |
---|
511 | &next_sendtime); |
---|
512 | nretries++; |
---|
513 | |
---|
514 | /* |
---|
515 | * When retransmitting a RPCSEC_GSS message, |
---|
516 | * we must use a new sequence number (handled |
---|
517 | * by __rpc_gss_wrap above). |
---|
518 | */ |
---|
519 | if (cl->cl_auth->ah_cred.oa_flavor != RPCSEC_GSS) |
---|
520 | goto send_again; |
---|
521 | else |
---|
522 | goto call_again_same_xid; |
---|
523 | } |
---|
524 | } |
---|
525 | inlen = (socklen_t)recvlen; |
---|
526 | |
---|
527 | /* |
---|
528 | * now decode and validate the response |
---|
529 | */ |
---|
530 | |
---|
531 | xdrmem_create(&reply_xdrs, cu->cu_inbuf, (u_int)recvlen, XDR_DECODE); |
---|
532 | ok = xdr_replymsg(&reply_xdrs, &reply_msg); |
---|
533 | /* XDR_DESTROY(&reply_xdrs); save a few cycles on noop destroy */ |
---|
534 | if (ok) { |
---|
535 | if ((reply_msg.rm_reply.rp_stat == MSG_ACCEPTED) && |
---|
536 | (reply_msg.acpted_rply.ar_stat == SUCCESS)) |
---|
537 | cu->cu_error.re_status = RPC_SUCCESS; |
---|
538 | else |
---|
539 | _seterr_reply(&reply_msg, &(cu->cu_error)); |
---|
540 | |
---|
541 | if (cu->cu_error.re_status == RPC_SUCCESS) { |
---|
542 | if (! AUTH_VALIDATE(cl->cl_auth, |
---|
543 | &reply_msg.acpted_rply.ar_verf)) { |
---|
544 | if (nretries && |
---|
545 | cl->cl_auth->ah_cred.oa_flavor |
---|
546 | == RPCSEC_GSS) |
---|
547 | /* |
---|
548 | * If we retransmitted, its |
---|
549 | * possible that we will |
---|
550 | * receive a reply for one of |
---|
551 | * the earlier transmissions |
---|
552 | * (which will use an older |
---|
553 | * RPCSEC_GSS sequence |
---|
554 | * number). In this case, just |
---|
555 | * go back and listen for a |
---|
556 | * new reply. We could keep a |
---|
557 | * record of all the seq |
---|
558 | * numbers we have transmitted |
---|
559 | * so far so that we could |
---|
560 | * accept a reply for any of |
---|
561 | * them here. |
---|
562 | */ |
---|
563 | goto get_reply; |
---|
564 | cu->cu_error.re_status = RPC_AUTHERROR; |
---|
565 | cu->cu_error.re_why = AUTH_INVALIDRESP; |
---|
566 | } else { |
---|
567 | if (cl->cl_auth->ah_cred.oa_flavor |
---|
568 | == RPCSEC_GSS) { |
---|
569 | if (!__rpc_gss_unwrap(cl->cl_auth, |
---|
570 | &reply_xdrs, xresults, |
---|
571 | resultsp)) |
---|
572 | cu->cu_error.re_status = |
---|
573 | RPC_CANTDECODERES; |
---|
574 | } |
---|
575 | } |
---|
576 | if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) { |
---|
577 | xdrs->x_op = XDR_FREE; |
---|
578 | (void) xdr_opaque_auth(xdrs, |
---|
579 | &(reply_msg.acpted_rply.ar_verf)); |
---|
580 | } |
---|
581 | } /* end successful completion */ |
---|
582 | /* |
---|
583 | * If unsuccesful AND error is an authentication error |
---|
584 | * then refresh credentials and try again, else break |
---|
585 | */ |
---|
586 | else if (cu->cu_error.re_status == RPC_AUTHERROR) |
---|
587 | /* maybe our credentials need to be refreshed ... */ |
---|
588 | if (nrefreshes > 0 && |
---|
589 | AUTH_REFRESH(cl->cl_auth, &reply_msg)) { |
---|
590 | nrefreshes--; |
---|
591 | goto call_again; |
---|
592 | } |
---|
593 | /* end of unsuccessful completion */ |
---|
594 | } /* end of valid reply message */ |
---|
595 | else { |
---|
596 | cu->cu_error.re_status = RPC_CANTDECODERES; |
---|
597 | |
---|
598 | } |
---|
599 | out: |
---|
600 | if (cu->cu_kq >= 0) |
---|
601 | _close(cu->cu_kq); |
---|
602 | cu->cu_kq = -1; |
---|
603 | release_fd_lock(cu->cu_fd, mask); |
---|
604 | return (cu->cu_error.re_status); |
---|
605 | } |
---|
606 | |
---|
607 | static void |
---|
608 | clnt_dg_geterr(cl, errp) |
---|
609 | CLIENT *cl; |
---|
610 | struct rpc_err *errp; |
---|
611 | { |
---|
612 | struct cu_data *cu = (struct cu_data *)cl->cl_private; |
---|
613 | |
---|
614 | *errp = cu->cu_error; |
---|
615 | } |
---|
616 | |
---|
617 | static bool_t |
---|
618 | clnt_dg_freeres(cl, xdr_res, res_ptr) |
---|
619 | CLIENT *cl; |
---|
620 | xdrproc_t xdr_res; |
---|
621 | void *res_ptr; |
---|
622 | { |
---|
623 | struct cu_data *cu = (struct cu_data *)cl->cl_private; |
---|
624 | XDR *xdrs = &(cu->cu_outxdrs); |
---|
625 | bool_t dummy; |
---|
626 | sigset_t mask; |
---|
627 | sigset_t newmask; |
---|
628 | |
---|
629 | sigfillset(&newmask); |
---|
630 | thr_sigsetmask(SIG_SETMASK, &newmask, &mask); |
---|
631 | mutex_lock(&clnt_fd_lock); |
---|
632 | while (dg_fd_locks[cu->cu_fd]) |
---|
633 | cond_wait(&dg_cv[cu->cu_fd], &clnt_fd_lock); |
---|
634 | xdrs->x_op = XDR_FREE; |
---|
635 | dummy = (*xdr_res)(xdrs, res_ptr); |
---|
636 | mutex_unlock(&clnt_fd_lock); |
---|
637 | thr_sigsetmask(SIG_SETMASK, &mask, NULL); |
---|
638 | cond_signal(&dg_cv[cu->cu_fd]); |
---|
639 | return (dummy); |
---|
640 | } |
---|
641 | |
---|
642 | /*ARGSUSED*/ |
---|
643 | static void |
---|
644 | clnt_dg_abort(h) |
---|
645 | CLIENT *h; |
---|
646 | { |
---|
647 | } |
---|
648 | |
---|
649 | static bool_t |
---|
650 | clnt_dg_control(cl, request, info) |
---|
651 | CLIENT *cl; |
---|
652 | u_int request; |
---|
653 | void *info; |
---|
654 | { |
---|
655 | struct cu_data *cu = (struct cu_data *)cl->cl_private; |
---|
656 | struct netbuf *addr; |
---|
657 | sigset_t mask; |
---|
658 | sigset_t newmask; |
---|
659 | int rpc_lock_value; |
---|
660 | |
---|
661 | sigfillset(&newmask); |
---|
662 | thr_sigsetmask(SIG_SETMASK, &newmask, &mask); |
---|
663 | mutex_lock(&clnt_fd_lock); |
---|
664 | while (dg_fd_locks[cu->cu_fd]) |
---|
665 | cond_wait(&dg_cv[cu->cu_fd], &clnt_fd_lock); |
---|
666 | if (__isthreaded) |
---|
667 | rpc_lock_value = 1; |
---|
668 | else |
---|
669 | rpc_lock_value = 0; |
---|
670 | dg_fd_locks[cu->cu_fd] = rpc_lock_value; |
---|
671 | mutex_unlock(&clnt_fd_lock); |
---|
672 | switch (request) { |
---|
673 | case CLSET_FD_CLOSE: |
---|
674 | cu->cu_closeit = TRUE; |
---|
675 | release_fd_lock(cu->cu_fd, mask); |
---|
676 | return (TRUE); |
---|
677 | case CLSET_FD_NCLOSE: |
---|
678 | cu->cu_closeit = FALSE; |
---|
679 | release_fd_lock(cu->cu_fd, mask); |
---|
680 | return (TRUE); |
---|
681 | } |
---|
682 | |
---|
683 | /* for other requests which use info */ |
---|
684 | if (info == NULL) { |
---|
685 | release_fd_lock(cu->cu_fd, mask); |
---|
686 | return (FALSE); |
---|
687 | } |
---|
688 | switch (request) { |
---|
689 | case CLSET_TIMEOUT: |
---|
690 | if (time_not_ok((struct timeval *)info)) { |
---|
691 | release_fd_lock(cu->cu_fd, mask); |
---|
692 | return (FALSE); |
---|
693 | } |
---|
694 | cu->cu_total = *(struct timeval *)info; |
---|
695 | break; |
---|
696 | case CLGET_TIMEOUT: |
---|
697 | *(struct timeval *)info = cu->cu_total; |
---|
698 | break; |
---|
699 | case CLGET_SERVER_ADDR: /* Give him the fd address */ |
---|
700 | /* Now obsolete. Only for backward compatibility */ |
---|
701 | (void) memcpy(info, &cu->cu_raddr, (size_t)cu->cu_rlen); |
---|
702 | break; |
---|
703 | case CLSET_RETRY_TIMEOUT: |
---|
704 | if (time_not_ok((struct timeval *)info)) { |
---|
705 | release_fd_lock(cu->cu_fd, mask); |
---|
706 | return (FALSE); |
---|
707 | } |
---|
708 | cu->cu_wait = *(struct timeval *)info; |
---|
709 | break; |
---|
710 | case CLGET_RETRY_TIMEOUT: |
---|
711 | *(struct timeval *)info = cu->cu_wait; |
---|
712 | break; |
---|
713 | case CLGET_FD: |
---|
714 | *(int *)info = cu->cu_fd; |
---|
715 | break; |
---|
716 | case CLGET_SVC_ADDR: |
---|
717 | addr = (struct netbuf *)info; |
---|
718 | addr->buf = &cu->cu_raddr; |
---|
719 | addr->len = cu->cu_rlen; |
---|
720 | addr->maxlen = sizeof cu->cu_raddr; |
---|
721 | break; |
---|
722 | case CLSET_SVC_ADDR: /* set to new address */ |
---|
723 | addr = (struct netbuf *)info; |
---|
724 | if (addr->len < sizeof cu->cu_raddr) { |
---|
725 | release_fd_lock(cu->cu_fd, mask); |
---|
726 | return (FALSE); |
---|
727 | } |
---|
728 | (void) memcpy(&cu->cu_raddr, addr->buf, addr->len); |
---|
729 | cu->cu_rlen = addr->len; |
---|
730 | break; |
---|
731 | case CLGET_XID: |
---|
732 | /* |
---|
733 | * use the knowledge that xid is the |
---|
734 | * first element in the call structure *. |
---|
735 | * This will get the xid of the PREVIOUS call |
---|
736 | */ |
---|
737 | *(u_int32_t *)info = |
---|
738 | ntohl(*(u_int32_t *)(void *)cu->cu_outhdr); |
---|
739 | break; |
---|
740 | |
---|
741 | case CLSET_XID: |
---|
742 | /* This will set the xid of the NEXT call */ |
---|
743 | *(u_int32_t *)(void *)cu->cu_outhdr = |
---|
744 | htonl(*(u_int32_t *)info - 1); |
---|
745 | /* decrement by 1 as clnt_dg_call() increments once */ |
---|
746 | break; |
---|
747 | |
---|
748 | case CLGET_VERS: |
---|
749 | /* |
---|
750 | * This RELIES on the information that, in the call body, |
---|
751 | * the version number field is the fifth field from the |
---|
752 | * begining of the RPC header. MUST be changed if the |
---|
753 | * call_struct is changed |
---|
754 | */ |
---|
755 | *(u_int32_t *)info = |
---|
756 | ntohl(*(u_int32_t *)(void *)(cu->cu_outhdr + |
---|
757 | 4 * BYTES_PER_XDR_UNIT)); |
---|
758 | break; |
---|
759 | |
---|
760 | case CLSET_VERS: |
---|
761 | *(u_int32_t *)(void *)(cu->cu_outhdr + 4 * BYTES_PER_XDR_UNIT) |
---|
762 | = htonl(*(u_int32_t *)info); |
---|
763 | break; |
---|
764 | |
---|
765 | case CLGET_PROG: |
---|
766 | /* |
---|
767 | * This RELIES on the information that, in the call body, |
---|
768 | * the program number field is the fourth field from the |
---|
769 | * begining of the RPC header. MUST be changed if the |
---|
770 | * call_struct is changed |
---|
771 | */ |
---|
772 | *(u_int32_t *)info = |
---|
773 | ntohl(*(u_int32_t *)(void *)(cu->cu_outhdr + |
---|
774 | 3 * BYTES_PER_XDR_UNIT)); |
---|
775 | break; |
---|
776 | |
---|
777 | case CLSET_PROG: |
---|
778 | *(u_int32_t *)(void *)(cu->cu_outhdr + 3 * BYTES_PER_XDR_UNIT) |
---|
779 | = htonl(*(u_int32_t *)info); |
---|
780 | break; |
---|
781 | case CLSET_ASYNC: |
---|
782 | cu->cu_async = *(int *)info; |
---|
783 | break; |
---|
784 | case CLSET_CONNECT: |
---|
785 | cu->cu_connect = *(int *)info; |
---|
786 | break; |
---|
787 | default: |
---|
788 | release_fd_lock(cu->cu_fd, mask); |
---|
789 | return (FALSE); |
---|
790 | } |
---|
791 | release_fd_lock(cu->cu_fd, mask); |
---|
792 | return (TRUE); |
---|
793 | } |
---|
794 | |
---|
795 | static void |
---|
796 | clnt_dg_destroy(cl) |
---|
797 | CLIENT *cl; |
---|
798 | { |
---|
799 | struct cu_data *cu = (struct cu_data *)cl->cl_private; |
---|
800 | int cu_fd = cu->cu_fd; |
---|
801 | sigset_t mask; |
---|
802 | sigset_t newmask; |
---|
803 | |
---|
804 | sigfillset(&newmask); |
---|
805 | thr_sigsetmask(SIG_SETMASK, &newmask, &mask); |
---|
806 | mutex_lock(&clnt_fd_lock); |
---|
807 | while (dg_fd_locks[cu_fd]) |
---|
808 | cond_wait(&dg_cv[cu_fd], &clnt_fd_lock); |
---|
809 | if (cu->cu_closeit) |
---|
810 | (void)_close(cu_fd); |
---|
811 | if (cu->cu_kq >= 0) |
---|
812 | _close(cu->cu_kq); |
---|
813 | XDR_DESTROY(&(cu->cu_outxdrs)); |
---|
814 | mem_free(cu, (sizeof (*cu) + cu->cu_sendsz + cu->cu_recvsz)); |
---|
815 | if (cl->cl_netid && cl->cl_netid[0]) |
---|
816 | mem_free(cl->cl_netid, strlen(cl->cl_netid) +1); |
---|
817 | if (cl->cl_tp && cl->cl_tp[0]) |
---|
818 | mem_free(cl->cl_tp, strlen(cl->cl_tp) +1); |
---|
819 | mem_free(cl, sizeof (CLIENT)); |
---|
820 | mutex_unlock(&clnt_fd_lock); |
---|
821 | thr_sigsetmask(SIG_SETMASK, &mask, NULL); |
---|
822 | cond_signal(&dg_cv[cu_fd]); |
---|
823 | } |
---|
824 | |
---|
825 | static struct clnt_ops * |
---|
826 | clnt_dg_ops() |
---|
827 | { |
---|
828 | static struct clnt_ops ops; |
---|
829 | sigset_t mask; |
---|
830 | sigset_t newmask; |
---|
831 | |
---|
832 | /* VARIABLES PROTECTED BY ops_lock: ops */ |
---|
833 | |
---|
834 | sigfillset(&newmask); |
---|
835 | thr_sigsetmask(SIG_SETMASK, &newmask, &mask); |
---|
836 | mutex_lock(&ops_lock); |
---|
837 | if (ops.cl_call == NULL) { |
---|
838 | ops.cl_call = clnt_dg_call; |
---|
839 | ops.cl_abort = clnt_dg_abort; |
---|
840 | ops.cl_geterr = clnt_dg_geterr; |
---|
841 | ops.cl_freeres = clnt_dg_freeres; |
---|
842 | ops.cl_destroy = clnt_dg_destroy; |
---|
843 | ops.cl_control = clnt_dg_control; |
---|
844 | } |
---|
845 | mutex_unlock(&ops_lock); |
---|
846 | thr_sigsetmask(SIG_SETMASK, &mask, NULL); |
---|
847 | return (&ops); |
---|
848 | } |
---|
849 | |
---|
850 | /* |
---|
851 | * Make sure that the time is not garbage. -1 value is allowed. |
---|
852 | */ |
---|
853 | static bool_t |
---|
854 | time_not_ok(t) |
---|
855 | struct timeval *t; |
---|
856 | { |
---|
857 | return (t->tv_sec < -1 || t->tv_sec > 100000000 || |
---|
858 | t->tv_usec < -1 || t->tv_usec > 1000000); |
---|
859 | } |
---|
860 | |
---|