walters / rpms / nfs-utils

Forked from rpms/nfs-utils 6 years ago
Clone
605d1f1
commit 541bf913ec64dee719b34d2a6850fcfee550e6c0
605d1f1
Author: Chuck Lever <chuck.lever@oracle.com>
605d1f1
Date:   Mon Nov 17 16:13:48 2008 -0500
605d1f1
605d1f1
    Introduce rpcbind client utility functions
605d1f1
    
605d1f1
    It turns out that at least the mount command and the showmount command
605d1f1
    need to query a server's rpcbind daemon.  They need to query over
605d1f1
    AF_INET6 as well as AF_INET.
605d1f1
    
605d1f1
    libtirpc provides an rpcbind query capability with the rpcb_getaddr(3)
605d1f1
    interface, but it takes a hostname and netconfig entry rather than a
605d1f1
    sockaddr and a protocol type, and always uses a lengthy timeout.  The
605d1f1
    former is important to the mount command because it sometimes must
605d1f1
    operate using a specific port and IP address rather than depending on
605d1f1
    rpcbind and DNS to convert a [hostname, RPC program, netconfig] tuple
605d1f1
    to a [socket address, port number, transport protocol] tuple.
605d1f1
    
605d1f1
    The rpcb_getaddr(3) API also always uses a privileged port (at least
605d1f1
    for setuid root executables like mount.nfs), which is not required for
605d1f1
    an rpcbind query.  This can exhaust the local system's reserved port
605d1f1
    space quickly.
605d1f1
    
605d1f1
    This patch provides a reserved-port-friendly AF_INET6-capable rpcbind
605d1f1
    query C API that can be shared among commands and tools in nfs-utils,
605d1f1
    and allows a query to a specified socket address and port rather than
605d1f1
    a hostname.
605d1f1
    
605d1f1
    In addition to an rpcbind query interface, this patch also provides a
605d1f1
    facility to ping the remote RPC service to ensure that it is operating
605d1f1
    as advertised by rpcbind.  It's useful to combine an RPC ping with an
605d1f1
    rpcbind query because in many cases, components of nfs-utils already
605d1f1
    ping an RPC service immediately after receiving a successful GETPORT
605d1f1
    result.
605d1f1
    
605d1f1
    There are also a handful of utility routines provided, such as a
605d1f1
    functions that can map between [sockaddr, port] and a universal
605d1f1
    address.
605d1f1
    
605d1f1
    I've made an attempt to make these new functions build and operate on
605d1f1
    systems that do not have libtirpc.
605d1f1
    
605d1f1
    Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
605d1f1
    Signed-off-by: Steve Dickson <steved@redhat.com>
605d1f1
605d1f1
--- nfs-utils-1.1.4/support/nfs/Makefile.am.orig	2008-11-18 15:06:29.115299000 -0500
605d1f1
+++ nfs-utils-1.1.4/support/nfs/Makefile.am	2008-11-18 15:08:43.272905000 -0500
605d1f1
@@ -3,7 +3,7 @@
605d1f1
 noinst_LIBRARIES = libnfs.a
605d1f1
 libnfs_a_SOURCES = exports.c rmtab.c xio.c rpcmisc.c rpcdispatch.c \
605d1f1
 		   xlog.c xcommon.c wildmat.c nfssvc.c nfsclient.c \
605d1f1
-		   nfsexport.c getfh.c nfsctl.c rpc_socket.c \
605d1f1
+		   nfsexport.c getfh.c nfsctl.c rpc_socket.c getport.c \
605d1f1
 		   svc_socket.c cacheio.c closeall.c nfs_mntent.c
605d1f1
 
605d1f1
 MAINTAINERCLEANFILES = Makefile.in
605d1f1
--- /dev/null	2008-11-18 08:07:41.940431098 -0500
605d1f1
+++ nfs-utils-1.1.4/support/nfs/getport.c	2008-11-18 15:08:58.025493000 -0500
605d1f1
@@ -0,0 +1,965 @@
605d1f1
+/*
605d1f1
+ * Provide a variety of APIs that query an rpcbind daemon to
605d1f1
+ * discover RPC service ports and allowed protocol version
605d1f1
+ * numbers.
605d1f1
+ *
605d1f1
+ * Copyright (C) 2008 Oracle Corporation.  All rights reserved.
605d1f1
+ *
605d1f1
+ * This program is free software; you can redistribute it and/or
605d1f1
+ * modify it under the terms of the GNU General Public
605d1f1
+ * License as published by the Free Software Foundation; either
605d1f1
+ * version 2 of the License, or (at your option) any later version.
605d1f1
+ *
605d1f1
+ * This program is distributed in the hope that it will be useful,
605d1f1
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
605d1f1
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
605d1f1
+ * General Public License for more details.
605d1f1
+ *
605d1f1
+ * You should have received a copy of the GNU General Public
605d1f1
+ * License along with this program; if not, write to the
605d1f1
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
605d1f1
+ * Boston, MA 021110-1307, USA.
605d1f1
+ *
605d1f1
+ */
605d1f1
+
605d1f1
+#ifdef HAVE_CONFIG_H
605d1f1
+#include <config.h>
605d1f1
+#endif
605d1f1
+
605d1f1
+#include <sys/types.h>
605d1f1
+#include <sys/time.h>
605d1f1
+#include <unistd.h>
605d1f1
+#include <fcntl.h>
605d1f1
+#include <errno.h>
605d1f1
+
605d1f1
+#include <sys/socket.h>
605d1f1
+#include <netinet/in.h>
605d1f1
+#include <netdb.h>
605d1f1
+#include <arpa/inet.h>
605d1f1
+
605d1f1
+#include <rpc/rpc.h>
605d1f1
+#include <rpc/pmap_prot.h>
605d1f1
+
605d1f1
+#ifdef HAVE_TIRPC_NETCONFIG_H
605d1f1
+#include <tirpc/netconfig.h>
605d1f1
+#include <tirpc/rpc/rpcb_prot.h>
605d1f1
+#endif
605d1f1
+
605d1f1
+#include "nfsrpc.h"
605d1f1
+
605d1f1
+/*
605d1f1
+ * Try a local socket first to access the local rpcbind daemon
605d1f1
+ *
605d1f1
+ * Rpcbind's local socket service does not seem to be working.
605d1f1
+ * Disable this logic for now.
605d1f1
+ */
605d1f1
+#ifdef HAVE_XDR_RPCB
605d1f1
+#undef NFS_GP_LOCAL
605d1f1
+#else	/* HAVE_XDR_RPCB */
605d1f1
+#undef NFS_GP_LOCAL
605d1f1
+#endif	/* HAVE_XDR_RPCB */
605d1f1
+
605d1f1
+#ifdef HAVE_XDR_RPCB
605d1f1
+const static rpcvers_t default_rpcb_version = RPCBVERS_4;
605d1f1
+#else
605d1f1
+const static rpcvers_t default_rpcb_version = PMAPVERS;
605d1f1
+#endif
605d1f1
+
605d1f1
+static const char *nfs_gp_rpcb_pgmtbl[] = {
605d1f1
+	"rpcbind",
605d1f1
+	"portmap",
605d1f1
+	"portmapper",
605d1f1
+	"sunrpc",
605d1f1
+	NULL,
605d1f1
+};
605d1f1
+
605d1f1
+/*
605d1f1
+ * Discover the port number that should be used to contact an
605d1f1
+ * rpcbind service.  This will detect if the port has a local
605d1f1
+ * value that may have been set in /etc/services.
605d1f1
+ *
605d1f1
+ * NB: s_port is already in network byte order.
605d1f1
+ *
605d1f1
+ * Returns network byte-order port number of rpcbind service
605d1f1
+ * on this system.
605d1f1
+ */
605d1f1
+static in_port_t nfs_gp_get_rpcb_port(const unsigned short protocol)
605d1f1
+{
605d1f1
+	struct protoent *proto;
605d1f1
+
605d1f1
+	proto = getprotobynumber((int)protocol);
605d1f1
+	if (proto != NULL) {
605d1f1
+		struct servent *entry;
605d1f1
+
605d1f1
+		entry = getservbyname("rpcbind", proto->p_name);
605d1f1
+		if (entry != NULL)
605d1f1
+			return (in_port_t)entry->s_port;
605d1f1
+
605d1f1
+		entry = getservbyname("portmapper", proto->p_name);
605d1f1
+		if (entry != NULL)
605d1f1
+			return (in_port_t)entry->s_port;
605d1f1
+
605d1f1
+		entry = getservbyname("sunrpc", proto->p_name);
605d1f1
+		if (entry != NULL)
605d1f1
+			return (in_port_t)entry->s_port;
605d1f1
+	}
605d1f1
+	return htons((uint16_t)PMAPPORT);
605d1f1
+}
605d1f1
+
605d1f1
+/*
605d1f1
+ * Plant port number in @sap.  @port is already in network byte order.
605d1f1
+ */
605d1f1
+static void nfs_gp_set_port(struct sockaddr *sap, const in_port_t port)
605d1f1
+{
605d1f1
+	struct sockaddr_in *sin = (struct sockaddr_in *)sap;
605d1f1
+	struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
605d1f1
+
605d1f1
+	switch (sap->sa_family) {
605d1f1
+	case AF_INET:
605d1f1
+		sin->sin_port = port;
605d1f1
+		break;
605d1f1
+	case AF_INET6:
605d1f1
+		sin6->sin6_port = port;
605d1f1
+		break;
605d1f1
+	default:
605d1f1
+		fprintf(stderr, "%s: unrecognized address family\n",
605d1f1
+			__func__);
605d1f1
+	}
605d1f1
+}
605d1f1
+
605d1f1
+/*
605d1f1
+ * Set up an RPC client for communicating with an rpcbind daemon at
605d1f1
+ * @sap over @transport with protocol version @version.
605d1f1
+ *
605d1f1
+ * Returns a pointer to a prepared RPC client if successful, and
605d1f1
+ * @timeout is initialized; caller must destroy a non-NULL returned RPC
605d1f1
+ * client.  Otherwise returns NULL, and rpc_createerr.cf_stat is set to
605d1f1
+ * reflect the error.
605d1f1
+ */
605d1f1
+static CLIENT *nfs_gp_get_rpcbclient(const struct sockaddr *sap,
605d1f1
+				     const socklen_t salen,
605d1f1
+				     const unsigned short transport,
605d1f1
+				     const rpcvers_t version,
605d1f1
+				     struct timeval *timeout)
605d1f1
+{
605d1f1
+	struct sockaddr_storage address;
605d1f1
+	struct sockaddr *saddr = (struct sockaddr *)&address;
605d1f1
+	rpcprog_t rpcb_prog = nfs_getrpcbyname(RPCBPROG, nfs_gp_rpcb_pgmtbl);
605d1f1
+
605d1f1
+	memcpy(saddr, sap, (size_t)salen);
605d1f1
+	nfs_gp_set_port(saddr, nfs_gp_get_rpcb_port(transport));
605d1f1
+
605d1f1
+	return nfs_get_rpcclient(saddr, salen, transport, rpcb_prog,
605d1f1
+					version, timeout);
605d1f1
+}
605d1f1
+
605d1f1
+/*
605d1f1
+ * One of the arguments passed when querying remote rpcbind services
605d1f1
+ * via rpcbind v3 or v4 is a netid string.  This replaces the pm_prot
605d1f1
+ * field used in legacy PMAP_GETPORT calls.
605d1f1
+ *
605d1f1
+ * RFC 1833 says netids are not standard but rather defined on the local
605d1f1
+ * host.  There are, however, standard definitions for nc_protofmly and
605d1f1
+ * nc_proto that can be used to derive a netid string on the local host,
605d1f1
+ * based on the contents of /etc/netconfig.
605d1f1
+ *
605d1f1
+ * Walk through the local netconfig database and grab the netid of the
605d1f1
+ * first entry that matches @family and @protocol and whose netid string
605d1f1
+ * fits in the provided buffer.
605d1f1
+ *
605d1f1
+ * Returns a '\0'-terminated string if successful; otherwise NULL.
605d1f1
+ * rpc_createerr.cf_stat is set to reflect the error.
605d1f1
+ */
605d1f1
+#ifdef HAVE_XDR_RPCB
605d1f1
+
605d1f1
+static char *nfs_gp_get_netid(const sa_family_t family,
605d1f1
+			      const unsigned short protocol)
605d1f1
+{
605d1f1
+	char *nc_protofmly, *nc_proto, *nc_netid;
605d1f1
+	struct netconfig *nconf;
605d1f1
+	struct protoent *proto;
605d1f1
+	void *handle;
605d1f1
+
605d1f1
+	switch (family) {
605d1f1
+	case AF_LOCAL:
605d1f1
+	case AF_INET:
605d1f1
+		nc_protofmly = NC_INET;
605d1f1
+		break;
605d1f1
+	case AF_INET6:
605d1f1
+		nc_protofmly = NC_INET6;
605d1f1
+		break;
605d1f1
+	default:
605d1f1
+		goto out;
605d1f1
+	}
605d1f1
+
605d1f1
+	proto = getprotobynumber(protocol);
605d1f1
+	if (proto == NULL)
605d1f1
+		goto out;
605d1f1
+	nc_proto = proto->p_name;
605d1f1
+
605d1f1
+	handle = setnetconfig();
605d1f1
+	while ((nconf = getnetconfig(handle)) != NULL) {
605d1f1
+
605d1f1
+		if (nconf->nc_protofmly != NULL &&
605d1f1
+		    strcmp(nconf->nc_protofmly, nc_protofmly) != 0)
605d1f1
+			continue;
605d1f1
+		if (nconf->nc_proto != NULL &&
605d1f1
+		    strcmp(nconf->nc_proto, nc_proto) != 0)
605d1f1
+			continue;
605d1f1
+
605d1f1
+		nc_netid = strdup(nconf->nc_netid);
605d1f1
+		endnetconfig(handle);
605d1f1
+		return nc_netid;
605d1f1
+	}
605d1f1
+	endnetconfig(handle);
605d1f1
+
605d1f1
+out:
605d1f1
+	rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
605d1f1
+	return NULL;
605d1f1
+}
605d1f1
+
605d1f1
+#endif	/* HAVE_XDR_RPCB */
605d1f1
+
605d1f1
+/*
605d1f1
+ * Extract a port number from a universal address, and terminate the
605d1f1
+ * string in @addrstr just after the address part.
605d1f1
+ *
605d1f1
+ * Returns -1 if unsuccesful; otherwise a decoded port number (possibly 0)
605d1f1
+ * is returned.
605d1f1
+ */
605d1f1
+static int nfs_gp_universal_porthelper(char *addrstr)
605d1f1
+{
605d1f1
+	char *p, *endptr;
605d1f1
+	unsigned long portlo, porthi;
605d1f1
+	int port = -1;
605d1f1
+
605d1f1
+	p = strrchr(addrstr, '.');
605d1f1
+	if (p == NULL)
605d1f1
+		goto out;
605d1f1
+	portlo = strtoul(p + 1, &endptr, 10);
605d1f1
+	if (*endptr != '\0' || portlo > 255)
605d1f1
+		goto out;
605d1f1
+	*p = '\0';
605d1f1
+
605d1f1
+	p = strrchr(addrstr, '.');
605d1f1
+	if (p == NULL)
605d1f1
+		goto out;
605d1f1
+	porthi = strtoul(p + 1, &endptr, 10);
605d1f1
+	if (*endptr != '\0' || porthi > 255)
605d1f1
+		goto out;
605d1f1
+	*p = '\0';
605d1f1
+	port = (porthi << 8) | portlo;
605d1f1
+
605d1f1
+out:
605d1f1
+	return port;
605d1f1
+}
605d1f1
+
605d1f1
+/**
605d1f1
+ * nfs_universal2port - extract port number from a "universal address"
605d1f1
+ * @uaddr: '\0'-terminated C string containing a universal address
605d1f1
+ *
605d1f1
+ * Universal addresses (defined in RFC 1833) are used when calling an
605d1f1
+ * rpcbind daemon via protocol versions 3 or 4..
605d1f1
+ *
605d1f1
+ * Returns -1 if unsuccesful; otherwise a decoded port number (possibly 0)
605d1f1
+ * is returned.
605d1f1
+ */
605d1f1
+int nfs_universal2port(const char *uaddr)
605d1f1
+{
605d1f1
+	char *addrstr;
605d1f1
+	int port = -1;
605d1f1
+
605d1f1
+	addrstr = strdup(uaddr);
605d1f1
+	if (addrstr != NULL) {
605d1f1
+		port = nfs_gp_universal_porthelper(addrstr);
605d1f1
+		free(addrstr);
605d1f1
+	}
605d1f1
+	return port;
605d1f1
+}
605d1f1
+
605d1f1
+/**
605d1f1
+ * nfs_sockaddr2universal - convert a sockaddr to a "universal address"
605d1f1
+ * @sap: pointer to a socket address
605d1f1
+ * @salen: length of socket address
605d1f1
+ *
605d1f1
+ * Universal addresses (defined in RFC 1833) are used when calling an
605d1f1
+ * rpcbind daemon via protocol versions 3 or 4..
605d1f1
+ *
605d1f1
+ * Returns a '\0'-terminated string if successful; caller must free
605d1f1
+ * the returned string.  Otherwise NULL is returned and
605d1f1
+ * rpc_createerr.cf_stat is set to reflect the error.
605d1f1
+ *
605d1f1
+ */
605d1f1
+#ifdef HAVE_GETNAMEINFO
605d1f1
+
605d1f1
+char *nfs_sockaddr2universal(const struct sockaddr *sap,
605d1f1
+			     const socklen_t salen)
605d1f1
+{
605d1f1
+	struct sockaddr_un *sun = (struct sockaddr_un *)sap;
605d1f1
+	char buf[NI_MAXHOST];
605d1f1
+	uint16_t port;
605d1f1
+
605d1f1
+	switch (sap->sa_family) {
605d1f1
+	case AF_LOCAL:
605d1f1
+		return strndup(sun->sun_path, sizeof(sun->sun_path));
605d1f1
+	case AF_INET:
605d1f1
+		if (getnameinfo(sap, salen, buf, (socklen_t)sizeof(buf),
605d1f1
+					NULL, 0, NI_NUMERICHOST) != 0)
605d1f1
+			goto out_err;
605d1f1
+		port = ntohs(((struct sockaddr_in *)sap)->sin_port);
605d1f1
+		break;
605d1f1
+	case AF_INET6:
605d1f1
+		if (getnameinfo(sap, salen, buf, (socklen_t)sizeof(buf),
605d1f1
+					NULL, 0, NI_NUMERICHOST) != 0)
605d1f1
+			goto out_err;
605d1f1
+		port = ntohs(((struct sockaddr_in6 *)sap)->sin6_port);
605d1f1
+		break;
605d1f1
+	default:
605d1f1
+		goto out_err;
605d1f1
+	}
605d1f1
+
605d1f1
+	(void)snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ".%u.%u",
605d1f1
+			(unsigned)(port >> 8), (unsigned)(port & 0xff));
605d1f1
+
605d1f1
+	return strdup(buf);
605d1f1
+
605d1f1
+out_err:
605d1f1
+	rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE;
605d1f1
+	return NULL;
605d1f1
+}
605d1f1
+
605d1f1
+#else	/* HAVE_GETNAMEINFO */
605d1f1
+
605d1f1
+char *nfs_sockaddr2universal(const struct sockaddr *sap,
605d1f1
+			     const socklen_t salen)
605d1f1
+{
605d1f1
+	struct sockaddr_un *sun = (struct sockaddr_un *)sap;
605d1f1
+	char buf[NI_MAXHOST];
605d1f1
+	uint16_t port;
605d1f1
+	char *addr;
605d1f1
+
605d1f1
+	switch (sap->sa_family) {
605d1f1
+	case AF_LOCAL:
605d1f1
+		return strndup(sun->sun_path, sizeof(sun->sun_path));
605d1f1
+	case AF_INET:
605d1f1
+		addr = inet_ntoa(((struct sockaddr_in *)sap)->sin_addr);
605d1f1
+		if (addr != NULL && strlen(addr) > sizeof(buf))
605d1f1
+			goto out_err;
605d1f1
+		strcpy(buf, addr);
605d1f1
+		port = ntohs(((struct sockaddr_in *)sap)->sin_port);
605d1f1
+		break;
605d1f1
+	default:
605d1f1
+		goto out_err;
605d1f1
+	}
605d1f1
+
605d1f1
+	(void)snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ".%u.%u",
605d1f1
+			(unsigned)(port >> 8), (unsigned)(port & 0xff));
605d1f1
+
605d1f1
+	return strdup(buf);
605d1f1
+
605d1f1
+out_err:
605d1f1
+	rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE;
605d1f1
+	return NULL;
605d1f1
+}
605d1f1
+
605d1f1
+#endif	/* HAVE_GETNAMEINFO */
605d1f1
+
605d1f1
+/*
605d1f1
+ * Send a NULL request to the indicated RPC service.
605d1f1
+ *
605d1f1
+ * Returns 1 if the service responded; otherwise 0;
605d1f1
+ */
605d1f1
+static int nfs_gp_ping(CLIENT *client, struct timeval timeout)
605d1f1
+{
605d1f1
+	enum clnt_stat status;
605d1f1
+
605d1f1
+	status = CLNT_CALL(client, NULLPROC,
605d1f1
+			   (xdrproc_t)xdr_void, NULL,
605d1f1
+			   (xdrproc_t)xdr_void, NULL,
605d1f1
+			   timeout);
605d1f1
+
605d1f1
+	return (int)(status == RPC_SUCCESS);
605d1f1
+}
605d1f1
+
605d1f1
+#ifdef HAVE_XDR_RPCB
605d1f1
+
605d1f1
+/*
605d1f1
+ * Initialize the rpcb argument for a GETADDR request.
605d1f1
+ *
605d1f1
+ * The rpcbind daemon ignores the parms.r_owner field in GETADDR
605d1f1
+ * requests, but we plant an eye-catcher to help distinguish these
605d1f1
+ * requests in network traces.
605d1f1
+ *
605d1f1
+ * Returns 1 if successful, and caller must free strings pointed
605d1f1
+ * to by r_netid and r_addr; otherwise 0.
605d1f1
+ */
605d1f1
+static int nfs_gp_init_rpcb_parms(const struct sockaddr *sap,
605d1f1
+				  const socklen_t salen,
605d1f1
+				  const rpcprog_t program,
605d1f1
+				  const rpcvers_t version,
605d1f1
+				  const unsigned short protocol,
605d1f1
+				  struct rpcb *parms)
605d1f1
+{
605d1f1
+	char *netid, *addr;
605d1f1
+
605d1f1
+	netid = nfs_gp_get_netid(sap->sa_family, protocol);
605d1f1
+	if (netid == NULL)
605d1f1
+		return 0;
605d1f1
+
605d1f1
+	addr = nfs_sockaddr2universal(sap, salen);
605d1f1
+	if (addr == NULL) {
605d1f1
+		free(netid);
605d1f1
+		return 0;
605d1f1
+	}
605d1f1
+
605d1f1
+	memset(parms, 0, sizeof(*parms));
605d1f1
+	parms->r_prog	= program;
605d1f1
+	parms->r_vers	= version;
605d1f1
+	parms->r_netid	= netid;
605d1f1
+	parms->r_addr	= addr;
605d1f1
+	parms->r_owner	= "nfs-utils";	/* eye-catcher */
605d1f1
+
605d1f1
+	return 1;
605d1f1
+}
605d1f1
+
605d1f1
+static void nfs_gp_free_rpcb_parms(struct rpcb *parms)
605d1f1
+{
605d1f1
+	free(parms->r_netid);
605d1f1
+	free(parms->r_addr);
605d1f1
+}
605d1f1
+
605d1f1
+/*
605d1f1
+ * Try rpcbind GETADDR via version 4.  If that fails, try same
605d1f1
+ * request via version 3.
605d1f1
+ *
605d1f1
+ * Returns non-zero port number on success; otherwise returns
605d1f1
+ * zero.  rpccreateerr is set to reflect the nature of the error.
605d1f1
+ */
605d1f1
+static unsigned short nfs_gp_rpcb_getaddr(CLIENT *client,
605d1f1
+					  struct rpcb *parms,
605d1f1
+					  struct timeval timeout)
605d1f1
+{
605d1f1
+	rpcvers_t rpcb_version;
605d1f1
+	struct rpc_err rpcerr;
605d1f1
+	int port = 0;
605d1f1
+
605d1f1
+	for (rpcb_version = RPCBVERS_4;
605d1f1
+	     rpcb_version >= RPCBVERS_3;
605d1f1
+	     rpcb_version--) {
605d1f1
+		enum clnt_stat status;
605d1f1
+		char *uaddr = NULL;
605d1f1
+
605d1f1
+		CLNT_CONTROL(client, CLSET_VERS, (void *)&rpcb_version);
605d1f1
+		status = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETADDR,
605d1f1
+				   (xdrproc_t)xdr_rpcb, (void *)parms,
605d1f1
+				   (xdrproc_t)xdr_wrapstring, (void *)&uaddr,
605d1f1
+				   timeout);
605d1f1
+
605d1f1
+		switch (status) {
605d1f1
+		case RPC_SUCCESS:
605d1f1
+			if ((uaddr == NULL) || (uaddr[0] == '\0')) {
605d1f1
+				rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
605d1f1
+				continue;
605d1f1
+			}
605d1f1
+
605d1f1
+			port = nfs_universal2port(uaddr);
605d1f1
+			xdr_free((xdrproc_t)xdr_wrapstring, (char *)&uaddr);
605d1f1
+			if (port == -1) {
605d1f1
+				rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE;
605d1f1
+				return 0;
605d1f1
+			}
605d1f1
+			return (unsigned short)port;
605d1f1
+		case RPC_PROGVERSMISMATCH:
605d1f1
+			clnt_geterr(client, &rpcerr);
605d1f1
+			if (rpcerr.re_vers.low > RPCBVERS4)
605d1f1
+				return 0;
605d1f1
+			continue;
605d1f1
+		case RPC_PROCUNAVAIL:
605d1f1
+		case RPC_PROGUNAVAIL:
605d1f1
+			continue;
605d1f1
+		default:
605d1f1
+			/* Most likely RPC_TIMEDOUT or RPC_CANTRECV */
605d1f1
+			rpc_createerr.cf_stat = status;
605d1f1
+			clnt_geterr(client, &rpc_createerr.cf_error);
605d1f1
+			return 0;
605d1f1
+		}
605d1f1
+
605d1f1
+	}
605d1f1
+
605d1f1
+	if (port == 0) {
605d1f1
+		rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
605d1f1
+		clnt_geterr(client, &rpc_createerr.cf_error);
605d1f1
+	}
605d1f1
+	return port;
605d1f1
+}
605d1f1
+
605d1f1
+#endif	/* HAVE_XDR_RPCB */
605d1f1
+
605d1f1
+/*
605d1f1
+ * Try GETPORT request via rpcbind version 2.
605d1f1
+ *
605d1f1
+ * Returns non-zero port number on success; otherwise returns
605d1f1
+ * zero.  rpccreateerr is set to reflect the nature of the error.
605d1f1
+ */
605d1f1
+static unsigned long nfs_gp_pmap_getport(CLIENT *client,
605d1f1
+					 struct pmap *parms,
605d1f1
+					 struct timeval timeout)
605d1f1
+{
605d1f1
+	enum clnt_stat status;
605d1f1
+	unsigned long port;
605d1f1
+
605d1f1
+	status = CLNT_CALL(client, (rpcproc_t)PMAPPROC_GETPORT,
605d1f1
+			   (xdrproc_t)xdr_pmap, (void *)parms,
605d1f1
+			   (xdrproc_t)xdr_u_long, (void *)&port,
605d1f1
+			   timeout);
605d1f1
+
605d1f1
+	if (status != RPC_SUCCESS) {
605d1f1
+		rpc_createerr.cf_stat = status;
605d1f1
+		clnt_geterr(client, &rpc_createerr.cf_error);
605d1f1
+		port = 0;
605d1f1
+	} else if (port == 0)
605d1f1
+		rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
605d1f1
+
605d1f1
+	return port;
605d1f1
+}
605d1f1
+
605d1f1
+#ifdef HAVE_XDR_RPCB
605d1f1
+
605d1f1
+static unsigned short nfs_gp_getport_rpcb(CLIENT *client,
605d1f1
+					  const struct sockaddr *sap,
605d1f1
+					  const socklen_t salen,
605d1f1
+					  const rpcprog_t program,
605d1f1
+					  const rpcvers_t version,
605d1f1
+					  const unsigned short protocol,
605d1f1
+					  struct timeval timeout)
605d1f1
+{
605d1f1
+	unsigned short port = 0;
605d1f1
+	struct rpcb parms;
605d1f1
+
605d1f1
+	if (nfs_gp_init_rpcb_parms(sap, salen, program,
605d1f1
+					version, protocol, &parms) != 0) {
605d1f1
+		port = nfs_gp_rpcb_getaddr(client, &parms, timeout);
605d1f1
+		nfs_gp_free_rpcb_parms(&parms);
605d1f1
+	}
605d1f1
+
605d1f1
+	return port;
605d1f1
+}
605d1f1
+
605d1f1
+#endif	/* HAVE_XDR_RPCB */
605d1f1
+
605d1f1
+static unsigned long nfs_gp_getport_pmap(CLIENT *client,
605d1f1
+					 const rpcprog_t program,
605d1f1
+					 const rpcvers_t version,
605d1f1
+					 const unsigned short protocol,
605d1f1
+					 struct timeval timeout)
605d1f1
+{
605d1f1
+	struct pmap parms = {
605d1f1
+		.pm_prog	= program,
605d1f1
+		.pm_vers	= version,
605d1f1
+		.pm_prot	= protocol,
605d1f1
+	};
605d1f1
+	rpcvers_t pmap_version = PMAPVERS;
605d1f1
+
605d1f1
+	CLNT_CONTROL(client, CLSET_VERS, (void *)&pmap_version);
605d1f1
+	return nfs_gp_pmap_getport(client, &parms, timeout);
605d1f1
+}
605d1f1
+
605d1f1
+/*
605d1f1
+ * Try an AF_INET6 request via rpcbind v4/v3; try an AF_INET
605d1f1
+ * request via rpcbind v2.
605d1f1
+ *
605d1f1
+ * Returns non-zero port number on success; otherwise returns
605d1f1
+ * zero.  rpccreateerr is set to reflect the nature of the error.
605d1f1
+ */
605d1f1
+static unsigned short nfs_gp_getport(CLIENT *client,
605d1f1
+				     const struct sockaddr *sap,
605d1f1
+				     const socklen_t salen,
605d1f1
+				     const rpcprog_t program,
605d1f1
+				     const rpcvers_t version,
605d1f1
+				     const unsigned short protocol,
605d1f1
+				     struct timeval timeout)
605d1f1
+{
605d1f1
+	switch (sap->sa_family) {
605d1f1
+#ifdef HAVE_XDR_RPCB
605d1f1
+	case AF_INET6:
605d1f1
+		return nfs_gp_getport_rpcb(client, sap, salen, program,
605d1f1
+						version, protocol, timeout);
605d1f1
+#endif	/* HAVE_XDR_RPCB */
605d1f1
+	case AF_INET:
605d1f1
+		return nfs_gp_getport_pmap(client, program, version,
605d1f1
+							protocol, timeout);
605d1f1
+	}
605d1f1
+
605d1f1
+	rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
605d1f1
+	return 0;
605d1f1
+}
605d1f1
+
605d1f1
+/**
605d1f1
+ * nfs_rcp_ping - Determine if RPC service is responding to requests
605d1f1
+ * @sap: pointer to address of server to query (port is already filled in)
605d1f1
+ * @salen: length of server address
605d1f1
+ * @program: requested RPC program number
605d1f1
+ * @version: requested RPC version number
605d1f1
+ * @protocol: requested IPPROTO_ value of transport protocol
605d1f1
+ * @timeout: pointer to request timeout (NULL means use default timeout)
605d1f1
+ *
605d1f1
+ * Returns 1 if the remote service responded without an error; otherwise
605d1f1
+ * zero.
605d1f1
+ */
605d1f1
+int nfs_rpc_ping(const struct sockaddr *sap, const socklen_t salen,
605d1f1
+		 const rpcprog_t program, const rpcvers_t version,
605d1f1
+		 const unsigned short protocol, const struct timeval *timeout)
605d1f1
+{
605d1f1
+	CLIENT *client;
605d1f1
+	struct timeval tout = { -1, 0 };
605d1f1
+	int result = 0;
605d1f1
+
605d1f1
+	if (timeout != NULL)
605d1f1
+		tout = *timeout;
605d1f1
+
605d1f1
+	client = nfs_get_rpcclient(sap, salen, protocol, program, version, &tout);
605d1f1
+	if (client != NULL) {
605d1f1
+		result = nfs_gp_ping(client, tout);
605d1f1
+		CLNT_DESTROY(client);
605d1f1
+	}
605d1f1
+
605d1f1
+	return result;
605d1f1
+}
605d1f1
+
605d1f1
+/**
605d1f1
+ * nfs_getport - query server's rpcbind to get port number for an RPC service
605d1f1
+ * @sap: pointer to address of server to query
605d1f1
+ * @salen: length of server's address
605d1f1
+ * @program: requested RPC program number
605d1f1
+ * @version: requested RPC version number
605d1f1
+ * @protocol: IPPROTO_ value of requested transport protocol
605d1f1
+ *
605d1f1
+ * Uses any acceptable rpcbind version to discover the port number for the
605d1f1
+ * RPC service described by the given [program, version, transport] tuple.
605d1f1
+ * Uses a quick timeout and an ephemeral source port.  Supports AF_INET and
605d1f1
+ * AF_INET6 server addresses.
605d1f1
+ *
605d1f1
+ * Returns a positive integer representing the port number of the RPC
605d1f1
+ * service advertised by the server (in host byte order), or zero if the
605d1f1
+ * service is not advertised or there was some problem querying the server's
605d1f1
+ * rpcbind daemon.  rpccreateerr is set to reflect the underlying cause of
605d1f1
+ * the error.
605d1f1
+ *
605d1f1
+ * There are a variety of ways to choose which transport and rpcbind versions
605d1f1
+ * to use.  We chose to conserve local resources and try to avoid incurring
605d1f1
+ * timeouts.
605d1f1
+ *
605d1f1
+ * Transport
605d1f1
+ * To provide rudimentary support for traversing firewalls, query the remote
605d1f1
+ * using the same transport as the requested service.  This provides some
605d1f1
+ * guarantee that the requested transport is available between this client
605d1f1
+ * and the server, and if the caller specifically requests TCP, for example,
605d1f1
+ * this may be becuase a firewall is in place that blocks UDP traffic.  We
605d1f1
+ * could try both, but that could involve a lengthy timeout in several cases,
605d1f1
+ * and would often consume an extra ephemeral port.
605d1f1
+ *
605d1f1
+ * Rpcbind version
605d1f1
+ * To avoid using up too many ephemeral ports, AF_INET queries use tried-and-
605d1f1
+ * true rpcbindv2, and don't try the newer versions; and AF_INET6 queries use
605d1f1
+ * rpcbindv4, then rpcbindv3 on the same socket.  The newer rpcbind protocol
605d1f1
+ * versions can adequately detect if a remote RPC service does not support
605d1f1
+ * AF_INET6 at all.  The rpcbind socket is re-used in an attempt to keep the
605d1f1
+ * overall number of consumed ephemeral ports low.
605d1f1
+ */
605d1f1
+unsigned short nfs_getport(const struct sockaddr *sap,
605d1f1
+			   const socklen_t salen,
605d1f1
+			   const rpcprog_t program,
605d1f1
+			   const rpcvers_t version,
605d1f1
+			   const unsigned short protocol)
605d1f1
+{
605d1f1
+	struct timeval timeout = { -1, 0 };
605d1f1
+	unsigned short port = 0;
605d1f1
+	CLIENT *client;
605d1f1
+
605d1f1
+	client = nfs_gp_get_rpcbclient(sap, salen, protocol,
605d1f1
+						default_rpcb_version, &timeout);
605d1f1
+	if (client != NULL) {
605d1f1
+		port = nfs_gp_getport(client, sap, salen, program,
605d1f1
+					version, protocol, timeout);
605d1f1
+		CLNT_DESTROY(client);
605d1f1
+	}
605d1f1
+
605d1f1
+	return port;
605d1f1
+}
605d1f1
+
605d1f1
+/**
605d1f1
+ * nfs_getport_ping - query server's rpcbind and do RPC ping to verify result
605d1f1
+ * @sap: IN: pointer to address of server to query;
605d1f1
+ *	 OUT: pointer to updated address
605d1f1
+ * @salen: length of server's address
605d1f1
+ * @program: requested RPC program number
605d1f1
+ * @version: requested RPC version number
605d1f1
+ * @protocol: IPPROTO_ value of requested transport protocol
605d1f1
+ *
605d1f1
+ * Uses any acceptable rpcbind version to discover the port number for the
605d1f1
+ * RPC service described by the given [program, version, transport] tuple.
605d1f1
+ * Uses a quick timeout and an ephemeral source port.  Supports AF_INET and
605d1f1
+ * AF_INET6 server addresses.
605d1f1
+ *
605d1f1
+ * Returns a 1 and sets the port number in the passed-in server address
605d1f1
+ * if both the query and the ping were successful; otherwise zero.
605d1f1
+ * rpccreateerr is set to reflect the underlying cause of the error.
605d1f1
+ */
605d1f1
+int nfs_getport_ping(struct sockaddr *sap, const socklen_t salen,
605d1f1
+		     const rpcprog_t program, const rpcvers_t version,
605d1f1
+		     const unsigned short protocol)
605d1f1
+{
605d1f1
+	struct timeval timeout = { -1, 0 };
605d1f1
+	unsigned short port = 0;
605d1f1
+	CLIENT *client;
605d1f1
+	int result = 0;
605d1f1
+	
605d1f1
+	client = nfs_gp_get_rpcbclient(sap, salen, protocol,
605d1f1
+						default_rpcb_version, &timeout);
605d1f1
+	if (client != NULL) {
605d1f1
+		port = nfs_gp_getport(client, sap, salen, program,
605d1f1
+					version, protocol, timeout);
605d1f1
+		CLNT_DESTROY(client);
605d1f1
+		client = NULL;
605d1f1
+	}
605d1f1
+
605d1f1
+	if (port != 0) {
605d1f1
+		struct sockaddr_storage address;
605d1f1
+		struct sockaddr *saddr = (struct sockaddr *)&address;
605d1f1
+
605d1f1
+		memcpy(saddr, sap, (size_t)salen);
605d1f1
+		nfs_gp_set_port(saddr, htons(port));
605d1f1
+
605d1f1
+		client = nfs_get_rpcclient(saddr, salen, protocol,
605d1f1
+						program, version, &timeout);
605d1f1
+		if (client != NULL) {
605d1f1
+			result = nfs_gp_ping(client, timeout);
605d1f1
+			CLNT_DESTROY(client);
605d1f1
+		}
605d1f1
+	}
605d1f1
+
605d1f1
+	if (result)
605d1f1
+		nfs_gp_set_port(sap, htons(port));
605d1f1
+
605d1f1
+	return result;
605d1f1
+}
605d1f1
+
605d1f1
+/**
605d1f1
+ * nfs_getlocalport - query local rpcbind to get port number for an RPC service
605d1f1
+ * @program: requested RPC program number
605d1f1
+ * @version: requested RPC version number
605d1f1
+ * @protocol: IPPROTO_ value of requested transport protocol
605d1f1
+ *
605d1f1
+ * Uses any acceptable rpcbind version to discover the port number for the
605d1f1
+ * RPC service described by the given [program, version, transport] tuple.
605d1f1
+ * Uses a quick timeout and an ephemeral source port.  Supports AF_INET and
605d1f1
+ * AF_INET6 local addresses.
605d1f1
+ *
605d1f1
+ * Returns a positive integer representing the port number of the RPC
605d1f1
+ * service advertised by the server (in host byte order), or zero if the
605d1f1
+ * service is not advertised or there was some problem querying the server's
605d1f1
+ * rpcbind daemon.  rpccreateerr is set to reflect the underlying cause of
605d1f1
+ * the error.
605d1f1
+ *
605d1f1
+ * Try an AF_LOCAL connection first.  The rpcbind daemon implementation should
605d1f1
+ * listen on AF_LOCAL.
605d1f1
+ *
605d1f1
+ * If that doesn't work (for example, if portmapper is running, or rpcbind
605d1f1
+ * isn't listening on /var/run/rpcbind.sock), send a query via UDP to localhost
605d1f1
+ * (UDP doesn't leave a socket in TIME_WAIT, and the timeout is a relatively
605d1f1
+ * short 3 seconds).
605d1f1
+ *
605d1f1
+ * getaddrinfo(3) generates a usable loopback address.  RFC 3484 requires that
605d1f1
+ * the results are sorted so that the first result has the best likelihood of
605d1f1
+ * working, so we try just that first result.  If IPv6 is all that is
605d1f1
+ * available, we are sure to generate an AF_INET6 loopback address and use
605d1f1
+ * rpcbindv4/v3 GETADDR.  AF_INET6 requests go via rpcbind v4/3 in order to
605d1f1
+ * detect if the requested RPC service supports AF_INET6 or not.
605d1f1
+ */
605d1f1
+unsigned short nfs_getlocalport(const rpcprot_t program,
605d1f1
+				const rpcvers_t version,
605d1f1
+				const unsigned short protocol)
605d1f1
+{
605d1f1
+	struct addrinfo *gai_results;
605d1f1
+	struct addrinfo gai_hint = {
605d1f1
+		.ai_flags	= AI_ADDRCONFIG,
605d1f1
+	};
605d1f1
+	unsigned short port = 0;
605d1f1
+	int error;
605d1f1
+
605d1f1
+#ifdef NFS_GP_LOCAL
605d1f1
+	const struct sockaddr_un sun = {
605d1f1
+		.sun_family	= AF_LOCAL,
605d1f1
+		.sun_path	= _PATH_RPCBINDSOCK,
605d1f1
+	};
605d1f1
+	const struct sockaddr *sap = (struct sockaddr *)&sun;
605d1f1
+	const socklen_t salen = SUN_LEN(&sun);
605d1f1
+	CLIENT *client;
605d1f1
+	struct timeval timeout = { -1, 0 };
605d1f1
+
605d1f1
+	client = nfs_gp_get_rpcbclient(sap, salen, 0, RPCBVERS_4, &timeout);
605d1f1
+	if (client != NULL) {
605d1f1
+		struct rpcb parms;
605d1f1
+
605d1f1
+		if (nfs_gp_init_rpcb_parms(sap, salen, program, version,
605d1f1
+						protocol, &parms) != 0) {
605d1f1
+			port = nfs_gp_rpcb_getaddr(client, &parms, timeout);
605d1f1
+			nfs_gp_free_rpcb_parms(&parms);
605d1f1
+		}
605d1f1
+		CLNT_DESTROY(client);
605d1f1
+	}
605d1f1
+#endif	/* NFS_GP_LOCAL */
605d1f1
+
605d1f1
+	if (port == 0) {
605d1f1
+		error = getaddrinfo(NULL, "sunrpc", &gai_hint, &gai_results);
605d1f1
+		if (error == 0) {
605d1f1
+			port = nfs_getport(gai_results->ai_addr,
605d1f1
+						gai_results->ai_addrlen,
605d1f1
+						program, version, protocol);
605d1f1
+			freeaddrinfo(gai_results);
605d1f1
+		} else
605d1f1
+			rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
605d1f1
+	}
605d1f1
+
605d1f1
+	return port;
605d1f1
+}
605d1f1
+
605d1f1
+/**
605d1f1
+ * nfs_rpcb_getaddr - query rpcbind via rpcbind versions 4 and 3
605d1f1
+ * @sap: pointer to address of server to query
605d1f1
+ * @salen: length of server address
605d1f1
+ * @transport: transport protocol to use for the query
605d1f1
+ * @addr: pointer to r_addr address
605d1f1
+ * @addrlen: length of address
605d1f1
+ * @program: requested RPC program number
605d1f1
+ * @version: requested RPC version number
605d1f1
+ * @protocol: requested IPPROTO_ value of transport protocol
605d1f1
+ * @timeout: pointer to request timeout (NULL means use default timeout)
605d1f1
+ *
605d1f1
+ * Returns a positive integer representing the port number of the RPC
605d1f1
+ * service advertised by the server (in host byte order), or zero if the
605d1f1
+ * service is not advertised or there was some problem querying the
605d1f1
+ * server's rpcbind daemon.  rpccreateerr is set to reflect the
605d1f1
+ * underlying cause of the error.
605d1f1
+ *
605d1f1
+ * This function provides similar functionality to nfs_pmap_getport(),
605d1f1
+ * but performs the rpcbind lookup via rpcbind version 4.  If the server
605d1f1
+ * doesn't support rpcbind version 4, it will retry with version 3.
605d1f1
+ * The GETADDR procedure is exactly the same in these two versions of
605d1f1
+ * the rpcbind protocol, so the socket, RPC client, and arguments are
605d1f1
+ * re-used when retrying, saving ephemeral port space.
605d1f1
+ *
605d1f1
+ * These RPC procedures take a universal address as an argument, so the
605d1f1
+ * query will fail if the remote rpcbind daemon doesn't find an entry
605d1f1
+ * with a matching address.  A matching address includes an ANYADDR
605d1f1
+ * address of the same address family.  In this way an RPC server can
605d1f1
+ * advertise via rpcbind that it does not support AF_INET6.
605d1f1
+ */
605d1f1
+#ifdef HAVE_XDR_RPCB
605d1f1
+
605d1f1
+unsigned short nfs_rpcb_getaddr(const struct sockaddr *sap,
605d1f1
+				const socklen_t salen,
605d1f1
+				const unsigned short transport,
605d1f1
+				const struct sockaddr *addr,
605d1f1
+				const socklen_t addrlen,
605d1f1
+				const rpcprog_t program,
605d1f1
+				const rpcvers_t version,
605d1f1
+				const unsigned short protocol,
605d1f1
+				const struct timeval *timeout)
605d1f1
+{
605d1f1
+	CLIENT *client;
605d1f1
+	struct rpcb parms;
605d1f1
+	struct timeval tout = { -1, 0 };
605d1f1
+	unsigned short port = 0;
605d1f1
+
605d1f1
+	if (timeout != NULL)
605d1f1
+		tout = *timeout;
605d1f1
+
605d1f1
+	client = nfs_gp_get_rpcbclient(sap, salen, transport, RPCBVERS_4, &tout);
605d1f1
+	if (client != NULL) {
605d1f1
+		if (nfs_gp_init_rpcb_parms(addr, addrlen, program, version,
605d1f1
+						protocol, &parms) != 0) {
605d1f1
+			port = nfs_gp_rpcb_getaddr(client, &parms, tout);
605d1f1
+			nfs_gp_free_rpcb_parms(&parms);
605d1f1
+		}
605d1f1
+		CLNT_DESTROY(client);
605d1f1
+	}
605d1f1
+
605d1f1
+	return port;
605d1f1
+}
605d1f1
+
605d1f1
+#else	/* HAVE_XDR_RPCB */
605d1f1
+
605d1f1
+unsigned short nfs_rpcb_getaddr(const struct sockaddr *sap,
605d1f1
+				const socklen_t salen,
605d1f1
+				const unsigned short transport,
605d1f1
+				const struct sockaddr *addr,
605d1f1
+				const socklen_t addrlen,
605d1f1
+				const rpcprog_t program,
605d1f1
+				const rpcvers_t version,
605d1f1
+				const unsigned short protocol,
605d1f1
+				const struct timeval *timeout)
605d1f1
+{
605d1f1
+	rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
605d1f1
+	return 0;
605d1f1
+}
605d1f1
+
605d1f1
+#endif	/* HAVE_XDR_RPCB */
605d1f1
+
605d1f1
+/**
605d1f1
+ * nfs_pmap_getport - query rpcbind via the portmap protocol (rpcbindv2)
605d1f1
+ * @sin: pointer to AF_INET address of server to query
605d1f1
+ * @transport: transport protocol to use for the query
605d1f1
+ * @program: requested RPC program number
605d1f1
+ * @version: requested RPC version number
605d1f1
+ * @protocol: requested IPPROTO_ value of transport protocol
605d1f1
+ * @timeout: pointer to request timeout (NULL means use default timeout)
605d1f1
+ *
605d1f1
+ * Returns a positive integer representing the port number of the RPC service
605d1f1
+ * advertised by the server (in host byte order), or zero if the service is
605d1f1
+ * not advertised or there was some problem querying the server's rpcbind
605d1f1
+ * daemon.  rpccreateerr is set to reflect the underlying cause of the error.
605d1f1
+ *
605d1f1
+ * nfs_pmap_getport() is very similar to pmap_getport(), except that:
605d1f1
+ *
605d1f1
+ *  1.	This version always tries to use an ephemeral port, since reserved
605d1f1
+ *	ports are not needed for GETPORT queries.  This conserves the very
605d1f1
+ *	limited reserved port space, helping reduce failed socket binds
605d1f1
+ *	during mount storms.
605d1f1
+ *
605d1f1
+ *  2.	This version times out quickly by default.  It time-limits the
605d1f1
+ *	connect process as well as the actual RPC call, and even allows the
605d1f1
+ *	caller to specify the timeout.
605d1f1
+ *
605d1f1
+ *  3.	This version shares code with the rpcbindv3 and rpcbindv4 query
605d1f1
+ *	functions.  It can use a TI-RPC generated CLIENT.
605d1f1
+ */
605d1f1
+unsigned long nfs_pmap_getport(const struct sockaddr_in *sin,
605d1f1
+			       const unsigned short transport,
605d1f1
+			       const unsigned long program,
605d1f1
+			       const unsigned long version,
605d1f1
+			       const unsigned long protocol,
605d1f1
+			       const struct timeval *timeout)
605d1f1
+{
605d1f1
+	CLIENT *client;
605d1f1
+	struct pmap parms = {
605d1f1
+		.pm_prog	= program,
605d1f1
+		.pm_vers	= version,
605d1f1
+		.pm_prot	= protocol,
605d1f1
+	};
605d1f1
+	struct timeval tout = { -1, 0 };
605d1f1
+	unsigned long port = 0;
605d1f1
+
605d1f1
+	if (timeout != NULL)
605d1f1
+		tout = *timeout;
605d1f1
+
605d1f1
+	client = nfs_gp_get_rpcbclient((struct sockaddr *)sin,
605d1f1
+					(socklen_t)sizeof(*sin),
605d1f1
+					transport, PMAPVERS, &tout);
605d1f1
+	if (client != NULL) {
605d1f1
+		port = nfs_gp_pmap_getport(client, &parms, tout);
605d1f1
+		CLNT_DESTROY(client);
605d1f1
+	}
605d1f1
+
605d1f1
+	return port;
605d1f1
+}
605d1f1
--- nfs-utils-1.1.4/support/include/nfsrpc.h.orig	2008-11-18 15:06:29.121302000 -0500
605d1f1
+++ nfs-utils-1.1.4/support/include/nfsrpc.h	2008-11-18 15:08:43.267903000 -0500
605d1f1
@@ -67,4 +67,75 @@ extern CLIENT		*nfs_get_rpcclient(const 
605d1f1
 				const rpcprog_t, const rpcvers_t,
605d1f1
 				struct timeval *);
605d1f1
 
605d1f1
+/*
605d1f1
+ * Convert a socket address to a universal address
605d1f1
+ */
605d1f1
+extern char		*nfs_sockaddr2universal(const struct sockaddr *,
605d1f1
+				const socklen_t);
605d1f1
+
605d1f1
+/*
605d1f1
+ * Extract port number from a universal address
605d1f1
+ */
605d1f1
+extern int		nfs_universal2port(const char *);
605d1f1
+
605d1f1
+/*
605d1f1
+ * Generic function that maps an RPC service tuple to an IP port
605d1f1
+ * number of the service on a remote post, and sends a NULL
605d1f1
+ * request to determine if the service is responding to requests
605d1f1
+ */
605d1f1
+extern int		nfs_getport_ping(struct sockaddr *sap,
605d1f1
+				const socklen_t salen,
605d1f1
+				const rpcprog_t program,
605d1f1
+				const rpcvers_t version,
605d1f1
+				const unsigned short protocol);
605d1f1
+
605d1f1
+/*
605d1f1
+ * Generic function that maps an RPC service tuple to an IP port
605d1f1
+ * number of the service on a remote host
605d1f1
+ */
605d1f1
+extern unsigned short	nfs_getport(const struct sockaddr *,
605d1f1
+				const socklen_t, const rpcprog_t,
605d1f1
+				const rpcvers_t, const unsigned short);
605d1f1
+
605d1f1
+/*
605d1f1
+ * Generic function that maps an RPC service tuple to an IP port
605d1f1
+ * number of the service on the local host
605d1f1
+ */
605d1f1
+extern unsigned short	nfs_getlocalport(const rpcprot_t,
605d1f1
+				const rpcvers_t, const unsigned short);
605d1f1
+
605d1f1
+/*
605d1f1
+ * Function to invoke an rpcbind v3/v4 GETADDR request
605d1f1
+ */
605d1f1
+extern unsigned short	nfs_rpcb_getaddr(const struct sockaddr *,
605d1f1
+				const socklen_t,
605d1f1
+				const unsigned short,
605d1f1
+				const struct sockaddr *,
605d1f1
+				const socklen_t,
605d1f1
+				const rpcprog_t,
605d1f1
+				const rpcvers_t,
605d1f1
+				const unsigned short,
605d1f1
+				const struct timeval *);
605d1f1
+
605d1f1
+/*
605d1f1
+ * Function to invoke a portmap GETPORT request
605d1f1
+ */
605d1f1
+extern unsigned long	nfs_pmap_getport(const struct sockaddr_in *,
605d1f1
+				const unsigned short,
605d1f1
+				const unsigned long,
605d1f1
+				const unsigned long,
605d1f1
+				const unsigned long,
605d1f1
+				const struct timeval *);
605d1f1
+
605d1f1
+/*
605d1f1
+ * Contact a remote RPC service to discover whether it is responding
605d1f1
+ * to requests.
605d1f1
+ */
605d1f1
+extern int		nfs_rpc_ping(const struct sockaddr *sap,
605d1f1
+				const socklen_t salen,
605d1f1
+				const rpcprog_t program,
605d1f1
+				const rpcvers_t version,
605d1f1
+				const unsigned short protocol,
605d1f1
+				const struct timeval *timeout);
605d1f1
+
605d1f1
 #endif	/* __NFS_UTILS_NFSRPC_H */