walters / rpms / nfs-utils

Forked from rpms/nfs-utils 6 years ago
Clone
605d1f1
commit 162cbdd19830abaf6a3fd64a22839023ce99185d
605d1f1
Author: Chuck Lever <chuck.lever@oracle.com>
605d1f1
Date:   Mon Nov 17 16:08:03 2008 -0500
605d1f1
605d1f1
    Add AF_INET6-capable API to acquire an RPC	CLIENT *
605d1f1
    
605d1f1
    Provide a simple interface that any component of nfs-utils can use to acquire
605d1f1
    an RPC CLIENT *.  This is an AF_INET6-enabled API, and can also handle
605d1f1
    PF_LOCAL sockets if libtirpc is present on the system.
605d1f1
    
605d1f1
    When libtirpc is not available, legacy RPC services will be used instead,
605d1f1
    and an attempt to connect to an AF_INET6 address will fail.
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-10-17 10:20:09.000000000 -0400
605d1f1
+++ nfs-utils-1.1.4/support/nfs/Makefile.am	2008-11-18 14:59:08.894659000 -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 \
605d1f1
+		   nfsexport.c getfh.c nfsctl.c rpc_socket.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/rpc_socket.c	2008-11-18 14:59:08.904660000 -0500
605d1f1
@@ -0,0 +1,528 @@
605d1f1
+/*
605d1f1
+ * Generic RPC client socket-level APIs for nfs-utils
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
+#include "nfsrpc.h"
605d1f1
+
605d1f1
+#ifdef HAVE_TIRPC_NETCONFIG_H
605d1f1
+
605d1f1
+/*
605d1f1
+ * Most of the headers under /usr/include/tirpc are currently
605d1f1
+ * unusable for various reasons.  We statically define the bits
605d1f1
+ * we need here until the official headers are fixed.
605d1f1
+ *
605d1f1
+ * The commonly used RPC calls such as CLNT_CALL and CLNT_DESTROY
605d1f1
+ * are actually virtual functions in both the legacy and TI-RPC
605d1f1
+ * implementations.  The proper _CALL or _DESTROY will be invoked
605d1f1
+ * no matter if we used a legacy clnt_create() or clnt_tli_create()
605d1f1
+ * from libtirpc.
605d1f1
+ */
605d1f1
+
605d1f1
+#include <tirpc/netconfig.h>
605d1f1
+#include <tirpc/rpc/rpcb_prot.h>
605d1f1
+
605d1f1
+/* definitions from tirpc/rpc/types.h */
605d1f1
+
605d1f1
+/*
605d1f1
+ * The netbuf structure is used for transport-independent address storage.
605d1f1
+ */
605d1f1
+struct netbuf {
605d1f1
+	unsigned int	maxlen;
605d1f1
+	unsigned int	len;
605d1f1
+	void		*buf;
605d1f1
+};
605d1f1
+
605d1f1
+/* definitions from tirpc/rpc/clnt.h */
605d1f1
+
605d1f1
+/*
605d1f1
+ * Low level clnt create routine for connectionless transports, e.g. udp.
605d1f1
+ */
605d1f1
+extern CLIENT *clnt_dg_create(const int, const struct netbuf *,
605d1f1
+			      const rpcprog_t, const rpcvers_t,
605d1f1
+			      const u_int, const u_int);
605d1f1
+
605d1f1
+/*
605d1f1
+ * Low level clnt create routine for connectionful transports, e.g. tcp.
605d1f1
+ */
605d1f1
+extern CLIENT *clnt_vc_create(const int, const struct netbuf *,
605d1f1
+			      const rpcprog_t, const rpcvers_t,
605d1f1
+			      u_int, u_int);
605d1f1
+
605d1f1
+#endif	/* HAVE_TIRPC_NETCONFIG_H */
605d1f1
+
605d1f1
+/*
605d1f1
+ * If "-1" is specified in the tv_sec field, use these defaults instead.
605d1f1
+ */
605d1f1
+#define NFSRPC_TIMEOUT_UDP	(3)
605d1f1
+#define NFSRPC_TIMEOUT_TCP	(10)
605d1f1
+
605d1f1
+/*
605d1f1
+ * Set up an RPC client for communicating via a AF_LOCAL socket.
605d1f1
+ *
605d1f1
+ * @timeout is initialized upon return
605d1f1
+ *
605d1f1
+ * Returns a pointer to a prepared RPC client if successful; caller
605d1f1
+ * must destroy a non-NULL returned RPC client.  Otherwise NULL, and
605d1f1
+ * rpc_createerr.cf_stat is set to reflect the error.
605d1f1
+ */
605d1f1
+static CLIENT *nfs_get_localclient(const struct sockaddr *sap,
605d1f1
+				   const socklen_t salen,
605d1f1
+				   const rpcprog_t program,
605d1f1
+				   const rpcvers_t version,
605d1f1
+				   struct timeval *timeout)
605d1f1
+{
605d1f1
+#ifdef HAVE_CLNT_VC_CREATE
605d1f1
+	struct sockaddr_storage address;
605d1f1
+	const struct netbuf nbuf = {
605d1f1
+		.maxlen		= sizeof(struct sockaddr_un),
605d1f1
+		.len		= (size_t)salen,
605d1f1
+		.buf		= &address,
605d1f1
+	};
605d1f1
+#endif	/* HAVE_CLNT_VC_CREATE */
605d1f1
+	CLIENT *client;
605d1f1
+	int sock;
605d1f1
+
605d1f1
+	sock = socket(AF_LOCAL, SOCK_STREAM, 0);
605d1f1
+	if (sock == -1) {
605d1f1
+		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
605d1f1
+		rpc_createerr.cf_error.re_errno = errno;
605d1f1
+		return NULL;
605d1f1
+	}
605d1f1
+
605d1f1
+	if (timeout->tv_sec == -1)
605d1f1
+		timeout->tv_sec = NFSRPC_TIMEOUT_TCP;
605d1f1
+
605d1f1
+#ifdef HAVE_CLNT_VC_CREATE
605d1f1
+	memcpy(nbuf.buf, sap, (size_t)salen);
605d1f1
+	client = clnt_vc_create(sock, &nbuf, program, version, 0, 0);
605d1f1
+#else	/* HAVE_CLNT_VC_CREATE */
605d1f1
+	client = clntunix_create((struct sockaddr_un *)sap,
605d1f1
+					program, version, &sock, 0, 0);
605d1f1
+#endif	/* HAVE_CLNT_VC_CREATE */
605d1f1
+	if (client != NULL)
605d1f1
+		CLNT_CONTROL(client, CLSET_FD_CLOSE, NULL);
605d1f1
+	else
605d1f1
+		(void)close(sock);
605d1f1
+
605d1f1
+	return client;
605d1f1
+}
605d1f1
+
605d1f1
+/*
605d1f1
+ * Bind a socket using an unused ephemeral source port.
605d1f1
+ *
605d1f1
+ * Returns zero on success, or returns -1 on error.  errno is
605d1f1
+ * set to reflect the nature of the error.
605d1f1
+ */
605d1f1
+static int nfs_bind(const int sock, const sa_family_t family)
605d1f1
+{
605d1f1
+	struct sockaddr_in sin = {
605d1f1
+		.sin_family		= AF_INET,
605d1f1
+		.sin_addr.s_addr	= htonl(INADDR_ANY),
605d1f1
+	};
605d1f1
+	struct sockaddr_in6 sin6 = {
605d1f1
+		.sin6_family		= AF_INET6,
605d1f1
+		.sin6_addr		= IN6ADDR_ANY_INIT,
605d1f1
+	};
605d1f1
+
605d1f1
+	switch (family) {
605d1f1
+	case AF_INET:
605d1f1
+		return bind(sock, (struct sockaddr *)&sin,
605d1f1
+					(socklen_t)sizeof(sin));
605d1f1
+	case AF_INET6:
605d1f1
+		return bind(sock, (struct sockaddr *)&sin6,
605d1f1
+					(socklen_t)sizeof(sin6));
605d1f1
+	}
605d1f1
+
605d1f1
+	errno = EAFNOSUPPORT;
605d1f1
+	return -1;
605d1f1
+}
605d1f1
+
605d1f1
+/*
605d1f1
+ * Perform a non-blocking connect on the socket fd.
605d1f1
+ *
605d1f1
+ * @timeout is modified to contain the time remaining (i.e. time provided
605d1f1
+ * minus time elasped).
605d1f1
+ *
605d1f1
+ * Returns zero on success, or returns -1 on error.  errno is
605d1f1
+ * set to reflect the nature of the error.
605d1f1
+ */
605d1f1
+static int nfs_connect_nb(const int fd, const struct sockaddr *sap,
605d1f1
+			  const socklen_t salen, struct timeval *timeout)
605d1f1
+{
605d1f1
+	int flags, ret;
605d1f1
+	fd_set rset;
605d1f1
+
605d1f1
+	flags = fcntl(fd, F_GETFL, 0);
605d1f1
+	if (flags < 0)
605d1f1
+		return -1;
605d1f1
+
605d1f1
+	ret = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
605d1f1
+	if (ret < 0)
605d1f1
+		return -1;
605d1f1
+
605d1f1
+	/*
605d1f1
+	 * From here on subsequent sys calls could change errno so
605d1f1
+	 * we set ret = -errno to capture it in case we decide to
605d1f1
+	 * use it later.
605d1f1
+	 */
605d1f1
+	ret = connect(fd, sap, salen);
605d1f1
+	if (ret < 0 && errno != EINPROGRESS) {
605d1f1
+		ret = -1;
605d1f1
+		goto done;
605d1f1
+	}
605d1f1
+
605d1f1
+	if (ret == 0)
605d1f1
+		goto done;
605d1f1
+
605d1f1
+	/* now wait */
605d1f1
+	FD_ZERO(&rset);
605d1f1
+	FD_SET(fd, &rset);
605d1f1
+
605d1f1
+	ret = select(fd + 1, NULL, &rset, NULL, timeout);
605d1f1
+	if (ret <= 0) {
605d1f1
+		if (ret == 0)
605d1f1
+			errno = ETIMEDOUT;
605d1f1
+		ret = -1;
605d1f1
+		goto done;
605d1f1
+	}
605d1f1
+
605d1f1
+	if (FD_ISSET(fd, &rset)) {
605d1f1
+		int status;
605d1f1
+		socklen_t len = (socklen_t)sizeof(ret);
605d1f1
+
605d1f1
+		status = getsockopt(fd, SOL_SOCKET, SO_ERROR, &ret, &len;;
605d1f1
+		if (status < 0) {
605d1f1
+			ret = -1;
605d1f1
+			goto done;
605d1f1
+		}
605d1f1
+
605d1f1
+		/* Oops - something wrong with connect */
605d1f1
+		if (ret != 0) {
605d1f1
+			errno = ret;
605d1f1
+			ret = -1;
605d1f1
+		}
605d1f1
+	}
605d1f1
+
605d1f1
+done:
605d1f1
+	(void)fcntl(fd, F_SETFL, flags);
605d1f1
+	return ret;
605d1f1
+}
605d1f1
+
605d1f1
+/*
605d1f1
+ * Set up an RPC client for communicating via a datagram socket.
605d1f1
+ * A connected UDP socket is used to detect a missing remote
605d1f1
+ * listener as quickly as possible.
605d1f1
+ *
605d1f1
+ * @timeout is initialized upon return
605d1f1
+ *
605d1f1
+ * Returns a pointer to a prepared RPC client if successful; caller
605d1f1
+ * must destroy a non-NULL returned RPC client.  Otherwise NULL, and
605d1f1
+ * rpc_createerr.cf_stat is set to reflect the error.
605d1f1
+ */
605d1f1
+static CLIENT *nfs_get_udpclient(const struct sockaddr *sap,
605d1f1
+				 const socklen_t salen,
605d1f1
+				 const rpcprog_t program,
605d1f1
+				 const rpcvers_t version,
605d1f1
+				 struct timeval *timeout)
605d1f1
+{
605d1f1
+#ifdef HAVE_CLNT_DG_CREATE
605d1f1
+	struct sockaddr_storage address;
605d1f1
+	const struct netbuf nbuf = {
605d1f1
+		.maxlen		= salen,
605d1f1
+		.len		= salen,
605d1f1
+		.buf		= &address,
605d1f1
+	};
605d1f1
+#endif	/* HAVE_CLNT_DG_CREATE */
605d1f1
+	CLIENT *client;
605d1f1
+	int ret, sock;
605d1f1
+
605d1f1
+#ifndef HAVE_CLNT_DG_CREATE
605d1f1
+	if (sap->sa_family != AF_INET) {
605d1f1
+		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
605d1f1
+		return NULL;
605d1f1
+	}
605d1f1
+#endif	/* !HAVE_CLNT_DG_CREATE */
605d1f1
+
605d1f1
+	sock = socket((int)sap->sa_family, SOCK_DGRAM, IPPROTO_UDP);
605d1f1
+	if (sock == -1) {
605d1f1
+		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
605d1f1
+		rpc_createerr.cf_error.re_errno = errno;
605d1f1
+		return NULL;
605d1f1
+	}
605d1f1
+
605d1f1
+	ret = nfs_bind(sock, sap->sa_family);
605d1f1
+	if (ret < 0) {
605d1f1
+		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
605d1f1
+		rpc_createerr.cf_error.re_errno = errno;
605d1f1
+		(void)close(sock);
605d1f1
+		return NULL;
605d1f1
+	}
605d1f1
+
605d1f1
+	if (timeout->tv_sec == -1)
605d1f1
+		timeout->tv_sec = NFSRPC_TIMEOUT_UDP;
605d1f1
+
605d1f1
+	ret = nfs_connect_nb(sock, sap, salen, timeout);
605d1f1
+	if (ret != 0) {
605d1f1
+		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
605d1f1
+		rpc_createerr.cf_error.re_errno = errno;
605d1f1
+		(void)close(sock);
605d1f1
+		return NULL;
605d1f1
+	}
605d1f1
+
605d1f1
+#ifdef HAVE_CLNT_DG_CREATE
605d1f1
+	memcpy(nbuf.buf, sap, (size_t)salen);
605d1f1
+	client = clnt_dg_create(sock, &nbuf, program, version, 0, 0);
605d1f1
+#else	/* HAVE_CLNT_DG_CREATE */
605d1f1
+	client = clntudp_create((struct sockaddr_in *)sap, program,
605d1f1
+					version, *timeout, &sock);
605d1f1
+#endif	/* HAVE_CLNT_DG_CREATE */
605d1f1
+	if (client != NULL) {
605d1f1
+		CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *)timeout);
605d1f1
+		CLNT_CONTROL(client, CLSET_FD_CLOSE, NULL);
605d1f1
+	} else
605d1f1
+		(void)close(sock);
605d1f1
+
605d1f1
+	return client;
605d1f1
+}
605d1f1
+
605d1f1
+/*
605d1f1
+ * Set up and connect an RPC client for communicating via a stream socket.
605d1f1
+ *
605d1f1
+ * @timeout is initialized upon return
605d1f1
+ *
605d1f1
+ * Returns a pointer to a prepared and connected RPC client if
605d1f1
+ * successful; caller must destroy a non-NULL returned RPC client.
605d1f1
+ * Otherwise NULL, and rpc_createerr.cf_stat is set to reflect the
605d1f1
+ * error.
605d1f1
+ */
605d1f1
+static CLIENT *nfs_get_tcpclient(const struct sockaddr *sap,
605d1f1
+				 const socklen_t salen,
605d1f1
+				 const rpcprog_t program,
605d1f1
+				 const rpcvers_t version,
605d1f1
+				 struct timeval *timeout)
605d1f1
+{
605d1f1
+#ifdef HAVE_CLNT_VC_CREATE
605d1f1
+	struct sockaddr_storage address;
605d1f1
+	const struct netbuf nbuf = {
605d1f1
+		.maxlen		= salen,
605d1f1
+		.len		= salen,
605d1f1
+		.buf		= &address,
605d1f1
+	};
605d1f1
+#endif	/* HAVE_CLNT_VC_CREATE */
605d1f1
+	CLIENT *client;
605d1f1
+	int ret, sock;
605d1f1
+
605d1f1
+#ifndef HAVE_CLNT_VC_CREATE
605d1f1
+	if (sap->sa_family != AF_INET) {
605d1f1
+		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
605d1f1
+		return NULL;
605d1f1
+	}
605d1f1
+#endif	/* !HAVE_CLNT_VC_CREATE */
605d1f1
+
605d1f1
+	sock = socket((int)sap->sa_family, SOCK_STREAM, IPPROTO_TCP);
605d1f1
+	if (sock == -1) {
605d1f1
+		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
605d1f1
+		rpc_createerr.cf_error.re_errno = errno;
605d1f1
+		return NULL;
605d1f1
+	}
605d1f1
+
605d1f1
+	ret = nfs_bind(sock, sap->sa_family);
605d1f1
+	if (ret < 0) {
605d1f1
+		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
605d1f1
+		rpc_createerr.cf_error.re_errno = errno;
605d1f1
+		(void)close(sock);
605d1f1
+		return NULL;
605d1f1
+	}
605d1f1
+
605d1f1
+	if (timeout->tv_sec == -1)
605d1f1
+		timeout->tv_sec = NFSRPC_TIMEOUT_TCP;
605d1f1
+
605d1f1
+	ret = nfs_connect_nb(sock, sap, salen, timeout);
605d1f1
+	if (ret != 0) {
605d1f1
+		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
605d1f1
+		rpc_createerr.cf_error.re_errno = errno;
605d1f1
+		(void)close(sock);
605d1f1
+		return NULL;
605d1f1
+	}
605d1f1
+
605d1f1
+#ifdef HAVE_CLNT_VC_CREATE
605d1f1
+	memcpy(nbuf.buf, sap, (size_t)salen);
605d1f1
+	client = clnt_vc_create(sock, &nbuf, program, version, 0, 0);
605d1f1
+#else	/* HAVE_CLNT_VC_CREATE */
605d1f1
+	client = clnttcp_create((struct sockaddr_in *)sap,
605d1f1
+					program, version, &sock, 0, 0);
605d1f1
+#endif	/* HAVE_CLNT_VC_CREATE */
605d1f1
+	if (client != NULL)
605d1f1
+		CLNT_CONTROL(client, CLSET_FD_CLOSE, NULL);
605d1f1
+	else
605d1f1
+		(void)close(sock);
605d1f1
+
605d1f1
+	return client;
605d1f1
+}
605d1f1
+
605d1f1
+/**
605d1f1
+ * nfs_get_rpcclient - acquire an RPC client
605d1f1
+ * @sap: pointer to socket address of RPC server
605d1f1
+ * @salen: length of socket address
605d1f1
+ * @transport: IPPROTO_ value of transport protocol to use
605d1f1
+ * @program: RPC program number
605d1f1
+ * @version: RPC version number
605d1f1
+ * @timeout: pointer to request timeout (must not be NULL)
605d1f1
+ *
605d1f1
+ * Set up an RPC client for communicating with an RPC program @program
605d1f1
+ * and @version on the server @sap over @transport.
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
+CLIENT *nfs_get_rpcclient(const struct sockaddr *sap,
605d1f1
+			  const socklen_t salen,
605d1f1
+			  const unsigned short transport,
605d1f1
+			  const rpcprog_t program,
605d1f1
+			  const rpcvers_t version,
605d1f1
+			  struct timeval *timeout)
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_LOCAL:
605d1f1
+		return nfs_get_localclient(sap, salen, program,
605d1f1
+						version, timeout);
605d1f1
+	case AF_INET:
605d1f1
+		if (sin->sin_port == 0) {
605d1f1
+			rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
605d1f1
+			return NULL;
605d1f1
+		}
605d1f1
+		break;
605d1f1
+	case AF_INET6:
605d1f1
+		if (sin6->sin6_port == 0) {
605d1f1
+			rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
605d1f1
+			return NULL;
605d1f1
+		}
605d1f1
+		break;
605d1f1
+	default:
605d1f1
+		rpc_createerr.cf_stat = RPC_UNKNOWNHOST;
605d1f1
+		return NULL;
605d1f1
+	}
605d1f1
+
605d1f1
+	switch (transport) {
605d1f1
+	case IPPROTO_TCP:
605d1f1
+		return nfs_get_tcpclient(sap, salen, program, version, timeout);
605d1f1
+	case 0:
605d1f1
+	case IPPROTO_UDP:
605d1f1
+		return nfs_get_udpclient(sap, salen, program, version, timeout);
605d1f1
+	}
605d1f1
+
605d1f1
+	rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
605d1f1
+	return NULL;
605d1f1
+}
605d1f1
+
605d1f1
+/**
605d1f1
+ * nfs_getrpcbyname - convert an RPC program name to a rpcprog_t
605d1f1
+ * @program: default program number to use if names not found in db
605d1f1
+ * @table: pointer to table of 'char *' names to try to find
605d1f1
+ *
605d1f1
+ * Returns program number of first name to be successfully looked
605d1f1
+ * up, or the default program number if all lookups fail.
605d1f1
+ */
605d1f1
+rpcprog_t nfs_getrpcbyname(const rpcprog_t program, const char *table[])
605d1f1
+{
605d1f1
+#ifdef HAVE_GETRPCBYNAME
605d1f1
+	struct rpcent *entry;
605d1f1
+	unsigned int i;
605d1f1
+
605d1f1
+	if (table != NULL)
605d1f1
+		for (i = 0; table[i] != NULL; i++) {
605d1f1
+			entry = getrpcbyname(table[i]);
605d1f1
+			if (entry)
605d1f1
+				return (rpcprog_t)entry->r_number;
605d1f1
+		}
605d1f1
+#endif	/* HAVE_GETRPCBYNAME */
605d1f1
+
605d1f1
+	return program;
605d1f1
+}
605d1f1
+
605d1f1
+static unsigned short nfs_tryportbyname(const char *name,
605d1f1
+					const char *protocol)
605d1f1
+{
605d1f1
+	struct servent *servp = NULL;
605d1f1
+
605d1f1
+	servp = getservbyname(name, protocol);
605d1f1
+	if (servp != NULL)
605d1f1
+		return (unsigned short)ntohl((uint32_t)servp->s_port);
605d1f1
+	return 0;
605d1f1
+}
605d1f1
+
605d1f1
+/**
605d1f1
+ * nfs_getportbynumber - convert an RPC program number to a port
605d1f1
+ * @program: RPC program number to look up
605d1f1
+ * @transport: IPPROTO_ value of transport protocol to use
605d1f1
+ *
605d1f1
+ * Returns a non-zero port number, in host byte order, on success;
605d1f1
+ * otherwise zero if some problem occurred.
605d1f1
+ */
605d1f1
+unsigned short nfs_getportbynumber(const rpcprog_t program,
605d1f1
+				   const unsigned short transport)
605d1f1
+{
605d1f1
+	char *protocol = (transport == IPPROTO_TCP) ? "tcp" : "udp";
605d1f1
+	struct rpcent *rpcp;
605d1f1
+	unsigned short port = 0;
605d1f1
+
605d1f1
+	rpcp = getrpcbynumber((int)program);
605d1f1
+	if (rpcp == NULL)
605d1f1
+		return port;
605d1f1
+
605d1f1
+	port = nfs_tryportbyname(rpcp->r_name, protocol);
605d1f1
+	if (port != 0)
605d1f1
+		return port;
605d1f1
+
605d1f1
+	if (rpcp->r_aliases) {
605d1f1
+		int i;
605d1f1
+		for (i = 0; rpcp->r_aliases[i] != NULL; i++) {
605d1f1
+			port = nfs_tryportbyname(rpcp->r_aliases[i], protocol);
605d1f1
+			if (port != 0)
605d1f1
+				break;
605d1f1
+		}
605d1f1
+	}
605d1f1
+
605d1f1
+	return port;
605d1f1
+}
605d1f1
--- /dev/null	2008-11-18 08:07:41.940431098 -0500
605d1f1
+++ nfs-utils-1.1.4/support/include/nfsrpc.h	2008-11-18 14:59:08.888662000 -0500
605d1f1
@@ -0,0 +1,70 @@
605d1f1
+/*
605d1f1
+ * nfsrpc.h -- RPC client APIs provided by support/nfs
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
+#ifndef __NFS_UTILS_NFSRPC_H
605d1f1
+#define __NFS_UTILS_NFSRPC_H
605d1f1
+
605d1f1
+#include <rpc/types.h>
605d1f1
+
605d1f1
+/*
605d1f1
+ * Conventional RPC program numbers
605d1f1
+ */
605d1f1
+#ifndef RPCBPROG
605d1f1
+#define RPCBPROG	((rpcprog_t)100000)
605d1f1
+#endif
605d1f1
+#ifndef PMAPPROG
605d1f1
+#define PMAPPROG	((rpcprog_t)100000)
605d1f1
+#endif
605d1f1
+
605d1f1
+#ifndef NFSPROG
605d1f1
+#define NFSPROG		((rpcprog_t)100003)
605d1f1
+#endif
605d1f1
+#ifndef MOUNTPROG
605d1f1
+#define MOUNTPROG	((rpcprog_t)100005)
605d1f1
+#endif
605d1f1
+#ifndef NLMPROG
605d1f1
+#define NLMPROG		((rpcprog_t)100021)
605d1f1
+#endif
605d1f1
+#ifndef NSMPROG
605d1f1
+#define NSMPROG		((rpcprog_t)100024)
605d1f1
+#endif
605d1f1
+
605d1f1
+/*
605d1f1
+ * Look up an RPC program name in /etc/rpc
605d1f1
+ */
605d1f1
+extern rpcprog_t	nfs_getrpcbyname(const rpcprog_t, const char *table[]);
605d1f1
+
605d1f1
+/*
605d1f1
+ * Look up a port number in /etc/services for an RPC program
605d1f1
+ */
605d1f1
+extern unsigned short	nfs_getportbynumber(const rpcprog_t program,
605d1f1
+				const unsigned short transport);
605d1f1
+
605d1f1
+/*
605d1f1
+ * Acquire an RPC CLIENT *
605d1f1
+ */
605d1f1
+extern CLIENT		*nfs_get_rpcclient(const struct sockaddr *,
605d1f1
+				const socklen_t, const unsigned short,
605d1f1
+				const rpcprog_t, const rpcvers_t,
605d1f1
+				struct timeval *);
605d1f1
+
605d1f1
+#endif	/* __NFS_UTILS_NFSRPC_H */
605d1f1
--- nfs-utils-1.1.4/configure.ac.orig	2008-10-17 10:20:09.000000000 -0400
605d1f1
+++ nfs-utils-1.1.4/configure.ac	2008-11-18 14:59:08.884659000 -0500
605d1f1
@@ -178,6 +178,12 @@ AC_CHECK_FUNC(connect, ,
605d1f1
 AC_CHECK_FUNC(getaddrinfo, , ,
605d1f1
                 AC_MSG_ERROR(Function 'getaddrinfo' not found.))
605d1f1
 
605d1f1
+AC_CHECK_FUNC(getrpcbynumber, , ,
605d1f1
+                AC_MSG_ERROR(Function 'getrpcbynumber' not found.))
605d1f1
+
605d1f1
+AC_CHECK_FUNC(getservbyname, , ,
605d1f1
+                AC_MSG_ERROR(Function 'getservbyname' not found.))
605d1f1
+
605d1f1
 AC_CHECK_LIB(crypt, crypt, [LIBCRYPT="-lcrypt"])
605d1f1
 if test "$enable_nfsv4" = yes; then
605d1f1
     AC_CHECK_LIB(event, event_dispatch, [libevent=1], AC_MSG_ERROR([libevent needed for nfsv4 support]))