diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2014-05-03 19:50:51 +0400 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2014-05-03 19:50:51 +0400 |
commit | 068d1ebeb9ed413bdf093b40454d11d11a81b8a1 (patch) | |
tree | 2987c97c942cd5854fe0538503a8198afba89b53 /illumos-rpcbind/debian/patches | |
parent | b5fb8e30bcd2f2b6631ac6e0f60caa3981649465 (diff) | |
download | illumos-packaging-068d1ebeb9ed413bdf093b40454d11d11a81b8a1.tar.gz |
illumos-rpcbind (4.3+20140330) unstable; urgency=medium
* New upstream snapshot
* Added illumos-4483-4575-rpcbind.patch:
4483 rpcbind: Reply for remote calls comes from incorrect UDP port
4575 Single threaded rpcbind is not scalable
* Refreshed rpcbind.c.patch
* Updated rpcbind-tcpd.h.patch
* Added rpcbind-fix-xdr-warnings.patch
* Added rpcbind-is_system_labeled.patch
Diffstat (limited to 'illumos-rpcbind/debian/patches')
6 files changed, 5743 insertions, 19 deletions
diff --git a/illumos-rpcbind/debian/patches/illumos-4483-4575-rpcbind.patch b/illumos-rpcbind/debian/patches/illumos-4483-4575-rpcbind.patch new file mode 100644 index 0000000..96e6113 --- /dev/null +++ b/illumos-rpcbind/debian/patches/illumos-4483-4575-rpcbind.patch @@ -0,0 +1,5647 @@ +commit 8f6d9dae92449b59bdafcb7777bc32f1b2726e48 +Author: Marcel Telka <marcel.telka@nexenta.com> +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 <ilya.usvyatsky@nexenta.com> + Reviewed by: Jan Kryl <jan.kryl@nexenta.com> + Reviewed by: Michael Schuster <michaelsprivate@gmail.com> + Reviewed by: Gary Mills <gary_mills@fastmail.fm> + Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com> + Approved by: Robert Mustacchi <rm@joyent.com> + +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 @@ + <!-- to configure rpc/bind --> + <propval name='value_authorization' type='astring' + value='solaris.smf.value.rpc.bind' /> ++ ++ <propval ++ name='listen_backlog' ++ type='integer' ++ value='64' /> ++ ++ <propval ++ name='max_threads' ++ type='integer' ++ value='72' /> + </property_group> + + <!-- Authorization --> +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 <errno.h> + #include <sys/socket.h> + #include <netinet/in.h> ++#include <thread.h> ++#include <synch.h> ++#include <syslog.h> + + 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 <rpc/rpc.h> ++#include <tcpd.h> ++ ++#include "rpcbind.h" ++ + #ifdef PORTMAP + #include <stdio.h> + #include <alloca.h> + #include <ucred.h> +-#include <rpc/rpc.h> + #include <rpc/pmap_prot.h> + #include <rpc/rpcb_prot.h> +-#include "rpcbind.h" ++#include <assert.h> + +-#ifdef RPCBIND_DEBUG +-#include <netdir.h> +-#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 <stdio.h> + #include <stdlib.h> +@@ -62,6 +63,9 @@ + #include <rpc/rpc.h> + #include <rpc/pmap_prot.h> + #include <rpc/rpcb_prot.h> ++#include <thread.h> ++#include <synch.h> ++#include <tcpd.h> + + #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 <stdio.h> + #include <netconfig.h> +@@ -35,98 +38,103 @@ + #include <rpc/pmap_prot.h> + #endif + #include <stdlib.h> ++#include <atomic.h> ++#include <assert.h> ++#include <thread.h> ++#include <synch.h> ++#include <string.h> + #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 ? "<unknown>" : 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 ? "<unknown>" : 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 <stdlib.h> + #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 <netinet/in.h> + #include <rpc/pmap_prot.h> ++#else ++#define PMAPVERS 2 + #endif /* PORTMAP */ + #include <syslog.h> + #include <netdir.h> +@@ -73,152 +78,143 @@ + #include <rpcsvc/rquota.h> + #include <rpcsvc/yppasswd.h> + #include <rpcsvc/ypupd.h> ++#include <assert.h> ++#include <synch.h> + #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 <dlfcn.h> +@@ -74,20 +127,14 @@ + #include <priv_utils.h> + #include <libscf.h> + #include <sys/ccompile.h> ++#include <zone.h> ++#include <ctype.h> ++#include <limits.h> ++#include <assert.h> + +-#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 <rpc/pmap_prot.h> + #endif + #include <rpc/rpcb_prot.h> +-#include <signal.h> +- +-#include <tcpd.h> ++#include <synch.h> + + #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 <stdio.h> + #include <errno.h> +@@ -43,6 +44,7 @@ + #include <syslog.h> + #include <unistd.h> + #include <rpcsvc/daemon_utils.h> ++#include <assert.h> + + /* 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 diff --git a/illumos-rpcbind/debian/patches/rpcbind-fix-xdr-warnings.patch b/illumos-rpcbind/debian/patches/rpcbind-fix-xdr-warnings.patch new file mode 100644 index 0000000..2f467cb --- /dev/null +++ b/illumos-rpcbind/debian/patches/rpcbind-fix-xdr-warnings.patch @@ -0,0 +1,57 @@ +Description: expected 'ulong_t *' but argument is of type 'rpcprog_t *' + sizeof(rpcprog_t) == sizeof(ulong_t) == sizeof(rpcvers_t) +Index: illumos-rpcbind/usr/src/cmd/rpcbind/rpcb_svc_com.c +=================================================================== +--- illumos-rpcbind.orig/usr/src/cmd/rpcbind/rpcb_svc_com.c 2014-05-03 18:55:06.557381077 +0400 ++++ illumos-rpcbind/usr/src/cmd/rpcbind/rpcb_svc_com.c 2014-05-03 19:33:13.955817403 +0400 +@@ -89,7 +89,7 @@ + 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 *); ++static rpcblist_ptr find_service(rpcprog_t, rpcvers_t, char *); + #ifdef PORTMAP + static int add_pmaplist(RPCB *); + #endif +@@ -488,11 +488,11 @@ + bool_t + xdr_rpcb_rmtcallargs(XDR *xdrs, rpcb_rmtcallargs *objp) + { +- if (!xdr_u_long(xdrs, &objp->prog)) ++ if (!xdr_u_long(xdrs, (ulong_t*)&objp->prog)) + return (FALSE); +- if (!xdr_u_long(xdrs, &objp->vers)) ++ if (!xdr_u_long(xdrs, (ulong_t*)&objp->vers)) + return (FALSE); +- if (!xdr_u_long(xdrs, &objp->proc)) ++ if (!xdr_u_long(xdrs, (ulong_t*)&objp->proc)) + return (FALSE); + if (!xdr_bytes(xdrs, (char **)&objp->args.args_val, + (uint_t *)&objp->args.args_len, ~0)) +@@ -504,7 +504,7 @@ + bool_t + xdr_rmtcallres(XDR *xdrs, rmtcallres *objp) + { +- if (!xdr_u_long(xdrs, &objp->port)) ++ if (!xdr_u_long(xdrs, (ulong_t*)&objp->port)) + return (FALSE); + if (!xdr_bytes(xdrs, (char **)&objp->res.res_val, + (uint_t *)&objp->res.res_len, ~0)) +@@ -893,7 +893,7 @@ + } + if (rbl->rpcb_map.r_vers != arg.vers) { + if (reply_type == RPCBPROC_INDIRECT) { +- ulong_t vers_low, vers_high; ++ rpcvers_t vers_low, vers_high; + + find_versions(arg.prog, transp->xp_netid, + &vers_low, &vers_high); +@@ -1022,7 +1022,7 @@ + goto error; + } + +- if (!xdr_u_long(&outxdr, &arg.proc)) { ++ if (!xdr_u_long(&outxdr, (ulong_t*)&arg.proc)) { + forward_destroy(fi); + (void) mutex_unlock(&finfo_lock); + if (reply_type == RPCBPROC_INDIRECT) diff --git a/illumos-rpcbind/debian/patches/rpcbind-is_system_labeled.patch b/illumos-rpcbind/debian/patches/rpcbind-is_system_labeled.patch new file mode 100644 index 0000000..6539de1 --- /dev/null +++ b/illumos-rpcbind/debian/patches/rpcbind-is_system_labeled.patch @@ -0,0 +1,15 @@ +Index: illumos-rpcbind/usr/src/cmd/rpcbind/rpcbind.c +=================================================================== +--- illumos-rpcbind.orig/usr/src/cmd/rpcbind/rpcbind.c 2014-05-03 18:55:06.812658728 +0400 ++++ illumos-rpcbind/usr/src/cmd/rpcbind/rpcbind.c 2014-05-03 19:43:20.961057375 +0400 +@@ -132,6 +132,10 @@ + #include <limits.h> + #include <assert.h> + ++ ++/* this function is in libc, but declared in /usr/include/tsol/label.h */ ++int is_system_labeled(void); ++ + static sigset_t sigwaitset; + + static void terminate(void); diff --git a/illumos-rpcbind/debian/patches/rpcbind-tcpd.h.patch b/illumos-rpcbind/debian/patches/rpcbind-tcpd.h.patch index f89b906..e6dbeea 100644 --- a/illumos-rpcbind/debian/patches/rpcbind-tcpd.h.patch +++ b/illumos-rpcbind/debian/patches/rpcbind-tcpd.h.patch @@ -1,10 +1,10 @@ Index: illumos-rpcbind/usr/src/cmd/rpcbind/rpcbind.h =================================================================== ---- illumos-rpcbind.orig/usr/src/cmd/rpcbind/rpcbind.h 2012-10-08 04:25:33.000000000 +0400 -+++ illumos-rpcbind/usr/src/cmd/rpcbind/rpcbind.h 2013-09-28 16:28:00.969913772 +0400 -@@ -51,6 +51,43 @@ - - #include <tcpd.h> +--- illumos-rpcbind.orig/usr/src/cmd/rpcbind/rpcbind.h 2014-05-03 18:27:55.050416665 +0400 ++++ illumos-rpcbind/usr/src/cmd/rpcbind/rpcbind.h 2014-05-03 18:29:35.542162039 +0400 +@@ -50,6 +50,43 @@ + #include <rpc/rpcb_prot.h> + #include <synch.h> +#include <sys/socket.h> +#include <netinet/in.h> @@ -48,14 +48,16 @@ Index: illumos-rpcbind/usr/src/cmd/rpcbind/rpcbind.h #endif Index: illumos-rpcbind/usr/src/cmd/rpcbind/rpcb_check.c =================================================================== ---- illumos-rpcbind.orig/usr/src/cmd/rpcbind/rpcb_check.c 2012-10-08 04:25:33.000000000 +0400 -+++ illumos-rpcbind/usr/src/cmd/rpcbind/rpcb_check.c 2013-09-28 16:30:52.274024004 +0400 -@@ -220,7 +220,7 @@ +--- illumos-rpcbind.orig/usr/src/cmd/rpcbind/rpcb_check.c 2014-05-03 18:27:55.025248817 +0400 ++++ illumos-rpcbind/usr/src/cmd/rpcbind/rpcb_check.c 2014-05-03 18:30:20.177608332 +0400 +@@ -218,8 +218,8 @@ + sizeof (buf)); - if (!localxprt(transp, ispmap) && - (local_only || -- hosts_ctl("rpcbind", addr_string, addr_string, "") == 0)) { -+ hosts_ctl("rpcbind", (char *)addr_string, (char *)addr_string, "") == 0)) { - res = B_FALSE; - } - } + (void) mutex_lock(&hosts_ctl_lock); +- if (hosts_ctl("rpcbind", addr_string, +- addr_string, "") == 0) ++ if (hosts_ctl("rpcbind", (char*)addr_string, ++ (char*)addr_string, "") == 0) + res = B_FALSE; + (void) mutex_unlock(&hosts_ctl_lock); + } diff --git a/illumos-rpcbind/debian/patches/rpcbind.c.patch b/illumos-rpcbind/debian/patches/rpcbind.c.patch index ebc516e..fc02c72 100644 --- a/illumos-rpcbind/debian/patches/rpcbind.c.patch +++ b/illumos-rpcbind/debian/patches/rpcbind.c.patch @@ -1,8 +1,8 @@ Index: illumos-rpcbind/usr/src/cmd/rpcbind/rpcbind.c =================================================================== ---- illumos-rpcbind.orig/usr/src/cmd/rpcbind/rpcbind.c 2012-10-08 04:25:33.000000000 +0400 -+++ illumos-rpcbind/usr/src/cmd/rpcbind/rpcbind.c 2013-09-28 15:51:33.630424214 +0400 -@@ -230,8 +230,8 @@ +--- illumos-rpcbind.orig/usr/src/cmd/rpcbind/rpcbind.c 2014-05-03 18:27:55.047144466 +0400 ++++ illumos-rpcbind/usr/src/cmd/rpcbind/rpcbind.c 2014-05-03 18:27:55.299337460 +0400 +@@ -323,8 +323,8 @@ } endnetconfig(nc_handle); @@ -13,7 +13,7 @@ Index: illumos-rpcbind/usr/src/cmd/rpcbind/rpcbind.c syslog(LOG_ERR, "could not find loopback transports"); exit(1); } -@@ -544,7 +544,7 @@ +@@ -617,7 +617,7 @@ PMAPLIST *pml; if (!svc_register(my_xprt, PMAPPROG, PMAPVERS, diff --git a/illumos-rpcbind/debian/patches/series b/illumos-rpcbind/debian/patches/series index c513f0e..19d737b 100644 --- a/illumos-rpcbind/debian/patches/series +++ b/illumos-rpcbind/debian/patches/series @@ -1,3 +1,6 @@ +illumos-4483-4575-rpcbind.patch rpcbind.c.patch rpcbind-tcpd.h.patch rpcbind-smf-no-sysidtool.patch +rpcbind-fix-xdr-warnings.patch +rpcbind-is_system_labeled.patch |