commit 8f6d9dae92449b59bdafcb7777bc32f1b2726e48 Author: Marcel Telka Date: Sun Mar 30 12:47:12 2014 +0200 4575 Single threaded rpcbind is not scalable 4483 rpcbind: Reply for remote calls comes from incorrect UDP port Reviewed by: Ilya Usvyatsky Reviewed by: Jan Kryl Reviewed by: Michael Schuster Reviewed by: Gary Mills Reviewed by: Jerry Jelinek Approved by: Robert Mustacchi diff --git a/usr/src/cmd/rpcbind/Makefile b/usr/src/cmd/rpcbind/Makefile index 1e77d8e..8b6ffae 100644 --- a/usr/src/cmd/rpcbind/Makefile +++ b/usr/src/cmd/rpcbind/Makefile @@ -22,6 +22,8 @@ # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# Copyright 2013 Nexenta Systems, Inc. All rights reserved. +# PROG= rpcbind MANIFEST= bind.xml @@ -41,11 +43,7 @@ ROOTMANIFESTDIR= $(ROOTSVCNETWORKRPC) CPPFLAGS= -I. -DPORTMAP $(CPPFLAGS.master) CPPFLAGS += -D_REENTRANT -CERRWARN += -_gcc=-Wno-implicit-function-declaration -CERRWARN += -_gcc=-Wno-unused-variable -CERRWARN += -_gcc=-Wno-uninitialized -CERRWARN += -_gcc=-Wno-parentheses -CERRWARN += -_gcc=-Wno-unused-label +$(RELEASE_BUILD)CPPFLAGS += -DNDEBUG LDLIBS += -lsocket -lnsl -lwrap -lscf .KEEP_STATE: diff --git a/usr/src/cmd/rpcbind/bind.xml b/usr/src/cmd/rpcbind/bind.xml index 6efc234..61c5acf 100644 --- a/usr/src/cmd/rpcbind/bind.xml +++ b/usr/src/cmd/rpcbind/bind.xml @@ -24,6 +24,8 @@ Copyright 2009 Sun Microsystems, Inc. All rights reserved. Use is subject to license terms. + Copyright 2014 Nexenta Systems, Inc. All rights reserved. + Service manifest for rpcbind NOTE: This service manifest is not editable; its contents will @@ -185,6 +187,16 @@ + + + + diff --git a/usr/src/cmd/rpcbind/check_bound.c b/usr/src/cmd/rpcbind/check_bound.c index 5e1d5dc..1841377 100644 --- a/usr/src/cmd/rpcbind/check_bound.c +++ b/usr/src/cmd/rpcbind/check_bound.c @@ -22,6 +22,9 @@ * Copyright 2002 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ +/* + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ @@ -35,8 +38,6 @@ * contributors. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * check_bound.c * Checks to see whether the program is still bound to the @@ -56,9 +57,13 @@ #include #include #include +#include +#include +#include struct fdlist { int fd; + mutex_t fd_lock; /* protects fd */ struct netconfig *nconf; struct fdlist *next; int check_binding; @@ -72,11 +77,12 @@ static char *nullstring = ""; * Returns 1 if the given address is bound for the given addr & transport * For all error cases, we assume that the address is bound * Returns 0 for success. + * + * fdl: My FD list + * uaddr: the universal address */ static bool_t -check_bound(fdl, uaddr) - struct fdlist *fdl; /* My FD list */ - char *uaddr; /* the universal address */ +check_bound(struct fdlist *fdl, char *uaddr) { int fd; struct netbuf *na; @@ -90,22 +96,22 @@ check_bound(fdl, uaddr) if (!na) return (TRUE); /* punt, should never happen */ - fd = fdl->fd; taddr.addr = *na; taddr.qlen = 1; + (void) mutex_lock(&fdl->fd_lock); + fd = fdl->fd; baddr = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR); if (baddr == NULL) { + (void) mutex_unlock(&fdl->fd_lock); netdir_free((char *)na, ND_ADDR); return (TRUE); } if (t_bind(fd, &taddr, baddr) != 0) { + (void) mutex_unlock(&fdl->fd_lock); netdir_free((char *)na, ND_ADDR); (void) t_free((char *)baddr, T_BIND); return (TRUE); } - ans = memcmp(taddr.addr.buf, baddr->addr.buf, baddr->addr.len); - netdir_free((char *)na, ND_ADDR); - (void) t_free((char *)baddr, T_BIND); if (t_unbind(fd) != 0) { /* Bad fd. Purge this fd */ (void) t_close(fd); @@ -113,6 +119,10 @@ check_bound(fdl, uaddr) if (fdl->fd == -1) fdl->check_binding = FALSE; } + (void) mutex_unlock(&fdl->fd_lock); + ans = memcmp(taddr.addr.buf, baddr->addr.buf, baddr->addr.len); + netdir_free((char *)na, ND_ADDR); + (void) t_free((char *)baddr, T_BIND); return (ans == 0 ? FALSE : TRUE); } @@ -127,15 +137,13 @@ check_bound(fdl, uaddr) * 1. Is it possible for t_bind to fail in the case where * we bind to an already bound address and have any * other error number besides TNOADDR. - * 2. If a address is specified in bind addr, can I bind to + * 2. If an address is specified in bind addr, can I bind to * the same address. * 3. If NULL is specified in bind addr, can I bind to the * address to which the fd finally got bound. */ int -add_bndlist(nconf, taddr, baddr) - struct netconfig *nconf; - struct t_bind *taddr, *baddr; +add_bndlist(struct netconfig *nconf, struct t_bind *taddr, struct t_bind *baddr) { int fd; struct fdlist *fdl; @@ -152,6 +160,7 @@ add_bndlist(nconf, taddr, baddr) syslog(LOG_ERR, "no memory!"); return (-1); } + (void) mutex_init(&fdl->fd_lock, USYNC_THREAD, NULL); fdl->nconf = newnconf; fdl->next = NULL; if (fdhead == NULL) { @@ -196,17 +205,14 @@ add_bndlist(nconf, taddr, baddr) /* Perhaps condition #1 */ if (debugging) { fprintf(stderr, "%s: add_bndlist cannot bind (1): %s", - nconf->nc_netid, t_errlist[t_errno]); + nconf->nc_netid, t_errlist[t_errno]); } goto not_bound; } /* Condition #2 */ if (!memcmp(taddr->addr.buf, baddr->addr.buf, - (int)baddr->addr.len)) { -#ifdef BIND_DEBUG - fprintf(stderr, "Condition #2\n"); -#endif + (int)baddr->addr.len)) { goto not_bound; } @@ -229,7 +235,7 @@ add_bndlist(nconf, taddr, baddr) if (t_bind(fdl->fd, &tmpaddr, taddr) != 0) { if (debugging) { fprintf(stderr, "%s: add_bndlist cannot bind (2): %s", - nconf->nc_netid, t_errlist[t_errno]); + nconf->nc_netid, t_errlist[t_errno]); } goto error; } @@ -237,8 +243,8 @@ add_bndlist(nconf, taddr, baddr) if ((fd = t_open(nconf->nc_device, O_RDWR, &tinfo)) < 0) { if (debugging) { fprintf(stderr, - "%s: add_bndlist cannot open connection: %s", - nconf->nc_netid, t_errlist[t_errno]); + "%s: add_bndlist cannot open connection: %s", + nconf->nc_netid, t_errlist[t_errno]); } goto error; } @@ -253,32 +259,27 @@ add_bndlist(nconf, taddr, baddr) * we'll just assume we can't do bind checking with * this transport. */ + t_close(fd); goto not_bound; } if (debugging) { fprintf(stderr, "%s: add_bndlist cannot bind (3): %s", - nconf->nc_netid, t_errlist[t_errno]); + nconf->nc_netid, t_errlist[t_errno]); } t_close(fd); goto error; } t_close(fd); if (!memcmp(taddr->addr.buf, baddr->addr.buf, - (int)baddr->addr.len)) { + (int)baddr->addr.len)) { switch (tinfo.servtype) { case T_COTS: case T_COTS_ORD: if (baddr->qlen == 1) { -#ifdef BIND_DEBUG - fprintf(stderr, "Condition #3\n"); -#endif goto not_bound; } break; case T_CLTS: -#ifdef BIND_DEBUG - fprintf(stderr, "Condition #3\n"); -#endif goto not_bound; default: goto error; @@ -301,9 +302,7 @@ error: } bool_t -is_bound(netid, uaddr) - char *netid; - char *uaddr; +is_bound(char *netid, char *uaddr) { struct fdlist *fdl; @@ -325,11 +324,7 @@ is_bound(netid, uaddr) * Returns the merged address otherwise. */ char * -mergeaddr(xprt, netid, uaddr, saddr) - SVCXPRT *xprt; - char *netid; - char *uaddr; - char *saddr; +mergeaddr(SVCXPRT *xprt, char *netid, char *uaddr, char *saddr) { struct fdlist *fdl; struct nd_mergearg ma; @@ -356,37 +351,22 @@ mergeaddr(xprt, netid, uaddr, saddr) ma.c_uaddr = taddr2uaddr(fdl->nconf, svc_getrpccaller(xprt)); if (ma.c_uaddr == NULL) { syslog(LOG_ERR, "taddr2uaddr failed for %s: %s", - fdl->nconf->nc_netid, netdir_sperror()); + fdl->nconf->nc_netid, netdir_sperror()); return (NULL); } } -#ifdef ND_DEBUG - if (saddr == NULL) { - fprintf(stderr, "mergeaddr: client uaddr = %s\n", ma.c_uaddr); - } else { - fprintf(stderr, "mergeaddr: contact uaddr = %s\n", ma.c_uaddr); - } -#endif - /* Not an INET adaress? */ + /* Not an INET address? */ if ((strcmp(fdl->nconf->nc_protofmly, NC_INET) != 0) && - (strcmp(fdl->nconf->nc_protofmly, NC_INET6) != 0)) { + (strcmp(fdl->nconf->nc_protofmly, NC_INET6) != 0)) { ma.s_uaddr = uaddr; -#ifdef ND_DEBUG - fprintf(stderr, "mergeaddr: Call to the original" - " ND_MERGEADDR interface\n"); -#endif stat = netdir_options(fdl->nconf, ND_MERGEADDR, 0, (char *)&ma); } /* Inet address, but no xp_ltaddr */ else if ((ma.s_uaddr = taddr2uaddr(fdl->nconf, - &(xprt)->xp_ltaddr)) == NULL) { + &(xprt)->xp_ltaddr)) == NULL) { ma.s_uaddr = uaddr; -#ifdef ND_DEBUG - fprintf(stderr, "mergeaddr: Call to the original" - " ND_MERGEADDR interface\n"); -#endif stat = netdir_options(fdl->nconf, ND_MERGEADDR, 0, (char *)&ma); } else { /* @@ -418,24 +398,16 @@ mergeaddr(xprt, netid, uaddr, saddr) strcat(ma.m_uaddr, uport); free(ma.s_uaddr); stat = 0; - -#ifdef ND_DEBUG - fprintf(stderr, "mergeaddr: Just return the address which was" - " used for contacting us\n"); -#endif } if (saddr == NULL) { free(ma.c_uaddr); } if (stat) { syslog(LOG_ERR, "netdir_merge failed for %s: %s", - fdl->nconf->nc_netid, netdir_sperror()); + fdl->nconf->nc_netid, netdir_sperror()); return (NULL); } -#ifdef ND_DEBUG - fprintf(stderr, "mergeaddr: uaddr = %s, merged uaddr = %s\n", - uaddr, ma.m_uaddr); -#endif + return (ma.m_uaddr); } @@ -444,8 +416,7 @@ mergeaddr(xprt, netid, uaddr, saddr) * structure should not be freed. */ struct netconfig * -rpcbind_get_conf(netid) - char *netid; +rpcbind_get_conf(char *netid) { struct fdlist *fdl; @@ -456,16 +427,3 @@ rpcbind_get_conf(netid) return (NULL); return (fdl->nconf); } - -#ifdef BIND_DEBUG -syslog(a, msg, b, c, d) - int a; - char *msg; - caddr_t b, c, d; -{ - char buf[1024]; - - sprintf(buf, msg, b, c, d); - fprintf(stderr, "Syslog: %s\n", buf); -} -#endif diff --git a/usr/src/cmd/rpcbind/pmap_svc.c b/usr/src/cmd/rpcbind/pmap_svc.c index 09df64c..b5b3100 100644 --- a/usr/src/cmd/rpcbind/pmap_svc.c +++ b/usr/src/cmd/rpcbind/pmap_svc.c @@ -23,6 +23,9 @@ * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ +/* + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* @@ -35,53 +38,46 @@ * contributors. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * pmap_svc.c * The server procedure for the version 2 portmaper. * All the portmapper related interface from the portmap side. */ +#include +#include + +#include "rpcbind.h" + #ifdef PORTMAP #include #include #include -#include #include #include -#include "rpcbind.h" +#include -#ifdef RPCBIND_DEBUG -#include -#endif - -static PMAPLIST *find_service_pmap(); -static bool_t pmapproc_change(); -static bool_t pmapproc_getport(); -static bool_t pmapproc_dump(); +static bool_t pmapproc_change(struct svc_req *, SVCXPRT *, unsigned long); +static bool_t pmapproc_getport(struct svc_req *, SVCXPRT *); +static bool_t pmapproc_dump(struct svc_req *, SVCXPRT *); /* * Called for all the version 2 inquiries. */ void -pmap_service(rqstp, xprt) - register struct svc_req *rqstp; - register SVCXPRT *xprt; +pmap_service(struct svc_req *rqstp, SVCXPRT *xprt) { rpcbs_procinfo(RPCBVERS_2_STAT, rqstp->rq_proc); + switch (rqstp->rq_proc) { case PMAPPROC_NULL: /* * Null proc call */ -#ifdef RPCBIND_DEBUG - fprintf(stderr, "PMAPPROC_NULL\n"); -#endif PMAP_CHECK(xprt, rqstp->rq_proc); if ((!svc_sendreply(xprt, (xdrproc_t)xdr_void, NULL)) && - debugging) { + debugging) { if (doabort) { rpcbind_abort(); } @@ -114,9 +110,6 @@ pmap_service(rqstp, xprt) /* * Return the current set of mapped program, version */ -#ifdef RPCBIND_DEBUG - fprintf(stderr, "PMAPPROC_DUMP\n"); -#endif PMAP_CHECK(xprt, rqstp->rq_proc); pmapproc_dump(rqstp, xprt); break; @@ -149,52 +142,40 @@ pmap_service(rqstp, xprt) * use those program version numbers. */ static PMAPLIST * -find_service_pmap(prog, vers, prot) - ulong_t prog; - ulong_t vers; - ulong_t prot; +find_service_pmap(rpcprog_t prog, rpcvers_t vers, rpcprot_t prot) { - register PMAPLIST *hit = NULL; - register PMAPLIST *pml; + PMAPLIST *hit = NULL; + PMAPLIST *pml; + + assert(RW_LOCK_HELD(&list_pml_lock)); for (pml = list_pml; pml != NULL; pml = pml->pml_next) { if ((pml->pml_map.pm_prog != prog) || - (pml->pml_map.pm_prot != prot)) + (pml->pml_map.pm_prot != prot)) continue; hit = pml; if (pml->pml_map.pm_vers == vers) break; } + return (hit); } -extern char *getowner(SVCXPRT *xprt, char *); - -/*ARGSUSED*/ +/* ARGSUSED */ static bool_t -pmapproc_change(rqstp, xprt, op) - struct svc_req *rqstp; - register SVCXPRT *xprt; - unsigned long op; +pmapproc_change(struct svc_req *rqstp, SVCXPRT *xprt, unsigned long op) { PMAP reg; RPCB rpcbreg; int ans; struct sockaddr_in *who; - extern bool_t map_set(), map_unset(); char owner[64]; if (!svc_getargs(xprt, (xdrproc_t)xdr_pmap, (char *)®)) { svcerr_decode(xprt); return (FALSE); } - who = svc_getcaller(xprt); - -#ifdef RPCBIND_DEBUG - fprintf(stderr, "%s request for (%lu, %lu) : ", - op == PMAPPROC_SET ? "PMAP_SET" : "PMAP_UNSET", - reg.pm_prog, reg.pm_vers); -#endif + who = (struct sockaddr_in *)svc_getrpccaller(xprt)->buf; /* Don't allow unset/set from remote. */ if (!localxprt(xprt, B_TRUE)) { @@ -216,7 +197,7 @@ pmapproc_change(rqstp, xprt, op) char buf[32]; sprintf(buf, "0.0.0.0.%d.%d", (reg.pm_port >> 8) & 0xff, - reg.pm_port & 0xff); + reg.pm_port & 0xff); rpcbreg.r_addr = buf; if (reg.pm_prot == IPPROTO_UDP) { rpcbreg.r_netid = udptrans; @@ -249,9 +230,6 @@ done_change: rpcbind_abort(); } } -#ifdef RPCBIND_DEBUG - fprintf(stderr, "%s\n", ans == TRUE ? "succeeded" : "failed"); -#endif if (op == PMAPPROC_SET) rpcbs_set(RPCBVERS_2_STAT, ans); else @@ -261,16 +239,12 @@ done_change: /* ARGSUSED */ static bool_t -pmapproc_getport(rqstp, xprt) - struct svc_req *rqstp; - register SVCXPRT *xprt; +pmapproc_getport(struct svc_req *rqstp, SVCXPRT *xprt) { PMAP reg; int port = 0; PMAPLIST *fnd; -#ifdef RPCBIND_DEBUG - char *uaddr; -#endif + bool_t rbl_locked = FALSE; if (!svc_getargs(xprt, (xdrproc_t)xdr_pmap, (char *)®)) { svcerr_decode(xprt); @@ -278,14 +252,8 @@ pmapproc_getport(rqstp, xprt) } PMAP_CHECK_RET(xprt, rqstp->rq_proc, FALSE); -#ifdef RPCBIND_DEBUG - uaddr = taddr2uaddr(rpcbind_get_conf(xprt->xp_netid), - svc_getrpccaller(xprt)); - fprintf(stderr, "PMAP_GETPORT request for (%lu, %lu, %s) from %s :", - reg.pm_prog, reg.pm_vers, - reg.pm_prot == IPPROTO_UDP ? "udp" : "tcp", uaddr); - free(uaddr); -#endif + (void) rw_rdlock(&list_pml_lock); +retry: fnd = find_service_pmap(reg.pm_prog, reg.pm_vers, reg.pm_prot); if (fnd) { char serveuaddr[32], *ua; @@ -300,58 +268,74 @@ pmapproc_getport(rqstp, xprt) netid = tcptrans; } if (ua == NULL) { + (void) rw_unlock(&list_pml_lock); + if (rbl_locked) + (void) rw_unlock(&list_rbl_lock); goto sendreply; } if (sscanf(ua, "%d.%d.%d.%d.%d.%d", &h1, &h2, &h3, - &h4, &p1, &p2) == 6) { + &h4, &p1, &p2) == 6) { p1 = (fnd->pml_map.pm_port >> 8) & 0xff; p2 = (fnd->pml_map.pm_port) & 0xff; sprintf(serveuaddr, "%d.%d.%d.%d.%d.%d", - h1, h2, h3, h4, p1, p2); + h1, h2, h3, h4, p1, p2); if (is_bound(netid, serveuaddr)) { port = fnd->pml_map.pm_port; } else { /* this service is dead; delete it */ + if (!rbl_locked) { + (void) rw_unlock(&list_pml_lock); + (void) rw_wrlock(&list_rbl_lock); + (void) rw_wrlock(&list_pml_lock); + rbl_locked = TRUE; + goto retry; + } delete_prog(reg.pm_prog); } } } + (void) rw_unlock(&list_pml_lock); + if (rbl_locked) + (void) rw_unlock(&list_rbl_lock); + sendreply: if ((!svc_sendreply(xprt, (xdrproc_t)xdr_long, (caddr_t)&port)) && - debugging) { + debugging) { (void) fprintf(stderr, "portmap: svc_sendreply\n"); if (doabort) { rpcbind_abort(); } } -#ifdef RPCBIND_DEBUG - fprintf(stderr, "port = %d\n", port); -#endif rpcbs_getaddr(RPCBVERS_2_STAT, reg.pm_prog, reg.pm_vers, - reg.pm_prot == IPPROTO_UDP ? udptrans : tcptrans, - port ? udptrans : ""); + reg.pm_prot == IPPROTO_UDP ? udptrans : tcptrans, + port ? udptrans : ""); return (TRUE); } /* ARGSUSED */ static bool_t -pmapproc_dump(rqstp, xprt) - struct svc_req *rqstp; - register SVCXPRT *xprt; +pmapproc_dump(struct svc_req *rqstp, SVCXPRT *xprt) { if (!svc_getargs(xprt, (xdrproc_t)xdr_void, NULL)) { svcerr_decode(xprt); return (FALSE); } + + (void) rw_rdlock(&list_pml_lock); if ((!svc_sendreply(xprt, (xdrproc_t)xdr_pmaplist_ptr, - (caddr_t)&list_pml)) && debugging) { + (caddr_t)&list_pml)) && debugging) { + (void) rw_unlock(&list_pml_lock); (void) fprintf(stderr, "portmap: svc_sendreply\n"); if (doabort) { rpcbind_abort(); } + } else { + (void) rw_unlock(&list_pml_lock); } + return (TRUE); } +#endif /* PORTMAP */ /* * Is the transport local? The original rpcbind code tried to @@ -385,4 +369,3 @@ localxprt(SVCXPRT *transp, boolean_t forceipv4) */ return (rpcb_caller_uid(transp) != -1); } -#endif /* PORTMAP */ diff --git a/usr/src/cmd/rpcbind/rpcb_check.c b/usr/src/cmd/rpcbind/rpcb_check.c index 10aade0..150f8b4 100644 --- a/usr/src/cmd/rpcbind/rpcb_check.c +++ b/usr/src/cmd/rpcbind/rpcb_check.c @@ -45,8 +45,9 @@ * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ - -#pragma ident "%Z%%M% %I% %E% SMI" +/* + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + */ #include #include @@ -62,6 +63,9 @@ #include #include #include +#include +#include +#include #include "rpcbind.h" @@ -72,55 +76,55 @@ int allow_severity = LOG_INFO; int deny_severity = LOG_WARNING; -extern int hosts_ctl(); +static mutex_t hosts_ctl_lock = DEFAULTMUTEX; /* * "inet_ntoa/inet_pton" for struct sockaddr_gen */ static const char * -sgen_toa(struct sockaddr_gen *addr) +sgen_toa(struct sockaddr_gen *addr, char *buf, size_t bufsize) { - static char buf[INET6_ADDRSTRLEN]; - return (inet_ntop(SGFAM(addr), SGADDRP(addr), buf, sizeof (buf))); + return (inet_ntop(SGFAM(addr), SGADDRP(addr), buf, bufsize)); } +struct proc_map { + rpcproc_t code; + const char *proc; +}; + +static const struct proc_map pmapmap[] = { + PMAPPROC_CALLIT, "callit", + PMAPPROC_DUMP, "dump", + PMAPPROC_GETPORT, "getport", + PMAPPROC_SET, "set", + PMAPPROC_UNSET, "unset", + NULLPROC, "null", +}; + +static const struct proc_map rpcbmap[] = { + RPCBPROC_SET, "set", + RPCBPROC_UNSET, "unset", + RPCBPROC_GETADDR, "getaddr", + RPCBPROC_DUMP, "dump", + RPCBPROC_CALLIT, "callit", + RPCBPROC_GETTIME, "gettime", + RPCBPROC_UADDR2TADDR, "uaddr2taddr", + RPCBPROC_TADDR2UADDR, "taddr2uaddr", + RPCBPROC_GETVERSADDR, "getversaddr", + RPCBPROC_INDIRECT, "indirect", + RPCBPROC_GETADDRLIST, "getaddrlist", + RPCBPROC_GETSTAT, "getstat", + NULLPROC, "null", +}; + /* * find_procname - map rpcb/pmap procedure number to name */ static const char * find_procname(rpcproc_t procnum, boolean_t pm) { - static char procbuf[6 + 3 * sizeof (ulong_t)]; - struct proc_map { - rpcproc_t code; - const char *proc; - }; - static struct proc_map pmapmap[] = { - PMAPPROC_CALLIT, "callit", - PMAPPROC_DUMP, "dump", - PMAPPROC_GETPORT, "getport", - PMAPPROC_SET, "set", - PMAPPROC_UNSET, "unset", - NULLPROC, "null", - }; - static struct proc_map rpcbmap[] = { - RPCBPROC_SET, "set", - RPCBPROC_UNSET, "unset", - RPCBPROC_GETADDR, "getaddr", - RPCBPROC_DUMP, "dump", - RPCBPROC_CALLIT, "callit", - RPCBPROC_GETTIME, "gettime", - RPCBPROC_UADDR2TADDR, "uaddr2taddr", - RPCBPROC_TADDR2UADDR, "taddr2uaddr", - RPCBPROC_GETVERSADDR, "getversaddr", - RPCBPROC_INDIRECT, "indirect", - RPCBPROC_GETADDRLIST, "getaddrlist", - RPCBPROC_GETSTAT, "getstat", - NULLPROC, "null", - }; - int nitems, i; - struct proc_map *procp; + const struct proc_map *procp; if (pm) { procp = pmapmap; @@ -134,30 +138,7 @@ find_procname(rpcproc_t procnum, boolean_t pm) if (procp[i].code == procnum) return (procp[i].proc); } - (void) snprintf(procbuf, sizeof (procbuf), "%s-%lu", - pm ? "pmap" : "rpcb", (ulong_t)procnum); - return (procbuf); -} - -/* - * find_progname - map rpc program number to name. - */ -static const char * -find_progname(rpcprog_t prognum) -{ - static char progbuf[1 + 3 * sizeof (ulong_t)]; - - if (prognum == 0) - return (""); - - /* - * The original code contained a call to "getrpcbynumber()"; - * this call was removed because it may cause a call to a - * nameservice. - */ - - (void) snprintf(progbuf, sizeof (progbuf), "%lu", (ulong_t)prognum); - return (progbuf); + return (NULL); } /* @@ -171,6 +152,8 @@ rpcb_log(boolean_t verdict, SVCXPRT *transp, rpcproc_t proc, rpcprog_t prog, const char *client = "unknown"; char *uaddr; char buf[BUFSIZ]; + char toabuf[INET6_ADDRSTRLEN]; + const char *procname; /* * Transform the transport address into something printable. @@ -180,7 +163,8 @@ rpcb_log(boolean_t verdict, SVCXPRT *transp, rpcproc_t proc, rpcprog_t prog, "unknown transport (rpcbind_get_conf failed)"); } else if (strcmp(conf->nc_protofmly, "inet") == 0 || strcmp(conf->nc_protofmly, "inet6") == 0) { - client = sgen_toa(svc_getgencaller(transp)); + client = sgen_toa(svc_getgencaller(transp), toabuf, + sizeof (toabuf)); } else if ((uaddr = taddr2uaddr(conf, &(transp->xp_rtaddr))) == NULL) { syslog(LOG_WARNING, "unknown address (taddr2uaddr failed)"); } else { @@ -189,9 +173,17 @@ rpcb_log(boolean_t verdict, SVCXPRT *transp, rpcproc_t proc, rpcprog_t prog, free(uaddr); client = buf; } - qsyslog(verdict ? allow_severity : deny_severity, - "%sconnect from %s to %s(%s)", verdict ? "" : "refused ", - client, find_procname(proc, pm), find_progname(prog)); + + if ((procname = find_procname(proc, pm)) == NULL) { + qsyslog(verdict ? allow_severity : deny_severity, + "%sconnect from %s to %s-%lu(%lu)", + verdict ? "" : "refused ", client, pm ? "pmap" : "rpcb", + (ulong_t)proc, (ulong_t)prog); + } else { + qsyslog(verdict ? allow_severity : deny_severity, + "%sconnect from %s to %s(%lu)", verdict ? "" : "refused ", + client, procname, (ulong_t)prog); + } } /* @@ -216,15 +208,24 @@ rpcb_check(SVCXPRT *transp, rpcproc_t procnum, boolean_t ispmap) res = B_FALSE; } else if (strcmp(conf->nc_protofmly, "inet") == 0 || strcmp(conf->nc_protofmly, "inet6") == 0) { - const char *addr_string = sgen_toa(svc_getgencaller(transp)); - - if (!localxprt(transp, ispmap) && - (local_only || - hosts_ctl("rpcbind", addr_string, addr_string, "") == 0)) { - res = B_FALSE; + if (!localxprt(transp, ispmap)) { + if (local_only) { + res = B_FALSE; + } else { + char buf[INET6_ADDRSTRLEN]; + const char *addr_string = + sgen_toa(svc_getgencaller(transp), buf, + sizeof (buf)); + + (void) mutex_lock(&hosts_ctl_lock); + if (hosts_ctl("rpcbind", addr_string, + addr_string, "") == 0) + res = B_FALSE; + (void) mutex_unlock(&hosts_ctl_lock); + } } } -out: + if (!res) svcerr_auth(transp, AUTH_FAILED); diff --git a/usr/src/cmd/rpcbind/rpcb_stat.c b/usr/src/cmd/rpcbind/rpcb_stat.c index 634b448..ed83150 100644 --- a/usr/src/cmd/rpcbind/rpcb_stat.c +++ b/usr/src/cmd/rpcbind/rpcb_stat.c @@ -25,6 +25,9 @@ * * Copyright (c) 1990 by Sun Microsystems, Inc. */ +/* + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + */ #include #include @@ -35,98 +38,103 @@ #include #endif #include +#include +#include +#include +#include +#include #include "rpcbind.h" static rpcb_stat_byvers inf; +static rwlock_t inf_lock = DEFAULTRWLOCK; void -rpcbs_init() +rpcbs_procinfo(int rtype, rpcproc_t proc) { + assert(rtype >= 0 && rtype < RPCBVERS_STAT); -} - -void -rpcbs_procinfo(rtype, proc) - u_long rtype; - u_long proc; -{ - switch (rtype + 2) { #ifdef PORTMAP - case PMAPVERS: /* version 2 */ - if (proc > rpcb_highproc_2) - return; - break; + if ((rtype == RPCBVERS_2_STAT) && (proc > rpcb_highproc_2)) + return; +#else + assert(rtype != RPCBVERS_2_STAT); #endif - case RPCBVERS: /* version 3 */ - if (proc > rpcb_highproc_3) - return; - break; - case RPCBVERS4: /* version 4 */ - if (proc > rpcb_highproc_4) - return; - break; - default: return; - } - inf[rtype].info[proc]++; - return; + + if ((rtype == RPCBVERS_3_STAT) && (proc > rpcb_highproc_3)) + return; + + if ((rtype == RPCBVERS_4_STAT) && (proc > rpcb_highproc_4)) + return; + + atomic_add_int((uint_t *)&inf[rtype].info[proc], 1); } void -rpcbs_set(rtype, success) - u_long rtype; - bool_t success; +rpcbs_set(int rtype, bool_t success) { - if ((rtype >= RPCBVERS_STAT) || (success == FALSE)) + assert(rtype >= 0 && rtype < RPCBVERS_STAT); + + if (success == FALSE) return; - inf[rtype].setinfo++; - return; + + atomic_add_int((uint_t *)&inf[rtype].setinfo, 1); } void -rpcbs_unset(rtype, success) - u_long rtype; - bool_t success; +rpcbs_unset(int rtype, bool_t success) { - if ((rtype >= RPCBVERS_STAT) || (success == FALSE)) + assert(rtype >= 0 && rtype < RPCBVERS_STAT); + + if (success == FALSE) return; - inf[rtype].unsetinfo++; - return; + + atomic_add_int((uint_t *)&inf[rtype].unsetinfo, 1); } void -rpcbs_getaddr(rtype, prog, vers, netid, uaddr) - u_long rtype; - u_long prog; - u_long vers; - char *netid; - char *uaddr; +rpcbs_getaddr(int rtype, rpcprog_t prog, rpcvers_t vers, char *netid, + char *uaddr) { rpcbs_addrlist *al; + rpcbs_addrlist *s; + rpcbs_addrlist *wal; struct netconfig *nconf; - if (rtype >= RPCBVERS_STAT) - return; - for (al = inf[rtype].addrinfo; al; al = al->next) { + assert(rtype >= 0 && rtype < RPCBVERS_STAT); - if(al->netid == NULL) - return; + /* + * First try with read lock only. + */ + (void) rw_rdlock(&inf_lock); + for (s = al = inf[rtype].addrinfo; al; al = al->next) { if ((al->prog == prog) && (al->vers == vers) && (strcmp(al->netid, netid) == 0)) { + (void) rw_unlock(&inf_lock); + if ((uaddr == NULL) || (uaddr[0] == NULL)) - al->failure++; + atomic_add_int((uint_t *)&al->failure, 1); else - al->success++; + atomic_add_int((uint_t *)&al->success, 1); + return; } } + (void) rw_unlock(&inf_lock); + + /* + * If not found, we will likely need to add a new entry, + * so pre-allocate it, and then try to search again with write lock. + */ nconf = rpcbind_get_conf(netid); if (nconf == NULL) { return; } + al = (rpcbs_addrlist *) malloc(sizeof (rpcbs_addrlist)); if (al == NULL) { return; } + al->prog = prog; al->vers = vers; al->netid = nconf->nc_netid; @@ -137,73 +145,137 @@ rpcbs_getaddr(rtype, prog, vers, netid, uaddr) al->failure = 0; al->success = 1; } + + (void) rw_wrlock(&inf_lock); + for (wal = inf[rtype].addrinfo; wal != s; wal = wal->next) { + if ((wal->prog == prog) && (wal->vers == vers) && + (strcmp(wal->netid, netid) == 0)) { + (void) rw_unlock(&inf_lock); + + free(al); + + if ((uaddr == NULL) || (uaddr[0] == NULL)) + atomic_add_int((uint_t *)&wal->failure, 1); + else + atomic_add_int((uint_t *)&wal->success, 1); + + return; + } + } + al->next = inf[rtype].addrinfo; inf[rtype].addrinfo = al; + (void) rw_unlock(&inf_lock); } +/* + * rpcbproc - rpcbind proc number on which this was called + */ void -rpcbs_rmtcall(rtype, rpcbproc, prog, vers, proc, netid, rbl) - u_long rtype; - u_long rpcbproc; /* rpcbind proc number on which this was called */ - u_long prog; - u_long vers; - u_long proc; - char *netid; - rpcblist_ptr rbl; +rpcbs_rmtcall(int rtype, rpcproc_t rpcbproc, rpcprog_t prog, rpcvers_t vers, + rpcproc_t proc, char *netid, rpcblist_ptr rbl) { rpcbs_rmtcalllist *rl; + rpcbs_rmtcalllist *s; + rpcbs_rmtcalllist *wrl; struct netconfig *nconf; - if (rtype > RPCBVERS_STAT) - return; - for (rl = inf[rtype].rmtinfo; rl; rl = rl->next) { - - if(rl->netid == NULL) - return; + assert(rtype >= 0 && rtype < RPCBVERS_STAT); + /* + * First try with read lock only. + */ + (void) rw_rdlock(&inf_lock); + for (s = rl = inf[rtype].rmtinfo; rl; rl = rl->next) { if ((rl->prog == prog) && (rl->vers == vers) && - (rl->proc == proc) && - (strcmp(rl->netid, netid) == 0)) { - if ((rbl == NULL) || - (rbl->rpcb_map.r_vers != vers)) - rl->failure++; + (rl->proc == proc) && (strcmp(rl->netid, netid) == 0)) { + (void) rw_unlock(&inf_lock); + + if ((rbl == NULL) || (rbl->rpcb_map.r_vers != vers)) + atomic_add_int((uint_t *)&rl->failure, 1); else - rl->success++; + atomic_add_int((uint_t *)&rl->success, 1); if (rpcbproc == RPCBPROC_INDIRECT) - rl->indirect++; + atomic_add_int((uint_t *)&rl->indirect, 1); + return; } } + (void) rw_unlock(&inf_lock); + + /* + * If not found, we will likely need to add a new entry, + * so pre-allocate it, and then try to search again with write lock. + */ nconf = rpcbind_get_conf(netid); if (nconf == NULL) { return; } + rl = (rpcbs_rmtcalllist *) malloc(sizeof (rpcbs_rmtcalllist)); if (rl == NULL) { return; } + rl->prog = prog; rl->vers = vers; rl->proc = proc; rl->netid = nconf->nc_netid; - if ((rbl == NULL) || - (rbl->rpcb_map.r_vers != vers)) { + if ((rbl == NULL) || (rbl->rpcb_map.r_vers != vers)) { rl->failure = 1; rl->success = 0; } else { rl->failure = 0; rl->success = 1; } - rl->indirect = 1; + rl->indirect = rpcbproc == RPCBPROC_INDIRECT ? 1 : 0; + + (void) rw_wrlock(&inf_lock); + for (wrl = inf[rtype].rmtinfo; wrl != s; wrl = wrl->next) { + if ((wrl->prog == prog) && (wrl->vers == vers) && + (wrl->proc == proc) && (strcmp(wrl->netid, netid) == 0)) { + (void) rw_unlock(&inf_lock); + + free(rl); + + if ((rbl == NULL) || (rbl->rpcb_map.r_vers != vers)) + atomic_add_int((uint_t *)&wrl->failure, 1); + else + atomic_add_int((uint_t *)&wrl->success, 1); + if (rpcbproc == RPCBPROC_INDIRECT) + atomic_add_int((uint_t *)&wrl->indirect, 1); + + return; + } + } + rl->next = inf[rtype].rmtinfo; inf[rtype].rmtinfo = rl; - return; + (void) rw_unlock(&inf_lock); } -/* - */ -rpcb_stat_byvers * -rpcbproc_getstat() +/* ARGSUSED */ +bool_t +rpcbproc_getstat(void *argp, rpcb_stat_byvers **result) +{ + /* + * inf_lock is unlocked in xdr_rpcb_stat_byvers_ptr() + */ + (void) rw_rdlock(&inf_lock); + *result = &inf; + return (TRUE); +} + +bool_t +xdr_rpcb_stat_byvers_ptr(XDR *xdrs, rpcb_stat_byvers **objp) { - return (&inf); + if (xdrs->x_op == XDR_FREE) { + /* + * inf_lock is locked in rpcbproc_getstat() + */ + (void) rw_unlock(&inf_lock); + return (TRUE); + } + + return (xdr_rpcb_stat_byvers(xdrs, (rpcb_stat *)*objp)); } diff --git a/usr/src/cmd/rpcbind/rpcb_svc.c b/usr/src/cmd/rpcbind/rpcb_svc.c index d9f12ff..bc090b0 100644 --- a/usr/src/cmd/rpcbind/rpcb_svc.c +++ b/usr/src/cmd/rpcbind/rpcb_svc.c @@ -23,6 +23,9 @@ * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ +/* + * Copyright 2013 Nexenta Systems, Inc. All rights reserved. + */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* @@ -35,8 +38,6 @@ * contributors. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * rpcb_svc.c * The server procedure for the version 3 rpcbind (TLI). @@ -60,23 +61,30 @@ * every transport that it waits on. */ void -rpcb_service_3(rqstp, transp) - register struct svc_req *rqstp; - register SVCXPRT *transp; +rpcb_service_3(struct svc_req *rqstp, SVCXPRT *transp) { union { - RPCB rpcbproc_set_3_arg; - RPCB rpcbproc_unset_3_arg; - RPCB rpcbproc_getaddr_3_arg; - struct rpcb_rmtcallargs rpcbproc_callit_3_arg; + rpcb rpcbproc_set_3_arg; + rpcb rpcbproc_unset_3_arg; + rpcb rpcbproc_getaddr_3_arg; + rpcb_rmtcallargs rpcbproc_callit_3_arg; char *rpcbproc_uaddr2taddr_3_arg; struct netbuf rpcbproc_taddr2uaddr_3_arg; } argument; - char *result; - bool_t (*xdr_argument)(), (*xdr_result)(); - char *(*local)(); - - rpcbs_procinfo((ulong_t)RPCBVERS_3_STAT, rqstp->rq_proc); + union { + bool_t rpcbproc_set_3_res; + bool_t rpcbproc_unset_3_res; + char *rpcbproc_getaddr_3_res; + rpcblist_ptr *rpcbproc_dump_3_res; + ulong_t rpcbproc_gettime_3_res; + struct netbuf rpcbproc_uaddr2taddr_3_res; + char *rpcbproc_taddr2uaddr_3_res; + } result; + bool_t retval; + xdrproc_t xdr_argument, xdr_result; + bool_t (*local)(); + + rpcbs_procinfo(RPCBVERS_3_STAT, rqstp->rq_proc); RPCB_CHECK(transp, rqstp->rq_proc); @@ -85,9 +93,6 @@ rpcb_service_3(rqstp, transp) /* * Null proc call */ -#ifdef RPCBIND_DEBUG - fprintf(stderr, "RPCBPROC_NULL\n"); -#endif (void) svc_sendreply(transp, (xdrproc_t)xdr_void, (char *)NULL); return; @@ -97,21 +102,21 @@ rpcb_service_3(rqstp, transp) * loopback transports (for security reasons) */ if (strcasecmp(transp->xp_netid, loopback_dg) && - strcasecmp(transp->xp_netid, loopback_vc) && - strcasecmp(transp->xp_netid, loopback_vc_ord)) { + strcasecmp(transp->xp_netid, loopback_vc) && + strcasecmp(transp->xp_netid, loopback_vc_ord)) { char *uaddr; uaddr = taddr2uaddr(rpcbind_get_conf(transp->xp_netid), - svc_getrpccaller(transp)); + svc_getrpccaller(transp)); syslog(LOG_ERR, "non-local attempt to set from %s", - uaddr); + uaddr == NULL ? "" : uaddr); free(uaddr); svcerr_weakauth(transp); return; } xdr_argument = xdr_rpcb; xdr_result = xdr_bool; - local = (char *(*)()) rpcbproc_set_com; + local = (bool_t (*)()) rpcbproc_set_com; break; case RPCBPROC_UNSET: @@ -120,36 +125,33 @@ rpcb_service_3(rqstp, transp) * loopback transports (for security reasons) */ if (strcasecmp(transp->xp_netid, loopback_dg) && - strcasecmp(transp->xp_netid, loopback_vc) && - strcasecmp(transp->xp_netid, loopback_vc_ord)) { + strcasecmp(transp->xp_netid, loopback_vc) && + strcasecmp(transp->xp_netid, loopback_vc_ord)) { char *uaddr; uaddr = taddr2uaddr(rpcbind_get_conf(transp->xp_netid), - svc_getrpccaller(transp)); + svc_getrpccaller(transp)); syslog(LOG_ERR, "non-local attempt to unset from %s", - uaddr); + uaddr == NULL ? "" : uaddr); free(uaddr); svcerr_weakauth(transp); return; } xdr_argument = xdr_rpcb; xdr_result = xdr_bool; - local = (char *(*)()) rpcbproc_unset_com; + local = (bool_t (*)()) rpcbproc_unset_com; break; case RPCBPROC_GETADDR: xdr_argument = xdr_rpcb; xdr_result = xdr_wrapstring; - local = (char *(*)()) rpcbproc_getaddr_3; + local = (bool_t (*)()) rpcbproc_getaddr_com; break; case RPCBPROC_DUMP: -#ifdef RPCBIND_DEBUG - fprintf(stderr, "RPCBPROC_DUMP\n"); -#endif xdr_argument = xdr_void; - xdr_result = xdr_rpcblist_ptr; - local = (char *(*)()) rpcbproc_dump_3; + xdr_result = xdr_rpcblist_ptr_ptr; + local = (bool_t (*)()) rpcbproc_dump_com; break; case RPCBPROC_CALLIT: @@ -157,30 +159,21 @@ rpcb_service_3(rqstp, transp) return; case RPCBPROC_GETTIME: -#ifdef RPCBIND_DEBUG - fprintf(stderr, "RPCBPROC_GETTIME\n"); -#endif xdr_argument = xdr_void; xdr_result = xdr_u_long; - local = (char *(*)()) rpcbproc_gettime_com; + local = (bool_t (*)()) rpcbproc_gettime_com; break; case RPCBPROC_UADDR2TADDR: -#ifdef RPCBIND_DEBUG - fprintf(stderr, "RPCBPROC_UADDR2TADDR\n"); -#endif xdr_argument = xdr_wrapstring; xdr_result = xdr_netbuf; - local = (char *(*)()) rpcbproc_uaddr2taddr_com; + local = (bool_t (*)()) rpcbproc_uaddr2taddr_com; break; case RPCBPROC_TADDR2UADDR: -#ifdef RPCBIND_DEBUG - fprintf(stderr, "RPCBPROC_TADDR2UADDR\n"); -#endif xdr_argument = xdr_netbuf; xdr_result = xdr_wrapstring; - local = (char *(*)()) rpcbproc_taddr2uaddr_com; + local = (bool_t (*)()) rpcbproc_taddr2uaddr_com; break; default: @@ -188,16 +181,14 @@ rpcb_service_3(rqstp, transp) return; } (void) memset((char *)&argument, 0, sizeof (argument)); - if (!svc_getargs(transp, (xdrproc_t)xdr_argument, - (char *)&argument)) { + if (!svc_getargs(transp, xdr_argument, (char *)&argument)) { svcerr_decode(transp); if (debugging) (void) fprintf(stderr, "rpcbind: could not decode\n"); return; } - result = (*local)(&argument, rqstp, transp, RPCBVERS); - if (result != NULL && !svc_sendreply(transp, (xdrproc_t)xdr_result, - result)) { + retval = (*local)(&argument, &result, rqstp, RPCBVERS); + if (retval > 0 && !svc_sendreply(transp, xdr_result, (char *)&result)) { svcerr_systemerr(transp); if (debugging) { (void) fprintf(stderr, "rpcbind: svc_sendreply\n"); @@ -206,8 +197,7 @@ rpcb_service_3(rqstp, transp) } } } - if (!svc_freeargs(transp, (xdrproc_t)xdr_argument, (char *) - &argument)) { + if (!svc_freeargs(transp, xdr_argument, (char *)&argument)) { if (debugging) { (void) fprintf(stderr, "unable to free arguments\n"); if (doabort) { @@ -215,39 +205,6 @@ rpcb_service_3(rqstp, transp) } } } -} -/* - * Lookup the mapping for a program, version and return its - * address. Assuming that the caller wants the address of the - * server running on the transport on which the request came. - * - * We also try to resolve the universal address in terms of - * address of the caller. - */ -/* ARGSUSED */ -char ** -rpcbproc_getaddr_3(regp, rqstp, transp) - RPCB *regp; - struct svc_req *rqstp; /* Not used here */ - SVCXPRT *transp; -{ -#ifdef RPCBIND_DEBUG - char *uaddr; - - uaddr = taddr2uaddr(rpcbind_get_conf(transp->xp_netid), - svc_getrpccaller(transp)); - fprintf(stderr, "RPCB_GETADDR request for (%lu, %lu, %s) from %s : ", - regp->r_prog, regp->r_vers, transp->xp_netid, uaddr); - free(uaddr); -#endif - return (rpcbproc_getaddr_com(regp, rqstp, transp, RPCBVERS, - (ulong_t)RPCB_ALLVERS)); -} - -/* ARGSUSED */ -rpcblist_ptr * -rpcbproc_dump_3() -{ - return ((rpcblist_ptr *)&list_rbl); + xdr_free(xdr_result, (char *)&result); } diff --git a/usr/src/cmd/rpcbind/rpcb_svc_4.c b/usr/src/cmd/rpcbind/rpcb_svc_4.c index 5a71ba9..3fc73fb 100644 --- a/usr/src/cmd/rpcbind/rpcb_svc_4.c +++ b/usr/src/cmd/rpcbind/rpcb_svc_4.c @@ -22,8 +22,9 @@ * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ - -#pragma ident "%Z%%M% %I% %E% SMI" +/* + * Copyright 2013 Nexenta Systems, Inc. All rights reserved. + */ /* * rpcb_svc_4.c @@ -43,16 +44,17 @@ #include #include "rpcbind.h" -static void free_rpcb_entry_list(); +static void free_rpcb_entry_list(rpcb_entry_list_ptr); +static bool_t xdr_rpcb_entry_list_ptr_wrap(XDR *, rpcb_entry_list_ptr *); +static bool_t rpcbproc_getaddrlist(rpcb *, rpcb_entry_list_ptr *, + struct svc_req *); /* * Called by svc_getreqset. There is a separate server handle for * every transport that it waits on. */ void -rpcb_service_4(rqstp, transp) - register struct svc_req *rqstp; - register SVCXPRT *transp; +rpcb_service_4(struct svc_req *rqstp, SVCXPRT *transp) { union { rpcb rpcbproc_set_4_arg; @@ -60,10 +62,24 @@ rpcb_service_4(rqstp, transp) rpcb rpcbproc_getaddr_4_arg; char *rpcbproc_uaddr2taddr_4_arg; struct netbuf rpcbproc_taddr2uaddr_4_arg; + rpcb rpcbproc_getversaddr_4_arg; + rpcb rpcbproc_getaddrlist_4_arg; } argument; - char *result; - bool_t (*xdr_argument)(), (*xdr_result)(); - char *(*local)(); + union { + bool_t rpcbproc_set_4_res; + bool_t rpcbproc_unset_4_res; + char *rpcbproc_getaddr_4_res; + rpcblist_ptr *rpcbproc_dump_4_res; + ulong_t rpcbproc_gettime_4_res; + struct netbuf rpcbproc_uaddr2taddr_4_res; + char *rpcbproc_taddr2uaddr_4_res; + char *rpcbproc_getversaddr_4_res; + rpcb_entry_list_ptr rpcbproc_getaddrlist_4_res; + rpcb_stat_byvers *rpcbproc_getstat_4_res; + } result; + bool_t retval; + xdrproc_t xdr_argument, xdr_result; + bool_t (*local)(); rpcbs_procinfo(RPCBVERS_4_STAT, rqstp->rq_proc); @@ -74,11 +90,7 @@ rpcb_service_4(rqstp, transp) /* * Null proc call */ -#ifdef RPCBIND_DEBUG - fprintf(stderr, "RPCBPROC_NULL\n"); -#endif - (void) svc_sendreply(transp, (xdrproc_t)xdr_void, - (char *)NULL); + (void) svc_sendreply(transp, (xdrproc_t)xdr_void, (char *)NULL); return; case RPCBPROC_SET: @@ -87,15 +99,15 @@ rpcb_service_4(rqstp, transp) * loopback transports (for security reasons) */ if (strcasecmp(transp->xp_netid, loopback_dg) && - strcasecmp(transp->xp_netid, loopback_vc) && - strcasecmp(transp->xp_netid, loopback_vc_ord)) { + strcasecmp(transp->xp_netid, loopback_vc) && + strcasecmp(transp->xp_netid, loopback_vc_ord)) { syslog(LOG_ERR, "non-local attempt to set"); svcerr_weakauth(transp); return; } xdr_argument = xdr_rpcb; xdr_result = xdr_bool; - local = (char *(*)()) rpcbproc_set_com; + local = (bool_t (*)()) rpcbproc_set_com; break; case RPCBPROC_UNSET: @@ -104,116 +116,86 @@ rpcb_service_4(rqstp, transp) * loopback transports (for security reasons) */ if (strcasecmp(transp->xp_netid, loopback_dg) && - strcasecmp(transp->xp_netid, loopback_vc) && - strcasecmp(transp->xp_netid, loopback_vc_ord)) { + strcasecmp(transp->xp_netid, loopback_vc) && + strcasecmp(transp->xp_netid, loopback_vc_ord)) { syslog(LOG_ERR, "non-local attempt to unset"); svcerr_weakauth(transp); return; } xdr_argument = xdr_rpcb; xdr_result = xdr_bool; - local = (char *(*)()) rpcbproc_unset_com; + local = (bool_t (*)()) rpcbproc_unset_com; break; case RPCBPROC_GETADDR: xdr_argument = xdr_rpcb; xdr_result = xdr_wrapstring; - local = (char *(*)()) rpcbproc_getaddr_4; - break; - - case RPCBPROC_GETVERSADDR: -#ifdef RPCBIND_DEBUG - fprintf(stderr, "RPCBPROC_GETVERSADDR\n"); -#endif - xdr_argument = xdr_rpcb; - xdr_result = xdr_wrapstring; - local = (char *(*)()) rpcbproc_getversaddr_4; + local = (bool_t (*)()) rpcbproc_getaddr_com; break; case RPCBPROC_DUMP: -#ifdef RPCBIND_DEBUG - fprintf(stderr, "RPCBPROC_DUMP\n"); -#endif xdr_argument = xdr_void; - xdr_result = xdr_rpcblist_ptr; - local = (char *(*)()) rpcbproc_dump_4; + xdr_result = xdr_rpcblist_ptr_ptr; + local = (bool_t (*)()) rpcbproc_dump_com; break; - case RPCBPROC_INDIRECT: -#ifdef RPCBIND_DEBUG - fprintf(stderr, "RPCBPROC_INDIRECT\n"); -#endif - rpcbproc_callit_com(rqstp, transp, rqstp->rq_proc, RPCBVERS4); - return; - -/* case RPCBPROC_CALLIT: */ case RPCBPROC_BCAST: -#ifdef RPCBIND_DEBUG - fprintf(stderr, "RPCBPROC_BCAST\n"); -#endif rpcbproc_callit_com(rqstp, transp, rqstp->rq_proc, RPCBVERS4); return; case RPCBPROC_GETTIME: -#ifdef RPCBIND_DEBUG - fprintf(stderr, "RPCBPROC_GETTIME\n"); -#endif xdr_argument = xdr_void; xdr_result = xdr_u_long; - local = (char *(*)()) rpcbproc_gettime_com; + local = (bool_t (*)()) rpcbproc_gettime_com; break; case RPCBPROC_UADDR2TADDR: -#ifdef RPCBIND_DEBUG - fprintf(stderr, "RPCBPROC_UADDR2TADDR\n"); -#endif xdr_argument = xdr_wrapstring; xdr_result = xdr_netbuf; - local = (char *(*)()) rpcbproc_uaddr2taddr_com; + local = (bool_t (*)()) rpcbproc_uaddr2taddr_com; break; case RPCBPROC_TADDR2UADDR: -#ifdef RPCBIND_DEBUG - fprintf(stderr, "RPCBPROC_TADDR2UADDR\n"); -#endif xdr_argument = xdr_netbuf; xdr_result = xdr_wrapstring; - local = (char *(*)()) rpcbproc_taddr2uaddr_com; + local = (bool_t (*)()) rpcbproc_taddr2uaddr_com; break; + case RPCBPROC_GETVERSADDR: + xdr_argument = xdr_rpcb; + xdr_result = xdr_wrapstring; + local = (bool_t (*)()) rpcbproc_getaddr_com; + break; + + case RPCBPROC_INDIRECT: + rpcbproc_callit_com(rqstp, transp, rqstp->rq_proc, RPCBVERS4); + return; + case RPCBPROC_GETADDRLIST: -#ifdef RPCBIND_DEBUG - fprintf(stderr, "RPCBPROC_GETADDRLIST\n"); -#endif xdr_argument = xdr_rpcb; - xdr_result = xdr_rpcb_entry_list_ptr; - local = (char *(*)()) rpcbproc_getaddrlist_4; + xdr_result = xdr_rpcb_entry_list_ptr_wrap; + local = (bool_t (*)()) rpcbproc_getaddrlist; break; case RPCBPROC_GETSTAT: -#ifdef RPCBIND_DEBUG - fprintf(stderr, "RPCBPROC_GETSTAT\n"); -#endif xdr_argument = xdr_void; - xdr_result = xdr_rpcb_stat_byvers; - local = (char *(*)()) rpcbproc_getstat; + xdr_result = xdr_rpcb_stat_byvers_ptr; + local = (bool_t (*)()) rpcbproc_getstat; break; default: svcerr_noproc(transp); return; } - memset((char *)&argument, 0, sizeof (argument)); - if (!svc_getargs(transp, (xdrproc_t)xdr_argument, - (char *)&argument)) { + (void) memset((char *)&argument, 0, sizeof (argument)); + if (!svc_getargs(transp, xdr_argument, (char *)&argument)) { svcerr_decode(transp); if (debugging) (void) fprintf(stderr, "rpcbind: could not decode\n"); return; } - result = (*local)(&argument, rqstp, transp, RPCBVERS4); - if (result != NULL && !svc_sendreply(transp, (xdrproc_t)xdr_result, - result)) { + retval = (*local)(&argument, &result, rqstp, RPCBVERS4); + if (retval > 0 && !svc_sendreply(transp, xdr_result, (char *)&result)) { svcerr_systemerr(transp); if (debugging) { (void) fprintf(stderr, "rpcbind: svc_sendreply\n"); @@ -222,8 +204,7 @@ rpcb_service_4(rqstp, transp) } } } - if (!svc_freeargs(transp, (xdrproc_t)xdr_argument, - (char *)&argument)) { + if (!svc_freeargs(transp, xdr_argument, (char *)&argument)) { if (debugging) { (void) fprintf(stderr, "unable to free arguments\n"); if (doabort) { @@ -231,65 +212,8 @@ rpcb_service_4(rqstp, transp) } } } -} -/* - * Lookup the mapping for a program, version and return its - * address. Assuming that the caller wants the address of the - * server running on the transport on which the request came. - * Even if a service with a different version number is available, - * it will return that address. The client should check with an - * clnt_call to verify whether the service is the one that is desired. - * We also try to resolve the universal address in terms of - * address of the caller. - */ -/* ARGSUSED */ -char ** -rpcbproc_getaddr_4(regp, rqstp, transp, rpcbversnum) - rpcb *regp; - struct svc_req *rqstp; /* Not used here */ - SVCXPRT *transp; - int rpcbversnum; /* unused here */ -{ -#ifdef RPCBIND_DEBUG - char *uaddr; - - uaddr = taddr2uaddr(rpcbind_get_conf(transp->xp_netid), - svc_getrpccaller(transp)); - fprintf(stderr, "RPCB_GETADDR request for (%lu, %lu, %s) from %s : ", - regp->r_prog, regp->r_vers, transp->xp_netid, uaddr); - free(uaddr); -#endif - return (rpcbproc_getaddr_com(regp, rqstp, transp, RPCBVERS4, - (ulong_t)RPCB_ALLVERS)); -} - -/* - * Lookup the mapping for a program, version and return its - * address. Assuming that the caller wants the address of the - * server running on the transport on which the request came. - * - * We also try to resolve the universal address in terms of - * address of the caller. - */ -/* ARGSUSED */ -char ** -rpcbproc_getversaddr_4(regp, rqstp, transp) - rpcb *regp; - struct svc_req *rqstp; /* Not used here */ - SVCXPRT *transp; -{ -#ifdef RPCBIND_DEBUG - char *uaddr; - - uaddr = taddr2uaddr(rpcbind_get_conf(transp->xp_netid), - svc_getrpccaller(transp)); - fprintf(stderr, "RPCB_GETVERSADDR rqst for (%lu, %lu, %s) from %s : ", - regp->r_prog, regp->r_vers, transp->xp_netid, uaddr); - free(uaddr); -#endif - return (rpcbproc_getaddr_com(regp, rqstp, transp, RPCBVERS4, - (ulong_t)RPCB_ONEVERS)); + xdr_free(xdr_result, (char *)&result); } /* @@ -297,22 +221,20 @@ rpcbproc_getversaddr_4(regp, rqstp, transp) * addresses for all transports in the current transport family. * We return a merged address. */ -/* ARGSUSED */ -rpcb_entry_list_ptr * -rpcbproc_getaddrlist_4( - rpcb *regp, - struct svc_req *rqstp, /* Not used here */ - SVCXPRT *transp) +static bool_t +rpcbproc_getaddrlist(rpcb *regp, rpcb_entry_list_ptr *result, + struct svc_req *rqstp) { - static rpcb_entry_list_ptr rlist; + rpcb_entry_list_ptr rlist = *result = NULL; rpcblist_ptr rbl, next, prev; - rpcb_entry_list_ptr rp, tail; + rpcb_entry_list_ptr rp, tail = NULL; ulong_t prog, vers; rpcb_entry *a; struct netconfig *nconf; struct netconfig *reg_nconf; char *saddr, *maddr = NULL; struct netconfig *trans_conf; /* transport netconfig */ + SVCXPRT *transp = rqstp->rq_xprt; /* * Deal with a possible window during which we could return an IPv6 @@ -328,134 +250,124 @@ rpcbproc_getaddrlist_4( syslog(LOG_DEBUG, "IPv4 GETADDRLIST request mapped " "to IPv6: ignoring"); - return (NULL); + return (FALSE); } } - free_rpcb_entry_list(&rlist); prog = regp->r_prog; vers = regp->r_vers; reg_nconf = rpcbind_get_conf(transp->xp_netid); if (reg_nconf == NULL) - return (NULL); + return (FALSE); if (*(regp->r_addr) != '\0') { saddr = regp->r_addr; } else { saddr = NULL; } -#ifdef RPCBIND_DEBUG - fprintf(stderr, "r_addr: %s r_netid: %s nc_protofmly: %s\n", - regp->r_addr, transp->xp_netid, reg_nconf->nc_protofmly); -#endif + prev = NULL; + (void) rw_wrlock(&list_rbl_lock); for (rbl = list_rbl; rbl != NULL; rbl = next) { - next = rbl->rpcb_next; - if ((rbl->rpcb_map.r_prog == prog) && - (rbl->rpcb_map.r_vers == vers)) { - nconf = rpcbind_get_conf(rbl->rpcb_map.r_netid); - if (nconf == NULL) - goto fail; - if (strcmp(nconf->nc_protofmly, reg_nconf->nc_protofmly) - != 0) { - prev = rbl; - continue; /* not same proto family */ - } -#ifdef RPCBIND_DEBUG - fprintf(stderr, "\tmerge with: %s", rbl->rpcb_map.r_addr); -#endif - if ((maddr = mergeaddr(transp, rbl->rpcb_map.r_netid, - rbl->rpcb_map.r_addr, saddr)) == NULL) { -#ifdef RPCBIND_DEBUG - fprintf(stderr, " FAILED\n"); -#endif - prev = rbl; - continue; - } else if (!maddr[0]) { -#ifdef RPCBIND_DEBUG - fprintf(stderr, " SUCCEEDED, but port died - maddr: nullstring\n"); -#endif - /* - * The server died, remove this rpcb_map element from - * the list and free it. - */ + next = rbl->rpcb_next; + if ((rbl->rpcb_map.r_prog == prog) && + (rbl->rpcb_map.r_vers == vers)) { + nconf = rpcbind_get_conf(rbl->rpcb_map.r_netid); + if (nconf == NULL) { + (void) rw_unlock(&list_rbl_lock); + goto fail; + } + if (strcmp(nconf->nc_protofmly, reg_nconf->nc_protofmly) + != 0) { + prev = rbl; + continue; /* not same proto family */ + } + if ((maddr = mergeaddr(transp, rbl->rpcb_map.r_netid, + rbl->rpcb_map.r_addr, saddr)) == NULL) { + prev = rbl; + continue; + } else if (!maddr[0]) { + /* + * The server died, remove this rpcb_map element + * from the list and free it. + */ #ifdef PORTMAP - (void) del_pmaplist(&rbl->rpcb_map); + (void) rw_wrlock(&list_pml_lock); + (void) del_pmaplist(&rbl->rpcb_map); + (void) rw_unlock(&list_pml_lock); #endif - (void) delete_rbl(rbl); + (void) delete_rbl(rbl); - if (prev == NULL) - list_rbl = next; - else - prev->rpcb_next = next; - continue; - } -#ifdef RPCBIND_DEBUG - fprintf(stderr, " SUCCEEDED maddr: %s\n", maddr); -#endif - /* - * Add it to rlist. - */ - rp = (rpcb_entry_list_ptr) - malloc((uint_t)sizeof (rpcb_entry_list)); - if (rp == NULL) - goto fail; - a = &rp->rpcb_entry_map; - a->r_maddr = maddr; - a->r_nc_netid = nconf->nc_netid; - a->r_nc_semantics = nconf->nc_semantics; - a->r_nc_protofmly = nconf->nc_protofmly; - a->r_nc_proto = nconf->nc_proto; - rp->rpcb_entry_next = NULL; - if (rlist == NULL) { - rlist = rp; - tail = rp; - } else { - tail->rpcb_entry_next = rp; - tail = rp; + if (prev == NULL) + list_rbl = next; + else + prev->rpcb_next = next; + continue; + } + /* + * Add it to rlist. + */ + rp = (rpcb_entry_list_ptr) + malloc((uint_t)sizeof (rpcb_entry_list)); + if (rp == NULL) { + (void) rw_unlock(&list_rbl_lock); + goto fail; + } + a = &rp->rpcb_entry_map; + a->r_maddr = maddr; + a->r_nc_netid = nconf->nc_netid; + a->r_nc_semantics = nconf->nc_semantics; + a->r_nc_protofmly = nconf->nc_protofmly; + a->r_nc_proto = nconf->nc_proto; + rp->rpcb_entry_next = NULL; + if (rlist == NULL) { + rlist = rp; + tail = rp; + } else { + tail->rpcb_entry_next = rp; + tail = rp; + } + rp = NULL; } - rp = NULL; - } - prev = rbl; + prev = rbl; } -#ifdef RPCBIND_DEBUG - for (rp = rlist; rp; rp = rp->rpcb_entry_next) { - fprintf(stderr, "\t%s %s\n", rp->rpcb_entry_map.r_maddr, - rp->rpcb_entry_map.r_nc_proto); - } -#endif + (void) rw_unlock(&list_rbl_lock); + /* * XXX: getaddrlist info is also being stuffed into getaddr. * Perhaps wrong, but better than it not getting counted at all. */ - rpcbs_getaddr(RPCBVERS4 - 2, prog, vers, transp->xp_netid, maddr); - return (&rlist); + rpcbs_getaddr(RPCBVERS_4_STAT, prog, vers, transp->xp_netid, maddr); + + *result = rlist; + return (TRUE); -fail: free_rpcb_entry_list(&rlist); - return (NULL); +fail: + free_rpcb_entry_list(rlist); + return (FALSE); } /* * Free only the allocated structure, rest is all a pointer to some * other data somewhere else. */ -void -free_rpcb_entry_list(rlistp) - rpcb_entry_list_ptr *rlistp; +static void +free_rpcb_entry_list(rpcb_entry_list_ptr rlist) { - register rpcb_entry_list_ptr rbl, tmp; - - for (rbl = *rlistp; rbl != NULL; ) { - tmp = rbl; - rbl = rbl->rpcb_entry_next; - free((char *)tmp->rpcb_entry_map.r_maddr); - free((char *)tmp); + while (rlist != NULL) { + rpcb_entry_list_ptr tmp = rlist; + rlist = rlist->rpcb_entry_next; + free(tmp->rpcb_entry_map.r_maddr); + free(tmp); } - *rlistp = NULL; } -/* VARARGS */ -rpcblist_ptr * -rpcbproc_dump_4() +static bool_t +xdr_rpcb_entry_list_ptr_wrap(XDR *xdrs, rpcb_entry_list_ptr *rp) { - return ((rpcblist_ptr *)&list_rbl); + if (xdrs->x_op == XDR_FREE) { + free_rpcb_entry_list(*rp); + return (TRUE); + } + + return (xdr_rpcb_entry_list_ptr(xdrs, rp)); } diff --git a/usr/src/cmd/rpcbind/rpcb_svc_com.c b/usr/src/cmd/rpcbind/rpcb_svc_com.c index 85284a6..36dcbfb 100644 --- a/usr/src/cmd/rpcbind/rpcb_svc_com.c +++ b/usr/src/cmd/rpcbind/rpcb_svc_com.c @@ -22,6 +22,9 @@ * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ +/* + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* @@ -58,6 +61,8 @@ #ifdef PORTMAP #include #include +#else +#define PMAPVERS 2 #endif /* PORTMAP */ #include #include @@ -73,152 +78,143 @@ #include #include #include +#include +#include #include "rpcbind.h" -static bool_t xdr_opaque_parms(); -char *getowner(); -static ulong_t forward_register(); -static void handle_reply(); -static int netbufcmp(); -static int free_slot_by_xid(); -static int free_slot_by_index(); -static int check_rmtcalls(); -static void netbuffree(); -static void find_versions(); -static struct netbuf *netbufdup(); -static rpcblist_ptr find_service(); -static int add_pmaplist(RPCB *); -int del_pmaplist(RPCB *); -void delete_rbl(rpcblist_ptr); - -static char *nullstring = ""; -static int rpcb_rmtcalls; +static struct finfo *forward_register(ulong_t, struct netbuf *, int, char *); +static void forward_destroy(struct finfo *); +static void handle_reply(svc_input_id_t, int, unsigned int, void *); +static int netbufcmp(struct netbuf *, struct netbuf *); +static void netbuffree(struct netbuf *); +static struct netbuf *netbufdup(struct netbuf *); +static void find_versions(rpcprog_t, char *, rpcvers_t *, rpcvers_t *); +static rpcblist_ptr find_service(ulong_t, ulong_t, char *); +#ifdef PORTMAP +static int add_pmaplist(RPCB *); +#endif + +zoneid_t myzone; /* * Set a mapping of program, version, netid */ -/* ARGSUSED */ -bool_t * -rpcbproc_set_com(regp, rqstp, transp, rpcbversnum) - RPCB *regp; - struct svc_req *rqstp; /* Not used here */ - SVCXPRT *transp; - int rpcbversnum; +bool_t +rpcbproc_set_com(RPCB *regp, bool_t *result, struct svc_req *rqstp, + int rpcbversnum) { - static bool_t ans; char owner[64]; -#ifdef RPCBIND_DEBUG - fprintf(stderr, "RPCB_SET request for (%lu, %lu, %s, %s) : ", - regp->r_prog, regp->r_vers, regp->r_netid, regp->r_addr); -#endif - ans = map_set(regp, getowner(transp, owner)); -#ifdef RPCBIND_DEBUG - fprintf(stderr, "%s\n", ans == TRUE ? "succeeded" : "failed"); -#endif - /* XXX: should have used some defined constant here */ - rpcbs_set((ulong_t)(rpcbversnum - 2), ans); - return (&ans); + *result = map_set(regp, getowner(rqstp->rq_xprt, owner)); + + rpcbs_set(rpcbversnum - PMAPVERS, *result); + + return (TRUE); } bool_t -map_set(regp, owner) - RPCB *regp; - char *owner; +map_set(RPCB *regp, char *owner) { - RPCB reg, *a; + RPCB *a; rpcblist_ptr rbl, fnd; - reg = *regp; /* * check to see if already used * find_service returns a hit even if * the versions don't match, so check for it */ - fnd = find_service(reg.r_prog, reg.r_vers, reg.r_netid); - if (fnd && (fnd->rpcb_map.r_vers == reg.r_vers)) { - if (strcmp(fnd->rpcb_map.r_addr, reg.r_addr) == 0) + (void) rw_wrlock(&list_rbl_lock); +#ifdef PORTMAP + (void) rw_wrlock(&list_pml_lock); +#endif /* PORTMAP */ + fnd = find_service(regp->r_prog, regp->r_vers, regp->r_netid); + if (fnd && (fnd->rpcb_map.r_vers == regp->r_vers)) { + if (strcmp(fnd->rpcb_map.r_addr, regp->r_addr) == 0) { /* * if these match then it is already * registered so just say "OK". */ +#ifdef PORTMAP + (void) rw_unlock(&list_pml_lock); +#endif /* PORTMAP */ + (void) rw_unlock(&list_rbl_lock); return (TRUE); - else { + } else { /* * Check if server is up. If so, return FALSE. * If not, cleanup old registrations for the * program and register the new server. */ if (is_bound(fnd->rpcb_map.r_netid, - fnd->rpcb_map.r_addr)) + fnd->rpcb_map.r_addr)) { +#ifdef PORTMAP + (void) rw_unlock(&list_pml_lock); +#endif /* PORTMAP */ + (void) rw_unlock(&list_rbl_lock); return (FALSE); - delete_prog(reg.r_prog); + } + + delete_prog(regp->r_prog); fnd = NULL; } } +#ifdef PORTMAP + (void) rw_unlock(&list_pml_lock); +#endif /* PORTMAP */ + /* * add to the end of the list */ - rbl = (rpcblist_ptr) malloc((uint_t)sizeof (RPCBLIST)); - if (rbl == (rpcblist_ptr)NULL) { + rbl = malloc(sizeof (RPCBLIST)); + if (rbl == NULL) { + (void) rw_unlock(&list_rbl_lock); return (FALSE); } - a = &(rbl->rpcb_map); - a->r_prog = reg.r_prog; - a->r_vers = reg.r_vers; - a->r_netid = strdup(reg.r_netid); - a->r_addr = strdup(reg.r_addr); + a = &rbl->rpcb_map; + a->r_prog = regp->r_prog; + a->r_vers = regp->r_vers; + a->r_netid = strdup(regp->r_netid); + a->r_addr = strdup(regp->r_addr); a->r_owner = strdup(owner); if (a->r_addr == NULL || a->r_netid == NULL|| a->r_owner == NULL) { + (void) rw_unlock(&list_rbl_lock); delete_rbl(rbl); return (FALSE); } - rbl->rpcb_next = (rpcblist_ptr)NULL; + rbl->rpcb_next = NULL; if (list_rbl == NULL) { list_rbl = rbl; } else { - for (fnd = list_rbl; fnd->rpcb_next; - fnd = fnd->rpcb_next) + for (fnd = list_rbl; fnd->rpcb_next; fnd = fnd->rpcb_next) ; fnd->rpcb_next = rbl; } + #ifdef PORTMAP (void) add_pmaplist(regp); #endif + (void) rw_unlock(&list_rbl_lock); return (TRUE); } /* * Unset a mapping of program, version, netid */ -/* ARGSUSED */ -bool_t * -rpcbproc_unset_com(regp, rqstp, transp, rpcbversnum) - RPCB *regp; - struct svc_req *rqstp; /* Not used here */ - SVCXPRT *transp; - int rpcbversnum; +bool_t +rpcbproc_unset_com(RPCB *regp, bool_t *result, struct svc_req *rqstp, + int rpcbversnum) { - static bool_t ans; char owner[64]; -#ifdef RPCBIND_DEBUG - fprintf(stderr, "RPCB_UNSET request for (%lu, %lu, %s) : ", - regp->r_prog, regp->r_vers, regp->r_netid); -#endif - ans = map_unset(regp, getowner(transp, owner)); -#ifdef RPCBIND_DEBUG - fprintf(stderr, "%s\n", ans == TRUE ? "succeeded" : "failed"); -#endif - /* XXX: should have used some defined constant here */ - rpcbs_unset((ulong_t)(rpcbversnum - 2), ans); - return (&ans); + *result = map_unset(regp, getowner(rqstp->rq_xprt, owner)); + + rpcbs_unset(rpcbversnum - PMAPVERS, *result); + + return (TRUE); } bool_t -map_unset(regp, owner) - RPCB *regp; - char *owner; +map_unset(RPCB *regp, char *owner) { #ifdef PORTMAP int ans = 0; @@ -228,24 +224,29 @@ map_unset(regp, owner) if (owner == NULL) return (0); + (void) rw_wrlock(&list_rbl_lock); for (rbl = list_rbl; rbl != NULL; rbl = next) { next = rbl->rpcb_next; if ((rbl->rpcb_map.r_prog != regp->r_prog) || (rbl->rpcb_map.r_vers != regp->r_vers) || (regp->r_netid[0] && strcasecmp(regp->r_netid, - rbl->rpcb_map.r_netid))) { + rbl->rpcb_map.r_netid))) { /* prev moves forwards */ prev = rbl; continue; } + /* * Check whether appropriate uid. Unset only * if superuser or the owner itself. */ if (strcmp(owner, "superuser") && - strcmp(rbl->rpcb_map.r_owner, owner)) + strcmp(rbl->rpcb_map.r_owner, owner)) { + (void) rw_unlock(&list_rbl_lock); return (0); + } + /* prev stays */ #ifdef PORTMAP ans = 1; @@ -258,9 +259,14 @@ map_unset(regp, owner) prev->rpcb_next = next; } #ifdef PORTMAP - if (ans) + if (ans != 0) { + (void) rw_wrlock(&list_pml_lock); (void) del_pmaplist(regp); + (void) rw_unlock(&list_pml_lock); + } #endif + (void) rw_unlock(&list_rbl_lock); + /* * We return 1 either when the entry was not there or it * was able to unset it. It can come to this point only if @@ -279,11 +285,12 @@ delete_rbl(rpcblist_ptr rbl) } void -delete_prog(prog) - unsigned long prog; +delete_prog(rpcprog_t prog) { rpcblist_ptr rbl, next, prev = NULL; + assert(RW_WRITE_HELD(&list_rbl_lock)); + for (rbl = list_rbl; rbl != NULL; rbl = next) { next = rbl->rpcb_next; @@ -305,19 +312,32 @@ delete_prog(prog) } } -/*ARGSUSED*/ -char ** -rpcbproc_getaddr_com(regp, rqstp, transp, rpcbversnum, verstype) - RPCB *regp; - struct svc_req *rqstp; /* Not used here */ - SVCXPRT *transp; - ulong_t rpcbversnum; - ulong_t verstype; +/* + * Lookup the mapping for a program, version and return its + * address. Assuming that the caller wants the address of the + * server running on the transport on which the request came. + * + * For RPCBPROC_GETVERSADDR it will return a service with the exact version + * number only. + * + * Otherwise, even if a service with a different version number is available, + * it will return that address. The client should check with an + * clnt_call to verify whether the service is the one that is desired. + * + * We also try to resolve the universal address in terms of + * address of the caller. + */ +bool_t +rpcbproc_getaddr_com(RPCB *regp, char **result, struct svc_req *rqstp, + ulong_t rpcbversnum) { - static char *uaddr; char *saddr = NULL; rpcblist_ptr fnd; struct netconfig *trans_conf; /* transport netconfig */ + SVCXPRT *transp = rqstp->rq_xprt; + int verstype = rqstp->rq_proc == RPCBPROC_GETVERSADDR ? RPCB_ONEVERS : + RPCB_ALLVERS; + bool_t pml_locked = FALSE; /* * There is a potential window at startup during which rpcbind @@ -339,207 +359,173 @@ rpcbproc_getaddr_com(regp, rqstp, transp, rpcbversnum, verstype) if (IN6_IS_ADDR_V4MAPPED(&rmtaddr->sin6_addr)) { syslog(LOG_DEBUG, "IPv4 GETADDR request mapped to IPv6: ignoring"); - return (NULL); + *result = NULL; + return (FALSE); } } - if (uaddr && uaddr[0]) - free((void *) uaddr); + (void) rw_rdlock(&list_rbl_lock); +retry: fnd = find_service(regp->r_prog, regp->r_vers, transp->xp_netid); if (fnd && ((verstype == RPCB_ALLVERS) || - (regp->r_vers == fnd->rpcb_map.r_vers))) { + (regp->r_vers == fnd->rpcb_map.r_vers))) { if (*(regp->r_addr) != '\0') { /* may contain a hint about */ saddr = regp->r_addr; /* the interface that we */ } /* should use */ - if (!(uaddr = mergeaddr(transp, transp->xp_netid, - fnd->rpcb_map.r_addr, saddr))) { + if (!(*result = mergeaddr(transp, transp->xp_netid, + fnd->rpcb_map.r_addr, saddr))) { /* Try whatever we have */ - uaddr = strdup(fnd->rpcb_map.r_addr); - } else if (!uaddr[0]) { + *result = strdup(fnd->rpcb_map.r_addr); + } else if (!(*result)[0]) { + if (!pml_locked) { + (void) rw_unlock(&list_rbl_lock); + (void) rw_wrlock(&list_rbl_lock); +#ifdef PORTMAP + (void) rw_wrlock(&list_pml_lock); +#endif /* PORTMAP */ + pml_locked = TRUE; + goto retry; + } /* * The server died. Unset all versions of this prog. */ delete_prog(regp->r_prog); - uaddr = nullstring; + *result = NULL; } } else { - uaddr = nullstring; + *result = NULL; } -#ifdef RPCBIND_DEBUG - fprintf(stderr, "getaddr: %s\n", uaddr); -#endif - /* XXX: should have used some defined constant here */ - rpcbs_getaddr(rpcbversnum - 2, regp->r_prog, regp->r_vers, - transp->xp_netid, uaddr); - return (&uaddr); +#ifdef PORTMAP + if (pml_locked) + (void) rw_unlock(&list_pml_lock); +#endif /* PORTMAP */ + (void) rw_unlock(&list_rbl_lock); + + rpcbs_getaddr(rpcbversnum - PMAPVERS, regp->r_prog, regp->r_vers, + transp->xp_netid, *result); + return (TRUE); } -/* VARARGS */ -ulong_t * -rpcbproc_gettime_com() +/* ARGSUSED */ +bool_t +rpcbproc_dump_com(void *argp, rpcblist_ptr **result) { - static time_t curtime; + /* + * list_rbl_lock is unlocked in xdr_rpcblist_ptr_ptr() + */ + (void) rw_rdlock(&list_rbl_lock); + *result = &list_rbl; + return (TRUE); +} - (void) time(&curtime); - return ((ulong_t *)&curtime); +bool_t +xdr_rpcblist_ptr_ptr(XDR *xdrs, rpcblist_ptr **objp) +{ + if (xdrs->x_op == XDR_FREE) { + /* + * list_rbl_lock is locked in rpcbproc_dump_com() + */ + rw_unlock(&list_rbl_lock); + return (TRUE); + } + + return (xdr_rpcblist_ptr(xdrs, *objp)); +} + +/* ARGSUSED */ +bool_t +rpcbproc_gettime_com(void *argp, ulong_t *result) +{ + (void) time((time_t *)result); + + return (TRUE); } /* * Convert uaddr to taddr. Should be used only by * local servers/clients. (kernel level stuff only) */ -/* ARGSUSED */ -struct netbuf * -rpcbproc_uaddr2taddr_com(uaddrp, rqstp, transp, rpcbversnum) - char **uaddrp; - struct svc_req *rqstp; /* Not used here */ - SVCXPRT *transp; - int rpcbversnum; /* Not used here */ +bool_t +rpcbproc_uaddr2taddr_com(char **uaddrp, struct netbuf *result, + struct svc_req *rqstp) { struct netconfig *nconf; - static struct netbuf nbuf; - static struct netbuf *taddr; + struct netbuf *taddr; - if (taddr) { - free((void *) taddr->buf); - free((void *) taddr); - } - if (((nconf = rpcbind_get_conf(transp->xp_netid)) == NULL) || + if (((nconf = rpcbind_get_conf(rqstp->rq_xprt->xp_netid)) == NULL) || ((taddr = uaddr2taddr(nconf, *uaddrp)) == NULL)) { - (void) memset((char *)&nbuf, 0, sizeof (struct netbuf)); - return (&nbuf); + (void) memset(result, 0, sizeof (*result)); + return (TRUE); } - return (taddr); + + memcpy(result, taddr, sizeof (*result)); + free(taddr); + + return (TRUE); } /* * Convert taddr to uaddr. Should be used only by * local servers/clients. (kernel level stuff only) */ -/* ARGSUSED */ -char ** -rpcbproc_taddr2uaddr_com(taddr, rqstp, transp, rpcbversnum) - struct netbuf *taddr; - struct svc_req *rqstp; /* Not used here */ - SVCXPRT *transp; - int rpcbversnum; /* unused */ +bool_t +rpcbproc_taddr2uaddr_com(struct netbuf *taddr, char **result, + struct svc_req *rqstp) { - static char *uaddr; struct netconfig *nconf; -#ifdef CHEW_FDS - int fd; + if ((nconf = rpcbind_get_conf(rqstp->rq_xprt->xp_netid)) == NULL) + *result = NULL; + else + *result = taddr2uaddr(nconf, taddr); - if ((fd = open("/dev/null", O_RDONLY)) == -1) { - uaddr = (char *)strerror(errno); - return (&uaddr); - } -#endif /* CHEW_FDS */ - if (uaddr && uaddr[0]) - free((void *) uaddr); - if (((nconf = rpcbind_get_conf(transp->xp_netid)) == NULL) || - ((uaddr = taddr2uaddr(nconf, taddr)) == NULL)) { - uaddr = nullstring; - } - return (&uaddr); + return (TRUE); } - /* * Stuff for the rmtcall service */ -struct encap_parms { - ulong_t arglen; - char *args; -}; - -static bool_t -xdr_encap_parms(xdrs, epp) - XDR *xdrs; - struct encap_parms *epp; -{ - return (xdr_bytes(xdrs, &(epp->args), (uint_t *)&(epp->arglen), ~0)); -} - - -struct r_rmtcall_args { - ulong_t rmt_prog; - ulong_t rmt_vers; - ulong_t rmt_proc; - int rmt_localvers; /* whether to send port # or uaddr */ - char *rmt_uaddr; - struct encap_parms rmt_args; -}; - -/* - * XDR remote call arguments. It ignores the address part. - * written for XDR_DECODE direction only - */ -static bool_t -xdr_rmtcall_args(xdrs, cap) - register XDR *xdrs; - register struct r_rmtcall_args *cap; +bool_t +xdr_rpcb_rmtcallargs(XDR *xdrs, rpcb_rmtcallargs *objp) { - /* does not get the address or the arguments */ - if (xdr_u_long(xdrs, &(cap->rmt_prog)) && - xdr_u_long(xdrs, &(cap->rmt_vers)) && - xdr_u_long(xdrs, &(cap->rmt_proc))) { - return (xdr_encap_parms(xdrs, &(cap->rmt_args))); - } - return (FALSE); + if (!xdr_u_long(xdrs, &objp->prog)) + return (FALSE); + if (!xdr_u_long(xdrs, &objp->vers)) + return (FALSE); + if (!xdr_u_long(xdrs, &objp->proc)) + return (FALSE); + if (!xdr_bytes(xdrs, (char **)&objp->args.args_val, + (uint_t *)&objp->args.args_len, ~0)) + return (FALSE); + return (TRUE); } -/* - * XDR remote call results along with the address. Ignore - * program number, version number and proc number. - * Written for XDR_ENCODE direction only. - */ -static bool_t -xdr_rmtcall_result(xdrs, cap) - register XDR *xdrs; - register struct r_rmtcall_args *cap; -{ - bool_t result; - #ifdef PORTMAP - if (cap->rmt_localvers == PMAPVERS) { - int h1, h2, h3, h4, p1, p2; - ulong_t port; - - /* interpret the universal address for TCP/IP */ - if (sscanf(cap->rmt_uaddr, "%d.%d.%d.%d.%d.%d", - &h1, &h2, &h3, &h4, &p1, &p2) != 6) - return (FALSE); - port = ((p1 & 0xff) << 8) + (p2 & 0xff); - result = xdr_u_long(xdrs, &port); - } else -#endif - if ((cap->rmt_localvers == RPCBVERS) || - (cap->rmt_localvers == RPCBVERS4)) { - result = xdr_wrapstring(xdrs, &(cap->rmt_uaddr)); - } else { +bool_t +xdr_rmtcallres(XDR *xdrs, rmtcallres *objp) +{ + if (!xdr_u_long(xdrs, &objp->port)) return (FALSE); - } - if (result == TRUE) - return (xdr_encap_parms(xdrs, &(cap->rmt_args))); - return (FALSE); + if (!xdr_bytes(xdrs, (char **)&objp->res.res_val, + (uint_t *)&objp->res.res_len, ~0)) + return (FALSE); + return (TRUE); } +#endif -/* - * only worries about the struct encap_parms part of struct r_rmtcall_args. - * The arglen must already be set!! - */ -static bool_t -xdr_opaque_parms(xdrs, cap) - XDR *xdrs; - struct r_rmtcall_args *cap; +bool_t +xdr_rpcb_rmtcallres(XDR *xdrs, rpcb_rmtcallres *objp) { - return (xdr_opaque(xdrs, cap->rmt_args.args, cap->rmt_args.arglen)); + if (!xdr_string(xdrs, &objp->addr, ~0)) + return (FALSE); + if (!xdr_bytes(xdrs, (char **)&objp->results.results_val, + (uint_t *)&objp->results.results_len, ~0)) + return (FALSE); + return (TRUE); } struct rmtcallfd_list { int fd; - SVCXPRT *xprt; char *netid; struct rmtcallfd_list *next; }; @@ -547,45 +533,50 @@ struct rmtcallfd_list { static struct rmtcallfd_list *rmthead; static struct rmtcallfd_list *rmttail; +#define MASKVAL (POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND) + int -create_rmtcall_fd(nconf) -struct netconfig *nconf; +create_rmtcall_fd(struct netconfig *nconf) { int fd; struct rmtcallfd_list *rmt; - SVCXPRT *xprt; if ((fd = t_open(nconf->nc_device, O_RDWR, NULL)) == -1) { if (debugging) - fprintf(stderr, - "create_rmtcall_fd: couldn't open \"%s\" (errno %d, t_errno %d)\n", - nconf->nc_device, errno, t_errno); - return (-1); - } - if (t_bind(fd, (struct t_bind *)0, - (struct t_bind *)0) == -1) { - if (debugging) - fprintf(stderr, -"create_rmtcall_fd: couldn't bind to fd for \"%s\" (errno %d, t_errno %d)\n", - nconf->nc_device, errno, t_errno); + fprintf(stderr, "create_rmtcall_fd: couldn't open " + "\"%s\" (errno %d, t_errno %d)\n", + nconf->nc_device, errno, t_errno); return (-1); } - xprt = svc_tli_create(fd, 0, (struct t_bind *)0, 0, 0); - if (xprt == NULL) { + + if (t_bind(fd, NULL, NULL) == -1) { if (debugging) - fprintf(stderr, - "create_rmtcall_fd: svc_tli_create failed\n"); + fprintf(stderr, "create_rmtcall_fd: couldn't bind to " + "fd for \"%s\" (errno %d, t_errno %d)\n", + nconf->nc_device, errno, t_errno); return (-1); } - rmt = (struct rmtcallfd_list *)malloc((uint_t) - sizeof (struct rmtcallfd_list)); + + rmt = malloc(sizeof (struct rmtcallfd_list)); if (rmt == NULL) { syslog(LOG_ERR, "create_rmtcall_fd: no memory!"); return (-1); } - rmt->xprt = xprt; + rmt->netid = strdup(nconf->nc_netid); - xprt->xp_netid = rmt->netid; + if (rmt->netid == NULL) { + free(rmt); + syslog(LOG_ERR, "create_rmtcall_fd: no memory!"); + return (-1); + } + + if (svc_add_input(fd, MASKVAL, handle_reply, rmt->netid) == -1) { + free(rmt->netid); + free(rmt); + syslog(LOG_ERR, "create_rmtcall_fd: svc_add_input() failed!"); + return (-1); + } + rmt->fd = fd; rmt->next = NULL; if (rmthead == NULL) { @@ -595,23 +586,12 @@ struct netconfig *nconf; rmttail->next = rmt; rmttail = rmt; } -#if defined(DEBUG_RMTCALL) && defined(PORTMAP) - if (debugging) { - struct sockaddr_in *sin; - struct netbuf *nb; - nb = &xprt->xp_ltaddr; - sin = (struct sockaddr_in *)nb->buf; - fprintf(stderr, - "create_rmtcall_fd %d, port %d\n", - fd, sin->sin_port); - } -#endif + return (fd); } static int -find_rmtcallfd_by_netid(netid) -char *netid; +find_rmtcallfd_by_netid(char *netid) { struct rmtcallfd_list *rmt; @@ -623,21 +603,49 @@ char *netid; return (-1); } -static SVCXPRT * -find_rmtcallxprt_by_fd(fd) -int fd; -{ - struct rmtcallfd_list *rmt; +#define MAXTIME_OFF 300 /* 5 minutes timeout for rmtcalls */ - for (rmt = rmthead; rmt != NULL; rmt = rmt->next) { - if (fd == rmt->fd) { - return (rmt->xprt); - } +struct finfo { + struct finfo *prev; + struct finfo *next; + int flag; +#define FINFO_ACTIVE 0x1 + ulong_t caller_xid; + struct netbuf *caller_addr; + ulong_t forward_xid; + int forward_fd; + char *uaddr; + struct t_unitdata *reply_data; + struct rpc_err reply_error; + uint_t res_len; + void *res_val; + cond_t cv; +}; + +/* + * finfo_lock protects rpcb_rmtcalls, rpcb_rmtcalls_max, lastxid, + * fihead, and fitail. + */ +static mutex_t finfo_lock = DEFAULTMUTEX; + +static int rpcb_rmtcalls; +static int rpcb_rmtcalls_max; +static ulong_t lastxid; +static struct finfo *fihead; +static struct finfo *fitail; + +void +set_rpcb_rmtcalls_max(int max) +{ + (void) mutex_lock(&finfo_lock); + rpcb_rmtcalls_max = max; + if (rpcb_rmtcalls > rpcb_rmtcalls_max) { + assert(fitail != NULL); + (void) cond_signal(&fitail->cv); } - return (NULL); + (void) mutex_unlock(&finfo_lock); } - /* * Call a remote procedure service. This procedure is very quiet when things * go wrong. The proc is written to support broadcast rpc. In the broadcast @@ -657,23 +665,22 @@ int fd; * remembering the XID used to send this request (for later use in * reassociating the answer with the original request), the requestor's * address, the file descriptor on which the forwarded request is - * made and the service's address. + * made and the service's address * - * mark the file descriptor on which we anticipate receiving a reply from - * the service and one to select for in our private svc_run procedure + * wait for either the timeout or the condition variable is signalled from + * handle_reply(). * * At some time in the future, a reply will be received from the service to - * which we forwarded the request. At that time, we detect that the socket - * used was for forwarding (by looking through the finfo structures to see - * whether the fd corresponds to one of those) and call handle_reply() to + * which we forwarded the request. At that time, svc_run() detect that the + * socket used was for forwarding and call handle_reply() to * * receive the reply * * bundle the reply, along with the service's universal address * - * create a SVCXPRT structure and use a version of svc_sendreply - * that allows us to specify the reply XID and destination, send the reply - * to the original requestor. + * put the reply into the particular finfo + * + * signal the condition variable. */ #define RPC_BUF_MAX 65536 /* can be raised if required */ @@ -685,33 +692,48 @@ int fd; #define YPBINDPROG ((ulong_t)100007) #define YPBINDPROC_SETDOM ((ulong_t)2) +/* + * reply_type - which proc number + * versnum - which vers was called + */ void -rpcbproc_callit_com(rqstp, transp, reply_type, versnum) - struct svc_req *rqstp; - SVCXPRT *transp; - ulong_t reply_type; /* which proc number */ - ulong_t versnum; /* which vers was called */ +rpcbproc_callit_com(struct svc_req *rqstp, SVCXPRT *transp, ulong_t reply_type, + int versnum) { - register rpcblist_ptr rbl; + struct t_info tinfo; + uint_t sendsz; + + rpcb_rmtcallargs arg; + rpcblist_ptr rbl; + struct netconfig *nconf; struct netbuf *caller; - struct r_rmtcall_args a; - char *buf_alloc = NULL; - char *outbuf_alloc = NULL; - char buf[RPC_BUF_MAX], outbuf[RPC_BUF_MAX]; - struct netbuf *na = (struct netbuf *)NULL; - struct t_info tinfo; - struct t_unitdata tu_data; - struct rpc_msg call_msg; + struct nd_mergearg ma; + int stat; + + int fd; struct svc_dg_data *bd; - int outlen; - uint_t sendsz; + struct finfo *fi; + + struct rpc_msg call_msg; + char outbuf[RPC_BUF_MAX]; + char *outbuf_alloc = NULL; XDR outxdr; + bool_t outxdr_created = FALSE; + AUTH *auth; - int fd = -1; - char *uaddr; - struct nd_mergearg ma; - int stat; + + struct t_unitdata tu_data; + struct netbuf *na; + + timestruc_t to; + + (void) mutex_lock(&finfo_lock); + if (!allow_indirect || rpcb_rmtcalls_max == 0) { + (void) mutex_unlock(&finfo_lock); + return; + } + (void) mutex_unlock(&finfo_lock); if (t_getinfo(transp->xp_fd, &tinfo) == -1) { if (reply_type == RPCBPROC_INDIRECT) @@ -720,6 +742,7 @@ rpcbproc_callit_com(rqstp, transp, reply_type, versnum) } if (tinfo.servtype != T_CLTS) return; /* Only datagram type accepted */ + sendsz = __rpc_get_t_size(0, tinfo.tsdu); if (sendsz == 0) { /* data transfer not supported */ if (reply_type == RPCBPROC_INDIRECT) @@ -730,28 +753,9 @@ rpcbproc_callit_com(rqstp, transp, reply_type, versnum) * Should be multiple of 4 for XDR. */ sendsz = ((sendsz + 3) / 4) * 4; - if (sendsz > RPC_BUF_MAX) { -#ifdef notyet - buf_alloc = alloca(sendsz); /* not in IDR2? */ -#else - buf_alloc = malloc(sendsz); -#endif /* notyet */ - if (buf_alloc == NULL) { - if (debugging) - fprintf(stderr, - "rpcbproc_callit_com: No Memory!\n"); - if (reply_type == RPCBPROC_INDIRECT) - svcerr_systemerr(transp); - return; - } - a.rmt_args.args = buf_alloc; - } else { - a.rmt_args.args = buf; - } - call_msg.rm_xid = 0; /* For error checking purposes */ - ma.m_uaddr = NULL; - if (!svc_getargs(transp, (xdrproc_t)xdr_rmtcall_args, (char *)&a)) { + (void) memset((char *)&arg, 0, sizeof (arg)); + if (!svc_getargs(transp, xdr_rpcb_rmtcallargs, (char *)&arg)) { if (reply_type == RPCBPROC_INDIRECT) svcerr_decode(transp); if (debugging) @@ -759,37 +763,22 @@ rpcbproc_callit_com(rqstp, transp, reply_type, versnum) "rpcbproc_callit_com: svc_getargs failed\n"); goto error; } - if (!allow_indirect) - goto error; - caller = svc_getrpccaller(transp); -#ifdef RPCBIND_DEBUG - uaddr = taddr2uaddr(rpcbind_get_conf(transp->xp_netid), caller); - fprintf(stderr, "%s %s request for (%lu, %lu, %lu, %s) from %s : ", - versnum == PMAPVERS ? "pmap_rmtcall" : - versnum == RPCBVERS ? "rpcb_rmtcall" : - versnum == RPCBVERS4 ? "rpcb_indirect" : "unknown", - reply_type == RPCBPROC_INDIRECT ? "indirect" : "callit", - a.rmt_prog, a.rmt_vers, a.rmt_proc, transp->xp_netid, - uaddr ? uaddr : "unknown"); - if (uaddr) - free((void *) uaddr); -#endif /* * Disallow calling rpcbind for certain procedures. * Allow calling NULLPROC - per man page on rpcb_rmtcall(). * switch is in alphabetical order. */ - if (a.rmt_proc != NULLPROC) { - switch (a.rmt_prog) { + if (arg.proc != NULLPROC) { + switch (arg.prog) { case KEY_PROG: if (debugging) fprintf(stderr, - "rpcbind: rejecting KEY_PROG(%d)\n", - a.rmt_proc); + "rpcbind: rejecting KEY_PROG(%d)\n", + arg.proc); goto error; case MOUNTPROG: - if (a.rmt_proc != MOUNTPROC_MNT) + if (arg.proc != MOUNTPROC_MNT) break; /* * In Solaris 2.6, the host-based accesss control @@ -798,21 +787,21 @@ rpcbproc_callit_com(rqstp, transp, reply_type, versnum) */ if (debugging) fprintf(stderr, - "rpcbind: rejecting MOUNTPROG(%d)\n", - a.rmt_proc); + "rpcbind: rejecting MOUNTPROG(%d)\n", + arg.proc); goto error; case NFS_ACL_PROGRAM: if (debugging) fprintf(stderr, - "rpcbind: rejecting NFS_ACL_PROGRAM(%d)\n", - a.rmt_proc); + "rpcbind: rejecting NFS_ACL_PROGRAM(%d)\n", + arg.proc); goto error; case NFS_PROGRAM: /* also NFS3_PROGRAM */ if (debugging) fprintf(stderr, - "rpcbind: rejecting NFS_PROGRAM(%d)\n", - a.rmt_proc); + "rpcbind: rejecting NFS_PROGRAM(%d)\n", + arg.proc); goto error; case RPCBPROG: /* @@ -820,7 +809,7 @@ rpcbproc_callit_com(rqstp, transp, reply_type, versnum) * Luckily Portmap set/unset/callit also have same * procedure numbers. So, will not check for those. */ - switch (a.rmt_proc) { + switch (arg.proc) { case RPCBPROC_SET: case RPCBPROC_UNSET: case RPCBPROC_CALLIT: @@ -828,9 +817,10 @@ rpcbproc_callit_com(rqstp, transp, reply_type, versnum) if (reply_type == RPCBPROC_INDIRECT) svcerr_weakauth(transp); /* XXX */ if (debugging) - fprintf(stderr, -"rpcbproc_callit_com: calling RPCBPROG procs SET, UNSET, CALLIT, or INDIRECT \ -not allowed \n"); + fprintf(stderr, "rpcbproc_callit_com: " + "calling RPCBPROG procs SET, " + "UNSET, CALLIT, or INDIRECT not " + "allowed\n"); goto error; default: /* @@ -846,39 +836,39 @@ not allowed \n"); case RQUOTAPROG: if (debugging) fprintf(stderr, - "rpcbind: rejecting RQUOTAPROG(%d)\n", - a.rmt_proc); + "rpcbind: rejecting RQUOTAPROG(%d)\n", + arg.proc); goto error; case YPPASSWDPROG: if (debugging) fprintf(stderr, - "rpcbind: rejecting YPPASSWDPROG(%d)\n", - a.rmt_proc); + "rpcbind: rejecting YPPASSWDPROG(%d)\n", + arg.proc); goto error; case YPU_PROG: if (debugging) fprintf(stderr, - "rpcbind: rejecting YPU_PROG(%d)\n", - a.rmt_proc); + "rpcbind: rejecting YPU_PROG(%d)\n", + arg.proc); goto error; case YPBINDPROG: - if (a.rmt_proc != YPBINDPROC_SETDOM) + if (arg.proc != YPBINDPROC_SETDOM) break; if (debugging) fprintf(stderr, - "rpcbind: rejecting YPBINDPROG(%d)\n", - a.rmt_proc); + "rpcbind: rejecting YPBINDPROG(%d)\n", + arg.proc); goto error; case YPPROG: - switch (a.rmt_proc) { + switch (arg.proc) { case YPPROC_FIRST: case YPPROC_NEXT: case YPPROC_MATCH: case YPPROC_ALL: if (debugging) fprintf(stderr, - "rpcbind: rejecting YPPROG(%d)\n", - a.rmt_proc); + "rpcbind: rejecting YPPROG(%d)\n", + arg.proc); goto error; default: break; @@ -889,151 +879,157 @@ not allowed \n"); } } - rbl = find_service(a.rmt_prog, a.rmt_vers, transp->xp_netid); + (void) rw_rdlock(&list_rbl_lock); + rbl = find_service(arg.prog, arg.vers, transp->xp_netid); - rpcbs_rmtcall(versnum - 2, reply_type, a.rmt_prog, a.rmt_vers, - a.rmt_proc, transp->xp_netid, rbl); + rpcbs_rmtcall(versnum - PMAPVERS, reply_type, arg.prog, arg.vers, + arg.proc, transp->xp_netid, rbl); - if (rbl == (rpcblist_ptr)NULL) { -#ifdef RPCBIND_DEBUG - fprintf(stderr, "not found\n"); -#endif + if (rbl == NULL) { + (void) rw_unlock(&list_rbl_lock); if (reply_type == RPCBPROC_INDIRECT) svcerr_noprog(transp); goto error; } - if (rbl->rpcb_map.r_vers != a.rmt_vers) { -#ifdef RPCBIND_DEBUG - fprintf(stderr, "version not found\n"); -#endif + if (rbl->rpcb_map.r_vers != arg.vers) { if (reply_type == RPCBPROC_INDIRECT) { ulong_t vers_low, vers_high; - find_versions(a.rmt_prog, transp->xp_netid, - &vers_low, &vers_high); + find_versions(arg.prog, transp->xp_netid, + &vers_low, &vers_high); + (void) rw_unlock(&list_rbl_lock); svcerr_progvers(transp, vers_low, vers_high); + } else { + (void) rw_unlock(&list_rbl_lock); } goto error; } -#ifdef RPCBIND_DEBUG - fprintf(stderr, "found at uaddr %s\n", rbl->rpcb_map.r_addr); -#endif /* - * Check whether this entry is valid and a server is present - * Mergeaddr() returns NULL if no such entry is present, and - * returns "" if the entry was present but the server is not - * present (i.e., it crashed). + * Check whether this entry is valid and a server is present + * Mergeaddr() returns NULL if no such entry is present, and + * returns "" if the entry was present but the server is not + * present (i.e., it crashed). */ if (reply_type == RPCBPROC_INDIRECT) { - uaddr = mergeaddr(transp, transp->xp_netid, - rbl->rpcb_map.r_addr, NULL); + char *uaddr = mergeaddr(transp, transp->xp_netid, + rbl->rpcb_map.r_addr, NULL); if ((uaddr == (char *)NULL) || uaddr[0] == '\0') { + (void) rw_unlock(&list_rbl_lock); svcerr_noprog(transp); goto error; } else { - free((void *) uaddr); + free(uaddr); } } + nconf = rpcbind_get_conf(transp->xp_netid); - if (nconf == (struct netconfig *)NULL) { + if (nconf == NULL) { + (void) rw_unlock(&list_rbl_lock); if (reply_type == RPCBPROC_INDIRECT) svcerr_systemerr(transp); if (debugging) fprintf(stderr, - "rpcbproc_callit_com: rpcbind_get_conf failed\n"); + "rpcbproc_callit_com: rpcbind_get_conf failed\n"); goto error; } + + caller = svc_getrpccaller(transp); ma.c_uaddr = taddr2uaddr(nconf, caller); ma.s_uaddr = rbl->rpcb_map.r_addr; + /* - * A mergeaddr operation allocates a string, which it stores in - * ma.m_uaddr. It's passed to forward_register() and is - * eventually freed by free_slot_*(). + * A mergeaddr operation allocates a string, which it stores in + * ma.m_uaddr. It's passed to forward_register() and is + * eventually freed by forward_destroy(). */ - stat = netdir_options(nconf, ND_MERGEADDR, 0, (char *)&ma); - free((void *) ma.c_uaddr); + (void) rw_unlock(&list_rbl_lock); + free(ma.c_uaddr); if (stat) (void) syslog(LOG_ERR, "netdir_merge failed for %s: %s", - nconf->nc_netid, netdir_sperror()); -#ifdef ND_DEBUG -fprintf(stderr, -"rpcbproc_callit_com: s_uaddr = %s, c_uaddr = %s, merged m_uaddr = %s\n", - ma.s_uaddr, ma.c_uaddr, ma.m_uaddr); -#endif + nconf->nc_netid, netdir_sperror()); + if ((fd = find_rmtcallfd_by_netid(nconf->nc_netid)) == -1) { if (reply_type == RPCBPROC_INDIRECT) svcerr_systemerr(transp); - free((void *) ma.m_uaddr); - ma.m_uaddr = NULL; + free(ma.m_uaddr); goto error; } + bd = get_svc_dg_data(transp); - call_msg.rm_xid = forward_register(bd->su_xid, - caller, fd, ma.m_uaddr, reply_type, versnum); - if (call_msg.rm_xid == 0) { + + assert(!MUTEX_HELD(&finfo_lock)); + fi = forward_register(bd->su_xid, caller, fd, ma.m_uaddr); + if (fi == NULL) { + /* forward_register failed. Perhaps no memory. */ + free(ma.m_uaddr); + if (debugging) + fprintf(stderr, + "rpcbproc_callit_com: forward_register failed\n"); + assert(!MUTEX_HELD(&finfo_lock)); + goto error; + } + /* forward_register() returns with finfo_lock held when successful */ + assert(MUTEX_HELD(&finfo_lock)); + + if (fi->flag & FINFO_ACTIVE) { /* * A duplicate request for the slow server. Let's not * beat on it any more. */ + (void) mutex_unlock(&finfo_lock); + free(ma.m_uaddr); if (debugging) fprintf(stderr, - "rpcbproc_callit_com: duplicate request\n"); - free((void *) ma.m_uaddr); - ma.m_uaddr = NULL; - goto error; - } else if (call_msg.rm_xid == (uint32_t)-1) { - /* forward_register failed. Perhaps no memory. */ - if (debugging) - fprintf(stderr, - "rpcbproc_callit_com: forward_register failed\n"); - free((void *) ma.m_uaddr); - ma.m_uaddr = NULL; + "rpcbproc_callit_com: duplicate request\n"); goto error; } + fi->flag |= FINFO_ACTIVE; -#ifdef DEBUG_RMTCALL - fprintf(stderr, - "rpcbproc_callit_com: original XID %x, new XID %x\n", - bd->su_xid, call_msg.rm_xid); -#endif + call_msg.rm_xid = fi->forward_xid; call_msg.rm_direction = CALL; call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; - call_msg.rm_call.cb_prog = a.rmt_prog; - call_msg.rm_call.cb_vers = a.rmt_vers; + call_msg.rm_call.cb_prog = arg.prog; + call_msg.rm_call.cb_vers = arg.vers; + if (sendsz > RPC_BUF_MAX) { -#ifdef notyet - outbuf_alloc = alloca(sendsz); /* not in IDR2? */ -#else outbuf_alloc = malloc(sendsz); -#endif /* notyet */ if (outbuf_alloc == NULL) { + forward_destroy(fi); + (void) mutex_unlock(&finfo_lock); if (reply_type == RPCBPROC_INDIRECT) svcerr_systemerr(transp); if (debugging) fprintf(stderr, - "rpcbproc_callit_com: No memory!\n"); + "rpcbproc_callit_com: No memory!\n"); goto error; } xdrmem_create(&outxdr, outbuf_alloc, sendsz, XDR_ENCODE); } else { xdrmem_create(&outxdr, outbuf, sendsz, XDR_ENCODE); } + outxdr_created = TRUE; + if (!xdr_callhdr(&outxdr, &call_msg)) { + forward_destroy(fi); + (void) mutex_unlock(&finfo_lock); if (reply_type == RPCBPROC_INDIRECT) svcerr_systemerr(transp); if (debugging) fprintf(stderr, - "rpcbproc_callit_com: xdr_callhdr failed\n"); + "rpcbproc_callit_com: xdr_callhdr failed\n"); goto error; } - if (!xdr_u_long(&outxdr, &(a.rmt_proc))) { + + if (!xdr_u_long(&outxdr, &arg.proc)) { + forward_destroy(fi); + (void) mutex_unlock(&finfo_lock); if (reply_type == RPCBPROC_INDIRECT) svcerr_systemerr(transp); if (debugging) fprintf(stderr, - "rpcbproc_callit_com: xdr_u_long failed\n"); + "rpcbproc_callit_com: xdr_u_long failed\n"); goto error; } @@ -1043,56 +1039,66 @@ fprintf(stderr, struct authsys_parms *au; au = (struct authsys_parms *)rqstp->rq_clntcred; - auth = authsys_create(au->aup_machname, - au->aup_uid, au->aup_gid, - au->aup_len, au->aup_gids); + auth = authsys_create(au->aup_machname, au->aup_uid, + au->aup_gid, au->aup_len, au->aup_gids); if (auth == NULL) /* fall back */ auth = authnone_create(); } else { /* we do not support any other authentication scheme */ - if (debugging) - fprintf(stderr, -"rpcbproc_callit_com: oa_flavor != AUTH_NONE and oa_flavor != AUTH_SYS\n"); + forward_destroy(fi); + (void) mutex_unlock(&finfo_lock); if (reply_type == RPCBPROC_INDIRECT) svcerr_weakauth(transp); /* XXX too strong.. */ + if (debugging) + fprintf(stderr, "rpcbproc_callit_com: oa_flavor != " + "AUTH_NONE and oa_flavor != AUTH_SYS\n"); goto error; } if (auth == NULL) { + forward_destroy(fi); + (void) mutex_unlock(&finfo_lock); if (reply_type == RPCBPROC_INDIRECT) svcerr_systemerr(transp); if (debugging) - fprintf(stderr, - "rpcbproc_callit_com: authwhatever_create returned NULL\n"); + fprintf(stderr, "rpcbproc_callit_com: " + "authwhatever_create returned NULL\n"); goto error; } if (!AUTH_MARSHALL(auth, &outxdr)) { + forward_destroy(fi); + (void) mutex_unlock(&finfo_lock); if (reply_type == RPCBPROC_INDIRECT) svcerr_systemerr(transp); AUTH_DESTROY(auth); if (debugging) fprintf(stderr, - "rpcbproc_callit_com: AUTH_MARSHALL failed\n"); + "rpcbproc_callit_com: AUTH_MARSHALL failed\n"); goto error; } AUTH_DESTROY(auth); - if (!xdr_opaque_parms(&outxdr, &a)) { + + if (!xdr_opaque(&outxdr, arg.args.args_val, arg.args.args_len)) { + forward_destroy(fi); + (void) mutex_unlock(&finfo_lock); if (reply_type == RPCBPROC_INDIRECT) svcerr_systemerr(transp); if (debugging) fprintf(stderr, - "rpcbproc_callit_com: xdr_opaque_parms failed\n"); + "rpcbproc_callit_com: xdr_opaque failed\n"); goto error; } - outlen = (int)XDR_GETPOS(&outxdr); + + tu_data.udata.len = XDR_GETPOS(&outxdr); if (outbuf_alloc) tu_data.udata.buf = outbuf_alloc; else tu_data.udata.buf = outbuf; - tu_data.udata.len = outlen; tu_data.opt.len = 0; na = uaddr2taddr(nconf, ma.m_uaddr); if (!na) { + forward_destroy(fi); + (void) mutex_unlock(&finfo_lock); if (reply_type == RPCBPROC_INDIRECT) svcerr_systemerr(transp); goto error; @@ -1100,207 +1106,262 @@ fprintf(stderr, tu_data.addr = *na; if (t_sndudata(fd, &tu_data) == -1) { + int err = errno; + forward_destroy(fi); + (void) mutex_unlock(&finfo_lock); + + netdir_free((char *)na, ND_ADDR); + + if (reply_type == RPCBPROC_INDIRECT) + svcerr_systemerr(transp); if (debugging) fprintf(stderr, - "rpcbproc_callit_com: t_sndudata failed: t_errno %d, errno %d\n", - t_errno, errno); + "rpcbproc_callit_com: t_sndudata failed: " + "t_errno %d, errno %d\n", t_errno, err); + goto error; + } + + netdir_free((char *)na, ND_ADDR); + xdr_destroy(&outxdr); + outxdr_created = FALSE; + if (outbuf_alloc != NULL) { + free(outbuf_alloc); + outbuf_alloc = NULL; + } + svc_freeargs(transp, xdr_rpcb_rmtcallargs, (char *)&arg); + + to.tv_sec = time(NULL) + MAXTIME_OFF; + to.tv_nsec = 0; + + while (fi->reply_data == NULL && + cond_timedwait(&fi->cv, &finfo_lock, &to) != ETIME) + ; + + if (fi->reply_data == NULL) { + forward_destroy(fi); + (void) mutex_unlock(&finfo_lock); + if (reply_type == RPCBPROC_INDIRECT) svcerr_systemerr(transp); - goto error; + if (debugging) + (void) fprintf(stderr, + "rpcbproc_callit_com: timeout\n"); + return; + } + + if (fi->reply_error.re_status != RPC_SUCCESS) { + forward_destroy(fi); + (void) mutex_unlock(&finfo_lock); + + if (reply_type == RPCBPROC_INDIRECT) + svcerr_systemerr(transp); + if (debugging) + (void) fprintf(stderr, + "rpcbproc_callit_com: error in reply: %s\n", + clnt_sperrno(fi->reply_error.re_status)); + return; } - goto out; + + switch (versnum) { +#ifdef PORTMAP + case PMAPVERS: + { + rmtcallres result; + int h1, h2, h3, h4, p1, p2; + + /* interpret the universal address for TCP/IP */ + if (sscanf(fi->uaddr, "%d.%d.%d.%d.%d.%d", + &h1, &h2, &h3, &h4, &p1, &p2) != 6) + break; + + result.port = ((p1 & 0xff) << 8) + (p2 & 0xff); + result.res.res_len = fi->res_len; + result.res.res_val = fi->res_val; + + svc_sendreply(transp, xdr_rmtcallres, (char *)&result); + } + break; +#endif + case RPCBVERS: + case RPCBVERS4: + { + rpcb_rmtcallres result; + + result.addr = fi->uaddr; + result.results.results_len = fi->res_len; + result.results.results_val = fi->res_val; + + svc_sendreply(transp, xdr_rpcb_rmtcallres, + (char *)&result); + } + break; + } + + forward_destroy(fi); + (void) mutex_unlock(&finfo_lock); + + return; error: - if ((call_msg.rm_xid != 0) && (ma.m_uaddr != NULL)) - (void) free_slot_by_xid(call_msg.rm_xid, ma.m_uaddr); -out: - if (buf_alloc) - free((void *) buf_alloc); - if (outbuf_alloc) - free((void *) outbuf_alloc); - if (na) - netdir_free((char *)na, ND_ADDR); + if (outxdr_created) + xdr_destroy(&outxdr); + free(outbuf_alloc); + svc_freeargs(transp, xdr_rpcb_rmtcallargs, (char *)&arg); } -#define NFORWARD 64 -#define MAXTIME_OFF 300 /* 5 minutes */ +static struct finfo *forward_find(ulong_t, char *); -struct finfo { - int flag; -#define FINFO_ACTIVE 0x1 - ulong_t caller_xid; - struct netbuf *caller_addr; - ulong_t forward_xid; - int forward_fd; - char *uaddr; - ulong_t reply_type; - ulong_t versnum; - time_t time; -}; -static struct finfo FINFO[NFORWARD]; /* - * Makes an entry into the FIFO for the given request. - * If duplicate request, returns a 0, else returns the xid of its call. + * Adds an entry into the finfo list for the given request. Returns the finfo + * and finfo_lock is left held. If duplicate request, returns finfo with + * FINFO_ACTIVE, else returns finfo without FINFO_ACTIVE. + * If failed, returns NULL and finfo_lock is left unheld. */ -static ulong_t -forward_register(caller_xid, caller_addr, forward_fd, uaddr, - reply_type, versnum) - ulong_t caller_xid; - struct netbuf *caller_addr; - int forward_fd; - char *uaddr; - ulong_t reply_type; - ulong_t versnum; +static struct finfo * +forward_register(ulong_t caller_xid, struct netbuf *caller_addr, int forward_fd, + char *uaddr) { - int i; - int j = 0; - time_t min_time, time_now; - static ulong_t lastxid; - int entry = -1; - - min_time = FINFO[0].time; - time_now = time((time_t *)0); + struct finfo *fi; + + (void) mutex_lock(&finfo_lock); + if (rpcb_rmtcalls_max == 0) { + (void) mutex_unlock(&finfo_lock); + return (NULL); + } + /* * initialization: once this has happened, lastxid will - * - always be a multiple of NFORWARD (which has to be a power of 2), - * - never be 0 again, - * - never be (ulong_t)(-NFORWARD) - * when entering or returning from this function. + * never be 0 again, when entering or returning from this function. */ - if (lastxid == 0) { - lastxid = time_now * NFORWARD; - /* - * avoid lastxid wraparound to 0, - * and generating a forward_xid of -1 - */ - if (lastxid >= (ulong_t)(-NFORWARD)) - lastxid = NFORWARD; - } + if (lastxid == 0) + lastxid = time(NULL); /* - * Check if it is an duplicate entry. Then, - * try to find an empty slot. If not available, then - * use the slot with the earliest time. + * Check if it is an duplicate entry */ - for (i = 0; i < NFORWARD; i++) { - if (FINFO[i].flag & FINFO_ACTIVE) { - if ((FINFO[i].caller_xid == caller_xid) && - (FINFO[i].reply_type == reply_type) && - (FINFO[i].versnum == versnum) && - (!netbufcmp(FINFO[i].caller_addr, - caller_addr))) { - FINFO[i].time = time((time_t *)0); - return (0); /* Duplicate entry */ - } else { - /* Should we wait any longer */ - if ((time_now - FINFO[i].time) > MAXTIME_OFF) - (void) free_slot_by_index(i); - } - } - if (entry == -1) { - if ((FINFO[i].flag & FINFO_ACTIVE) == 0) { - entry = i; - } else if (FINFO[i].time < min_time) { - j = i; - min_time = FINFO[i].time; - } + for (fi = fihead; fi != NULL; fi = fi->next) { + if (fi->caller_xid == caller_xid && + netbufcmp(fi->caller_addr, caller_addr)) { + assert(fi->flag & FINFO_ACTIVE); + return (fi); } } - if (entry != -1) { - /* use this empty slot */ - j = entry; - } else { - (void) free_slot_by_index(j); - } - if ((FINFO[j].caller_addr = netbufdup(caller_addr)) == NULL) { - return ((ulong_t)-1); - } - rpcb_rmtcalls++; /* no of pending calls */ - FINFO[j].flag = FINFO_ACTIVE; - FINFO[j].reply_type = reply_type; - FINFO[j].versnum = versnum; - FINFO[j].time = time_now; - FINFO[j].caller_xid = caller_xid; - FINFO[j].forward_fd = forward_fd; + + fi = malloc(sizeof (*fi)); + if (fi == NULL) { + (void) mutex_unlock(&finfo_lock); + return (NULL); + } + + if ((fi->caller_addr = netbufdup(caller_addr)) == NULL) { + (void) mutex_unlock(&finfo_lock); + free(fi); + return (NULL); + } + + /* + * Generate new xid and make sure it is unique. + */ + do { + lastxid++; + /* avoid lastxid wraparound to 0 */ + if (lastxid == 0) + lastxid = 1; + } while (forward_find(lastxid, uaddr) != NULL); + + fi->prev = NULL; + fi->next = fihead; + fihead = fi; + if (fitail == NULL) + fitail = fi; + + fi->flag = 0; + fi->caller_xid = caller_xid; + + fi->forward_xid = lastxid; + fi->forward_fd = forward_fd; + /* * Though uaddr is not allocated here, it will still be freed - * from free_slot_*(). + * from forward_destroy(). */ - FINFO[j].uaddr = uaddr; - lastxid = lastxid + NFORWARD; - /* avoid lastxid wraparound to 0, and generating a forward_xid of -1 */ - if (lastxid >= (ulong_t)(-NFORWARD)) - lastxid = NFORWARD; - - FINFO[j].forward_xid = lastxid + j; /* encode slot */ - return (FINFO[j].forward_xid); /* forward on this xid */ -} + fi->uaddr = uaddr; -static struct finfo * -forward_find(reply_xid, uaddr) - ulong_t reply_xid; - char *uaddr; -{ - int i; + fi->reply_data = NULL; + (void) cond_init(&fi->cv, USYNC_THREAD, NULL); - i = reply_xid % NFORWARD; - if (i < 0) - i += NFORWARD; - if ((FINFO[i].flag & FINFO_ACTIVE) && - (strcmp(FINFO[i].uaddr, uaddr) == 0) && - (FINFO[i].forward_xid == reply_xid)) { - return (&FINFO[i]); + rpcb_rmtcalls++; + if (rpcb_rmtcalls > rpcb_rmtcalls_max) { + assert(fitail != fi); + (void) cond_signal(&fitail->cv); } - return (NULL); + + return (fi); } -static int -free_slot_by_xid(xid, uaddr) - ulong_t xid; - char *uaddr; +static void +forward_destroy(struct finfo *fi) { - int entry; + assert(MUTEX_HELD(&finfo_lock)); + assert(fi->flag & FINFO_ACTIVE); - if (forward_find(xid, uaddr)) { - entry = xid % NFORWARD; - if (entry < 0) - entry += NFORWARD; - return (free_slot_by_index(entry)); + if (fihead == fi) { + assert(fi->prev == NULL); + fihead = fi->next; + } else { + fi->prev->next = fi->next; + } + + if (fitail == fi) { + assert(fi->next == NULL); + fitail = fi->prev; + } else { + fi->next->prev = fi->prev; + } + + netbuffree(fi->caller_addr); + free(fi->uaddr); + if (fi->reply_data != NULL) + t_free((char *)fi->reply_data, T_UNITDATA); + (void) cond_destroy(&fi->cv); + + free(fi); + + rpcb_rmtcalls--; + if (rpcb_rmtcalls > rpcb_rmtcalls_max) { + assert(fitail != NULL); + (void) cond_signal(&fitail->cv); } - return (0); } -static int -free_slot_by_index(index) - int index; +static struct finfo * +forward_find(ulong_t reply_xid, char *uaddr) { - struct finfo *fi; + struct finfo *fi; - fi = &FINFO[index]; - if (fi->flag & FINFO_ACTIVE) { - netbuffree(fi->caller_addr); - free((void *) fi->uaddr); - fi->flag &= ~FINFO_ACTIVE; - rpcb_rmtcalls--; - return (1); + assert(MUTEX_HELD(&finfo_lock)); + + for (fi = fihead; fi != NULL; fi = fi->next) { + if (fi->forward_xid == reply_xid && + strcmp(fi->uaddr, uaddr) == 0) + return (fi); } - return (0); + + return (NULL); } static int -netbufcmp(n1, n2) - struct netbuf *n1, *n2; +netbufcmp(struct netbuf *n1, struct netbuf *n2) { return ((n1->len != n2->len) || memcmp(n1->buf, n2->buf, n1->len)); } static struct netbuf * -netbufdup(ap) - register struct netbuf *ap; +netbufdup(struct netbuf *ap) { - register struct netbuf *np; + struct netbuf *np; - np = (struct netbuf *) malloc(sizeof (struct netbuf) + ap->len); + np = malloc(sizeof (struct netbuf) + ap->len); if (np) { np->maxlen = np->len = ap->len; np->buf = ((char *)np) + sizeof (struct netbuf); @@ -1310,368 +1371,137 @@ netbufdup(ap) } static void -netbuffree(ap) - register struct netbuf *ap; +netbuffree(struct netbuf *ap) { - free((void *) ap); -} - -/* - * active_fd is used to determine whether an entry in svc_pollfd is: - * 1. not a forward fd (should be polled) - * 2. an active forward fd (should be polled) - * 3. an inactive forward fd (should not be polled) - */ -static bool_t -active_fd(fd) - int fd; -{ - int i; - time_t time_now; - - if (find_rmtcallxprt_by_fd(fd) == (SVCXPRT *)NULL) - return (TRUE); - if (rpcb_rmtcalls == 0) - return (FALSE); - time_now = time((time_t *)0); - for (i = 0; i < NFORWARD; i++) - if (FINFO[i].forward_fd == fd) { - if (FINFO[i].flag & FINFO_ACTIVE) { - /* Should we wait any longer */ - if ((time_now - FINFO[i].time) > MAXTIME_OFF) - (void) free_slot_by_index(i); - else - return (TRUE); - } - } - return (FALSE); -} - -#define MASKVAL (POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND) - -void -my_svc_run() -{ - size_t nfds; - struct pollfd pollfds[FD_SETSIZE]; - int poll_ret, check_ret; -#ifdef SVC_RUN_DEBUG - int i; -#endif - register struct pollfd *p; - - for (;;) { - { - register pollfd_t *in; - register int n; /* loop counter */ - - /* - * compress the sparse svc_pollfd strcutre - * into pollfds - */ - memset(pollfds, 0, sizeof (pollfds)); - p = pollfds; - for (in = svc_pollfd, n = 0; n < svc_max_pollfd; - n++, in++) { - if ((in->fd >= 0) && active_fd(in->fd)) { - p->fd = in->fd; - p->events = MASKVAL; - p->revents = 0; - p++; - } - } - nfds = p - pollfds; - } - poll_ret = 0; -#ifdef SVC_RUN_DEBUG - if (debugging) { - fprintf(stderr, "polling for read on fd < "); - for (i = 0, p = pollfds; i < nfds; i++, p++) - if (p->events) - fprintf(stderr, "%d ", p->fd); - fprintf(stderr, ">\n"); - } -#endif - switch (poll_ret = poll(pollfds, nfds, INFTIM)) { - case -1: - /* - * We ignore all errors, continuing with the assumption - * that it was set by the signal handlers (or any - * other outside event) and not caused by poll(). - * If it was our refresh signal, call the refresh - * function. - */ - if (sigrefresh) { - sigrefresh = 0; - rpcb_check_init(); - } - case 0: - continue; - default: -#ifdef SVC_RUN_DEBUG - if (debugging) { - fprintf(stderr, "poll returned read fds < "); - for (i = 0, p = pollfds; i < nfds; i++, p++) - if (p->revents) - fprintf(stderr, "%d ", p->fd); - fprintf(stderr, ">\n"); - } -#endif - /* - * If we found as many replies on callback fds - * as the number of descriptors selectable which - * poll() returned, there can be no more so we - * don't call svc_getreq_poll. Otherwise, there - * must be another so we must call svc_getreq_poll. - */ - if ((check_ret = check_rmtcalls(pollfds, nfds)) == - poll_ret) - continue; - svc_getreq_poll(pollfds, poll_ret-check_ret); - } - } -} - -static int -check_rmtcalls(pfds, nfds) - struct pollfd *pfds; - int nfds; -{ - int j, ncallbacks_found = 0; - SVCXPRT *xprt; - - /* - * This fd will not be polled if rpcb_rmtcalls == 0 - */ - if (rpcb_rmtcalls == 0) - return (0); - - for (j = 0; j < nfds; j++) { - if ((xprt = find_rmtcallxprt_by_fd(pfds[j].fd)) != NULL) { - if (pfds[j].revents) { - ncallbacks_found++; -#ifdef DEBUG_RMTCALL - if (debugging) - fprintf(stderr, -"my_svc_run: polled on forwarding fd %d, netid %s - calling handle_reply\n", - pfds[j].fd, xprt->xp_netid); -#endif - handle_reply(pfds[j].fd, xprt); - pfds[j].revents = 0; - } - } - } - return (ncallbacks_found); + free(ap); } static void -xprt_set_caller(xprt, fi) - SVCXPRT *xprt; - struct finfo *fi; +handle_reply(svc_input_id_t id, int fd, unsigned int events, void *cookie) { - struct svc_dg_data *bd; + struct t_unitdata *tr_data; + int res; - *(svc_getrpccaller(xprt)) = *(fi->caller_addr); - bd = get_svc_dg_data(xprt); - bd->su_xid = fi->caller_xid; /* set xid on reply */ -} + unsigned int inlen; + char *buffer; + XDR reply_xdrs; -/* - * Call svcerr_systemerr() only if RPCBVERS4 - */ -static void -send_svcsyserr(xprt, fi) - SVCXPRT *xprt; - struct finfo *fi; -{ - if (fi->reply_type == RPCBPROC_INDIRECT) { - xprt_set_caller(xprt, fi); - svcerr_systemerr(xprt); - } -} + struct rpc_msg reply_msg; + unsigned int pos; + unsigned int len; -static void -handle_reply(fd, xprt) - int fd; - SVCXPRT *xprt; -{ - XDR reply_xdrs; - struct rpc_msg reply_msg; - struct rpc_err reply_error; - char *buffer; - struct finfo *fi = NULL; - int inlen, pos, len, res, i; - struct r_rmtcall_args a; - struct t_unitdata *tr_data = NULL, *tu_data; - struct netconfig *nconf = NULL; + struct netconfig *nconf; char *uaddr = NULL; - nconf = rpcbind_get_conf(xprt->xp_netid); - if (nconf == NULL) { -#ifdef SVC_RUN_DEBUG - if (debugging) - fprintf(stderr, "handle_reply: null xp_netid\n"); -#endif - goto done; - } - /* - * If this fd is not active on the forward list, ignore it - * If the svc_pollfd structure has multiple settings - * of the same fd, then it will enter handle_reply() for the first one, - * set FINFO_ACTIVE false and then get another call to handle_reply() - * with the same, now inactive, fd. - */ - - for (i = 0; i < NFORWARD; i++) { - if ((FINFO[i].forward_fd == fd) && - (FINFO[i].flag & FINFO_ACTIVE)) - break; - } + struct finfo *fi; - if (i == NFORWARD) { -#ifdef SVC_RUN_DEBUG - if (debugging) { - fprintf(stderr, "Unsolicited message on rmtcall fd\n"); - } -#endif + tr_data = (struct t_unitdata *)t_alloc(fd, T_UNITDATA, + T_ADDR | T_UDATA); + if (tr_data == NULL) { + syslog(LOG_ERR, "handle_reply: t_alloc failed!"); return; } - reply_msg.rm_xid = 0; /* for easier error handling */ - tr_data = (struct t_unitdata *)t_alloc(fd, T_UNITDATA, - T_ADDR | T_UDATA); - if (tr_data == (struct t_unitdata *)NULL) { - if (debugging) - fprintf(stderr, - "handle_reply: t_alloc T_UNITDATA failed\n"); - goto done; - } do { - int moreflag; + int moreflag = 0; - moreflag = 0; if (errno == EINTR) errno = 0; res = t_rcvudata(fd, tr_data, &moreflag); if (moreflag & T_MORE) { /* Drop this packet - we have no more space. */ if (debugging) - fprintf(stderr, - "handle_reply: recvd packet with T_MORE flag set\n"); + fprintf(stderr, "handle_reply: recvd packet " + "with T_MORE flag set\n"); goto done; } - } while (res < 0 && (t_errno == TSYSERR && errno == EINTR)); - if (res < 0) { - if (t_errno == TLOOK) { - if (debugging) - fprintf(stderr, - "handle_reply: t_rcvudata returned %d, t_errno TLOOK\n", res); - (void) t_rcvuderr(fd, (struct t_uderr *)NULL); - } + } while (res < 0 && t_errno == TSYSERR && errno == EINTR); + if (res < 0) { if (debugging) - fprintf(stderr, - "handle_reply: t_rcvudata returned %d, t_errno %d, errno %d\n", - res, t_errno, errno); + fprintf(stderr, "handle_reply: t_rcvudata returned " + "%d, t_errno %d, errno %d\n", res, t_errno, errno); + + if (t_errno == TLOOK) + (void) t_rcvuderr(fd, NULL); goto done; } inlen = tr_data->udata.len; - uaddr = taddr2uaddr(nconf, &tr_data->addr); - if (uaddr == NULL) - goto done; - -#ifdef DEBUG_MORE - if (debugging) - fprintf(stderr, - "handle_reply: t_rcvudata received %d-byte packet from %s\n", - inlen, uaddr); -#endif buffer = tr_data->udata.buf; - if (buffer == (char *)NULL) { - goto done; - } + assert(buffer != NULL); + xdrmem_create(&reply_xdrs, buffer, inlen, XDR_DECODE); + reply_msg.acpted_rply.ar_verf = _null_auth; reply_msg.acpted_rply.ar_results.where = 0; reply_msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void; - xdrmem_create(&reply_xdrs, buffer, (uint_t)inlen, XDR_DECODE); if (!xdr_replymsg(&reply_xdrs, &reply_msg)) { + xdr_destroy(&reply_xdrs); if (debugging) (void) fprintf(stderr, - "handle_reply: xdr_replymsg failed\n"); + "handle_reply: xdr_replymsg failed\n"); goto done; } - fi = forward_find((ulong_t)reply_msg.rm_xid, uaddr); - if (fi == NULL) + pos = XDR_GETPOS(&reply_xdrs); + xdr_destroy(&reply_xdrs); + + len = inlen - pos; + + nconf = rpcbind_get_conf((char *)cookie); + if (nconf == NULL) { + syslog(LOG_ERR, "handle_reply: rpcbind_get_conf failed!"); goto done; -#ifdef SVC_RUN_DEBUG - if (debugging) { - fprintf(stderr, "handle_reply: reply xid: %d fi addr: %x\n", - reply_msg.rm_xid, fi); } -#endif - __seterr_reply(&reply_msg, &reply_error); - if (reply_error.re_status != RPC_SUCCESS) { - if (debugging) - (void) fprintf(stderr, "handle_reply: %s\n", - clnt_sperrno(reply_error.re_status)); - send_svcsyserr(xprt, fi); + uaddr = taddr2uaddr(nconf, &tr_data->addr); + if (uaddr == NULL) { + syslog(LOG_ERR, "handle_reply: taddr2uaddr failed!"); goto done; } - pos = XDR_GETPOS(&reply_xdrs); - len = inlen - pos; - a.rmt_args.args = &buffer[pos]; - a.rmt_args.arglen = len; - a.rmt_uaddr = fi->uaddr; - a.rmt_localvers = fi->versnum; - - xprt_set_caller(xprt, fi); - /* XXX hack */ - tu_data = &(get_svc_dg_data(xprt)->su_tudata); - - tu_data->addr = xprt->xp_rtaddr; -#ifdef SVC_RUN_DEBUG - if (uaddr) - free((void *) uaddr); - uaddr = taddr2uaddr(nconf, svc_getrpccaller(xprt)); - if (debugging) { - fprintf(stderr, "handle_reply: forwarding address %s to %s\n", - a.rmt_uaddr, uaddr ? uaddr : "unknown"); + + (void) mutex_lock(&finfo_lock); + fi = forward_find(reply_msg.rm_xid, uaddr); + if (fi == NULL) { + (void) mutex_unlock(&finfo_lock); + goto done; } -#endif - svc_sendreply(xprt, (xdrproc_t)xdr_rmtcall_result, (char *)&a); + + fi->reply_data = tr_data; + tr_data = NULL; + + __seterr_reply(&reply_msg, &fi->reply_error); + + fi->res_len = len; + fi->res_val = &buffer[pos]; + + (void) cond_signal(&fi->cv); + (void) mutex_unlock(&finfo_lock); + done: - if (uaddr) - free((void *) uaddr); + free(uaddr); if (tr_data) t_free((char *)tr_data, T_UNITDATA); - if ((fi == NULL) || (reply_msg.rm_xid == 0)) { -#ifdef SVC_RUN_DEBUG - if (debugging) { - fprintf(stderr, "handle_reply: NULL xid on exit!\n"); - } -#endif - } else - (void) free_slot_by_xid((ulong_t)reply_msg.rm_xid, fi->uaddr); } +/* + * prog: Program Number + * netid: Transport Provider token + * lowvp: Low version number + * highvp: High version number + */ static void -find_versions(prog, netid, lowvp, highvp) - ulong_t prog; /* Program Number */ - char *netid; /* Transport Provider token */ - ulong_t *lowvp; /* Low version number */ - ulong_t *highvp; /* High version number */ +find_versions(rpcprog_t prog, char *netid, rpcvers_t *lowvp, rpcvers_t *highvp) { - register rpcblist_ptr rbl; - int lowv = 0; - int highv = 0; + rpcblist_ptr rbl; + rpcvers_t lowv = 0; + rpcvers_t highv = 0; + + assert(RW_LOCK_HELD(&list_rbl_lock)); for (rbl = list_rbl; rbl != NULL; rbl = rbl->rpcb_next) { if ((rbl->rpcb_map.r_prog != prog) || - ((rbl->rpcb_map.r_netid != NULL) && - (strcasecmp(rbl->rpcb_map.r_netid, netid) != 0))) + (strcasecmp(rbl->rpcb_map.r_netid, netid) != 0)) continue; if (lowv == 0) { highv = rbl->rpcb_map.r_vers; @@ -1682,6 +1512,7 @@ find_versions(prog, netid, lowvp, highvp) highv = rbl->rpcb_map.r_vers; } } + *lowvp = lowv; *highvp = highv; } @@ -1696,25 +1527,28 @@ find_versions(prog, netid, lowvp, highvp) * program using clnt_geterr() and use those program version numbers. * * Returns the RPCBLIST for the given prog, vers and netid + * + * prog: Program Number + * vers: Version Number + * netid: Transport Provider token */ static rpcblist_ptr -find_service(prog, vers, netid) - ulong_t prog; /* Program Number */ - ulong_t vers; /* Version Number */ - char *netid; /* Transport Provider token */ +find_service(rpcprog_t prog, rpcvers_t vers, char *netid) { - register rpcblist_ptr hit = NULL; - register rpcblist_ptr rbl; + rpcblist_ptr hit = NULL; + rpcblist_ptr rbl; + + assert(RW_LOCK_HELD(&list_rbl_lock)); for (rbl = list_rbl; rbl != NULL; rbl = rbl->rpcb_next) { if ((rbl->rpcb_map.r_prog != prog) || - ((rbl->rpcb_map.r_netid != NULL) && - (strcasecmp(rbl->rpcb_map.r_netid, netid) != 0))) + (strcasecmp(rbl->rpcb_map.r_netid, netid) != 0)) continue; hit = rbl; if (rbl->rpcb_map.r_vers == vers) break; } + return (hit); } @@ -1726,11 +1560,6 @@ uid_t rpcb_caller_uid(SVCXPRT *transp) { ucred_t *uc = alloca(ucred_size()); - static zoneid_t myzone = MIN_ZONEID - 1; - uid_t uid; - - if (myzone == MIN_ZONEID - 1) - myzone = getzoneid(); if (svc_getcallerucred(transp, &uc) != 0 || (ucred_getzoneid(uc)) != myzone) { @@ -1745,9 +1574,7 @@ rpcb_caller_uid(SVCXPRT *transp) * a pointer to it. Similar to getwd(). */ char * -getowner(transp, owner) - SVCXPRT *transp; - char *owner; +getowner(SVCXPRT *transp, char *owner) { uid_t uid = rpcb_caller_uid(transp); @@ -1767,8 +1594,7 @@ getowner(transp, owner) * Add this to the pmap list only if it is UDP or TCP. */ static int -add_pmaplist(arg) - RPCB *arg; +add_pmaplist(RPCB *arg) { pmap pmap; pmaplist *pml; @@ -1786,7 +1612,7 @@ add_pmaplist(arg) /* interpret the universal address for TCP/IP */ if (sscanf(arg->r_addr, "%d.%d.%d.%d.%d.%d", - &h1, &h2, &h3, &h4, &p1, &p2) != 6) + &h1, &h2, &h3, &h4, &p1, &p2) != 6) return (0); pmap.pm_port = ((p1 & 0xff) << 8) + (p2 & 0xff); pmap.pm_prog = arg->r_prog; @@ -1801,6 +1627,8 @@ add_pmaplist(arg) } pml->pml_map = pmap; pml->pml_next = NULL; + + (void) rw_wrlock(&list_pml_lock); if (list_pml == NULL) { list_pml = pml; } else { @@ -1811,6 +1639,8 @@ add_pmaplist(arg) ; fnd->pml_next = pml; } + (void) rw_unlock(&list_pml_lock); + return (0); } @@ -1820,9 +1650,9 @@ add_pmaplist(arg) int del_pmaplist(RPCB *arg) { - register pmaplist *pml; + pmaplist *pml; pmaplist *prevpml, *fnd; - long prot; + rpcport_t prot; if (strcmp(arg->r_netid, udptrans) == 0) { /* It is UDP! */ @@ -1830,12 +1660,15 @@ del_pmaplist(RPCB *arg) } else if (strcmp(arg->r_netid, tcptrans) == 0) { /* It is TCP */ prot = IPPROTO_TCP; - } else if (arg->r_netid[0] == NULL) { + } else if (arg->r_netid[0] == '\0') { prot = 0; /* Remove all occurrences */ } else { /* Not a IP protocol */ return (0); } + + assert(RW_WRITE_HELD(&list_pml_lock)); + for (prevpml = NULL, pml = list_pml; pml; /* cstyle */) { if ((pml->pml_map.pm_prog != arg->r_prog) || (pml->pml_map.pm_vers != arg->r_vers) || @@ -1852,8 +1685,9 @@ del_pmaplist(RPCB *arg) list_pml = pml; else prevpml->pml_next = pml; - free((void *) fnd); + free(fnd); } + return (0); } #endif /* PORTMAP */ diff --git a/usr/src/cmd/rpcbind/rpcbind.c b/usr/src/cmd/rpcbind/rpcbind.c index c6016ca..5703482 100644 --- a/usr/src/cmd/rpcbind/rpcbind.c +++ b/usr/src/cmd/rpcbind/rpcbind.c @@ -22,6 +22,9 @@ * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ +/* + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* @@ -35,9 +38,59 @@ */ /* - * rpcbind.c - * Implements the program, version to address mapping for rpc. + * This is an implementation of RCPBIND according the RFC 1833: Binding + * Protocols for ONC RPC Version 2. The RFC specifies three versions of the + * binding protocol: + * + * 1) RPCBIND Version 3 (Section 2.2.1 of the RFC) + * 2) RPCBIND, Version 4 (Section 2.2.2 of the RFC) + * 3) Port Mapper Program Protocol (Section 3 of the RFC) + * + * Where the "Port Mapper Program Protocol" is refered as Version 2 of the + * binding protocol. The implementation of the Version 2 of the binding + * protocol is compiled in only in a case the PORTMAP macro is defined (by + * default it is defined). + * + * The implementation is based on top of the networking services library - + * libnsl(3lib) and uses Automatic MT mode (see rcp_control(3nsl) and + * svc_run(3nsl) for more details). + * + * Usually, when a thread handles an RPCBIND procedure (one that arrived from a + * client), it obtains the data for the response internally, and immediately + * sends the response back to the client. The only exception to this rule are + * remote (aka indirect) RPC calls, for example RPCBPROC_INDIRECT. Such + * procedures are designed to forward the RPC request from the client to some + * other RPC service specified by the client, wait for the result, and forward + * the result back to the client. This is implemented in rpcbproc_callit_com(). * + * The response from the other (remote) RPC service is handled in + * handle_reply(), where the thread waiting in rpcbproc_callit_com() is woken + * up to finish the handling and to send (forward) the response back to the + * client. + * + * The thread implementing the indirect RPC call might be blocked in the + * rpcbproc_callit_com() waiting for the response from the other RPC service + * for very long time. During this time the thread is unable to handle other + * RPCBIND requests. To avoid a case when all threads are waiting in + * rpcbproc_callit_com() and there is no free thread able to handle other + * RPCBIND requests, the implementation has reserved eight threads to never be + * used for the remote RPC calls. The number of active remote RPC calls is in + * rpcb_rmtcalls, the upper limit of such calls is in rpcb_rmtcalls_max. + * + * In addition to the worker threads described above, there are two other + * threads. The logthread() thread is responsible for asynchronous logging to + * syslog. The terminate() thread is signal handler responsible for reload of + * the rpcbind configuration (on SIGHUP), or for gracefully shutting down + * rpcbind (otherwise). + * + * There are two global lists used for holding the information about the + * registered services: list_rbl is for Version 3 and 4 of the binding + * protocol, and list_pml is for Version 2. To protect these lists, two global + * readers/writer locks are defined and heavily used across the rpcbind + * implementation: list_rbl_lock protecting list_rbl, and list_pml_lock, + * protecting list_pml. + * + * The defined locking order is: list_rbl_lock first, list_pml_lock second. */ #include @@ -74,20 +127,14 @@ #include #include #include +#include +#include +#include +#include -#ifdef PORTMAP -extern void pmap_service(struct svc_req *, SVCXPRT *xprt); -#endif -extern void rpcb_service_3(struct svc_req *, SVCXPRT *xprt); -extern void rpcb_service_4(struct svc_req *, SVCXPRT *xprt); -extern void read_warmstart(void); -extern void write_warmstart(void); -extern int Is_ipv6present(void); - -#define MAX_FILEDESC_LIMIT 1023 +static sigset_t sigwaitset; -static void terminate(int); -static void note_refresh(int); +static void terminate(void); static void detachfromtty(void); static void parseargs(int, char *[]); static void rbllist_add(ulong_t, ulong_t, struct netconfig *, struct netbuf *); @@ -99,32 +146,42 @@ static int setopt_reuseaddr(int); static int setopt_anon_mlp(int); static int setup_callit(int); +static void rpcb_check_init(void); + /* Global variables */ -#ifdef ND_DEBUG -int debugging = 1; /* Tell me what's going on */ -#else int debugging = 0; /* Tell me what's going on */ -#endif static int ipv6flag = 0; int doabort = 0; /* When debugging, do an abort on errors */ -static int listen_backlog = 64; +static int listen_backlog; +static const int reserved_threads = 8; + +/* + * list_rbl_lock protects list_rbl + * lock order: list_rbl_lock, list_pml_lock + */ +rwlock_t list_rbl_lock = DEFAULTRWLOCK; rpcblist_ptr list_rbl; /* A list of version 3/4 rpcbind services */ + char *loopback_dg; /* Datagram loopback transport, for set and unset */ char *loopback_vc; /* COTS loopback transport, for set and unset */ char *loopback_vc_ord; /* COTS_ORD loopback transport, for set and unset */ -boolean_t verboselog = B_FALSE; -boolean_t wrap_enabled = B_FALSE; -boolean_t allow_indirect = B_TRUE; -boolean_t local_only = B_FALSE; - -volatile sig_atomic_t sigrefresh; +volatile boolean_t verboselog = B_FALSE; +volatile boolean_t wrap_enabled = B_FALSE; +volatile boolean_t allow_indirect = B_TRUE; +volatile boolean_t local_only = B_TRUE; /* Local Variable */ static int warmstart = 0; /* Grab a old copy of registrations */ #ifdef PORTMAP +/* + * list_pml_lock protects list_pml + * lock order: list_rbl_lock, list_pml_lock + */ +rwlock_t list_pml_lock = DEFAULTRWLOCK; PMAPLIST *list_pml; /* A list of version 2 rpcbind services */ + char *udptrans; /* Name of UDP transport */ char *tcptrans; /* Name of TCP transport */ char *udp_uaddr; /* Universal UDP address */ @@ -135,25 +192,42 @@ static char superuser[] = "superuser"; static const char daemon_dir[] = DAEMON_DIR; +static void +block_signals(void) +{ + (void) sigemptyset(&sigwaitset); + (void) sigaddset(&sigwaitset, SIGINT); + (void) sigaddset(&sigwaitset, SIGTERM); + (void) sigaddset(&sigwaitset, SIGQUIT); + (void) sigaddset(&sigwaitset, SIGHUP); + (void) sigprocmask(SIG_BLOCK, &sigwaitset, NULL); + + /* ignore other signals that could get sent */ + (void) signal(SIGUSR1, SIG_IGN); + (void) signal(SIGUSR2, SIG_IGN); +} + int main(int argc, char *argv[]) { struct netconfig *nconf; void *nc_handle; /* Net config handle */ struct rlimit rl; + int rpc_svc_fdunlim = 1; + int rpc_svc_mode = RPC_SVC_MT_AUTO; int maxrecsz = RPC_MAXDATASIZE; boolean_t can_do_mlp; - parseargs(argc, argv); + block_signals(); - getrlimit(RLIMIT_NOFILE, &rl); + parseargs(argc, argv); - if (rl.rlim_cur < MAX_FILEDESC_LIMIT) { - if (rl.rlim_max <= MAX_FILEDESC_LIMIT) - rl.rlim_cur = rl.rlim_max; - else - rl.rlim_cur = MAX_FILEDESC_LIMIT; - setrlimit(RLIMIT_NOFILE, &rl); + if (getrlimit(RLIMIT_NOFILE, &rl) != 0) { + syslog(LOG_ERR, "getrlimit failed"); + } else { + rl.rlim_cur = rl.rlim_max; + if (setrlimit(RLIMIT_NOFILE, &rl) != 0) + syslog(LOG_ERR, "setrlimit failed"); } (void) enable_extended_FILE_stdio(-1, -1); @@ -181,6 +255,24 @@ main(int argc, char *argv[]) exit(1); } + myzone = getzoneid(); + + /* + * Set number of file descriptors to unlimited + */ + if (!rpc_control(RPC_SVC_USE_POLLFD, &rpc_svc_fdunlim)) { + syslog(LOG_INFO, "unable to set number of FD to unlimited"); + } + + /* + * Tell RPC that we want automatic thread mode. + * A new thread will be spawned for each request. + */ + if (!rpc_control(RPC_SVC_MTMODE_SET, &rpc_svc_mode)) { + syslog(LOG_ERR, "unable to set automatic MT mode"); + exit(1); + } + /* * Enable non-blocking mode and maximum record size checks for * connection oriented transports. @@ -189,19 +281,6 @@ main(int argc, char *argv[]) syslog(LOG_INFO, "unable to set RPC max record size"); } - nc_handle = setnetconfig(); /* open netconfig file */ - if (nc_handle == NULL) { - syslog(LOG_ERR, "could not read /etc/netconfig"); - exit(1); - } - loopback_dg = ""; - loopback_vc = ""; - loopback_vc_ord = ""; -#ifdef PORTMAP - udptrans = ""; - tcptrans = ""; -#endif - { /* * rpcbind is the first application to encounter the @@ -213,18 +292,32 @@ main(int argc, char *argv[]) trouble = check_netconfig(); if (trouble) { - syslog(LOG_ERR, - "%s: found %d errors with network configuration files. Exiting.", - argv[0], trouble); - fprintf(stderr, - "%s: found %d errors with network configuration files. Exiting.\n", + syslog(LOG_ERR, "%s: found %d errors with network " + "configuration files. Exiting.", argv[0], trouble); + fprintf(stderr, "%s: found %d errors with network " + "configuration files. Exiting.\n", argv[0], trouble); exit(1); } } + + loopback_dg = ""; + loopback_vc = ""; + loopback_vc_ord = ""; +#ifdef PORTMAP + udptrans = ""; + tcptrans = ""; +#endif + ipv6flag = Is_ipv6present(); rpcb_check_init(); - while (nconf = getnetconfig(nc_handle)) { + + nc_handle = setnetconfig(); /* open netconfig file */ + if (nc_handle == NULL) { + syslog(LOG_ERR, "could not read /etc/netconfig"); + exit(1); + } + while ((nconf = getnetconfig(nc_handle)) != NULL) { if (nconf->nc_flag & NC_VISIBLE) init_transport(nconf); } @@ -236,17 +329,16 @@ main(int argc, char *argv[]) exit(1); } - /* catch the usual termination signals for graceful exit */ - (void) signal(SIGINT, terminate); - (void) signal(SIGTERM, terminate); - (void) signal(SIGQUIT, terminate); - /* ignore others that could get sent */ - (void) sigset(SIGHUP, note_refresh); - (void) signal(SIGUSR1, SIG_IGN); - (void) signal(SIGUSR2, SIG_IGN); if (warmstart) { read_warmstart(); } + + /* Create terminate signal handler for graceful exit */ + if (thr_create(NULL, 0, (void *(*)(void *))terminate, NULL, 0, NULL)) { + syslog(LOG_ERR, "Failed to create terminate thread"); + exit(1); + } + if (debugging) { printf("rpcbind debugging enabled."); if (doabort) { @@ -262,7 +354,7 @@ main(int argc, char *argv[]) __fini_daemon_priv(PRIV_PROC_EXEC, PRIV_PROC_SESSION, PRIV_FILE_LINK_ANY, PRIV_PROC_INFO, (char *)NULL); - my_svc_run(); + svc_run(); syslog(LOG_ERR, "svc_run returned unexpectedly"); rpcbind_abort(); /* NOTREACHED */ @@ -291,7 +383,7 @@ check_netconfig(void) syslog(LOG_ALERT, "setnetconfig() failed: %s", nc_sperror()); return (1); } - while (np = getnetconfig(nc)) { + while ((np = getnetconfig(nc)) != NULL) { if ((np->nc_flag & NC_VISIBLE) == 0) continue; if (debugging) @@ -330,21 +422,23 @@ check_netconfig(void) dlerrstr = dlerror(); if (debugging) { - fprintf(stderr, - "\tnetid %s: dlopen of name-to-address library %s failed\ndlerror: %s", + fprintf(stderr, "\tnetid %s: dlopen of " + "name-to-address library %s " + "failed\ndlerror: %s", np->nc_netid, libname, dlerrstr ? dlerrstr : ""); } - syslog(LOG_ERR, - "netid %s: dlopen of name-to-address library %s failed", + syslog(LOG_ERR, "netid %s: dlopen of " + "name-to-address library %s failed", np->nc_netid, libname); if (dlerrstr) syslog(LOG_ERR, "%s", dlerrstr); busted++; } else { if (debugging) - fprintf(stderr, - "\tdlopen of name-to-address library %s succeeded\n", libname); + fprintf(stderr, "\tdlopen of " + "name-to-address library %s " + "succeeded\n", libname); (void) dlclose(dlcookie); } } @@ -405,7 +499,6 @@ init_transport(struct netconfig *nconf) SVCXPRT *my_xprt; struct nd_addrlist *nas; struct nd_hostserv hs; - int status; /* bound checking ? */ static int msgprt = 0; if ((nconf->nc_semantics != NC_TPI_CLTS) && @@ -415,22 +508,11 @@ init_transport(struct netconfig *nconf) if ((strcmp(nconf->nc_protofmly, NC_INET6) == 0) && !ipv6flag) { if (!msgprt) - syslog(LOG_DEBUG, -"/etc/netconfig has IPv6 entries but IPv6 is not configured"); + syslog(LOG_DEBUG, "/etc/netconfig has IPv6 entries but " + "IPv6 is not configured"); msgprt++; return (1); } -#ifdef ND_DEBUG - { - int i; - char **s; - - (void) fprintf(stderr, "%s: %d lookup routines :\n", - nconf->nc_netid, nconf->nc_nlookups); - for (i = 0, s = nconf->nc_lookups; i < nconf->nc_nlookups; i++, s++) - fprintf(stderr, "[%d] - %s\n", i, *s); - } -#endif if ((fd = t_open(nconf->nc_device, O_RDWR, NULL)) < 0) { syslog(LOG_ERR, "%s: cannot open connection: %s", @@ -470,16 +552,6 @@ init_transport(struct netconfig *nconf) /* Copy the address */ taddr->addr.len = nas->n_addrs->len; memcpy(taddr->addr.buf, nas->n_addrs->buf, (int)nas->n_addrs->len); -#ifdef ND_DEBUG - { - /* for debugging print out our universal address */ - char *uaddr; - - uaddr = taddr2uaddr(nconf, nas->n_addrs); - (void) fprintf(stderr, "rpcbind : my address is %s\n", uaddr); - (void) free(uaddr); - } -#endif netdir_free((char *)nas, ND_ADDRLIST); if (nconf->nc_semantics == NC_TPI_CLTS) @@ -493,9 +565,6 @@ init_transport(struct netconfig *nconf) * so that we can bind to our preferred address even if * previous connections are in FIN_WAIT state */ -#ifdef ND_DEBUG - fprintf(stdout, "Setting SO_REUSEADDR.\n"); -#endif if (setopt_reuseaddr(fd) == -1) { syslog(LOG_ERR, "Couldn't set SO_REUSEADDR option"); } @@ -507,14 +576,18 @@ init_transport(struct netconfig *nconf) goto error; } + if (nconf->nc_semantics != NC_TPI_CLTS && taddr->qlen != baddr->qlen) + syslog(LOG_NOTICE, "%s: unable to set listen backlog to %d " + "(negotiated: %d)", nconf->nc_netid, taddr->qlen, + baddr->qlen); if (memcmp(taddr->addr.buf, baddr->addr.buf, (int)baddr->addr.len)) { syslog(LOG_ERR, "%s: address in use", nconf->nc_netid); goto error; } - my_xprt = (SVCXPRT *)svc_tli_create(fd, nconf, baddr, 0, 0); - if (my_xprt == (SVCXPRT *)NULL) { + my_xprt = svc_tli_create(fd, nconf, baddr, 0, 0); + if (my_xprt == NULL) { syslog(LOG_ERR, "%s: could not create service", nconf->nc_netid); goto error; @@ -525,13 +598,13 @@ init_transport(struct netconfig *nconf) if ((strcmp(nconf->nc_protofmly, NC_INET6) == 0) && (strcmp(nconf->nc_proto, NC_UDP) == 0)) { if (setup_callit(fd) < 0) { - syslog(LOG_ERR, "Unable to join IPv6 multicast group \ -for rpc broadcast %s", RPCB_MULTICAST_ADDR); + syslog(LOG_ERR, "Unable to join IPv6 multicast group " + "for rpc broadcast %s", RPCB_MULTICAST_ADDR); } } if (strcmp(nconf->nc_proto, NC_TCP) == 0) { - svc_control(my_xprt, SVCSET_KEEPALIVE, (void *) TRUE); + svc_control(my_xprt, SVCSET_KEEPALIVE, (void *) TRUE); } #ifdef PORTMAP @@ -549,8 +622,8 @@ for rpc broadcast %s", RPCB_MULTICAST_ADDR); nconf->nc_netid); goto error; } - pml = (PMAPLIST *)malloc((uint_t)sizeof (PMAPLIST)); - if (pml == (PMAPLIST *)NULL) { + pml = malloc(sizeof (PMAPLIST)); + if (pml == NULL) { syslog(LOG_ERR, "no memory!"); exit(1); } @@ -586,8 +659,8 @@ for rpc broadcast %s", RPCB_MULTICAST_ADDR); list_pml = pml; /* Add version 3 information */ - pml = (PMAPLIST *)malloc((uint_t)sizeof (PMAPLIST)); - if (pml == (PMAPLIST *)NULL) { + pml = malloc(sizeof (PMAPLIST)); + if (pml == NULL) { syslog(LOG_ERR, "no memory!"); exit(1); } @@ -597,8 +670,8 @@ for rpc broadcast %s", RPCB_MULTICAST_ADDR); list_pml = pml; /* Add version 4 information */ - pml = (PMAPLIST *)malloc((uint_t)sizeof (PMAPLIST)); - if (pml == (PMAPLIST *)NULL) { + pml = malloc(sizeof (PMAPLIST)); + if (pml == NULL) { syslog(LOG_ERR, "no memory!"); exit(1); } @@ -638,36 +711,14 @@ for rpc broadcast %s", RPCB_MULTICAST_ADDR); } /* decide if bound checking works for this transport */ - status = add_bndlist(nconf, taddr, baddr); -#ifdef BIND_DEBUG - if (status < 0) { - fprintf(stderr, "Error in finding bind status for %s\n", - nconf->nc_netid); - } else if (status == 0) { - fprintf(stderr, "check binding for %s\n", - nconf->nc_netid); - } else if (status > 0) { - fprintf(stderr, "No check binding for %s\n", - nconf->nc_netid); - } -#endif + (void) add_bndlist(nconf, taddr, baddr); + /* * rmtcall only supported on CLTS transports for now. - * only needed when we are allowing indirect calls */ - if (allow_indirect && nconf->nc_semantics == NC_TPI_CLTS) { - status = create_rmtcall_fd(nconf); + if (nconf->nc_semantics == NC_TPI_CLTS) + (void) create_rmtcall_fd(nconf); -#ifdef BIND_DEBUG - if (status < 0) { - fprintf(stderr, "Could not create rmtcall fd for %s\n", - nconf->nc_netid); - } else { - fprintf(stderr, "rmtcall fd for %s is %d\n", - nconf->nc_netid, status); - } -#endif - } (void) t_free((char *)taddr, T_BIND); (void) t_free((char *)baddr, T_BIND); return (0); @@ -680,12 +731,12 @@ error: static void rbllist_add(ulong_t prog, ulong_t vers, struct netconfig *nconf, - struct netbuf *addr) + struct netbuf *addr) { rpcblist_ptr rbl; - rbl = (rpcblist_ptr)malloc((uint_t)sizeof (rpcblist)); - if (rbl == (rpcblist_ptr)NULL) { + rbl = malloc(sizeof (rpcblist)); + if (rbl == NULL) { syslog(LOG_ERR, "no memory!"); exit(1); } @@ -694,34 +745,62 @@ rbllist_add(ulong_t prog, ulong_t vers, struct netconfig *nconf, rbl->rpcb_map.r_vers = vers; rbl->rpcb_map.r_netid = strdup(nconf->nc_netid); rbl->rpcb_map.r_addr = taddr2uaddr(nconf, addr); + if (rbl->rpcb_map.r_addr == NULL) + rbl->rpcb_map.r_addr = strdup(""); rbl->rpcb_map.r_owner = strdup(superuser); + + if (rbl->rpcb_map.r_netid == NULL || rbl->rpcb_map.r_addr == NULL || + rbl->rpcb_map.r_owner == NULL) { + syslog(LOG_ERR, "no memory!"); + exit(1); + } + rbl->rpcb_next = list_rbl; /* Attach to global list */ list_rbl = rbl; } /* - * Catch the signal and die + * Catch the signal and die, if not SIGHUP */ -/* ARGSUSED */ static void -terminate(int sig) +terminate(void) { - syslog(LOG_ERR, "rpcbind terminating on signal."); + int sig; + + for (;;) { + sig = sigwait(&sigwaitset); + if (sig == SIGHUP) { + rpcb_check_init(); + continue; + } + if (sig != -1 || errno != EINTR) + break; + } + + syslog(LOG_ERR, "rpcbind terminating on signal %d.", sig); + + rw_wrlock(&list_rbl_lock); +#ifdef PORTMAP + rw_wrlock(&list_pml_lock); +#endif write_warmstart(); /* Dump yourself */ - exit(2); -} -/* ARGSUSED */ -static void -note_refresh(int sig) -{ - sigrefresh = 1; + exit(2); } void rpcbind_abort(void) { + /* + * We need to hold write locks to make sure + * write_warmstart() is executed exactly once + */ + rw_wrlock(&list_rbl_lock); +#ifdef PORTMAP + rw_wrlock(&list_pml_lock); +#endif write_warmstart(); /* Dump yourself */ + abort(); } @@ -749,6 +828,24 @@ detachfromtty(void) dup(0); } +static int +convert_int(int *val, char *str) +{ + long lval; + + if (str == NULL || !isdigit(*str)) + return (-1); + + lval = strtol(str, &str, 10); + if (*str != '\0' || lval > INT_MAX) + return (-2); + + *val = (int)lval; + return (0); +} + +static int get_smf_iprop(const char *, int, int, int); + /* get command line options */ static void parseargs(int argc, char *argv[]) @@ -756,6 +853,8 @@ parseargs(int argc, char *argv[]) int c; int tmp; + listen_backlog = get_smf_iprop("listen_backlog", 64, 1, INT_MAX); + while ((c = getopt(argc, argv, "dwal:")) != EOF) { switch (c) { case 'd': @@ -770,15 +869,17 @@ parseargs(int argc, char *argv[]) break; case 'l': - if (sscanf(optarg, "%d", &tmp)) { - if (tmp > listen_backlog) { - listen_backlog = tmp; - } + if (convert_int(&tmp, optarg) != 0 || tmp < 1) { + (void) fprintf(stderr, "%s: invalid " + "listen_backlog option, using defaults\n", + argv[0]); + break; } + listen_backlog = tmp; break; default: /* error */ fprintf(stderr, - "usage: rpcbind [-d] [-w] [-l listen_backlog]\n"); + "usage: rpcbind [-d] [-w] [-l listen_backlog]\n"); exit(1); } } @@ -930,11 +1031,11 @@ static mutex_t logmutex = DEFAULTMUTEX; static cond_t logcond = DEFAULTCV; static int logcount = 0; -/*ARGSUSED*/ +/* ARGSUSED */ static void * __NORETURN logthread(void *arg) { - while (1) { + for (;;) { logmsg *msg; (void) mutex_lock(&logmutex); while ((msg = loghead) == NULL) @@ -961,7 +1062,7 @@ get_smf_prop(const char *var, boolean_t def_val) boolean_t res = def_val; prop = scf_simple_prop_get(NULL, NULL, "config", var); - if (prop) { + if (prop != NULL) { if ((val = scf_simple_prop_next_boolean(prop)) != NULL) res = (*val == 0) ? B_FALSE : B_TRUE; scf_simple_prop_free(prop); @@ -976,26 +1077,83 @@ get_smf_prop(const char *var, boolean_t def_val) return (res); } +static int +get_smf_iprop(const char *var, int def_val, int min, int max) +{ + scf_simple_prop_t *prop; + int64_t *val; + int res = def_val; + + prop = scf_simple_prop_get(NULL, NULL, "config", var); + if (prop != NULL) { + if ((val = scf_simple_prop_next_integer(prop)) != NULL) { + if (*val < min || *val > max) + syslog(LOG_ALERT, "value for config/%s out of " + "range. Using default %d", var, def_val); + else + res = (int)*val; + } + scf_simple_prop_free(prop); + } + + if (prop == NULL || val == NULL) { + syslog(LOG_ALERT, "no value for config/%s (%s). " + "Using default %d", var, scf_strerror(scf_error()), + def_val); + } + + return (res); +} + /* * Initialize: read the configuration parameters from SMF. * This function must be idempotent because it can be called from the - * main poll() loop in my_svc_run(). + * signal handler. */ -void +static void rpcb_check_init(void) { thread_t tid; + int max_threads; static int thr_running; wrap_enabled = get_smf_prop("enable_tcpwrappers", B_FALSE); verboselog = get_smf_prop("verbose_logging", B_FALSE); allow_indirect = get_smf_prop("allow_indirect", B_TRUE); - local_only = get_smf_prop("local_only", B_FALSE); + local_only = get_smf_prop("local_only", B_TRUE); if (wrap_enabled && !thr_running) { (void) thr_create(NULL, 0, logthread, NULL, THR_DETACHED, &tid); thr_running = 1; } + + /* + * Set the maximum number of threads. + */ + max_threads = get_smf_iprop("max_threads", 72, 1, INT_MAX); + if (!rpc_control(RPC_SVC_THRMAX_SET, &max_threads)) { + int tmp; + + /* + * The following rpc_control() call cannot fail + */ + if (!rpc_control(RPC_SVC_THRMAX_GET, &tmp)) + assert(0); + + if (tmp != max_threads) { + syslog(LOG_ERR, "setting max_threads to %d failed, " + "using %d worker threads", max_threads, tmp); + max_threads = tmp; + } + } + + /* + * Set rpcb_rmtcalls_max. + */ + if (max_threads < reserved_threads) + set_rpcb_rmtcalls_max(0); + else + set_rpcb_rmtcalls_max(max_threads - reserved_threads); } /* @@ -1007,7 +1165,6 @@ void qsyslog(int pri, const char *fmt, ...) { logmsg *msg = malloc(sizeof (*msg)); - int oldcount; va_list ap; if (msg == NULL) @@ -1022,15 +1179,15 @@ qsyslog(int pri, const char *fmt, ...) msg->log_next = NULL; (void) mutex_lock(&logmutex); - oldcount = logcount; if (logcount < MAXLOG) { + if (logcount == 0) + (void) cond_signal(&logcond); logcount++; *logtailp = msg; logtailp = &msg->log_next; + (void) mutex_unlock(&logmutex); } else { + (void) mutex_unlock(&logmutex); free(msg); } - (void) mutex_unlock(&logmutex); - if (oldcount == 0) - (void) cond_signal(&logcond); } diff --git a/usr/src/cmd/rpcbind/rpcbind.h b/usr/src/cmd/rpcbind/rpcbind.h index 78c0757..4d68864 100644 --- a/usr/src/cmd/rpcbind/rpcbind.h +++ b/usr/src/cmd/rpcbind/rpcbind.h @@ -21,6 +21,9 @@ * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ +/* + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* @@ -41,15 +44,11 @@ #ifndef _RPCBIND_H #define _RPCBIND_H -#pragma ident "%Z%%M% %I% %E% SMI" - #ifdef PORTMAP #include #endif #include -#include - -#include +#include #ifdef __cplusplus extern "C" { @@ -57,13 +56,14 @@ extern "C" { extern int debugging; extern int doabort; +extern rwlock_t list_rbl_lock; /* Protects list_rbl */ extern rpcblist_ptr list_rbl; /* A list of version 3 & 4 rpcbind services */ extern char *loopback_dg; /* CLTS loopback transport, for set/unset */ extern char *loopback_vc; /* COTS loopback transport, for set/unset */ extern char *loopback_vc_ord; /* COTS_ORD loopback transport, for set/unset */ -extern volatile sig_atomic_t sigrefresh; /* Did we receive a SIGHUP recently? */ #ifdef PORTMAP +extern rwlock_t list_pml_lock; /* Protects list_pml */ extern pmaplist *list_pml; /* A list of version 2 rpcbind services */ extern char *udptrans; /* Name of UDP transport */ extern char *tcptrans; /* Name of TCP transport */ @@ -71,19 +71,21 @@ extern char *udp_uaddr; /* Universal UDP address */ extern char *tcp_uaddr; /* Universal TCP address */ #endif -extern char *mergeaddr(); -extern int add_bndlist(); -extern int create_rmtcall_fd(); -extern bool_t is_bound(); -extern void my_svc_run(); -extern void rpcb_check_init(void); +char *mergeaddr(SVCXPRT *, char *, char *, char *); +int add_bndlist(struct netconfig *, struct t_bind *, struct t_bind *); +int create_rmtcall_fd(struct netconfig *); +bool_t is_bound(char *, char *); +void set_rpcb_rmtcalls_max(int); /* TCP wrapper functions and variables. */ -extern boolean_t localxprt(SVCXPRT *, boolean_t); -extern void qsyslog(int pri, const char *fmt, ...); -extern boolean_t rpcb_check(SVCXPRT *, rpcproc_t, boolean_t); -extern void rpcb_log(boolean_t, SVCXPRT *, rpcproc_t, rpcprog_t, boolean_t); -extern boolean_t allow_indirect, wrap_enabled, verboselog, local_only; +boolean_t localxprt(SVCXPRT *, boolean_t); +void qsyslog(int pri, const char *fmt, ...); +boolean_t rpcb_check(SVCXPRT *, rpcproc_t, boolean_t); +void rpcb_log(boolean_t, SVCXPRT *, rpcproc_t, rpcprog_t, boolean_t); +extern volatile boolean_t allow_indirect; +extern volatile boolean_t wrap_enabled; +extern volatile boolean_t verboselog; +extern volatile boolean_t local_only; #define svc_getgencaller(transp) \ ((struct sockaddr_gen *)svc_getrpccaller((transp))->buf) @@ -111,30 +113,52 @@ extern boolean_t allow_indirect, wrap_enabled, verboselog, local_only; if (wrap_enabled) \ rpcb_log(ans, (xprt), (proc), (prog), B_TRUE) -extern bool_t map_set(), map_unset(); +bool_t map_set(RPCB *, char *); +bool_t map_unset(RPCB *, char *); /* Statistics gathering functions */ -extern void rpcbs_procinfo(); -extern void rpcbs_set(); -extern void rpcbs_unset(); -extern void rpcbs_getaddr(); -extern void rpcbs_rmtcall(); -extern rpcb_stat_byvers *rpcbproc_getstat(); +void rpcbs_procinfo(int, rpcproc_t); +void rpcbs_set(int, bool_t); +void rpcbs_unset(int, bool_t); +void rpcbs_getaddr(int, rpcprog_t, rpcvers_t, char *, char *); +void rpcbs_rmtcall(int, rpcproc_t, rpcprog_t, rpcvers_t, rpcproc_t, char *, + rpcblist_ptr); +bool_t rpcbproc_getstat(void *, rpcb_stat_byvers **); +bool_t xdr_rpcb_stat_byvers_ptr(XDR *, rpcb_stat_byvers **); + +struct netconfig *rpcbind_get_conf(); +void rpcbind_abort() __NORETURN; + +#ifdef PORTMAP +void pmap_service(struct svc_req *, SVCXPRT *xprt); +#endif +void rpcb_service_3(struct svc_req *, SVCXPRT *xprt); +void rpcb_service_4(struct svc_req *, SVCXPRT *xprt); +void read_warmstart(void); +void write_warmstart(void); +int Is_ipv6present(void); -extern struct netconfig *rpcbind_get_conf(); -extern void rpcbind_abort() __NORETURN; +extern zoneid_t myzone; /* Common functions shared between versions */ -extern void rpcbproc_callit_com(); -extern bool_t *rpcbproc_set_com(); -extern bool_t *rpcbproc_unset_com(); -extern ulong_t *rpcbproc_gettime_com(); -extern struct netbuf *rpcbproc_uaddr2taddr_com(); -extern char **rpcbproc_taddr2uaddr_com(); -extern char **rpcbproc_getaddr_com(); -extern void delete_prog(); - -extern uid_t rpcb_caller_uid(SVCXPRT *); +void rpcbproc_callit_com(struct svc_req *, SVCXPRT *, ulong_t, int); +bool_t rpcbproc_set_com(RPCB *, bool_t *, struct svc_req *, int); +bool_t rpcbproc_unset_com(RPCB *, bool_t *, struct svc_req *, int); +bool_t rpcbproc_gettime_com(void *, ulong_t *); +bool_t rpcbproc_uaddr2taddr_com(char **, struct netbuf *, struct svc_req *); +bool_t rpcbproc_taddr2uaddr_com(struct netbuf *, char **, struct svc_req *); +bool_t rpcbproc_getaddr_com(RPCB *, char **, struct svc_req *, ulong_t); +void delete_prog(rpcprog_t); +bool_t rpcbproc_dump_com(void *, rpcblist_ptr **); +char *getowner(SVCXPRT *, char *); + +int del_pmaplist(RPCB *); +void delete_rbl(rpcblist_ptr); + +uid_t rpcb_caller_uid(SVCXPRT *); + +/* XDR functions */ +bool_t xdr_rpcblist_ptr_ptr(XDR *, rpcblist_ptr **); /* For different getaddr semantics */ #define RPCB_ALLVERS 0 diff --git a/usr/src/cmd/rpcbind/warmstart.c b/usr/src/cmd/rpcbind/warmstart.c index aa66e8a..6ffa85c 100644 --- a/usr/src/cmd/rpcbind/warmstart.c +++ b/usr/src/cmd/rpcbind/warmstart.c @@ -26,8 +26,9 @@ * Copyright 1990,2002-2003 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ - -#pragma ident "%Z%%M% %I% %E% SMI" +/* + * Copyright 2013 Nexenta Systems, Inc. All rights reserved. + */ #include #include @@ -43,6 +44,7 @@ #include #include #include +#include /* These files keep the pmap_list and rpcb_list in XDR format */ static const char rpcbfile[] = DAEMON_DIR "/rpcbind.file"; @@ -50,13 +52,8 @@ static const char rpcbfile[] = DAEMON_DIR "/rpcbind.file"; static const char pmapfile[] = DAEMON_DIR "/portmap.file"; #endif - -static bool_t write_struct(); -static bool_t read_struct(); - -FILE * -open_tmp_file(filename) - char *filename; +static FILE * +open_tmp_file(const char *filename) { int fd; FILE *fp; @@ -86,10 +83,7 @@ open_tmp_file(filename) } static bool_t -write_struct(filename, structproc, list) - char *filename; - xdrproc_t structproc; - void *list; +write_struct(const char *filename, xdrproc_t structproc, void *list) { FILE *fp; XDR xdrs; @@ -103,7 +97,7 @@ write_struct(filename, structproc, list) fp = open_tmp_file(filename); if (fp == NULL) { syslog(LOG_ERR, - "cannot open file = %s for writing", filename); + "cannot open file = %s for writing", filename); syslog(LOG_ERR, "cannot save any registration"); return (FALSE); } @@ -111,6 +105,7 @@ write_struct(filename, structproc, list) xdrstdio_create(&xdrs, fp, XDR_ENCODE); if (structproc(&xdrs, list) == FALSE) { + XDR_DESTROY(&xdrs); syslog(LOG_ERR, "rpcbind: xdr_%s: failed", filename); fclose(fp); return (FALSE); @@ -121,10 +116,7 @@ write_struct(filename, structproc, list) } static bool_t -read_struct(filename, structproc, list) - char *filename; - xdrproc_t structproc; - void *list; +read_struct(const char *filename, xdrproc_t structproc, void *list) { int fd; FILE *fp = NULL; @@ -134,19 +126,19 @@ read_struct(filename, structproc, list) fd = open(filename, O_RDONLY, 0600); if (fd == -1) { fprintf(stderr, - "rpcbind: cannot open file = %s for reading\n", filename); + "rpcbind: cannot open file = %s for reading\n", filename); goto error; } fp = fdopen(fd, "r"); if (fp == NULL) { close(fd); fprintf(stderr, - "rpcbind: cannot open file = %s for reading\n", filename); + "rpcbind: cannot open file = %s for reading\n", filename); goto error; } if (fstat(fd, &sbuf_fstat) != 0) { fprintf(stderr, - "rpcbind: cannot stat file = %s for reading\n", filename); + "rpcbind: cannot stat file = %s for reading\n", filename); goto error; } if (sbuf_fstat.st_uid != DAEMON_UID || @@ -154,9 +146,8 @@ read_struct(filename, structproc, list) (sbuf_fstat.st_mode & S_IRWXG) || (sbuf_fstat.st_mode & S_IRWXO) || (sbuf_fstat.st_nlink != 1)) { - fprintf(stderr, - "rpcbind: invalid permissions on file = %s for reading\n", - filename); + fprintf(stderr, "rpcbind: invalid permissions on file = %s for " + "reading\n", filename); goto error; } /* @@ -167,7 +158,7 @@ read_struct(filename, structproc, list) */ if (lstat(filename, &sbuf_lstat) != 0) { fprintf(stderr, - "rpcbind: cannot lstat file = %s for reading\n", filename); + "rpcbind: cannot lstat file = %s for reading\n", filename); goto error; } if (sbuf_lstat.st_uid != DAEMON_UID || @@ -177,14 +168,14 @@ read_struct(filename, structproc, list) (sbuf_lstat.st_nlink != 1) || (sbuf_fstat.st_dev != sbuf_lstat.st_dev) || (sbuf_fstat.st_ino != sbuf_lstat.st_ino)) { - fprintf(stderr, - "rpcbind: invalid lstat permissions on file = %s for reading\n", - filename); + fprintf(stderr, "rpcbind: invalid lstat permissions on file = " + "%s for reading\n", filename); goto error; } xdrstdio_create(&xdrs, fp, XDR_DECODE); if (structproc(&xdrs, list) == FALSE) { + XDR_DESTROY(&xdrs); fprintf(stderr, "rpcbind: xdr_%s: failed\n", filename); goto error; } @@ -192,7 +183,8 @@ read_struct(filename, structproc, list) fclose(fp); return (TRUE); -error: fprintf(stderr, "rpcbind: will start from scratch\n"); +error: + fprintf(stderr, "rpcbind: will start from scratch\n"); if (fp != NULL) fclose(fp); return (FALSE); @@ -201,8 +193,10 @@ error: fprintf(stderr, "rpcbind: will start from scratch\n"); void write_warmstart(void) { + assert(RW_WRITE_HELD(&list_rbl_lock)); (void) write_struct(rpcbfile, xdr_rpcblist_ptr, &list_rbl); #ifdef PORTMAP + assert(RW_WRITE_HELD(&list_pml_lock)); (void) write_struct(pmapfile, xdr_pmaplist_ptr, &list_pml); #endif @@ -215,18 +209,17 @@ read_warmstart(void) #ifdef PORTMAP pmaplist_ptr tmp_pmapl = NULL; #endif - int ok1, ok2 = TRUE; - ok1 = read_struct(rpcbfile, xdr_rpcblist_ptr, &tmp_rpcbl); - if (ok1 == FALSE) + if (read_struct(rpcbfile, xdr_rpcblist_ptr, &tmp_rpcbl) == FALSE) return; + #ifdef PORTMAP - ok2 = read_struct(pmapfile, xdr_pmaplist_ptr, &tmp_pmapl); -#endif - if (ok2 == FALSE) { + if (read_struct(pmapfile, xdr_pmaplist_ptr, &tmp_pmapl) == FALSE) { xdr_free((xdrproc_t)xdr_rpcblist_ptr, (char *)&tmp_rpcbl); return; } +#endif + xdr_free((xdrproc_t)xdr_rpcblist_ptr, (char *)&list_rbl); list_rbl = tmp_rpcbl; #ifdef PORTMAP