1 1.29 christos /* $NetBSD: clnt_vc.c,v 1.29 2024/01/23 17:24:38 christos Exp $ */ 2 1.1 fvdl 3 1.1 fvdl /* 4 1.21 tron * Copyright (c) 2010, Oracle America, Inc. 5 1.21 tron * 6 1.21 tron * Redistribution and use in source and binary forms, with or without 7 1.21 tron * modification, are permitted provided that the following conditions are 8 1.21 tron * met: 9 1.21 tron * 10 1.21 tron * * Redistributions of source code must retain the above copyright 11 1.21 tron * notice, this list of conditions and the following disclaimer. 12 1.21 tron * * Redistributions in binary form must reproduce the above 13 1.21 tron * copyright notice, this list of conditions and the following 14 1.21 tron * disclaimer in the documentation and/or other materials 15 1.21 tron * provided with the distribution. 16 1.21 tron * * Neither the name of the "Oracle America, Inc." nor the names of its 17 1.21 tron * contributors may be used to endorse or promote products derived 18 1.21 tron * from this software without specific prior written permission. 19 1.21 tron * 20 1.21 tron * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 1.21 tron * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 1.21 tron * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 1.21 tron * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 1.21 tron * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 25 1.21 tron * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 1.21 tron * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 27 1.21 tron * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 1.21 tron * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 29 1.21 tron * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 30 1.21 tron * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 1.21 tron * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 1.1 fvdl */ 33 1.1 fvdl 34 1.1 fvdl #include <sys/cdefs.h> 35 1.1 fvdl #if defined(LIBC_SCCS) && !defined(lint) 36 1.1 fvdl #if 0 37 1.1 fvdl static char *sccsid = "@(#)clnt_tcp.c 1.37 87/10/05 Copyr 1984 Sun Micro"; 38 1.1 fvdl static char *sccsid = "@(#)clnt_tcp.c 2.2 88/08/01 4.0 RPCSRC"; 39 1.1 fvdl static char sccsid[] = "@(#)clnt_vc.c 1.19 89/03/16 Copyr 1988 Sun Micro"; 40 1.1 fvdl #else 41 1.29 christos __RCSID("$NetBSD: clnt_vc.c,v 1.29 2024/01/23 17:24:38 christos Exp $"); 42 1.1 fvdl #endif 43 1.1 fvdl #endif 44 1.1 fvdl 45 1.1 fvdl /* 46 1.1 fvdl * clnt_tcp.c, Implements a TCP/IP based, client side RPC. 47 1.1 fvdl * 48 1.1 fvdl * Copyright (C) 1984, Sun Microsystems, Inc. 49 1.1 fvdl * 50 1.1 fvdl * TCP based RPC supports 'batched calls'. 51 1.1 fvdl * A sequence of calls may be batched-up in a send buffer. The rpc call 52 1.1 fvdl * return immediately to the client even though the call was not necessarily 53 1.1 fvdl * sent. The batching occurs if the results' xdr routine is NULL (0) AND 54 1.1 fvdl * the rpc timeout value is zero (see clnt.h, rpc). 55 1.1 fvdl * 56 1.1 fvdl * Clients should NOT casually batch calls that in fact return results; that is, 57 1.1 fvdl * the server side should be aware that a call is batched and not produce any 58 1.1 fvdl * return message. Batched calls that produce many result messages can 59 1.1 fvdl * deadlock (netlock) the client and the server.... 60 1.1 fvdl * 61 1.1 fvdl * Now go hang yourself. 62 1.1 fvdl */ 63 1.1 fvdl 64 1.1 fvdl #include "namespace.h" 65 1.1 fvdl #include "reentrant.h" 66 1.1 fvdl #include <sys/types.h> 67 1.1 fvdl #include <sys/poll.h> 68 1.1 fvdl #include <sys/socket.h> 69 1.1 fvdl 70 1.1 fvdl #include <assert.h> 71 1.1 fvdl #include <err.h> 72 1.1 fvdl #include <errno.h> 73 1.1 fvdl #include <netdb.h> 74 1.1 fvdl #include <stdio.h> 75 1.1 fvdl #include <stdlib.h> 76 1.2 thorpej #include <string.h> 77 1.1 fvdl #include <unistd.h> 78 1.1 fvdl #include <signal.h> 79 1.1 fvdl 80 1.1 fvdl #include <rpc/rpc.h> 81 1.1 fvdl 82 1.20 christos #include "svc_fdset.h" 83 1.8 fvdl #include "rpc_internal.h" 84 1.1 fvdl 85 1.1 fvdl #ifdef __weak_alias 86 1.1 fvdl __weak_alias(clnt_vc_create,_clnt_vc_create) 87 1.1 fvdl #endif 88 1.1 fvdl 89 1.1 fvdl #define MCALL_MSG_SIZE 24 90 1.1 fvdl 91 1.18 christos static enum clnt_stat clnt_vc_call(CLIENT *, rpcproc_t, xdrproc_t, 92 1.18 christos const char *, xdrproc_t, caddr_t, struct timeval); 93 1.18 christos static void clnt_vc_geterr(CLIENT *, struct rpc_err *); 94 1.18 christos static bool_t clnt_vc_freeres(CLIENT *, xdrproc_t, caddr_t); 95 1.18 christos static void clnt_vc_abort(CLIENT *); 96 1.18 christos static bool_t clnt_vc_control(CLIENT *, u_int, char *); 97 1.18 christos static void clnt_vc_destroy(CLIENT *); 98 1.18 christos static struct clnt_ops *clnt_vc_ops(void); 99 1.18 christos static bool_t time_not_ok(struct timeval *); 100 1.18 christos static int read_vc(caddr_t, caddr_t, int); 101 1.18 christos static int write_vc(caddr_t, caddr_t, int); 102 1.1 fvdl 103 1.1 fvdl struct ct_data { 104 1.1 fvdl int ct_fd; 105 1.1 fvdl bool_t ct_closeit; 106 1.1 fvdl struct timeval ct_wait; 107 1.1 fvdl bool_t ct_waitset; /* wait set by clnt_control? */ 108 1.1 fvdl struct netbuf ct_addr; 109 1.1 fvdl struct rpc_err ct_error; 110 1.1 fvdl union { 111 1.1 fvdl char ct_mcallc[MCALL_MSG_SIZE]; /* marshalled callmsg */ 112 1.1 fvdl u_int32_t ct_mcalli; 113 1.1 fvdl } ct_u; 114 1.1 fvdl u_int ct_mpos; /* pos after marshal */ 115 1.1 fvdl XDR ct_xdrs; 116 1.1 fvdl }; 117 1.1 fvdl 118 1.1 fvdl /* 119 1.1 fvdl * This machinery implements per-fd locks for MT-safety. It is not 120 1.1 fvdl * sufficient to do per-CLIENT handle locks for MT-safety because a 121 1.1 fvdl * user may create more than one CLIENT handle with the same fd behind 122 1.27 andvar * it. Therefore, we allocate an array of flags (vc_fd_locks), protected 123 1.1 fvdl * by the clnt_fd_lock mutex, and an array (vc_cv) of condition variables 124 1.27 andvar * similarly protected. Vc_fd_lock[fd] == 1 => a call is active on some 125 1.1 fvdl * CLIENT handle created for that fd. 126 1.1 fvdl * The current implementation holds locks across the entire RPC and reply. 127 1.1 fvdl * Yes, this is silly, and as soon as this code is proven to work, this 128 1.1 fvdl * should be the first thing fixed. One step at a time. 129 1.1 fvdl */ 130 1.9 thorpej #ifdef _REENTRANT 131 1.1 fvdl static int *vc_fd_locks; 132 1.9 thorpej #define __rpc_lock_value __isthreaded; 133 1.1 fvdl static cond_t *vc_cv; 134 1.1 fvdl #define release_fd_lock(fd, mask) { \ 135 1.1 fvdl mutex_lock(&clnt_fd_lock); \ 136 1.1 fvdl vc_fd_locks[fd] = 0; \ 137 1.1 fvdl mutex_unlock(&clnt_fd_lock); \ 138 1.15 christos thr_sigsetmask(SIG_SETMASK, &(mask), NULL); \ 139 1.1 fvdl cond_signal(&vc_cv[fd]); \ 140 1.1 fvdl } 141 1.1 fvdl #else 142 1.1 fvdl #define release_fd_lock(fd,mask) 143 1.1 fvdl #define __rpc_lock_value 0 144 1.1 fvdl #endif 145 1.1 fvdl 146 1.24 christos static __inline void 147 1.25 christos htonlp(void *dst, const void *src, uint32_t incr) 148 1.24 christos { 149 1.24 christos #if 0 150 1.24 christos uint32_t tmp; 151 1.24 christos memcpy(&tmp, src, sizeof(tmp)); 152 1.25 christos tmp = htonl(tmp + incr); 153 1.24 christos memcpy(dst, &tmp, sizeof(tmp)); 154 1.24 christos #else 155 1.24 christos /* We are aligned, so we think */ 156 1.25 christos *(uint32_t *)dst = htonl(*(const uint32_t *)src + incr); 157 1.24 christos #endif 158 1.24 christos } 159 1.24 christos 160 1.24 christos static __inline void 161 1.24 christos ntohlp(void *dst, const void *src) 162 1.24 christos { 163 1.24 christos #if 0 164 1.24 christos uint32_t tmp; 165 1.24 christos memcpy(&tmp, src, sizeof(tmp)); 166 1.24 christos tmp = ntohl(tmp); 167 1.24 christos memcpy(dst, &tmp, sizeof(tmp)); 168 1.24 christos #else 169 1.24 christos /* We are aligned, so we think */ 170 1.24 christos *(uint32_t *)dst = htonl(*(const uint32_t *)src); 171 1.24 christos #endif 172 1.24 christos } 173 1.1 fvdl 174 1.1 fvdl /* 175 1.1 fvdl * Create a client handle for a connection. 176 1.1 fvdl * Default options are set, which the user can change using clnt_control()'s. 177 1.1 fvdl * The rpc/vc package does buffering similar to stdio, so the client 178 1.1 fvdl * must pick send and receive buffer sizes, 0 => use the default. 179 1.1 fvdl * NB: fd is copied into a private area. 180 1.1 fvdl * NB: The rpch->cl_auth is set null authentication. Caller may wish to 181 1.1 fvdl * set this something more useful. 182 1.1 fvdl * 183 1.1 fvdl * fd should be an open socket 184 1.1 fvdl */ 185 1.1 fvdl CLIENT * 186 1.18 christos clnt_vc_create( 187 1.18 christos int fd, 188 1.18 christos const struct netbuf *raddr, 189 1.18 christos rpcprog_t prog, 190 1.18 christos rpcvers_t vers, 191 1.18 christos u_int sendsz, 192 1.18 christos u_int recvsz 193 1.18 christos ) 194 1.1 fvdl { 195 1.1 fvdl CLIENT *h; 196 1.1 fvdl struct ct_data *ct = NULL; 197 1.1 fvdl struct rpc_msg call_msg; 198 1.9 thorpej #ifdef _REENTRANT 199 1.1 fvdl sigset_t mask; 200 1.1 fvdl #endif 201 1.1 fvdl sigset_t newmask; 202 1.1 fvdl struct sockaddr_storage ss; 203 1.1 fvdl socklen_t slen; 204 1.1 fvdl struct __rpc_sockinfo si; 205 1.1 fvdl 206 1.7 lukem _DIAGASSERT(raddr != NULL); 207 1.7 lukem 208 1.3 christos h = mem_alloc(sizeof(*h)); 209 1.1 fvdl if (h == NULL) { 210 1.1 fvdl warnx("clnt_vc_create: out of memory"); 211 1.1 fvdl rpc_createerr.cf_stat = RPC_SYSTEMERROR; 212 1.1 fvdl rpc_createerr.cf_error.re_errno = errno; 213 1.1 fvdl goto fooy; 214 1.1 fvdl } 215 1.3 christos ct = mem_alloc(sizeof(*ct)); 216 1.1 fvdl if (ct == NULL) { 217 1.1 fvdl warnx("clnt_vc_create: out of memory"); 218 1.1 fvdl rpc_createerr.cf_stat = RPC_SYSTEMERROR; 219 1.1 fvdl rpc_createerr.cf_error.re_errno = errno; 220 1.1 fvdl goto fooy; 221 1.1 fvdl } 222 1.1 fvdl 223 1.23 christos __clnt_sigfillset(&newmask); 224 1.1 fvdl thr_sigsetmask(SIG_SETMASK, &newmask, &mask); 225 1.9 thorpej #ifdef _REENTRANT 226 1.1 fvdl mutex_lock(&clnt_fd_lock); 227 1.15 christos if (vc_fd_locks == NULL) { 228 1.9 thorpej size_t cv_allocsz, fd_allocsz; 229 1.1 fvdl int dtbsize = __rpc_dtbsize(); 230 1.1 fvdl 231 1.1 fvdl fd_allocsz = dtbsize * sizeof (int); 232 1.15 christos vc_fd_locks = mem_alloc(fd_allocsz); 233 1.15 christos if (vc_fd_locks == NULL) { 234 1.23 christos goto blooy; 235 1.1 fvdl } else 236 1.1 fvdl memset(vc_fd_locks, '\0', fd_allocsz); 237 1.1 fvdl 238 1.16 uebayasi _DIAGASSERT(vc_cv == NULL); 239 1.1 fvdl cv_allocsz = dtbsize * sizeof (cond_t); 240 1.15 christos vc_cv = mem_alloc(cv_allocsz); 241 1.15 christos if (vc_cv == NULL) { 242 1.1 fvdl mem_free(vc_fd_locks, fd_allocsz); 243 1.15 christos vc_fd_locks = NULL; 244 1.23 christos goto blooy; 245 1.1 fvdl } else { 246 1.1 fvdl int i; 247 1.1 fvdl 248 1.1 fvdl for (i = 0; i < dtbsize; i++) 249 1.1 fvdl cond_init(&vc_cv[i], 0, (void *) 0); 250 1.1 fvdl } 251 1.1 fvdl } else 252 1.16 uebayasi _DIAGASSERT(vc_cv != NULL); 253 1.1 fvdl #endif 254 1.1 fvdl 255 1.1 fvdl /* 256 1.1 fvdl * XXX - fvdl connecting while holding a mutex? 257 1.1 fvdl */ 258 1.1 fvdl slen = sizeof ss; 259 1.3 christos if (getpeername(fd, (struct sockaddr *)(void *)&ss, &slen) < 0) { 260 1.1 fvdl if (errno != ENOTCONN) { 261 1.1 fvdl rpc_createerr.cf_stat = RPC_SYSTEMERROR; 262 1.1 fvdl rpc_createerr.cf_error.re_errno = errno; 263 1.23 christos goto blooy; 264 1.1 fvdl } 265 1.1 fvdl if (connect(fd, (struct sockaddr *)raddr->buf, raddr->len) < 0){ 266 1.1 fvdl rpc_createerr.cf_stat = RPC_SYSTEMERROR; 267 1.1 fvdl rpc_createerr.cf_error.re_errno = errno; 268 1.23 christos goto blooy; 269 1.1 fvdl } 270 1.1 fvdl } 271 1.1 fvdl mutex_unlock(&clnt_fd_lock); 272 1.14 drochner thr_sigsetmask(SIG_SETMASK, &(mask), NULL); 273 1.1 fvdl if (!__rpc_fd2sockinfo(fd, &si)) 274 1.1 fvdl goto fooy; 275 1.1 fvdl 276 1.1 fvdl ct->ct_closeit = FALSE; 277 1.1 fvdl 278 1.1 fvdl /* 279 1.1 fvdl * Set up private data struct 280 1.1 fvdl */ 281 1.1 fvdl ct->ct_fd = fd; 282 1.1 fvdl ct->ct_wait.tv_usec = 0; 283 1.1 fvdl ct->ct_waitset = FALSE; 284 1.11 christos ct->ct_addr.buf = malloc((size_t)raddr->maxlen); 285 1.1 fvdl if (ct->ct_addr.buf == NULL) 286 1.1 fvdl goto fooy; 287 1.19 christos memcpy(ct->ct_addr.buf, raddr->buf, (size_t)raddr->len); 288 1.19 christos ct->ct_addr.len = raddr->len; 289 1.1 fvdl ct->ct_addr.maxlen = raddr->maxlen; 290 1.1 fvdl 291 1.1 fvdl /* 292 1.1 fvdl * Initialize call message 293 1.1 fvdl */ 294 1.10 itojun call_msg.rm_xid = __RPC_GETXID(); 295 1.1 fvdl call_msg.rm_direction = CALL; 296 1.1 fvdl call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; 297 1.1 fvdl call_msg.rm_call.cb_prog = (u_int32_t)prog; 298 1.1 fvdl call_msg.rm_call.cb_vers = (u_int32_t)vers; 299 1.1 fvdl 300 1.1 fvdl /* 301 1.1 fvdl * pre-serialize the static part of the call msg and stash it away 302 1.1 fvdl */ 303 1.1 fvdl xdrmem_create(&(ct->ct_xdrs), ct->ct_u.ct_mcallc, MCALL_MSG_SIZE, 304 1.1 fvdl XDR_ENCODE); 305 1.1 fvdl if (! xdr_callhdr(&(ct->ct_xdrs), &call_msg)) { 306 1.1 fvdl if (ct->ct_closeit) { 307 1.1 fvdl (void)close(fd); 308 1.1 fvdl } 309 1.1 fvdl goto fooy; 310 1.1 fvdl } 311 1.1 fvdl ct->ct_mpos = XDR_GETPOS(&(ct->ct_xdrs)); 312 1.1 fvdl XDR_DESTROY(&(ct->ct_xdrs)); 313 1.1 fvdl 314 1.1 fvdl /* 315 1.1 fvdl * Create a client handle which uses xdrrec for serialization 316 1.1 fvdl * and authnone for authentication. 317 1.1 fvdl */ 318 1.1 fvdl h->cl_ops = clnt_vc_ops(); 319 1.1 fvdl h->cl_private = ct; 320 1.1 fvdl h->cl_auth = authnone_create(); 321 1.1 fvdl sendsz = __rpc_get_t_size(si.si_af, si.si_proto, (int)sendsz); 322 1.1 fvdl recvsz = __rpc_get_t_size(si.si_af, si.si_proto, (int)recvsz); 323 1.1 fvdl xdrrec_create(&(ct->ct_xdrs), sendsz, recvsz, 324 1.1 fvdl h->cl_private, read_vc, write_vc); 325 1.1 fvdl return (h); 326 1.1 fvdl 327 1.23 christos blooy: 328 1.23 christos mutex_unlock(&clnt_fd_lock); 329 1.23 christos thr_sigsetmask(SIG_SETMASK, &(mask), NULL); 330 1.1 fvdl fooy: 331 1.1 fvdl /* 332 1.1 fvdl * Something goofed, free stuff and barf 333 1.1 fvdl */ 334 1.1 fvdl if (ct) 335 1.1 fvdl mem_free(ct, sizeof(struct ct_data)); 336 1.1 fvdl if (h) 337 1.1 fvdl mem_free(h, sizeof(CLIENT)); 338 1.3 christos return (NULL); 339 1.1 fvdl } 340 1.1 fvdl 341 1.1 fvdl static enum clnt_stat 342 1.18 christos clnt_vc_call( 343 1.18 christos CLIENT *h, 344 1.18 christos rpcproc_t proc, 345 1.18 christos xdrproc_t xdr_args, 346 1.18 christos const char *args_ptr, 347 1.18 christos xdrproc_t xdr_results, 348 1.18 christos caddr_t results_ptr, 349 1.18 christos struct timeval timeout 350 1.18 christos ) 351 1.1 fvdl { 352 1.1 fvdl struct ct_data *ct; 353 1.1 fvdl XDR *xdrs; 354 1.1 fvdl struct rpc_msg reply_msg; 355 1.1 fvdl u_int32_t x_id; 356 1.1 fvdl u_int32_t *msg_x_id; 357 1.1 fvdl bool_t shipnow; 358 1.1 fvdl int refreshes = 2; 359 1.9 thorpej #ifdef _REENTRANT 360 1.1 fvdl sigset_t mask, newmask; 361 1.1 fvdl #endif 362 1.1 fvdl 363 1.1 fvdl _DIAGASSERT(h != NULL); 364 1.1 fvdl 365 1.9 thorpej ct = (struct ct_data *) h->cl_private; 366 1.9 thorpej 367 1.9 thorpej #ifdef _REENTRANT 368 1.23 christos __clnt_sigfillset(&newmask); 369 1.1 fvdl thr_sigsetmask(SIG_SETMASK, &newmask, &mask); 370 1.1 fvdl mutex_lock(&clnt_fd_lock); 371 1.1 fvdl while (vc_fd_locks[ct->ct_fd]) 372 1.1 fvdl cond_wait(&vc_cv[ct->ct_fd], &clnt_fd_lock); 373 1.9 thorpej vc_fd_locks[ct->ct_fd] = __rpc_lock_value; 374 1.1 fvdl mutex_unlock(&clnt_fd_lock); 375 1.1 fvdl #endif 376 1.1 fvdl 377 1.1 fvdl xdrs = &(ct->ct_xdrs); 378 1.1 fvdl msg_x_id = &ct->ct_u.ct_mcalli; 379 1.1 fvdl 380 1.1 fvdl if (!ct->ct_waitset) { 381 1.1 fvdl if (time_not_ok(&timeout) == FALSE) 382 1.1 fvdl ct->ct_wait = timeout; 383 1.1 fvdl } 384 1.1 fvdl 385 1.1 fvdl shipnow = 386 1.3 christos (xdr_results == NULL && timeout.tv_sec == 0 387 1.1 fvdl && timeout.tv_usec == 0) ? FALSE : TRUE; 388 1.1 fvdl 389 1.1 fvdl call_again: 390 1.1 fvdl xdrs->x_op = XDR_ENCODE; 391 1.1 fvdl ct->ct_error.re_status = RPC_SUCCESS; 392 1.1 fvdl x_id = ntohl(--(*msg_x_id)); 393 1.1 fvdl if ((! XDR_PUTBYTES(xdrs, ct->ct_u.ct_mcallc, ct->ct_mpos)) || 394 1.6 christos (! XDR_PUTINT32(xdrs, (int32_t *)&proc)) || 395 1.1 fvdl (! AUTH_MARSHALL(h->cl_auth, xdrs)) || 396 1.13 yamt (! (*xdr_args)(xdrs, __UNCONST(args_ptr)))) { 397 1.1 fvdl if (ct->ct_error.re_status == RPC_SUCCESS) 398 1.1 fvdl ct->ct_error.re_status = RPC_CANTENCODEARGS; 399 1.1 fvdl (void)xdrrec_endofrecord(xdrs, TRUE); 400 1.1 fvdl release_fd_lock(ct->ct_fd, mask); 401 1.1 fvdl return (ct->ct_error.re_status); 402 1.1 fvdl } 403 1.1 fvdl if (! xdrrec_endofrecord(xdrs, shipnow)) { 404 1.1 fvdl release_fd_lock(ct->ct_fd, mask); 405 1.1 fvdl return (ct->ct_error.re_status = RPC_CANTSEND); 406 1.1 fvdl } 407 1.1 fvdl if (! shipnow) { 408 1.1 fvdl release_fd_lock(ct->ct_fd, mask); 409 1.1 fvdl return (RPC_SUCCESS); 410 1.1 fvdl } 411 1.1 fvdl /* 412 1.1 fvdl * Hack to provide rpc-based message passing 413 1.1 fvdl */ 414 1.1 fvdl if (timeout.tv_sec == 0 && timeout.tv_usec == 0) { 415 1.1 fvdl release_fd_lock(ct->ct_fd, mask); 416 1.1 fvdl return(ct->ct_error.re_status = RPC_TIMEDOUT); 417 1.1 fvdl } 418 1.1 fvdl 419 1.1 fvdl 420 1.1 fvdl /* 421 1.1 fvdl * Keep receiving until we get a valid transaction id 422 1.1 fvdl */ 423 1.1 fvdl xdrs->x_op = XDR_DECODE; 424 1.1 fvdl for (;;) { 425 1.1 fvdl reply_msg.acpted_rply.ar_verf = _null_auth; 426 1.1 fvdl reply_msg.acpted_rply.ar_results.where = NULL; 427 1.1 fvdl reply_msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void; 428 1.1 fvdl if (! xdrrec_skiprecord(xdrs)) { 429 1.1 fvdl release_fd_lock(ct->ct_fd, mask); 430 1.1 fvdl return (ct->ct_error.re_status); 431 1.1 fvdl } 432 1.1 fvdl /* now decode and validate the response header */ 433 1.1 fvdl if (! xdr_replymsg(xdrs, &reply_msg)) { 434 1.1 fvdl if (ct->ct_error.re_status == RPC_SUCCESS) 435 1.1 fvdl continue; 436 1.1 fvdl release_fd_lock(ct->ct_fd, mask); 437 1.1 fvdl return (ct->ct_error.re_status); 438 1.1 fvdl } 439 1.1 fvdl if (reply_msg.rm_xid == x_id) 440 1.1 fvdl break; 441 1.1 fvdl } 442 1.1 fvdl 443 1.1 fvdl /* 444 1.1 fvdl * process header 445 1.1 fvdl */ 446 1.1 fvdl _seterr_reply(&reply_msg, &(ct->ct_error)); 447 1.1 fvdl if (ct->ct_error.re_status == RPC_SUCCESS) { 448 1.1 fvdl if (! AUTH_VALIDATE(h->cl_auth, 449 1.1 fvdl &reply_msg.acpted_rply.ar_verf)) { 450 1.1 fvdl ct->ct_error.re_status = RPC_AUTHERROR; 451 1.1 fvdl ct->ct_error.re_why = AUTH_INVALIDRESP; 452 1.1 fvdl } else if (! (*xdr_results)(xdrs, results_ptr)) { 453 1.1 fvdl if (ct->ct_error.re_status == RPC_SUCCESS) 454 1.1 fvdl ct->ct_error.re_status = RPC_CANTDECODERES; 455 1.1 fvdl } 456 1.1 fvdl /* free verifier ... */ 457 1.1 fvdl if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) { 458 1.1 fvdl xdrs->x_op = XDR_FREE; 459 1.1 fvdl (void)xdr_opaque_auth(xdrs, 460 1.1 fvdl &(reply_msg.acpted_rply.ar_verf)); 461 1.1 fvdl } 462 1.1 fvdl } /* end successful completion */ 463 1.1 fvdl else { 464 1.1 fvdl /* maybe our credentials need to be refreshed ... */ 465 1.1 fvdl if (refreshes-- && AUTH_REFRESH(h->cl_auth)) 466 1.1 fvdl goto call_again; 467 1.1 fvdl } /* end of unsuccessful completion */ 468 1.1 fvdl release_fd_lock(ct->ct_fd, mask); 469 1.1 fvdl return (ct->ct_error.re_status); 470 1.1 fvdl } 471 1.1 fvdl 472 1.1 fvdl static void 473 1.18 christos clnt_vc_geterr( 474 1.18 christos CLIENT *h, 475 1.18 christos struct rpc_err *errp 476 1.18 christos ) 477 1.1 fvdl { 478 1.1 fvdl struct ct_data *ct; 479 1.1 fvdl 480 1.1 fvdl _DIAGASSERT(h != NULL); 481 1.1 fvdl _DIAGASSERT(errp != NULL); 482 1.1 fvdl 483 1.1 fvdl ct = (struct ct_data *) h->cl_private; 484 1.1 fvdl *errp = ct->ct_error; 485 1.1 fvdl } 486 1.1 fvdl 487 1.1 fvdl static bool_t 488 1.18 christos clnt_vc_freeres( 489 1.18 christos CLIENT *cl, 490 1.18 christos xdrproc_t xdr_res, 491 1.18 christos caddr_t res_ptr 492 1.18 christos ) 493 1.1 fvdl { 494 1.1 fvdl struct ct_data *ct; 495 1.1 fvdl XDR *xdrs; 496 1.1 fvdl bool_t dummy; 497 1.9 thorpej #ifdef _REENTRANT 498 1.1 fvdl sigset_t mask; 499 1.1 fvdl #endif 500 1.1 fvdl sigset_t newmask; 501 1.1 fvdl 502 1.1 fvdl _DIAGASSERT(cl != NULL); 503 1.1 fvdl 504 1.1 fvdl ct = (struct ct_data *)cl->cl_private; 505 1.1 fvdl xdrs = &(ct->ct_xdrs); 506 1.1 fvdl 507 1.23 christos __clnt_sigfillset(&newmask); 508 1.1 fvdl thr_sigsetmask(SIG_SETMASK, &newmask, &mask); 509 1.1 fvdl mutex_lock(&clnt_fd_lock); 510 1.9 thorpej #ifdef _REENTRANT 511 1.1 fvdl while (vc_fd_locks[ct->ct_fd]) 512 1.1 fvdl cond_wait(&vc_cv[ct->ct_fd], &clnt_fd_lock); 513 1.1 fvdl #endif 514 1.1 fvdl 515 1.1 fvdl xdrs->x_op = XDR_FREE; 516 1.1 fvdl dummy = (*xdr_res)(xdrs, res_ptr); 517 1.1 fvdl mutex_unlock(&clnt_fd_lock); 518 1.1 fvdl thr_sigsetmask(SIG_SETMASK, &(mask), NULL); 519 1.1 fvdl cond_signal(&vc_cv[ct->ct_fd]); 520 1.1 fvdl 521 1.1 fvdl return dummy; 522 1.1 fvdl } 523 1.1 fvdl 524 1.1 fvdl /*ARGSUSED*/ 525 1.1 fvdl static void 526 1.18 christos clnt_vc_abort(CLIENT *cl) 527 1.1 fvdl { 528 1.1 fvdl } 529 1.1 fvdl 530 1.1 fvdl static bool_t 531 1.18 christos clnt_vc_control( 532 1.18 christos CLIENT *cl, 533 1.18 christos u_int request, 534 1.18 christos char *info 535 1.18 christos ) 536 1.1 fvdl { 537 1.1 fvdl struct ct_data *ct; 538 1.1 fvdl void *infop = info; 539 1.9 thorpej #ifdef _REENTRANT 540 1.1 fvdl sigset_t mask; 541 1.1 fvdl #endif 542 1.1 fvdl sigset_t newmask; 543 1.1 fvdl 544 1.1 fvdl _DIAGASSERT(cl != NULL); 545 1.1 fvdl 546 1.1 fvdl ct = (struct ct_data *)cl->cl_private; 547 1.1 fvdl 548 1.23 christos __clnt_sigfillset(&newmask); 549 1.1 fvdl thr_sigsetmask(SIG_SETMASK, &newmask, &mask); 550 1.1 fvdl mutex_lock(&clnt_fd_lock); 551 1.9 thorpej #ifdef _REENTRANT 552 1.1 fvdl while (vc_fd_locks[ct->ct_fd]) 553 1.1 fvdl cond_wait(&vc_cv[ct->ct_fd], &clnt_fd_lock); 554 1.1 fvdl vc_fd_locks[ct->ct_fd] = __rpc_lock_value; 555 1.1 fvdl #endif 556 1.1 fvdl mutex_unlock(&clnt_fd_lock); 557 1.1 fvdl 558 1.1 fvdl switch (request) { 559 1.1 fvdl case CLSET_FD_CLOSE: 560 1.1 fvdl ct->ct_closeit = TRUE; 561 1.1 fvdl release_fd_lock(ct->ct_fd, mask); 562 1.1 fvdl return (TRUE); 563 1.1 fvdl case CLSET_FD_NCLOSE: 564 1.1 fvdl ct->ct_closeit = FALSE; 565 1.1 fvdl release_fd_lock(ct->ct_fd, mask); 566 1.1 fvdl return (TRUE); 567 1.1 fvdl default: 568 1.1 fvdl break; 569 1.1 fvdl } 570 1.1 fvdl 571 1.1 fvdl /* for other requests which use info */ 572 1.1 fvdl if (info == NULL) { 573 1.1 fvdl release_fd_lock(ct->ct_fd, mask); 574 1.1 fvdl return (FALSE); 575 1.1 fvdl } 576 1.1 fvdl switch (request) { 577 1.1 fvdl case CLSET_TIMEOUT: 578 1.3 christos if (time_not_ok((struct timeval *)(void *)info)) { 579 1.1 fvdl release_fd_lock(ct->ct_fd, mask); 580 1.1 fvdl return (FALSE); 581 1.1 fvdl } 582 1.1 fvdl ct->ct_wait = *(struct timeval *)infop; 583 1.1 fvdl ct->ct_waitset = TRUE; 584 1.1 fvdl break; 585 1.1 fvdl case CLGET_TIMEOUT: 586 1.1 fvdl *(struct timeval *)infop = ct->ct_wait; 587 1.1 fvdl break; 588 1.1 fvdl case CLGET_SERVER_ADDR: 589 1.3 christos (void) memcpy(info, ct->ct_addr.buf, (size_t)ct->ct_addr.len); 590 1.1 fvdl break; 591 1.1 fvdl case CLGET_FD: 592 1.3 christos *(int *)(void *)info = ct->ct_fd; 593 1.1 fvdl break; 594 1.1 fvdl case CLGET_SVC_ADDR: 595 1.1 fvdl /* The caller should not free this memory area */ 596 1.3 christos *(struct netbuf *)(void *)info = ct->ct_addr; 597 1.1 fvdl break; 598 1.1 fvdl case CLSET_SVC_ADDR: /* set to new address */ 599 1.1 fvdl release_fd_lock(ct->ct_fd, mask); 600 1.1 fvdl return (FALSE); 601 1.1 fvdl case CLGET_XID: 602 1.1 fvdl /* 603 1.1 fvdl * use the knowledge that xid is the 604 1.1 fvdl * first element in the call structure 605 1.1 fvdl * This will get the xid of the PREVIOUS call 606 1.1 fvdl */ 607 1.24 christos ntohlp(info, &ct->ct_u.ct_mcalli); 608 1.1 fvdl break; 609 1.1 fvdl case CLSET_XID: 610 1.1 fvdl /* This will set the xid of the NEXT call */ 611 1.1 fvdl /* increment by 1 as clnt_vc_call() decrements once */ 612 1.25 christos htonlp(&ct->ct_u.ct_mcalli, info, 1); 613 1.1 fvdl break; 614 1.1 fvdl case CLGET_VERS: 615 1.1 fvdl /* 616 1.1 fvdl * This RELIES on the information that, in the call body, 617 1.1 fvdl * the version number field is the fifth field from the 618 1.28 andvar * beginning of the RPC header. MUST be changed if the 619 1.1 fvdl * call_struct is changed 620 1.1 fvdl */ 621 1.24 christos ntohlp(info, ct->ct_u.ct_mcallc + 4 * BYTES_PER_XDR_UNIT); 622 1.1 fvdl break; 623 1.1 fvdl 624 1.1 fvdl case CLSET_VERS: 625 1.25 christos htonlp(ct->ct_u.ct_mcallc + 4 * BYTES_PER_XDR_UNIT, info, 0); 626 1.1 fvdl break; 627 1.1 fvdl 628 1.1 fvdl case CLGET_PROG: 629 1.1 fvdl /* 630 1.1 fvdl * This RELIES on the information that, in the call body, 631 1.1 fvdl * the program number field is the fourth field from the 632 1.28 andvar * beginning of the RPC header. MUST be changed if the 633 1.1 fvdl * call_struct is changed 634 1.1 fvdl */ 635 1.24 christos ntohlp(info, ct->ct_u.ct_mcallc + 3 * BYTES_PER_XDR_UNIT); 636 1.1 fvdl break; 637 1.1 fvdl 638 1.1 fvdl case CLSET_PROG: 639 1.25 christos htonlp(ct->ct_u.ct_mcallc + 3 * BYTES_PER_XDR_UNIT, info, 0); 640 1.1 fvdl break; 641 1.1 fvdl 642 1.1 fvdl default: 643 1.1 fvdl release_fd_lock(ct->ct_fd, mask); 644 1.1 fvdl return (FALSE); 645 1.1 fvdl } 646 1.1 fvdl release_fd_lock(ct->ct_fd, mask); 647 1.1 fvdl return (TRUE); 648 1.1 fvdl } 649 1.1 fvdl 650 1.1 fvdl 651 1.1 fvdl static void 652 1.18 christos clnt_vc_destroy(CLIENT *cl) 653 1.1 fvdl { 654 1.1 fvdl struct ct_data *ct; 655 1.9 thorpej #ifdef _REENTRANT 656 1.9 thorpej int ct_fd; 657 1.1 fvdl sigset_t mask; 658 1.1 fvdl #endif 659 1.1 fvdl sigset_t newmask; 660 1.1 fvdl 661 1.1 fvdl _DIAGASSERT(cl != NULL); 662 1.1 fvdl 663 1.1 fvdl ct = (struct ct_data *) cl->cl_private; 664 1.1 fvdl 665 1.23 christos __clnt_sigfillset(&newmask); 666 1.1 fvdl thr_sigsetmask(SIG_SETMASK, &newmask, &mask); 667 1.1 fvdl mutex_lock(&clnt_fd_lock); 668 1.9 thorpej #ifdef _REENTRANT 669 1.26 christos ct_fd = ct->ct_fd; 670 1.1 fvdl while (vc_fd_locks[ct_fd]) 671 1.1 fvdl cond_wait(&vc_cv[ct_fd], &clnt_fd_lock); 672 1.1 fvdl #endif 673 1.1 fvdl if (ct->ct_closeit && ct->ct_fd != -1) { 674 1.1 fvdl (void)close(ct->ct_fd); 675 1.1 fvdl } 676 1.1 fvdl XDR_DESTROY(&(ct->ct_xdrs)); 677 1.1 fvdl if (ct->ct_addr.buf) 678 1.1 fvdl free(ct->ct_addr.buf); 679 1.1 fvdl mem_free(ct, sizeof(struct ct_data)); 680 1.1 fvdl mem_free(cl, sizeof(CLIENT)); 681 1.1 fvdl mutex_unlock(&clnt_fd_lock); 682 1.1 fvdl thr_sigsetmask(SIG_SETMASK, &(mask), NULL); 683 1.1 fvdl 684 1.1 fvdl cond_signal(&vc_cv[ct_fd]); 685 1.1 fvdl } 686 1.1 fvdl 687 1.1 fvdl /* 688 1.1 fvdl * Interface between xdr serializer and tcp connection. 689 1.1 fvdl * Behaves like the system calls, read & write, but keeps some error state 690 1.1 fvdl * around for the rpc level. 691 1.1 fvdl */ 692 1.1 fvdl static int 693 1.18 christos read_vc(char *ctp, char *buf, int len) 694 1.1 fvdl { 695 1.1 fvdl struct ct_data *ct = (struct ct_data *)(void *)ctp; 696 1.1 fvdl struct pollfd fd; 697 1.12 christos struct timespec ts; 698 1.18 christos ssize_t nread; 699 1.1 fvdl 700 1.1 fvdl if (len == 0) 701 1.1 fvdl return (0); 702 1.12 christos 703 1.12 christos TIMEVAL_TO_TIMESPEC(&ct->ct_wait, &ts); 704 1.1 fvdl fd.fd = ct->ct_fd; 705 1.1 fvdl fd.events = POLLIN; 706 1.1 fvdl for (;;) { 707 1.12 christos switch (pollts(&fd, 1, &ts, NULL)) { 708 1.1 fvdl case 0: 709 1.1 fvdl ct->ct_error.re_status = RPC_TIMEDOUT; 710 1.1 fvdl return (-1); 711 1.1 fvdl 712 1.1 fvdl case -1: 713 1.1 fvdl if (errno == EINTR) 714 1.1 fvdl continue; 715 1.1 fvdl ct->ct_error.re_status = RPC_CANTRECV; 716 1.1 fvdl ct->ct_error.re_errno = errno; 717 1.1 fvdl return (-1); 718 1.1 fvdl } 719 1.1 fvdl break; 720 1.1 fvdl } 721 1.18 christos switch (nread = read(ct->ct_fd, buf, (size_t)len)) { 722 1.1 fvdl 723 1.1 fvdl case 0: 724 1.1 fvdl /* premature eof */ 725 1.1 fvdl ct->ct_error.re_errno = ECONNRESET; 726 1.1 fvdl ct->ct_error.re_status = RPC_CANTRECV; 727 1.22 christos nread = -1; /* it's really an error */ 728 1.1 fvdl break; 729 1.1 fvdl 730 1.1 fvdl case -1: 731 1.1 fvdl ct->ct_error.re_errno = errno; 732 1.1 fvdl ct->ct_error.re_status = RPC_CANTRECV; 733 1.1 fvdl break; 734 1.1 fvdl } 735 1.18 christos return (int)nread; 736 1.1 fvdl } 737 1.1 fvdl 738 1.1 fvdl static int 739 1.18 christos write_vc(char *ctp, char *buf, int len) 740 1.1 fvdl { 741 1.1 fvdl struct ct_data *ct = (struct ct_data *)(void *)ctp; 742 1.18 christos ssize_t i; 743 1.18 christos size_t cnt; 744 1.1 fvdl 745 1.1 fvdl for (cnt = len; cnt > 0; cnt -= i, buf += i) { 746 1.18 christos if ((i = write(ct->ct_fd, buf, cnt)) == -1) { 747 1.1 fvdl ct->ct_error.re_errno = errno; 748 1.1 fvdl ct->ct_error.re_status = RPC_CANTSEND; 749 1.1 fvdl return (-1); 750 1.1 fvdl } 751 1.1 fvdl } 752 1.18 christos return len; 753 1.1 fvdl } 754 1.1 fvdl 755 1.1 fvdl static struct clnt_ops * 756 1.18 christos clnt_vc_ops(void) 757 1.1 fvdl { 758 1.1 fvdl static struct clnt_ops ops; 759 1.9 thorpej #ifdef _REENTRANT 760 1.1 fvdl sigset_t mask; 761 1.1 fvdl #endif 762 1.1 fvdl sigset_t newmask; 763 1.1 fvdl 764 1.1 fvdl /* VARIABLES PROTECTED BY ops_lock: ops */ 765 1.1 fvdl 766 1.23 christos __clnt_sigfillset(&newmask); 767 1.1 fvdl thr_sigsetmask(SIG_SETMASK, &newmask, &mask); 768 1.1 fvdl mutex_lock(&ops_lock); 769 1.1 fvdl if (ops.cl_call == NULL) { 770 1.1 fvdl ops.cl_call = clnt_vc_call; 771 1.1 fvdl ops.cl_abort = clnt_vc_abort; 772 1.1 fvdl ops.cl_geterr = clnt_vc_geterr; 773 1.1 fvdl ops.cl_freeres = clnt_vc_freeres; 774 1.1 fvdl ops.cl_destroy = clnt_vc_destroy; 775 1.1 fvdl ops.cl_control = clnt_vc_control; 776 1.1 fvdl } 777 1.1 fvdl mutex_unlock(&ops_lock); 778 1.1 fvdl thr_sigsetmask(SIG_SETMASK, &(mask), NULL); 779 1.1 fvdl return (&ops); 780 1.1 fvdl } 781 1.1 fvdl 782 1.1 fvdl /* 783 1.1 fvdl * Make sure that the time is not garbage. -1 value is disallowed. 784 1.1 fvdl * Note this is different from time_not_ok in clnt_dg.c 785 1.1 fvdl */ 786 1.1 fvdl static bool_t 787 1.18 christos time_not_ok(struct timeval *t) 788 1.1 fvdl { 789 1.7 lukem 790 1.7 lukem _DIAGASSERT(t != NULL); 791 1.7 lukem 792 1.1 fvdl return (t->tv_sec <= -1 || t->tv_sec > 100000000 || 793 1.1 fvdl t->tv_usec <= -1 || t->tv_usec > 1000000); 794 1.1 fvdl } 795