diff --git a/include/rpc/rpc_com.h b/include/rpc/rpc_com.h index a98e3f73bac8..a0a8c8872efa 100644 --- a/include/rpc/rpc_com.h +++ b/include/rpc/rpc_com.h @@ -1,83 +1,82 @@ /* $NetBSD: rpc_com.h,v 1.3 2000/12/10 04:10:08 christos Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 2009, Sun Microsystems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of Sun Microsystems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * Copyright (c) 1986 - 1991 by Sun Microsystems, Inc. */ /* * rpc_com.h, Common definitions for both the server and client side. * All for the topmost layer of rpc * */ #ifndef _RPC_RPCCOM_H #define _RPC_RPCCOM_H #include /* #pragma ident "@(#)rpc_com.h 1.11 93/07/05 SMI" */ /* * The max size of the transport, if the size cannot be determined * by other means. */ #define RPC_MAXDATASIZE 9000 #define RPC_MAXADDRSIZE 1024 #define __RPC_GETXID(now) ((u_int32_t)getpid() ^ (u_int32_t)(now)->tv_sec ^ \ (u_int32_t)(now)->tv_usec) __BEGIN_DECLS extern u_int __rpc_get_a_size(int); -extern int __rpc_dtbsize(void); extern int _rpc_dtablesize(void); extern struct netconfig * __rpcgettp(int); extern int __rpc_get_default_domain(char **); char *__rpc_taddr2uaddr_af(int, const struct netbuf *); struct netbuf *__rpc_uaddr2taddr_af(int, const char *); int __rpc_fixup_addr(struct netbuf *, const struct netbuf *); int __rpc_sockinfo2netid(struct __rpc_sockinfo *, const char **); int __rpc_seman2socktype(int); int __rpc_socktype2seman(int); void *rpc_nullproc(CLIENT *); int __rpc_sockisbound(int); struct netbuf *__rpcb_findaddr(rpcprog_t, rpcvers_t, const struct netconfig *, const char *, CLIENT **); bool_t rpc_control(int,void *); char *_get_next_token(char *, int); __END_DECLS #endif /* _RPC_RPCCOM_H */ diff --git a/lib/libc/rpc/clnt_dg.c b/lib/libc/rpc/clnt_dg.c index 40511f30135e..c2a45b9d037f 100644 --- a/lib/libc/rpc/clnt_dg.c +++ b/lib/libc/rpc/clnt_dg.c @@ -1,850 +1,844 @@ /* $NetBSD: clnt_dg.c,v 1.4 2000/07/14 08:40:41 fvdl Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 2009, Sun Microsystems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of Sun Microsystems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * Copyright (c) 1986-1991 by Sun Microsystems Inc. */ #if defined(LIBC_SCCS) && !defined(lint) #ident "@(#)clnt_dg.c 1.23 94/04/22 SMI" static char sccsid[] = "@(#)clnt_dg.c 1.19 89/03/16 Copyr 1988 Sun Micro"; #endif /* * Implements a connectionless client side RPC. */ #include "namespace.h" #include "reentrant.h" #include #include #include #include #include +#include #include #include #include +#include #include +#include #include #include #include +#include #include #include #include "un-namespace.h" #include "rpc_com.h" #include "mt_misc.h" #ifdef _FREEFALL_CONFIG /* * Disable RPC exponential back-off for FreeBSD.org systems. */ #define RPC_MAX_BACKOFF 1 /* second */ #else #define RPC_MAX_BACKOFF 30 /* seconds */ #endif static struct clnt_ops *clnt_dg_ops(void); static bool_t time_not_ok(struct timeval *); static enum clnt_stat clnt_dg_call(CLIENT *, rpcproc_t, xdrproc_t, void *, xdrproc_t, void *, struct timeval); static void clnt_dg_geterr(CLIENT *, struct rpc_err *); static bool_t clnt_dg_freeres(CLIENT *, xdrproc_t, void *); static void clnt_dg_abort(CLIENT *); static bool_t clnt_dg_control(CLIENT *, u_int, void *); static void clnt_dg_destroy(CLIENT *); /* * This machinery implements per-fd locks for MT-safety. It is not * sufficient to do per-CLIENT handle locks for MT-safety because a * user may create more than one CLIENT handle with the same fd behind - * it. Therefore, we allocate an array of flags (dg_fd_locks), protected - * by the clnt_fd_lock mutex, and an array (dg_cv) of condition variables - * similarly protected. Dg_fd_lock[fd] == 1 => a call is active on some - * CLIENT handle created for that fd. - * The current implementation holds locks across the entire RPC and reply, - * including retransmissions. Yes, this is silly, and as soon as this - * code is proven to work, this should be the first thing fixed. One step - * at a time. + * it. Therefore, we allocate an associative array of flags and condition + * variables (dg_fd). The flags and the array are protected by the + * clnt_fd_lock mutex. dg_fd[fd].lock == 1 => a call is active on some + * CLIENT handle created for that fd. The current implementation holds + * locks across the entire RPC and reply, including retransmissions. Yes, + * this is silly, and as soon as this code is proven to work, this should + * be the first thing fixed. One step at a time. */ -static int *dg_fd_locks; -static cond_t *dg_cv; -#define release_fd_lock(fd, mask) { \ - mutex_lock(&clnt_fd_lock); \ - dg_fd_locks[fd] = 0; \ - mutex_unlock(&clnt_fd_lock); \ - thr_sigsetmask(SIG_SETMASK, &(mask), NULL); \ - cond_signal(&dg_cv[fd]); \ +struct dg_fd { + RB_ENTRY(dg_fd) dg_link; + int fd; + mutex_t mtx; +}; +static inline int +cmp_dg_fd(struct dg_fd *a, struct dg_fd *b) +{ + if (a->fd > b->fd) { + return (1); + } else if (a->fd < b->fd) { + return (-1); + } else { + return (0); + } +} +RB_HEAD(dg_fd_list, dg_fd); +RB_PROTOTYPE(dg_fd_list, dg_fd, dg_link, cmp_dg_fd); +RB_GENERATE(dg_fd_list, dg_fd, dg_link, cmp_dg_fd); +struct dg_fd_list dg_fd_head = RB_INITIALIZER(&dg_fd_head); + +/* + * Find the lock structure for the given file descriptor, or initialize it if + * it does not already exist. The clnt_fd_lock mutex must be held. + */ +static struct dg_fd * +dg_fd_find(int fd) +{ + struct dg_fd key, *elem; + + key.fd = fd; + elem = RB_FIND(dg_fd_list, &dg_fd_head, &key); + if (elem == NULL) { + elem = calloc(1, sizeof(*elem)); + elem->fd = fd; + mutex_init(&elem->mtx, NULL); + RB_INSERT(dg_fd_list, &dg_fd_head, elem); + } + return (elem); +} + +static void +release_fd_lock(struct dg_fd *elem, sigset_t mask) +{ + mutex_unlock(&elem->mtx); + thr_sigsetmask(SIG_SETMASK, &mask, NULL); } static const char mem_err_clnt_dg[] = "clnt_dg_create: out of memory"; -/* VARIABLES PROTECTED BY clnt_fd_lock: dg_fd_locks, dg_cv */ +/* VARIABLES PROTECTED BY clnt_fd_lock: dg_fd */ #define MCALL_MSG_SIZE 24 /* * Private data kept per client handle */ struct cu_data { int cu_fd; /* connections fd */ bool_t cu_closeit; /* opened by library */ struct sockaddr_storage cu_raddr; /* remote address */ int cu_rlen; struct timeval cu_wait; /* retransmit interval */ struct timeval cu_total; /* total time for the call */ struct rpc_err cu_error; XDR cu_outxdrs; u_int cu_xdrpos; u_int cu_sendsz; /* send size */ char cu_outhdr[MCALL_MSG_SIZE]; char *cu_outbuf; u_int cu_recvsz; /* recv size */ int cu_async; int cu_connect; /* Use connect(). */ int cu_connected; /* Have done connect(). */ struct kevent cu_kin; int cu_kq; char cu_inbuf[1]; }; /* * Connection less client creation returns with client handle parameters. * Default options are set, which the user can change using clnt_control(). * fd should be open and bound. * NB: The rpch->cl_auth is initialized to null authentication. * Caller may wish to set this something more useful. * * sendsz and recvsz are the maximum allowable packet sizes that can be * sent and received. Normally they are the same, but they can be * changed to improve the program efficiency and buffer allocation. * If they are 0, use the transport default. * * If svcaddr is NULL, returns NULL. * * fd - open file descriptor * svcaddr - servers address * program - program number * version - version number * sendsz - buffer recv size * recvsz - buffer send size */ CLIENT * clnt_dg_create(int fd, const struct netbuf *svcaddr, rpcprog_t program, rpcvers_t version, u_int sendsz, u_int recvsz) { CLIENT *cl = NULL; /* client handle */ struct cu_data *cu = NULL; /* private data */ struct timeval now; struct rpc_msg call_msg; - sigset_t mask; - sigset_t newmask; struct __rpc_sockinfo si; int one = 1; - sigfillset(&newmask); - thr_sigsetmask(SIG_SETMASK, &newmask, &mask); - mutex_lock(&clnt_fd_lock); - if (dg_fd_locks == (int *) NULL) { - int cv_allocsz; - size_t fd_allocsz; - int dtbsize = __rpc_dtbsize(); - - fd_allocsz = dtbsize * sizeof (int); - dg_fd_locks = (int *) mem_alloc(fd_allocsz); - if (dg_fd_locks == (int *) NULL) { - mutex_unlock(&clnt_fd_lock); - thr_sigsetmask(SIG_SETMASK, &(mask), NULL); - goto err1; - } else - memset(dg_fd_locks, '\0', fd_allocsz); - - cv_allocsz = dtbsize * sizeof (cond_t); - dg_cv = (cond_t *) mem_alloc(cv_allocsz); - if (dg_cv == (cond_t *) NULL) { - mem_free(dg_fd_locks, fd_allocsz); - dg_fd_locks = (int *) NULL; - mutex_unlock(&clnt_fd_lock); - thr_sigsetmask(SIG_SETMASK, &(mask), NULL); - goto err1; - } else { - int i; - - for (i = 0; i < dtbsize; i++) - cond_init(&dg_cv[i], 0, (void *) 0); - } - } - - mutex_unlock(&clnt_fd_lock); - thr_sigsetmask(SIG_SETMASK, &(mask), NULL); - if (svcaddr == NULL) { rpc_createerr.cf_stat = RPC_UNKNOWNADDR; return (NULL); } if (!__rpc_fd2sockinfo(fd, &si)) { rpc_createerr.cf_stat = RPC_TLIERROR; rpc_createerr.cf_error.re_errno = 0; return (NULL); } /* * Find the receive and the send size */ sendsz = __rpc_get_t_size(si.si_af, si.si_proto, (int)sendsz); recvsz = __rpc_get_t_size(si.si_af, si.si_proto, (int)recvsz); if ((sendsz == 0) || (recvsz == 0)) { rpc_createerr.cf_stat = RPC_TLIERROR; /* XXX */ rpc_createerr.cf_error.re_errno = 0; return (NULL); } if ((cl = mem_alloc(sizeof (CLIENT))) == NULL) goto err1; /* * Should be multiple of 4 for XDR. */ sendsz = ((sendsz + 3) / 4) * 4; recvsz = ((recvsz + 3) / 4) * 4; cu = mem_alloc(sizeof (*cu) + sendsz + recvsz); if (cu == NULL) goto err1; (void) memcpy(&cu->cu_raddr, svcaddr->buf, (size_t)svcaddr->len); cu->cu_rlen = svcaddr->len; cu->cu_outbuf = &cu->cu_inbuf[recvsz]; /* Other values can also be set through clnt_control() */ cu->cu_wait.tv_sec = 15; /* heuristically chosen */ cu->cu_wait.tv_usec = 0; cu->cu_total.tv_sec = -1; cu->cu_total.tv_usec = -1; cu->cu_sendsz = sendsz; cu->cu_recvsz = recvsz; cu->cu_async = FALSE; cu->cu_connect = FALSE; cu->cu_connected = FALSE; (void) gettimeofday(&now, NULL); call_msg.rm_xid = __RPC_GETXID(&now); call_msg.rm_call.cb_prog = program; call_msg.rm_call.cb_vers = version; xdrmem_create(&(cu->cu_outxdrs), cu->cu_outhdr, MCALL_MSG_SIZE, XDR_ENCODE); if (! xdr_callhdr(&cu->cu_outxdrs, &call_msg)) { rpc_createerr.cf_stat = RPC_CANTENCODEARGS; /* XXX */ rpc_createerr.cf_error.re_errno = 0; goto err2; } cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs)); XDR_DESTROY(&cu->cu_outxdrs); xdrmem_create(&cu->cu_outxdrs, cu->cu_outbuf, sendsz, XDR_ENCODE); /* XXX fvdl - do we still want this? */ #if 0 (void)bindresvport_sa(fd, (struct sockaddr *)svcaddr->buf); #endif _ioctl(fd, FIONBIO, (char *)(void *)&one); /* * By default, closeit is always FALSE. It is users responsibility * to do a close on it, else the user may use clnt_control * to let clnt_destroy do it for him/her. */ cu->cu_closeit = FALSE; cu->cu_fd = fd; cl->cl_ops = clnt_dg_ops(); cl->cl_private = (caddr_t)(void *)cu; cl->cl_auth = authnone_create(); cl->cl_tp = NULL; cl->cl_netid = NULL; cu->cu_kq = -1; EV_SET(&cu->cu_kin, cu->cu_fd, EVFILT_READ, EV_ADD, 0, 0, 0); return (cl); err1: warnx(mem_err_clnt_dg); rpc_createerr.cf_stat = RPC_SYSTEMERROR; rpc_createerr.cf_error.re_errno = errno; err2: if (cl) { mem_free(cl, sizeof (CLIENT)); if (cu) mem_free(cu, sizeof (*cu) + sendsz + recvsz); } return (NULL); } /* * cl - client handle * proc - procedure number * xargs - xdr routine for args * argsp - pointer to args * xresults - xdr routine for results * resultsp - pointer to results * utimeout - seconds to wait before giving up */ static enum clnt_stat clnt_dg_call(CLIENT *cl, rpcproc_t proc, xdrproc_t xargs, void *argsp, xdrproc_t xresults, void *resultsp, struct timeval utimeout) { struct cu_data *cu = (struct cu_data *)cl->cl_private; XDR *xdrs; size_t outlen = 0; struct rpc_msg reply_msg; XDR reply_xdrs; bool_t ok; int nrefreshes = 2; /* number of times to refresh cred */ int nretries = 0; /* number of times we retransmitted */ struct timeval timeout; struct timeval retransmit_time; struct timeval next_sendtime, starttime, time_waited, tv; struct timespec ts; struct kevent kv; struct sockaddr *sa; + struct dg_fd *elem; sigset_t mask; sigset_t newmask; socklen_t salen; ssize_t recvlen = 0; - int kin_len, n, rpc_lock_value; + int kin_len, n; u_int32_t xid; outlen = 0; sigfillset(&newmask); thr_sigsetmask(SIG_SETMASK, &newmask, &mask); mutex_lock(&clnt_fd_lock); - while (dg_fd_locks[cu->cu_fd]) - cond_wait(&dg_cv[cu->cu_fd], &clnt_fd_lock); - if (__isthreaded) - rpc_lock_value = 1; - else - rpc_lock_value = 0; - dg_fd_locks[cu->cu_fd] = rpc_lock_value; + elem = dg_fd_find(cu->cu_fd); mutex_unlock(&clnt_fd_lock); + mutex_lock(&elem->mtx); if (cu->cu_total.tv_usec == -1) { timeout = utimeout; /* use supplied timeout */ } else { timeout = cu->cu_total; /* use default timeout */ } if (cu->cu_connect && !cu->cu_connected) { if (_connect(cu->cu_fd, (struct sockaddr *)&cu->cu_raddr, cu->cu_rlen) < 0) { cu->cu_error.re_errno = errno; cu->cu_error.re_status = RPC_CANTSEND; goto out; } cu->cu_connected = 1; } if (cu->cu_connected) { sa = NULL; salen = 0; } else { sa = (struct sockaddr *)&cu->cu_raddr; salen = cu->cu_rlen; } time_waited.tv_sec = 0; time_waited.tv_usec = 0; retransmit_time = next_sendtime = cu->cu_wait; gettimeofday(&starttime, NULL); /* Clean up in case the last call ended in a longjmp(3) call. */ if (cu->cu_kq >= 0) _close(cu->cu_kq); if ((cu->cu_kq = kqueue()) < 0) { cu->cu_error.re_errno = errno; cu->cu_error.re_status = RPC_CANTSEND; goto out; } kin_len = 1; call_again: if (cu->cu_async == TRUE && xargs == NULL) goto get_reply; /* * the transaction is the first thing in the out buffer * XXX Yes, and it's in network byte order, so we should to * be careful when we increment it, shouldn't we. */ xid = ntohl(*(u_int32_t *)(void *)(cu->cu_outhdr)); xid++; *(u_int32_t *)(void *)(cu->cu_outhdr) = htonl(xid); call_again_same_xid: xdrs = &(cu->cu_outxdrs); xdrs->x_op = XDR_ENCODE; XDR_SETPOS(xdrs, 0); if (cl->cl_auth->ah_cred.oa_flavor != RPCSEC_GSS) { if ((! XDR_PUTBYTES(xdrs, cu->cu_outhdr, cu->cu_xdrpos)) || (! XDR_PUTINT32(xdrs, &proc)) || (! AUTH_MARSHALL(cl->cl_auth, xdrs)) || (! (*xargs)(xdrs, argsp))) { cu->cu_error.re_status = RPC_CANTENCODEARGS; goto out; } } else { *(uint32_t *) &cu->cu_outhdr[cu->cu_xdrpos] = htonl(proc); if (!__rpc_gss_wrap(cl->cl_auth, cu->cu_outhdr, cu->cu_xdrpos + sizeof(uint32_t), xdrs, xargs, argsp)) { cu->cu_error.re_status = RPC_CANTENCODEARGS; goto out; } } outlen = (size_t)XDR_GETPOS(xdrs); send_again: if (_sendto(cu->cu_fd, cu->cu_outbuf, outlen, 0, sa, salen) != outlen) { cu->cu_error.re_errno = errno; cu->cu_error.re_status = RPC_CANTSEND; goto out; } /* * Hack to provide rpc-based message passing */ if (timeout.tv_sec == 0 && timeout.tv_usec == 0) { cu->cu_error.re_status = RPC_TIMEDOUT; goto out; } get_reply: /* * sub-optimal code appears here because we have * some clock time to spare while the packets are in flight. * (We assume that this is actually only executed once.) */ reply_msg.acpted_rply.ar_verf = _null_auth; if (cl->cl_auth->ah_cred.oa_flavor != RPCSEC_GSS) { reply_msg.acpted_rply.ar_results.where = resultsp; reply_msg.acpted_rply.ar_results.proc = xresults; } else { reply_msg.acpted_rply.ar_results.where = NULL; reply_msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void; } for (;;) { /* Decide how long to wait. */ if (timercmp(&next_sendtime, &timeout, <)) timersub(&next_sendtime, &time_waited, &tv); else timersub(&timeout, &time_waited, &tv); if (tv.tv_sec < 0 || tv.tv_usec < 0) tv.tv_sec = tv.tv_usec = 0; TIMEVAL_TO_TIMESPEC(&tv, &ts); n = _kevent(cu->cu_kq, &cu->cu_kin, kin_len, &kv, 1, &ts); /* We don't need to register the event again. */ kin_len = 0; if (n == 1) { if (kv.flags & EV_ERROR) { cu->cu_error.re_errno = kv.data; cu->cu_error.re_status = RPC_CANTRECV; goto out; } /* We have some data now */ do { recvlen = _recvfrom(cu->cu_fd, cu->cu_inbuf, cu->cu_recvsz, 0, NULL, NULL); } while (recvlen < 0 && errno == EINTR); if (recvlen < 0 && errno != EWOULDBLOCK) { cu->cu_error.re_errno = errno; cu->cu_error.re_status = RPC_CANTRECV; goto out; } if (recvlen >= sizeof(u_int32_t) && (cu->cu_async == TRUE || *((u_int32_t *)(void *)(cu->cu_inbuf)) == *((u_int32_t *)(void *)(cu->cu_outbuf)))) { /* We now assume we have the proper reply. */ break; } } if (n == -1 && errno != EINTR) { cu->cu_error.re_errno = errno; cu->cu_error.re_status = RPC_CANTRECV; goto out; } gettimeofday(&tv, NULL); timersub(&tv, &starttime, &time_waited); /* Check for timeout. */ if (timercmp(&time_waited, &timeout, >)) { cu->cu_error.re_status = RPC_TIMEDOUT; goto out; } /* Retransmit if necessary. */ if (timercmp(&time_waited, &next_sendtime, >)) { /* update retransmit_time */ if (retransmit_time.tv_sec < RPC_MAX_BACKOFF) timeradd(&retransmit_time, &retransmit_time, &retransmit_time); timeradd(&next_sendtime, &retransmit_time, &next_sendtime); nretries++; /* * When retransmitting a RPCSEC_GSS message, * we must use a new sequence number (handled * by __rpc_gss_wrap above). */ if (cl->cl_auth->ah_cred.oa_flavor != RPCSEC_GSS) goto send_again; else goto call_again_same_xid; } } /* * now decode and validate the response */ xdrmem_create(&reply_xdrs, cu->cu_inbuf, (u_int)recvlen, XDR_DECODE); ok = xdr_replymsg(&reply_xdrs, &reply_msg); /* XDR_DESTROY(&reply_xdrs); save a few cycles on noop destroy */ if (ok) { if ((reply_msg.rm_reply.rp_stat == MSG_ACCEPTED) && (reply_msg.acpted_rply.ar_stat == SUCCESS)) cu->cu_error.re_status = RPC_SUCCESS; else _seterr_reply(&reply_msg, &(cu->cu_error)); if (cu->cu_error.re_status == RPC_SUCCESS) { if (! AUTH_VALIDATE(cl->cl_auth, &reply_msg.acpted_rply.ar_verf)) { if (nretries && cl->cl_auth->ah_cred.oa_flavor == RPCSEC_GSS) /* * If we retransmitted, its * possible that we will * receive a reply for one of * the earlier transmissions * (which will use an older * RPCSEC_GSS sequence * number). In this case, just * go back and listen for a * new reply. We could keep a * record of all the seq * numbers we have transmitted * so far so that we could * accept a reply for any of * them here. */ goto get_reply; cu->cu_error.re_status = RPC_AUTHERROR; cu->cu_error.re_why = AUTH_INVALIDRESP; } else { if (cl->cl_auth->ah_cred.oa_flavor == RPCSEC_GSS) { if (!__rpc_gss_unwrap(cl->cl_auth, &reply_xdrs, xresults, resultsp)) cu->cu_error.re_status = RPC_CANTDECODERES; } } if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) { xdrs->x_op = XDR_FREE; (void) xdr_opaque_auth(xdrs, &(reply_msg.acpted_rply.ar_verf)); } } /* end successful completion */ /* * If unsuccessful AND error is an authentication error * then refresh credentials and try again, else break */ else if (cu->cu_error.re_status == RPC_AUTHERROR) /* maybe our credentials need to be refreshed ... */ if (nrefreshes > 0 && AUTH_REFRESH(cl->cl_auth, &reply_msg)) { nrefreshes--; goto call_again; } /* end of unsuccessful completion */ } /* end of valid reply message */ else { cu->cu_error.re_status = RPC_CANTDECODERES; } out: if (cu->cu_kq >= 0) _close(cu->cu_kq); cu->cu_kq = -1; - release_fd_lock(cu->cu_fd, mask); + release_fd_lock(elem, mask); return (cu->cu_error.re_status); } static void clnt_dg_geterr(CLIENT *cl, struct rpc_err *errp) { struct cu_data *cu = (struct cu_data *)cl->cl_private; *errp = cu->cu_error; } static bool_t clnt_dg_freeres(CLIENT *cl, xdrproc_t xdr_res, void *res_ptr) { struct cu_data *cu = (struct cu_data *)cl->cl_private; + struct dg_fd *elem; XDR *xdrs = &(cu->cu_outxdrs); bool_t dummy; sigset_t mask; sigset_t newmask; sigfillset(&newmask); thr_sigsetmask(SIG_SETMASK, &newmask, &mask); mutex_lock(&clnt_fd_lock); - while (dg_fd_locks[cu->cu_fd]) - cond_wait(&dg_cv[cu->cu_fd], &clnt_fd_lock); + elem = dg_fd_find(cu->cu_fd); + mutex_lock(&elem->mtx); xdrs->x_op = XDR_FREE; dummy = (*xdr_res)(xdrs, res_ptr); mutex_unlock(&clnt_fd_lock); - thr_sigsetmask(SIG_SETMASK, &mask, NULL); - cond_signal(&dg_cv[cu->cu_fd]); + release_fd_lock(elem, mask); return (dummy); } /*ARGSUSED*/ static void clnt_dg_abort(CLIENT *h) { } static bool_t clnt_dg_control(CLIENT *cl, u_int request, void *info) { struct cu_data *cu = (struct cu_data *)cl->cl_private; struct netbuf *addr; + struct dg_fd *elem; sigset_t mask; sigset_t newmask; - int rpc_lock_value; sigfillset(&newmask); thr_sigsetmask(SIG_SETMASK, &newmask, &mask); mutex_lock(&clnt_fd_lock); - while (dg_fd_locks[cu->cu_fd]) - cond_wait(&dg_cv[cu->cu_fd], &clnt_fd_lock); - if (__isthreaded) - rpc_lock_value = 1; - else - rpc_lock_value = 0; - dg_fd_locks[cu->cu_fd] = rpc_lock_value; + elem = dg_fd_find(cu->cu_fd); mutex_unlock(&clnt_fd_lock); + mutex_lock(&elem->mtx); switch (request) { case CLSET_FD_CLOSE: cu->cu_closeit = TRUE; - release_fd_lock(cu->cu_fd, mask); + release_fd_lock(elem, mask); return (TRUE); case CLSET_FD_NCLOSE: cu->cu_closeit = FALSE; - release_fd_lock(cu->cu_fd, mask); + release_fd_lock(elem, mask); return (TRUE); } /* for other requests which use info */ if (info == NULL) { - release_fd_lock(cu->cu_fd, mask); + release_fd_lock(elem, mask); return (FALSE); } switch (request) { case CLSET_TIMEOUT: if (time_not_ok((struct timeval *)info)) { - release_fd_lock(cu->cu_fd, mask); + release_fd_lock(elem, mask); return (FALSE); } cu->cu_total = *(struct timeval *)info; break; case CLGET_TIMEOUT: *(struct timeval *)info = cu->cu_total; break; case CLGET_SERVER_ADDR: /* Give him the fd address */ /* Now obsolete. Only for backward compatibility */ (void) memcpy(info, &cu->cu_raddr, (size_t)cu->cu_rlen); break; case CLSET_RETRY_TIMEOUT: if (time_not_ok((struct timeval *)info)) { - release_fd_lock(cu->cu_fd, mask); + release_fd_lock(elem, mask); return (FALSE); } cu->cu_wait = *(struct timeval *)info; break; case CLGET_RETRY_TIMEOUT: *(struct timeval *)info = cu->cu_wait; break; case CLGET_FD: *(int *)info = cu->cu_fd; break; case CLGET_SVC_ADDR: addr = (struct netbuf *)info; addr->buf = &cu->cu_raddr; addr->len = cu->cu_rlen; addr->maxlen = sizeof cu->cu_raddr; break; case CLSET_SVC_ADDR: /* set to new address */ addr = (struct netbuf *)info; if (addr->len < sizeof cu->cu_raddr) { - release_fd_lock(cu->cu_fd, mask); + release_fd_lock(elem, mask); return (FALSE); } (void) memcpy(&cu->cu_raddr, addr->buf, addr->len); cu->cu_rlen = addr->len; break; case CLGET_XID: /* * use the knowledge that xid is the * first element in the call structure *. * This will get the xid of the PREVIOUS call */ *(u_int32_t *)info = ntohl(*(u_int32_t *)(void *)cu->cu_outhdr); break; case CLSET_XID: /* This will set the xid of the NEXT call */ *(u_int32_t *)(void *)cu->cu_outhdr = htonl(*(u_int32_t *)info - 1); /* decrement by 1 as clnt_dg_call() increments once */ break; case CLGET_VERS: /* * This RELIES on the information that, in the call body, * the version number field is the fifth field from the * beginning of the RPC header. MUST be changed if the * call_struct is changed */ *(u_int32_t *)info = ntohl(*(u_int32_t *)(void *)(cu->cu_outhdr + 4 * BYTES_PER_XDR_UNIT)); break; case CLSET_VERS: *(u_int32_t *)(void *)(cu->cu_outhdr + 4 * BYTES_PER_XDR_UNIT) = htonl(*(u_int32_t *)info); break; case CLGET_PROG: /* * This RELIES on the information that, in the call body, * the program number field is the fourth field from the * beginning of the RPC header. MUST be changed if the * call_struct is changed */ *(u_int32_t *)info = ntohl(*(u_int32_t *)(void *)(cu->cu_outhdr + 3 * BYTES_PER_XDR_UNIT)); break; case CLSET_PROG: *(u_int32_t *)(void *)(cu->cu_outhdr + 3 * BYTES_PER_XDR_UNIT) = htonl(*(u_int32_t *)info); break; case CLSET_ASYNC: cu->cu_async = *(int *)info; break; case CLSET_CONNECT: cu->cu_connect = *(int *)info; break; default: - release_fd_lock(cu->cu_fd, mask); + release_fd_lock(elem, mask); return (FALSE); } - release_fd_lock(cu->cu_fd, mask); + release_fd_lock(elem, mask); return (TRUE); } static void clnt_dg_destroy(CLIENT *cl) { struct cu_data *cu = (struct cu_data *)cl->cl_private; + struct dg_fd *elem; int cu_fd = cu->cu_fd; sigset_t mask; sigset_t newmask; sigfillset(&newmask); thr_sigsetmask(SIG_SETMASK, &newmask, &mask); mutex_lock(&clnt_fd_lock); - while (dg_fd_locks[cu_fd]) - cond_wait(&dg_cv[cu_fd], &clnt_fd_lock); + elem = dg_fd_find(cu_fd); + mutex_lock(&elem->mtx); if (cu->cu_closeit) (void)_close(cu_fd); if (cu->cu_kq >= 0) _close(cu->cu_kq); XDR_DESTROY(&(cu->cu_outxdrs)); mem_free(cu, (sizeof (*cu) + cu->cu_sendsz + cu->cu_recvsz)); if (cl->cl_netid && cl->cl_netid[0]) mem_free(cl->cl_netid, strlen(cl->cl_netid) +1); if (cl->cl_tp && cl->cl_tp[0]) mem_free(cl->cl_tp, strlen(cl->cl_tp) +1); mem_free(cl, sizeof (CLIENT)); mutex_unlock(&clnt_fd_lock); - thr_sigsetmask(SIG_SETMASK, &mask, NULL); - cond_signal(&dg_cv[cu_fd]); + release_fd_lock(elem, mask); } static struct clnt_ops * clnt_dg_ops(void) { static struct clnt_ops ops; sigset_t mask; sigset_t newmask; /* VARIABLES PROTECTED BY ops_lock: ops */ sigfillset(&newmask); thr_sigsetmask(SIG_SETMASK, &newmask, &mask); mutex_lock(&ops_lock); if (ops.cl_call == NULL) { ops.cl_call = clnt_dg_call; ops.cl_abort = clnt_dg_abort; ops.cl_geterr = clnt_dg_geterr; ops.cl_freeres = clnt_dg_freeres; ops.cl_destroy = clnt_dg_destroy; ops.cl_control = clnt_dg_control; } mutex_unlock(&ops_lock); thr_sigsetmask(SIG_SETMASK, &mask, NULL); return (&ops); } /* * Make sure that the time is not garbage. -1 value is allowed. */ static bool_t time_not_ok(struct timeval *t) { return (t->tv_sec < -1 || t->tv_sec > 100000000 || t->tv_usec < -1 || t->tv_usec > 1000000); } diff --git a/lib/libc/rpc/clnt_vc.c b/lib/libc/rpc/clnt_vc.c index 25cd5a273531..7a155d0f918b 100644 --- a/lib/libc/rpc/clnt_vc.c +++ b/lib/libc/rpc/clnt_vc.c @@ -1,846 +1,839 @@ /* $NetBSD: clnt_vc.c,v 1.4 2000/07/14 08:40:42 fvdl Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 2009, Sun Microsystems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of Sun Microsystems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #if defined(LIBC_SCCS) && !defined(lint) static char *sccsid2 = "@(#)clnt_tcp.c 1.37 87/10/05 Copyr 1984 Sun Micro"; static char *sccsid = "@(#)clnt_tcp.c 2.2 88/08/01 4.0 RPCSRC"; static char sccsid3[] = "@(#)clnt_vc.c 1.19 89/03/16 Copyr 1988 Sun Micro"; #endif /* * clnt_tcp.c, Implements a TCP/IP based, client side RPC. * * Copyright (C) 1984, Sun Microsystems, Inc. * * TCP based RPC supports 'batched calls'. * A sequence of calls may be batched-up in a send buffer. The rpc call * return immediately to the client even though the call was not necessarily * sent. The batching occurs if the results' xdr routine is NULL (0) AND * the rpc timeout value is zero (see clnt.h, rpc). * * Clients should NOT casually batch calls that in fact return results; that is, * the server side should be aware that a call is batched and not produce any * return message. Batched calls that produce many result messages can * deadlock (netlock) the client and the server.... * * Now go hang yourself. */ #include "namespace.h" #include "reentrant.h" #include #include #include #include +#include #include #include #include #include #include #include #include +#include #include +#include #include #include #include #include #include #include #include "un-namespace.h" #include "rpc_com.h" #include "mt_misc.h" #define MCALL_MSG_SIZE 24 struct cmessage { struct cmsghdr cmsg; struct cmsgcred cmcred; }; static enum clnt_stat clnt_vc_call(CLIENT *, rpcproc_t, xdrproc_t, void *, xdrproc_t, void *, struct timeval); static void clnt_vc_geterr(CLIENT *, struct rpc_err *); static bool_t clnt_vc_freeres(CLIENT *, xdrproc_t, void *); static void clnt_vc_abort(CLIENT *); static bool_t clnt_vc_control(CLIENT *, u_int, void *); static void clnt_vc_destroy(CLIENT *); static struct clnt_ops *clnt_vc_ops(void); static bool_t time_not_ok(struct timeval *); static int read_vc(void *, void *, int); static int write_vc(void *, void *, int); static int __msgwrite(int, void *, size_t); static int __msgread(int, void *, size_t); struct ct_data { int ct_fd; /* connection's fd */ bool_t ct_closeit; /* close it on destroy */ struct timeval ct_wait; /* wait interval in milliseconds */ bool_t ct_waitset; /* wait set by clnt_control? */ struct netbuf ct_addr; /* remote addr */ struct rpc_err ct_error; union { char ct_mcallc[MCALL_MSG_SIZE]; /* marshalled callmsg */ u_int32_t ct_mcalli; } ct_u; u_int ct_mpos; /* pos after marshal */ XDR ct_xdrs; /* XDR stream */ }; /* * This machinery implements per-fd locks for MT-safety. It is not * sufficient to do per-CLIENT handle locks for MT-safety because a * user may create more than one CLIENT handle with the same fd behind - * it. Therefore, we allocate an array of flags (vc_fd_locks), protected - * by the clnt_fd_lock mutex, and an array (vc_cv) of condition variables - * similarly protected. Vc_fd_lock[fd] == 1 => a call is active on some - * CLIENT handle created for that fd. - * The current implementation holds locks across the entire RPC and reply. - * Yes, this is silly, and as soon as this code is proven to work, this - * should be the first thing fixed. One step at a time. + * it. Therefore, we allocate an associative array of flags and condition + * variables (vc_fd). The flags and the array are protected by the + * clnt_fd_lock mutex. vc_fd_lock[fd] == 1 => a call is active on some + * CLIENT handle created for that fd. The current implementation holds + * locks across the entire RPC and reply. Yes, this is silly, and as soon + * as this code is proven to work, this should be the first thing fixed. + * One step at a time. */ -static int *vc_fd_locks; -static cond_t *vc_cv; -#define release_fd_lock(fd, mask) { \ - mutex_lock(&clnt_fd_lock); \ - vc_fd_locks[fd] = 0; \ - mutex_unlock(&clnt_fd_lock); \ - thr_sigsetmask(SIG_SETMASK, &(mask), (sigset_t *) NULL); \ - cond_signal(&vc_cv[fd]); \ +struct vc_fd { + RB_ENTRY(vc_fd) vc_link; + int fd; + mutex_t mtx; +}; +static inline int +cmp_vc_fd(struct vc_fd *a, struct vc_fd *b) +{ + if (a->fd > b->fd) { + return (1); + } else if (a->fd < b->fd) { + return (-1); + } else { + return (0); + } +} +RB_HEAD(vc_fd_list, vc_fd); +RB_PROTOTYPE(vc_fd_list, vc_fd, vc_link, cmp_vc_fd); +RB_GENERATE(vc_fd_list, vc_fd, vc_link, cmp_vc_fd); +struct vc_fd_list vc_fd_head = RB_INITIALIZER(&vc_fd_head); + +/* + * Find the lock structure for the given file descriptor, or initialize it if + * it does not already exist. The clnt_fd_lock mutex must be held. + */ +static struct vc_fd * +vc_fd_find(int fd) +{ + struct vc_fd key, *elem; + + key.fd = fd; + elem = RB_FIND(vc_fd_list, &vc_fd_head, &key); + if (elem == NULL) { + elem = calloc(1, sizeof(*elem)); + elem->fd = fd; + mutex_init(&elem->mtx, NULL); + RB_INSERT(vc_fd_list, &vc_fd_head, elem); + } + return (elem); +} + +static void +release_fd_lock(struct vc_fd *elem, sigset_t mask) +{ + mutex_unlock(&elem->mtx); + thr_sigsetmask(SIG_SETMASK, &mask, NULL); } static const char clnt_vc_errstr[] = "%s : %s"; static const char clnt_vc_str[] = "clnt_vc_create"; static const char __no_mem_str[] = "out of memory"; /* * Create a client handle for a connection. * Default options are set, which the user can change using clnt_control()'s. * The rpc/vc package does buffering similar to stdio, so the client * must pick send and receive buffer sizes, 0 => use the default. * NB: fd is copied into a private area. * NB: The rpch->cl_auth is set null authentication. Caller may wish to * set this something more useful. * * fd should be an open socket * * fd - open file descriptor * raddr - servers address * prog - program number * vers - version number * sendsz - buffer send size * recvsz - buffer recv size */ CLIENT * clnt_vc_create(int fd, const struct netbuf *raddr, const rpcprog_t prog, const rpcvers_t vers, u_int sendsz, u_int recvsz) { CLIENT *cl; /* client handle */ struct ct_data *ct = NULL; /* client handle */ struct timeval now; struct rpc_msg call_msg; static u_int32_t disrupt; - sigset_t mask; - sigset_t newmask; struct sockaddr_storage ss; socklen_t slen; struct __rpc_sockinfo si; if (disrupt == 0) disrupt = (u_int32_t)(long)raddr; cl = (CLIENT *)mem_alloc(sizeof (*cl)); ct = (struct ct_data *)mem_alloc(sizeof (*ct)); if ((cl == (CLIENT *)NULL) || (ct == (struct ct_data *)NULL)) { (void) syslog(LOG_ERR, clnt_vc_errstr, clnt_vc_str, __no_mem_str); rpc_createerr.cf_stat = RPC_SYSTEMERROR; rpc_createerr.cf_error.re_errno = errno; goto err; } ct->ct_addr.buf = NULL; - sigfillset(&newmask); - thr_sigsetmask(SIG_SETMASK, &newmask, &mask); - mutex_lock(&clnt_fd_lock); - if (vc_fd_locks == (int *) NULL) { - int cv_allocsz, fd_allocsz; - int dtbsize = __rpc_dtbsize(); - - fd_allocsz = dtbsize * sizeof (int); - vc_fd_locks = (int *) mem_alloc(fd_allocsz); - if (vc_fd_locks == (int *) NULL) { - mutex_unlock(&clnt_fd_lock); - thr_sigsetmask(SIG_SETMASK, &(mask), NULL); - goto err; - } else - memset(vc_fd_locks, '\0', fd_allocsz); - - assert(vc_cv == (cond_t *) NULL); - cv_allocsz = dtbsize * sizeof (cond_t); - vc_cv = (cond_t *) mem_alloc(cv_allocsz); - if (vc_cv == (cond_t *) NULL) { - mem_free(vc_fd_locks, fd_allocsz); - vc_fd_locks = (int *) NULL; - mutex_unlock(&clnt_fd_lock); - thr_sigsetmask(SIG_SETMASK, &(mask), NULL); - goto err; - } else { - int i; - - for (i = 0; i < dtbsize; i++) - cond_init(&vc_cv[i], 0, (void *) 0); - } - } else - assert(vc_cv != (cond_t *) NULL); /* * XXX - fvdl connecting while holding a mutex? */ slen = sizeof ss; if (_getpeername(fd, (struct sockaddr *)(void *)&ss, &slen) < 0) { if (errno != ENOTCONN) { rpc_createerr.cf_stat = RPC_SYSTEMERROR; rpc_createerr.cf_error.re_errno = errno; mutex_unlock(&clnt_fd_lock); - thr_sigsetmask(SIG_SETMASK, &(mask), NULL); goto err; } if (_connect(fd, (struct sockaddr *)raddr->buf, raddr->len) < 0){ rpc_createerr.cf_stat = RPC_SYSTEMERROR; rpc_createerr.cf_error.re_errno = errno; mutex_unlock(&clnt_fd_lock); - thr_sigsetmask(SIG_SETMASK, &(mask), NULL); goto err; } } mutex_unlock(&clnt_fd_lock); - thr_sigsetmask(SIG_SETMASK, &(mask), NULL); if (!__rpc_fd2sockinfo(fd, &si)) goto err; ct->ct_closeit = FALSE; /* * Set up private data struct */ ct->ct_fd = fd; ct->ct_wait.tv_usec = 0; ct->ct_waitset = FALSE; ct->ct_addr.buf = malloc(raddr->maxlen); if (ct->ct_addr.buf == NULL) goto err; memcpy(ct->ct_addr.buf, raddr->buf, raddr->len); ct->ct_addr.len = raddr->len; ct->ct_addr.maxlen = raddr->maxlen; /* * Initialize call message */ (void)gettimeofday(&now, NULL); call_msg.rm_xid = ((u_int32_t)++disrupt) ^ __RPC_GETXID(&now); call_msg.rm_direction = CALL; call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; call_msg.rm_call.cb_prog = (u_int32_t)prog; call_msg.rm_call.cb_vers = (u_int32_t)vers; /* * pre-serialize the static part of the call msg and stash it away */ xdrmem_create(&(ct->ct_xdrs), ct->ct_u.ct_mcallc, MCALL_MSG_SIZE, XDR_ENCODE); if (! xdr_callhdr(&(ct->ct_xdrs), &call_msg)) { if (ct->ct_closeit) { (void)_close(fd); } goto err; } ct->ct_mpos = XDR_GETPOS(&(ct->ct_xdrs)); XDR_DESTROY(&(ct->ct_xdrs)); assert(ct->ct_mpos + sizeof(uint32_t) <= MCALL_MSG_SIZE); /* * Create a client handle which uses xdrrec for serialization * and authnone for authentication. */ cl->cl_ops = clnt_vc_ops(); cl->cl_private = ct; cl->cl_auth = authnone_create(); sendsz = __rpc_get_t_size(si.si_af, si.si_proto, (int)sendsz); recvsz = __rpc_get_t_size(si.si_af, si.si_proto, (int)recvsz); xdrrec_create(&(ct->ct_xdrs), sendsz, recvsz, cl->cl_private, read_vc, write_vc); return (cl); err: if (ct) { if (ct->ct_addr.len) mem_free(ct->ct_addr.buf, ct->ct_addr.len); mem_free(ct, sizeof (struct ct_data)); } if (cl) mem_free(cl, sizeof (CLIENT)); return ((CLIENT *)NULL); } static enum clnt_stat clnt_vc_call(CLIENT *cl, rpcproc_t proc, xdrproc_t xdr_args, void *args_ptr, xdrproc_t xdr_results, void *results_ptr, struct timeval timeout) { struct ct_data *ct = (struct ct_data *) cl->cl_private; XDR *xdrs = &(ct->ct_xdrs); struct rpc_msg reply_msg; + struct vc_fd *elem; u_int32_t x_id; u_int32_t *msg_x_id = &ct->ct_u.ct_mcalli; /* yuk */ bool_t shipnow; int refreshes = 2; sigset_t mask, newmask; - int rpc_lock_value; bool_t reply_stat; assert(cl != NULL); sigfillset(&newmask); thr_sigsetmask(SIG_SETMASK, &newmask, &mask); mutex_lock(&clnt_fd_lock); - while (vc_fd_locks[ct->ct_fd]) - cond_wait(&vc_cv[ct->ct_fd], &clnt_fd_lock); - if (__isthreaded) - rpc_lock_value = 1; - else - rpc_lock_value = 0; - vc_fd_locks[ct->ct_fd] = rpc_lock_value; + elem = vc_fd_find(ct->ct_fd); mutex_unlock(&clnt_fd_lock); + mutex_lock(&elem->mtx); if (!ct->ct_waitset) { /* If time is not within limits, we ignore it. */ if (time_not_ok(&timeout) == FALSE) ct->ct_wait = timeout; } shipnow = (xdr_results == NULL && timeout.tv_sec == 0 && timeout.tv_usec == 0) ? FALSE : TRUE; call_again: xdrs->x_op = XDR_ENCODE; ct->ct_error.re_status = RPC_SUCCESS; x_id = ntohl(--(*msg_x_id)); if (cl->cl_auth->ah_cred.oa_flavor != RPCSEC_GSS) { if ((! XDR_PUTBYTES(xdrs, ct->ct_u.ct_mcallc, ct->ct_mpos)) || (! XDR_PUTINT32(xdrs, &proc)) || (! AUTH_MARSHALL(cl->cl_auth, xdrs)) || (! (*xdr_args)(xdrs, args_ptr))) { if (ct->ct_error.re_status == RPC_SUCCESS) ct->ct_error.re_status = RPC_CANTENCODEARGS; (void)xdrrec_endofrecord(xdrs, TRUE); - release_fd_lock(ct->ct_fd, mask); + release_fd_lock(elem, mask); return (ct->ct_error.re_status); } } else { *(uint32_t *) &ct->ct_u.ct_mcallc[ct->ct_mpos] = htonl(proc); if (! __rpc_gss_wrap(cl->cl_auth, ct->ct_u.ct_mcallc, ct->ct_mpos + sizeof(uint32_t), xdrs, xdr_args, args_ptr)) { if (ct->ct_error.re_status == RPC_SUCCESS) ct->ct_error.re_status = RPC_CANTENCODEARGS; (void)xdrrec_endofrecord(xdrs, TRUE); - release_fd_lock(ct->ct_fd, mask); + release_fd_lock(elem, mask); return (ct->ct_error.re_status); } } if (! xdrrec_endofrecord(xdrs, shipnow)) { - release_fd_lock(ct->ct_fd, mask); + release_fd_lock(elem, mask); return (ct->ct_error.re_status = RPC_CANTSEND); } if (! shipnow) { - release_fd_lock(ct->ct_fd, mask); + release_fd_lock(elem, mask); return (RPC_SUCCESS); } /* * Hack to provide rpc-based message passing */ if (timeout.tv_sec == 0 && timeout.tv_usec == 0) { - release_fd_lock(ct->ct_fd, mask); + release_fd_lock(elem, mask); return(ct->ct_error.re_status = RPC_TIMEDOUT); } /* * Keep receiving until we get a valid transaction id */ xdrs->x_op = XDR_DECODE; while (TRUE) { reply_msg.acpted_rply.ar_verf = _null_auth; reply_msg.acpted_rply.ar_results.where = NULL; reply_msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void; if (! xdrrec_skiprecord(xdrs)) { - release_fd_lock(ct->ct_fd, mask); + release_fd_lock(elem, mask); return (ct->ct_error.re_status); } /* now decode and validate the response header */ if (! xdr_replymsg(xdrs, &reply_msg)) { if (ct->ct_error.re_status == RPC_SUCCESS) continue; - release_fd_lock(ct->ct_fd, mask); + release_fd_lock(elem, mask); return (ct->ct_error.re_status); } if (reply_msg.rm_xid == x_id) break; } /* * process header */ _seterr_reply(&reply_msg, &(ct->ct_error)); if (ct->ct_error.re_status == RPC_SUCCESS) { if (! AUTH_VALIDATE(cl->cl_auth, &reply_msg.acpted_rply.ar_verf)) { ct->ct_error.re_status = RPC_AUTHERROR; ct->ct_error.re_why = AUTH_INVALIDRESP; } else { if (cl->cl_auth->ah_cred.oa_flavor != RPCSEC_GSS) { reply_stat = (*xdr_results)(xdrs, results_ptr); } else { reply_stat = __rpc_gss_unwrap(cl->cl_auth, xdrs, xdr_results, results_ptr); } if (! reply_stat) { if (ct->ct_error.re_status == RPC_SUCCESS) ct->ct_error.re_status = RPC_CANTDECODERES; } } /* free verifier ... */ if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) { xdrs->x_op = XDR_FREE; (void)xdr_opaque_auth(xdrs, &(reply_msg.acpted_rply.ar_verf)); } } /* end successful completion */ else { /* maybe our credentials need to be refreshed ... */ if (refreshes-- && AUTH_REFRESH(cl->cl_auth, &reply_msg)) goto call_again; } /* end of unsuccessful completion */ - release_fd_lock(ct->ct_fd, mask); + release_fd_lock(elem, mask); return (ct->ct_error.re_status); } static void clnt_vc_geterr(CLIENT *cl, struct rpc_err *errp) { struct ct_data *ct; assert(cl != NULL); assert(errp != NULL); ct = (struct ct_data *) cl->cl_private; *errp = ct->ct_error; } static bool_t clnt_vc_freeres(CLIENT *cl, xdrproc_t xdr_res, void *res_ptr) { struct ct_data *ct; + struct vc_fd *elem; XDR *xdrs; bool_t dummy; sigset_t mask; sigset_t newmask; assert(cl != NULL); ct = (struct ct_data *)cl->cl_private; xdrs = &(ct->ct_xdrs); sigfillset(&newmask); thr_sigsetmask(SIG_SETMASK, &newmask, &mask); mutex_lock(&clnt_fd_lock); - while (vc_fd_locks[ct->ct_fd]) - cond_wait(&vc_cv[ct->ct_fd], &clnt_fd_lock); + elem = vc_fd_find(ct->ct_fd); + mutex_lock(&elem->mtx); xdrs->x_op = XDR_FREE; dummy = (*xdr_res)(xdrs, res_ptr); - mutex_unlock(&clnt_fd_lock); - thr_sigsetmask(SIG_SETMASK, &(mask), NULL); - cond_signal(&vc_cv[ct->ct_fd]); + mutex_unlock(&clnt_fd_lock); + release_fd_lock(elem, mask); return dummy; } /*ARGSUSED*/ static void clnt_vc_abort(CLIENT *cl) { } static __inline void htonlp(void *dst, const void *src, uint32_t incr) { /* We are aligned, so we think */ *(uint32_t *)dst = htonl(*(const uint32_t *)src + incr); } static __inline void ntohlp(void *dst, const void *src) { /* We are aligned, so we think */ *(uint32_t *)dst = htonl(*(const uint32_t *)src); } static bool_t clnt_vc_control(CLIENT *cl, u_int request, void *info) { struct ct_data *ct; + struct vc_fd *elem; void *infop = info; sigset_t mask; sigset_t newmask; - int rpc_lock_value; assert(cl != NULL); ct = (struct ct_data *)cl->cl_private; sigfillset(&newmask); thr_sigsetmask(SIG_SETMASK, &newmask, &mask); mutex_lock(&clnt_fd_lock); - while (vc_fd_locks[ct->ct_fd]) - cond_wait(&vc_cv[ct->ct_fd], &clnt_fd_lock); - if (__isthreaded) - rpc_lock_value = 1; - else - rpc_lock_value = 0; - vc_fd_locks[ct->ct_fd] = rpc_lock_value; + elem = vc_fd_find(ct->ct_fd); mutex_unlock(&clnt_fd_lock); + mutex_lock(&elem->mtx); switch (request) { case CLSET_FD_CLOSE: ct->ct_closeit = TRUE; - release_fd_lock(ct->ct_fd, mask); + release_fd_lock(elem, mask); return (TRUE); case CLSET_FD_NCLOSE: ct->ct_closeit = FALSE; - release_fd_lock(ct->ct_fd, mask); + release_fd_lock(elem, mask); return (TRUE); default: break; } /* for other requests which use info */ if (info == NULL) { - release_fd_lock(ct->ct_fd, mask); + release_fd_lock(elem, mask); return (FALSE); } switch (request) { case CLSET_TIMEOUT: if (time_not_ok((struct timeval *)info)) { - release_fd_lock(ct->ct_fd, mask); + release_fd_lock(elem, mask); return (FALSE); } ct->ct_wait = *(struct timeval *)infop; ct->ct_waitset = TRUE; break; case CLGET_TIMEOUT: *(struct timeval *)infop = ct->ct_wait; break; case CLGET_SERVER_ADDR: (void) memcpy(info, ct->ct_addr.buf, (size_t)ct->ct_addr.len); break; case CLGET_FD: *(int *)info = ct->ct_fd; break; case CLGET_SVC_ADDR: /* The caller should not free this memory area */ *(struct netbuf *)info = ct->ct_addr; break; case CLSET_SVC_ADDR: /* set to new address */ - release_fd_lock(ct->ct_fd, mask); + release_fd_lock(elem, mask); return (FALSE); case CLGET_XID: /* * use the knowledge that xid is the * first element in the call structure * This will get the xid of the PREVIOUS call */ ntohlp(info, &ct->ct_u.ct_mcalli); break; case CLSET_XID: /* This will set the xid of the NEXT call */ /* increment by 1 as clnt_vc_call() decrements once */ htonlp(&ct->ct_u.ct_mcalli, info, 1); break; case CLGET_VERS: /* * This RELIES on the information that, in the call body, * the version number field is the fifth field from the * beginning of the RPC header. MUST be changed if the * call_struct is changed */ ntohlp(info, ct->ct_u.ct_mcallc + 4 * BYTES_PER_XDR_UNIT); break; case CLSET_VERS: htonlp(ct->ct_u.ct_mcallc + 4 * BYTES_PER_XDR_UNIT, info, 0); break; case CLGET_PROG: /* * This RELIES on the information that, in the call body, * the program number field is the fourth field from the * beginning of the RPC header. MUST be changed if the * call_struct is changed */ ntohlp(info, ct->ct_u.ct_mcallc + 3 * BYTES_PER_XDR_UNIT); break; case CLSET_PROG: htonlp(ct->ct_u.ct_mcallc + 3 * BYTES_PER_XDR_UNIT, info, 0); break; default: - release_fd_lock(ct->ct_fd, mask); + release_fd_lock(elem, mask); return (FALSE); } - release_fd_lock(ct->ct_fd, mask); + release_fd_lock(elem, mask); return (TRUE); } static void clnt_vc_destroy(CLIENT *cl) { struct ct_data *ct = (struct ct_data *) cl->cl_private; + struct vc_fd *elem; int ct_fd = ct->ct_fd; sigset_t mask; sigset_t newmask; assert(cl != NULL); ct = (struct ct_data *) cl->cl_private; sigfillset(&newmask); thr_sigsetmask(SIG_SETMASK, &newmask, &mask); mutex_lock(&clnt_fd_lock); - while (vc_fd_locks[ct_fd]) - cond_wait(&vc_cv[ct_fd], &clnt_fd_lock); + elem = vc_fd_find(ct_fd); + mutex_lock(&elem->mtx); if (ct->ct_closeit && ct->ct_fd != -1) { (void)_close(ct->ct_fd); } XDR_DESTROY(&(ct->ct_xdrs)); free(ct->ct_addr.buf); mem_free(ct, sizeof(struct ct_data)); if (cl->cl_netid && cl->cl_netid[0]) mem_free(cl->cl_netid, strlen(cl->cl_netid) +1); if (cl->cl_tp && cl->cl_tp[0]) mem_free(cl->cl_tp, strlen(cl->cl_tp) +1); mem_free(cl, sizeof(CLIENT)); mutex_unlock(&clnt_fd_lock); - thr_sigsetmask(SIG_SETMASK, &(mask), NULL); - cond_signal(&vc_cv[ct_fd]); + release_fd_lock(elem, mask); } /* * Interface between xdr serializer and tcp connection. * Behaves like the system calls, read & write, but keeps some error state * around for the rpc level. */ static int read_vc(void *ctp, void *buf, int len) { struct sockaddr sa; socklen_t sal; struct ct_data *ct = (struct ct_data *)ctp; struct pollfd fd; int milliseconds = (int)((ct->ct_wait.tv_sec * 1000) + (ct->ct_wait.tv_usec / 1000)); if (len == 0) return (0); fd.fd = ct->ct_fd; fd.events = POLLIN; for (;;) { switch (_poll(&fd, 1, milliseconds)) { case 0: ct->ct_error.re_status = RPC_TIMEDOUT; return (-1); case -1: if (errno == EINTR) continue; ct->ct_error.re_status = RPC_CANTRECV; ct->ct_error.re_errno = errno; return (-1); } break; } sal = sizeof(sa); if ((_getpeername(ct->ct_fd, &sa, &sal) == 0) && (sa.sa_family == AF_LOCAL)) { len = __msgread(ct->ct_fd, buf, (size_t)len); } else { len = _read(ct->ct_fd, buf, (size_t)len); } switch (len) { case 0: /* premature eof */ ct->ct_error.re_errno = ECONNRESET; ct->ct_error.re_status = RPC_CANTRECV; len = -1; /* it's really an error */ break; case -1: ct->ct_error.re_errno = errno; ct->ct_error.re_status = RPC_CANTRECV; break; } return (len); } static int write_vc(void *ctp, void *buf, int len) { struct sockaddr sa; socklen_t sal; struct ct_data *ct = (struct ct_data *)ctp; int i, cnt; sal = sizeof(sa); if ((_getpeername(ct->ct_fd, &sa, &sal) == 0) && (sa.sa_family == AF_LOCAL)) { for (cnt = len; cnt > 0; cnt -= i, buf = (char *)buf + i) { if ((i = __msgwrite(ct->ct_fd, buf, (size_t)cnt)) == -1) { ct->ct_error.re_errno = errno; ct->ct_error.re_status = RPC_CANTSEND; return (-1); } } } else { for (cnt = len; cnt > 0; cnt -= i, buf = (char *)buf + i) { if ((i = _write(ct->ct_fd, buf, (size_t)cnt)) == -1) { ct->ct_error.re_errno = errno; ct->ct_error.re_status = RPC_CANTSEND; return (-1); } } } return (len); } static struct clnt_ops * clnt_vc_ops(void) { static struct clnt_ops ops; sigset_t mask, newmask; /* VARIABLES PROTECTED BY ops_lock: ops */ sigfillset(&newmask); thr_sigsetmask(SIG_SETMASK, &newmask, &mask); mutex_lock(&ops_lock); if (ops.cl_call == NULL) { ops.cl_call = clnt_vc_call; ops.cl_abort = clnt_vc_abort; ops.cl_geterr = clnt_vc_geterr; ops.cl_freeres = clnt_vc_freeres; ops.cl_destroy = clnt_vc_destroy; ops.cl_control = clnt_vc_control; } mutex_unlock(&ops_lock); thr_sigsetmask(SIG_SETMASK, &(mask), NULL); return (&ops); } /* * Make sure that the time is not garbage. -1 value is disallowed. * Note this is different from time_not_ok in clnt_dg.c */ static bool_t time_not_ok(struct timeval *t) { return (t->tv_sec <= -1 || t->tv_sec > 100000000 || t->tv_usec <= -1 || t->tv_usec > 1000000); } static int __msgread(int sock, void *buf, size_t cnt) { struct iovec iov[1]; struct msghdr msg; union { struct cmsghdr cmsg; char control[CMSG_SPACE(sizeof(struct cmsgcred))]; } cm; bzero((char *)&cm, sizeof(cm)); iov[0].iov_base = buf; iov[0].iov_len = cnt; msg.msg_iov = iov; msg.msg_iovlen = 1; msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_control = (caddr_t)&cm; msg.msg_controllen = CMSG_SPACE(sizeof(struct cmsgcred)); msg.msg_flags = 0; return(_recvmsg(sock, &msg, 0)); } static int __msgwrite(int sock, void *buf, size_t cnt) { struct iovec iov[1]; struct msghdr msg; union { struct cmsghdr cmsg; char control[CMSG_SPACE(sizeof(struct cmsgcred))]; } cm; bzero((char *)&cm, sizeof(cm)); iov[0].iov_base = buf; iov[0].iov_len = cnt; cm.cmsg.cmsg_type = SCM_CREDS; cm.cmsg.cmsg_level = SOL_SOCKET; cm.cmsg.cmsg_len = CMSG_LEN(sizeof(struct cmsgcred)); msg.msg_iov = iov; msg.msg_iovlen = 1; msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_control = (caddr_t)&cm; msg.msg_controllen = CMSG_SPACE(sizeof(struct cmsgcred)); msg.msg_flags = 0; return(_sendmsg(sock, &msg, 0)); } diff --git a/lib/libc/rpc/rpc_com.h b/lib/libc/rpc/rpc_com.h index ce386e540f07..40fd81499165 100644 --- a/lib/libc/rpc/rpc_com.h +++ b/lib/libc/rpc/rpc_com.h @@ -1,93 +1,92 @@ /* $NetBSD: rpc_com.h,v 1.3 2000/12/10 04:10:08 christos Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 2009, Sun Microsystems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of Sun Microsystems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * Copyright (c) 1986 - 1991 by Sun Microsystems, Inc. */ /* * rpc_com.h, Common definitions for both the server and client side. * All for the topmost layer of rpc * * In Sun's tirpc distribution, this was installed as , * but as it contains only non-exported interfaces, it was moved here. */ #ifndef _RPC_RPCCOM_H #define _RPC_RPCCOM_H /* #pragma ident "@(#)rpc_com.h 1.11 93/07/05 SMI" */ /* * The max size of the transport, if the size cannot be determined * by other means. */ #define RPC_MAXDATASIZE 9000 #define RPC_MAXADDRSIZE 1024 #define __RPC_GETXID(now) ((u_int32_t)getpid() ^ (u_int32_t)(now)->tv_sec ^ \ (u_int32_t)(now)->tv_usec) __BEGIN_DECLS extern u_int __rpc_get_a_size(int); -extern int __rpc_dtbsize(void); extern struct netconfig * __rpcgettp(int); extern int __rpc_get_default_domain(char **); char *__rpc_taddr2uaddr_af(int, const struct netbuf *); struct netbuf *__rpc_uaddr2taddr_af(int, const char *); int __rpc_fixup_addr(struct netbuf *, const struct netbuf *); int __rpc_sockinfo2netid(struct __rpc_sockinfo *, const char **); int __rpc_seman2socktype(int); int __rpc_socktype2seman(int); void *rpc_nullproc(CLIENT *); int __rpc_sockisbound(int); struct netbuf *__rpcb_findaddr_timed(rpcprog_t, rpcvers_t, const struct netconfig *, const char *host, CLIENT **clpp, struct timeval *tp); bool_t __rpc_control(int,void *); char *_get_next_token(char *, int); bool_t __svc_clean_idle(fd_set *, int, bool_t); bool_t __xdrrec_setnonblock(XDR *, int); bool_t __xdrrec_getrec(XDR *, enum xprt_stat *, bool_t); void __xprt_unregister_unlocked(SVCXPRT *); extern SVCXPRT **__svc_xports; extern int __svc_maxrec; __END_DECLS #endif /* _RPC_RPCCOM_H */ diff --git a/lib/libc/rpc/rpc_generic.c b/lib/libc/rpc/rpc_generic.c index 99e3c1263c18..5195c0dc2ac0 100644 --- a/lib/libc/rpc/rpc_generic.c +++ b/lib/libc/rpc/rpc_generic.c @@ -1,843 +1,820 @@ /* $NetBSD: rpc_generic.c,v 1.4 2000/09/28 09:07:04 kleink Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 2009, Sun Microsystems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of Sun Microsystems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * Copyright (c) 1986-1991 by Sun Microsystems Inc. */ /* #pragma ident "@(#)rpc_generic.c 1.17 94/04/24 SMI" */ /* * rpc_generic.c, Miscl routines for RPC. * */ #include "namespace.h" #include "reentrant.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "un-namespace.h" #include "rpc_com.h" #include "mt_misc.h" struct handle { NCONF_HANDLE *nhandle; int nflag; /* Whether NETPATH or NETCONFIG */ int nettype; }; static const struct _rpcnettype { const char *name; const int type; } _rpctypelist[] = { { "netpath", _RPC_NETPATH }, { "visible", _RPC_VISIBLE }, { "circuit_v", _RPC_CIRCUIT_V }, { "datagram_v", _RPC_DATAGRAM_V }, { "circuit_n", _RPC_CIRCUIT_N }, { "datagram_n", _RPC_DATAGRAM_N }, { "tcp", _RPC_TCP }, { "udp", _RPC_UDP }, { 0, _RPC_NONE } }; struct netid_af { const char *netid; int af; int protocol; }; static const struct netid_af na_cvt[] = { { "udp", AF_INET, IPPROTO_UDP }, { "tcp", AF_INET, IPPROTO_TCP }, #ifdef INET6 { "udp6", AF_INET6, IPPROTO_UDP }, { "tcp6", AF_INET6, IPPROTO_TCP }, #endif { "local", AF_LOCAL, 0 } }; #if 0 static char *strlocase(char *); #endif static int getnettype(const char *); -/* - * Cache the result of getrlimit(), so we don't have to do an - * expensive call every time. - */ -int -__rpc_dtbsize(void) -{ - static int tbsize; - struct rlimit rl; - - if (tbsize) { - return (tbsize); - } - if (getrlimit(RLIMIT_NOFILE, &rl) == 0) { - return (tbsize = (int)rl.rlim_max); - } - /* - * Something wrong. I'll try to save face by returning a - * pessimistic number. - */ - return (32); -} - /* * Find the appropriate buffer size * * size - Size requested */ u_int /*ARGSUSED*/ __rpc_get_t_size(int af, int proto, int size) { int maxsize, defsize; maxsize = 256 * 1024; /* XXX */ switch (proto) { case IPPROTO_TCP: defsize = 64 * 1024; /* XXX */ break; case IPPROTO_UDP: defsize = UDPMSGSIZE; break; default: defsize = RPC_MAXDATASIZE; break; } if (size == 0) return defsize; /* Check whether the value is within the upper max limit */ return (size > maxsize ? (u_int)maxsize : (u_int)size); } /* * Find the appropriate address buffer size */ u_int __rpc_get_a_size(int af) { switch (af) { case AF_INET: return sizeof (struct sockaddr_in); #ifdef INET6 case AF_INET6: return sizeof (struct sockaddr_in6); #endif case AF_LOCAL: return sizeof (struct sockaddr_un); default: break; } return ((u_int)RPC_MAXADDRSIZE); } #if 0 static char * strlocase(char *p) { char *t = p; for (; *p; p++) if (isupper(*p)) *p = tolower(*p); return (t); } #endif /* * Returns the type of the network as defined in * If nettype is NULL, it defaults to NETPATH. */ static int getnettype(const char *nettype) { int i; if ((nettype == NULL) || (nettype[0] == 0)) { return (_RPC_NETPATH); /* Default */ } #if 0 nettype = strlocase(nettype); #endif for (i = 0; _rpctypelist[i].name; i++) if (strcasecmp(nettype, _rpctypelist[i].name) == 0) { return (_rpctypelist[i].type); } return (_rpctypelist[i].type); } static thread_key_t tcp_key, udp_key; static once_t keys_once = ONCE_INITIALIZER; static int tcp_key_error, udp_key_error; static void keys_init(void) { tcp_key_error = thr_keycreate(&tcp_key, free); udp_key_error = thr_keycreate(&udp_key, free); } /* * For the given nettype (tcp or udp only), return the first structure found. * This should be freed by calling freenetconfigent() */ struct netconfig * __rpc_getconfip(const char *nettype) { char *netid; char *netid_tcp = (char *) NULL; char *netid_udp = (char *) NULL; static char *netid_tcp_main; static char *netid_udp_main; struct netconfig *dummy; int main_thread; if ((main_thread = thr_main())) { netid_udp = netid_udp_main; netid_tcp = netid_tcp_main; } else { if (thr_once(&keys_once, keys_init) != 0 || tcp_key_error != 0 || udp_key_error != 0) return (NULL); netid_tcp = (char *)thr_getspecific(tcp_key); netid_udp = (char *)thr_getspecific(udp_key); } if (!netid_udp && !netid_tcp) { struct netconfig *nconf; void *confighandle; if (!(confighandle = setnetconfig())) { syslog (LOG_ERR, "rpc: failed to open " NETCONFIG); return (NULL); } while ((nconf = getnetconfig(confighandle)) != NULL) { if (strcmp(nconf->nc_protofmly, NC_INET) == 0) { if (strcmp(nconf->nc_proto, NC_TCP) == 0 && netid_tcp == NULL) { netid_tcp = strdup(nconf->nc_netid); if (main_thread) netid_tcp_main = netid_tcp; else thr_setspecific(tcp_key, (void *) netid_tcp); } else if (strcmp(nconf->nc_proto, NC_UDP) == 0 && netid_udp == NULL) { netid_udp = strdup(nconf->nc_netid); if (main_thread) netid_udp_main = netid_udp; else thr_setspecific(udp_key, (void *) netid_udp); } } } endnetconfig(confighandle); } if (strcmp(nettype, "udp") == 0) netid = netid_udp; else if (strcmp(nettype, "tcp") == 0) netid = netid_tcp; else { return (NULL); } if ((netid == NULL) || (netid[0] == 0)) { return (NULL); } dummy = getnetconfigent(netid); return (dummy); } /* * Returns the type of the nettype, which should then be used with * __rpc_getconf(). */ void * __rpc_setconf(const char *nettype) { struct handle *handle; handle = (struct handle *) malloc(sizeof (struct handle)); if (handle == NULL) { return (NULL); } switch (handle->nettype = getnettype(nettype)) { case _RPC_NETPATH: case _RPC_CIRCUIT_N: case _RPC_DATAGRAM_N: if (!(handle->nhandle = setnetpath())) goto failed; handle->nflag = TRUE; break; case _RPC_VISIBLE: case _RPC_CIRCUIT_V: case _RPC_DATAGRAM_V: case _RPC_TCP: case _RPC_UDP: if (!(handle->nhandle = setnetconfig())) { syslog (LOG_ERR, "rpc: failed to open " NETCONFIG); goto failed; } handle->nflag = FALSE; break; default: goto failed; } return (handle); failed: free(handle); return (NULL); } /* * Returns the next netconfig struct for the given "net" type. * __rpc_setconf() should have been called previously. */ struct netconfig * __rpc_getconf(void *vhandle) { struct handle *handle; struct netconfig *nconf; handle = (struct handle *)vhandle; if (handle == NULL) { return (NULL); } for (;;) { if (handle->nflag) nconf = getnetpath(handle->nhandle); else nconf = getnetconfig(handle->nhandle); if (nconf == NULL) break; if ((nconf->nc_semantics != NC_TPI_CLTS) && (nconf->nc_semantics != NC_TPI_COTS) && (nconf->nc_semantics != NC_TPI_COTS_ORD)) continue; switch (handle->nettype) { case _RPC_VISIBLE: if (!(nconf->nc_flag & NC_VISIBLE)) continue; /* FALLTHROUGH */ case _RPC_NETPATH: /* Be happy */ break; case _RPC_CIRCUIT_V: if (!(nconf->nc_flag & NC_VISIBLE)) continue; /* FALLTHROUGH */ case _RPC_CIRCUIT_N: if ((nconf->nc_semantics != NC_TPI_COTS) && (nconf->nc_semantics != NC_TPI_COTS_ORD)) continue; break; case _RPC_DATAGRAM_V: if (!(nconf->nc_flag & NC_VISIBLE)) continue; /* FALLTHROUGH */ case _RPC_DATAGRAM_N: if (nconf->nc_semantics != NC_TPI_CLTS) continue; break; case _RPC_TCP: if (((nconf->nc_semantics != NC_TPI_COTS) && (nconf->nc_semantics != NC_TPI_COTS_ORD)) || (strcmp(nconf->nc_protofmly, NC_INET) #ifdef INET6 && strcmp(nconf->nc_protofmly, NC_INET6)) #else ) #endif || strcmp(nconf->nc_proto, NC_TCP)) continue; break; case _RPC_UDP: if ((nconf->nc_semantics != NC_TPI_CLTS) || (strcmp(nconf->nc_protofmly, NC_INET) #ifdef INET6 && strcmp(nconf->nc_protofmly, NC_INET6)) #else ) #endif || strcmp(nconf->nc_proto, NC_UDP)) continue; break; } break; } return (nconf); } void __rpc_endconf(void *vhandle) { struct handle *handle; handle = (struct handle *) vhandle; if (handle == NULL) { return; } if (handle->nflag) { endnetpath(handle->nhandle); } else { endnetconfig(handle->nhandle); } free(handle); } /* * Used to ping the NULL procedure for clnt handle. * Returns NULL if fails, else a non-NULL pointer. */ void * rpc_nullproc(CLIENT *clnt) { struct timeval TIMEOUT = {25, 0}; if (clnt_call(clnt, NULLPROC, (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_void, NULL, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return ((void *) clnt); } /* * Try all possible transports until * one succeeds in finding the netconf for the given fd. */ struct netconfig * __rpcgettp(int fd) { const char *netid; struct __rpc_sockinfo si; if (!__rpc_fd2sockinfo(fd, &si)) return NULL; if (!__rpc_sockinfo2netid(&si, &netid)) return NULL; /*LINTED const castaway*/ return getnetconfigent((char *)netid); } int __rpc_fd2sockinfo(int fd, struct __rpc_sockinfo *sip) { socklen_t len; int type, proto; struct sockaddr_storage ss; len = sizeof ss; if (_getsockname(fd, (struct sockaddr *)(void *)&ss, &len) < 0) return 0; sip->si_alen = len; len = sizeof type; if (_getsockopt(fd, SOL_SOCKET, SO_TYPE, &type, &len) < 0) return 0; /* XXX */ if (ss.ss_family != AF_LOCAL) { if (type == SOCK_STREAM) proto = IPPROTO_TCP; else if (type == SOCK_DGRAM) proto = IPPROTO_UDP; else return 0; } else proto = 0; sip->si_af = ss.ss_family; sip->si_proto = proto; sip->si_socktype = type; return 1; } /* * Linear search, but the number of entries is small. */ int __rpc_nconf2sockinfo(const struct netconfig *nconf, struct __rpc_sockinfo *sip) { int i; for (i = 0; i < (sizeof na_cvt) / (sizeof (struct netid_af)); i++) if (strcmp(na_cvt[i].netid, nconf->nc_netid) == 0 || ( strcmp(nconf->nc_netid, "unix") == 0 && strcmp(na_cvt[i].netid, "local") == 0)) { sip->si_af = na_cvt[i].af; sip->si_proto = na_cvt[i].protocol; sip->si_socktype = __rpc_seman2socktype((int)nconf->nc_semantics); if (sip->si_socktype == -1) return 0; sip->si_alen = __rpc_get_a_size(sip->si_af); return 1; } return 0; } int __rpc_nconf2fd(const struct netconfig *nconf) { struct __rpc_sockinfo si; if (!__rpc_nconf2sockinfo(nconf, &si)) return 0; return _socket(si.si_af, si.si_socktype, si.si_proto); } int __rpc_sockinfo2netid(struct __rpc_sockinfo *sip, const char **netid) { int i; struct netconfig *nconf; nconf = getnetconfigent("local"); for (i = 0; i < (sizeof na_cvt) / (sizeof (struct netid_af)); i++) { if (na_cvt[i].af == sip->si_af && na_cvt[i].protocol == sip->si_proto) { if (strcmp(na_cvt[i].netid, "local") == 0 && nconf == NULL) { if (netid) *netid = "unix"; } else { if (netid) *netid = na_cvt[i].netid; } if (nconf != NULL) freenetconfigent(nconf); return 1; } } if (nconf != NULL) freenetconfigent(nconf); return 0; } char * taddr2uaddr(const struct netconfig *nconf, const struct netbuf *nbuf) { struct __rpc_sockinfo si; if (!__rpc_nconf2sockinfo(nconf, &si)) return NULL; return __rpc_taddr2uaddr_af(si.si_af, nbuf); } struct netbuf * uaddr2taddr(const struct netconfig *nconf, const char *uaddr) { struct __rpc_sockinfo si; if (!__rpc_nconf2sockinfo(nconf, &si)) return NULL; return __rpc_uaddr2taddr_af(si.si_af, uaddr); } char * __rpc_taddr2uaddr_af(int af, const struct netbuf *nbuf) { char *ret; struct sockaddr_in *sin; struct sockaddr_un *sun; char namebuf[INET_ADDRSTRLEN]; #ifdef INET6 struct sockaddr_in6 *sin6; char namebuf6[INET6_ADDRSTRLEN]; #endif u_int16_t port; switch (af) { case AF_INET: if (nbuf->len < sizeof(*sin)) return NULL; sin = nbuf->buf; if (inet_ntop(af, &sin->sin_addr, namebuf, sizeof namebuf) == NULL) return NULL; port = ntohs(sin->sin_port); if (asprintf(&ret, "%s.%u.%u", namebuf, ((u_int32_t)port) >> 8, port & 0xff) < 0) return NULL; break; #ifdef INET6 case AF_INET6: if (nbuf->len < sizeof(*sin6)) return NULL; sin6 = nbuf->buf; if (inet_ntop(af, &sin6->sin6_addr, namebuf6, sizeof namebuf6) == NULL) return NULL; port = ntohs(sin6->sin6_port); if (asprintf(&ret, "%s.%u.%u", namebuf6, ((u_int32_t)port) >> 8, port & 0xff) < 0) return NULL; break; #endif case AF_LOCAL: sun = nbuf->buf; if (asprintf(&ret, "%.*s", (int)(sun->sun_len - offsetof(struct sockaddr_un, sun_path)), sun->sun_path) < 0) return (NULL); break; default: return NULL; } return ret; } struct netbuf * __rpc_uaddr2taddr_af(int af, const char *uaddr) { struct netbuf *ret = NULL; char *addrstr, *p; unsigned port, portlo, porthi; struct sockaddr_in *sin; #ifdef INET6 struct sockaddr_in6 *sin6; #endif struct sockaddr_un *sun; port = 0; sin = NULL; if (uaddr == NULL) return NULL; addrstr = strdup(uaddr); if (addrstr == NULL) return NULL; /* * AF_LOCAL addresses are expected to be absolute * pathnames, anything else will be AF_INET or AF_INET6. */ if (*addrstr != '/') { p = strrchr(addrstr, '.'); if (p == NULL) goto out; portlo = (unsigned)atoi(p + 1); *p = '\0'; p = strrchr(addrstr, '.'); if (p == NULL) goto out; porthi = (unsigned)atoi(p + 1); *p = '\0'; port = (porthi << 8) | portlo; } ret = (struct netbuf *)malloc(sizeof *ret); if (ret == NULL) goto out; switch (af) { case AF_INET: sin = (struct sockaddr_in *)malloc(sizeof *sin); if (sin == NULL) goto out; memset(sin, 0, sizeof *sin); sin->sin_family = AF_INET; sin->sin_port = htons(port); if (inet_pton(AF_INET, addrstr, &sin->sin_addr) <= 0) { free(sin); free(ret); ret = NULL; goto out; } sin->sin_len = ret->maxlen = ret->len = sizeof *sin; ret->buf = sin; break; #ifdef INET6 case AF_INET6: sin6 = (struct sockaddr_in6 *)malloc(sizeof *sin6); if (sin6 == NULL) goto out; memset(sin6, 0, sizeof *sin6); sin6->sin6_family = AF_INET6; sin6->sin6_port = htons(port); if (inet_pton(AF_INET6, addrstr, &sin6->sin6_addr) <= 0) { free(sin6); free(ret); ret = NULL; goto out; } sin6->sin6_len = ret->maxlen = ret->len = sizeof *sin6; ret->buf = sin6; break; #endif case AF_LOCAL: sun = (struct sockaddr_un *)malloc(sizeof *sun); if (sun == NULL) goto out; memset(sun, 0, sizeof *sun); sun->sun_family = AF_LOCAL; strncpy(sun->sun_path, addrstr, sizeof(sun->sun_path) - 1); ret->len = ret->maxlen = sun->sun_len = SUN_LEN(sun); ret->buf = sun; break; default: break; } out: free(addrstr); return ret; } int __rpc_seman2socktype(int semantics) { switch (semantics) { case NC_TPI_CLTS: return SOCK_DGRAM; case NC_TPI_COTS_ORD: return SOCK_STREAM; case NC_TPI_RAW: return SOCK_RAW; default: break; } return -1; } int __rpc_socktype2seman(int socktype) { switch (socktype) { case SOCK_DGRAM: return NC_TPI_CLTS; case SOCK_STREAM: return NC_TPI_COTS_ORD; case SOCK_RAW: return NC_TPI_RAW; default: break; } return -1; } /* * XXXX - IPv6 scope IDs can't be handled in universal addresses. * Here, we compare the original server address to that of the RPC * service we just received back from a call to rpcbind on the remote * machine. If they are both "link local" or "site local", copy * the scope id of the server address over to the service address. */ int __rpc_fixup_addr(struct netbuf *new, const struct netbuf *svc) { #ifdef INET6 struct sockaddr *sa_new, *sa_svc; struct sockaddr_in6 *sin6_new, *sin6_svc; sa_svc = (struct sockaddr *)svc->buf; sa_new = (struct sockaddr *)new->buf; if (sa_new->sa_family == sa_svc->sa_family && sa_new->sa_family == AF_INET6) { sin6_new = (struct sockaddr_in6 *)new->buf; sin6_svc = (struct sockaddr_in6 *)svc->buf; if ((IN6_IS_ADDR_LINKLOCAL(&sin6_new->sin6_addr) && IN6_IS_ADDR_LINKLOCAL(&sin6_svc->sin6_addr)) || (IN6_IS_ADDR_SITELOCAL(&sin6_new->sin6_addr) && IN6_IS_ADDR_SITELOCAL(&sin6_svc->sin6_addr))) { sin6_new->sin6_scope_id = sin6_svc->sin6_scope_id; } } #endif return 1; } int __rpc_sockisbound(int fd) { struct sockaddr_storage ss; socklen_t slen; slen = sizeof (struct sockaddr_storage); if (_getsockname(fd, (struct sockaddr *)(void *)&ss, &slen) < 0) return 0; switch (ss.ss_family) { case AF_INET: return (((struct sockaddr_in *) (void *)&ss)->sin_port != 0); #ifdef INET6 case AF_INET6: return (((struct sockaddr_in6 *) (void *)&ss)->sin6_port != 0); #endif case AF_LOCAL: /* XXX check this */ return (((struct sockaddr_un *) (void *)&ss)->sun_path[0] != '\0'); default: break; } return 0; } diff --git a/sys/rpc/rpc_com.h b/sys/rpc/rpc_com.h index 0206971dde2a..facd36c26a39 100644 --- a/sys/rpc/rpc_com.h +++ b/sys/rpc/rpc_com.h @@ -1,122 +1,121 @@ /* $NetBSD: rpc_com.h,v 1.3 2000/12/10 04:10:08 christos Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 2009, Sun Microsystems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of Sun Microsystems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * Copyright (c) 1986 - 1991 by Sun Microsystems, Inc. */ /* * rpc_com.h, Common definitions for both the server and client side. * All for the topmost layer of rpc * * In Sun's tirpc distribution, this was installed as , * but as it contains only non-exported interfaces, it was moved here. */ #ifndef _RPC_RPCCOM_H #define _RPC_RPCCOM_H #include /* #pragma ident "@(#)rpc_com.h 1.11 93/07/05 SMI" */ /* * The max size of the transport, if the size cannot be determined * by other means. */ #define RPC_MAXDATASIZE 9000 #define RPC_MAXADDRSIZE 1024 #ifdef _KERNEL #define __RPC_GETXID(now) ((uint32_t)curproc->p_pid ^ (uint32_t)(now)->tv_sec ^ \ (uint32_t)(now)->tv_usec) #else #define __RPC_GETXID(now) ((uint32_t)getpid() ^ (uint32_t)(now)->tv_sec ^ \ (uint32_t)(now)->tv_usec) #endif __BEGIN_DECLS #ifndef _KERNEL extern u_int __rpc_get_a_size(int); -extern int __rpc_dtbsize(void); extern struct netconfig * __rpcgettp(int); extern int __rpc_get_default_domain(char **); char *__rpc_taddr2uaddr_af(int, const struct netbuf *); struct netbuf *__rpc_uaddr2taddr_af(int, const char *); int __rpc_fixup_addr(struct netbuf *, const struct netbuf *); int __rpc_sockinfo2netid(struct __rpc_sockinfo *, const char **); int __rpc_seman2socktype(int); int __rpc_socktype2seman(int); void *rpc_nullproc(CLIENT *); int __rpc_sockisbound(int); struct netbuf *__rpcb_findaddr_timed(rpcprog_t, rpcvers_t, const struct netconfig *, const char *host, CLIENT **clpp, struct timeval *tp); bool_t __rpc_control(int,void *); char *_get_next_token(char *, int); bool_t __svc_clean_idle(fd_set *, int, bool_t); bool_t __xdrrec_setnonblock(XDR *, int); bool_t __xdrrec_getrec(XDR *, enum xprt_stat *, bool_t); void __xprt_unregister_unlocked(SVCXPRT *); SVCXPRT **__svc_xports; int __svc_maxrec; #else #define SUN_LEN(su) \ (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path)) extern u_int __rpc_get_a_size(int); extern char *__rpc_taddr2uaddr_af(int, const struct netbuf *); extern struct netbuf *__rpc_uaddr2taddr_af(int, const char *); extern int __rpc_seman2socktype(int); extern int __rpc_socktype2seman(int); extern int __rpc_sockisbound(struct socket*); extern int bindresvport(struct socket *so, struct sockaddr *sa); struct xucred; struct XDR; bool_t xdr_authunix_parms(struct XDR *xdrs, uint32_t *time, struct xucred *cred); #endif __END_DECLS #endif /* _RPC_RPCCOM_H */