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