summaryrefslogtreecommitdiff
path: root/illumos-rpcbind/debian/patches
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2014-05-03 19:50:51 +0400
committerIgor Pashev <pashev.igor@gmail.com>2014-05-03 19:50:51 +0400
commit068d1ebeb9ed413bdf093b40454d11d11a81b8a1 (patch)
tree2987c97c942cd5854fe0538503a8198afba89b53 /illumos-rpcbind/debian/patches
parentb5fb8e30bcd2f2b6631ac6e0f60caa3981649465 (diff)
downloadillumos-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')
-rw-r--r--illumos-rpcbind/debian/patches/illumos-4483-4575-rpcbind.patch5647
-rw-r--r--illumos-rpcbind/debian/patches/rpcbind-fix-xdr-warnings.patch57
-rw-r--r--illumos-rpcbind/debian/patches/rpcbind-is_system_labeled.patch15
-rw-r--r--illumos-rpcbind/debian/patches/rpcbind-tcpd.h.patch32
-rw-r--r--illumos-rpcbind/debian/patches/rpcbind.c.patch8
-rw-r--r--illumos-rpcbind/debian/patches/series3
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 *)&reg)) {
+ 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 *)&reg)) {
+ 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