#include <rpc/rpc.h>
#include <rpc/pmap_prot.h>
-#ifdef HAVE_TIRPC_NETCONFIG_H
-#include <tirpc/netconfig.h>
-#include <tirpc/rpc/rpcb_prot.h>
+#ifdef HAVE_LIBTIRPC
+#include <netconfig.h>
+#include <rpc/rpcb_prot.h>
#endif
#include "nfsrpc.h"
* Rpcbind's local socket service does not seem to be working.
* Disable this logic for now.
*/
-#ifdef HAVE_XDR_RPCB
+#ifdef HAVE_LIBTIRPC
#undef NFS_GP_LOCAL
-#else /* HAVE_XDR_RPCB */
+#else /* !HAVE_LIBTIRPC */
#undef NFS_GP_LOCAL
-#endif /* HAVE_XDR_RPCB */
+#endif /* !HAVE_LIBTIRPC */
-#ifdef HAVE_XDR_RPCB
+#ifdef HAVE_LIBTIRPC
const static rpcvers_t default_rpcb_version = RPCBVERS_4;
-#else
+#else /* !HAVE_LIBTIRPC */
const static rpcvers_t default_rpcb_version = PMAPVERS;
-#endif
-
-static const char *nfs_gp_rpcb_pgmtbl[] = {
- "rpcbind",
- "portmap",
- "portmapper",
- "sunrpc",
- NULL,
-};
+#endif /* !HAVE_LIBTIRPC */
+#ifdef HAVE_DECL_AI_ADDRCONFIG
/*
- * 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.
+ * getaddrinfo(3) generates a usable loopback address based on how the
+ * local network interfaces are configured. 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.
*
- * Returns network byte-order port number of rpcbind service
- * on this system.
+ * Returns TRUE on success.
*/
-static in_port_t nfs_gp_get_rpcb_port(const unsigned short protocol)
+static int nfs_gp_loopback_address(struct sockaddr *sap, socklen_t *salen)
{
- struct protoent *proto;
+ struct addrinfo *gai_results;
+ struct addrinfo gai_hint = {
+ .ai_flags = AI_ADDRCONFIG,
+ };
+ socklen_t len = *salen;
+ int ret = 0;
+
+ if (getaddrinfo(NULL, "sunrpc", &gai_hint, &gai_results))
+ return 0;
- proto = getprotobynumber((int)protocol);
- if (proto != NULL) {
- struct servent *entry;
+ switch (gai_results->ai_addr->sa_family) {
+ case AF_INET:
+ case AF_INET6:
+ if (len >= gai_results->ai_addrlen) {
+ memcpy(sap, gai_results->ai_addr,
+ gai_results->ai_addrlen);
+ *salen = gai_results->ai_addrlen;
+ ret = 1;
+ }
+ }
- entry = getservbyname("rpcbind", proto->p_name);
- if (entry != NULL)
- return (in_port_t)entry->s_port;
+ freeaddrinfo(gai_results);
+ return ret;
+}
+#else
+/*
+ * Old versions of getaddrinfo(3) don't support AI_ADDRCONFIG, so we
+ * have a fallback for building on legacy systems.
+ */
+static int nfs_gp_loopback_address(struct sockaddr *sap, socklen_t *salen)
+{
+ struct sockaddr_in *sin = (struct sockaddr_in *)sap;
- entry = getservbyname("portmapper", proto->p_name);
- if (entry != NULL)
- return (in_port_t)entry->s_port;
+ memset(sin, 0, sizeof(*sin));
- entry = getservbyname("sunrpc", proto->p_name);
- if (entry != NULL)
- return (in_port_t)entry->s_port;
- }
- return htons((uint16_t)PMAPPORT);
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ *salen = sizeof(*sin);
+
+ return 1;
}
+#endif
/*
* Plant port number in @sap. @port is already in network byte order.
case AF_INET6:
sin6->sin6_port = port;
break;
- default:
- fprintf(stderr, "%s: unrecognized address family\n",
- __func__);
}
}
+/*
+ * Look up a network service in /etc/services and return the
+ * network-order port number of that service.
+ */
+static in_port_t nfs_gp_getservbyname(const char *service,
+ const unsigned short protocol)
+{
+ const struct addrinfo gai_hint = {
+ .ai_family = AF_INET,
+ .ai_protocol = protocol,
+ .ai_flags = AI_PASSIVE,
+ };
+ struct addrinfo *gai_results;
+ const struct sockaddr_in *sin;
+ in_port_t port;
+
+ if (getaddrinfo(NULL, service, &gai_hint, &gai_results) != 0)
+ return 0;
+
+ sin = (const struct sockaddr_in *)gai_results->ai_addr;
+ port = sin->sin_port;
+
+ freeaddrinfo(gai_results);
+ return port;
+}
+
+/*
+ * 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.
+ *
+ * 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)
+{
+ static const char *rpcb_netnametbl[] = {
+ "rpcbind",
+ "portmapper",
+ "sunrpc",
+ NULL,
+ };
+ unsigned int i;
+
+ for (i = 0; rpcb_netnametbl[i] != NULL; i++) {
+ in_port_t port;
+
+ port = nfs_gp_getservbyname(rpcb_netnametbl[i], protocol);
+ if (port != 0)
+ return port;
+ }
+
+ return (in_port_t)htons((uint16_t)PMAPPORT);
+}
+
/*
* Set up an RPC client for communicating with an rpcbind daemon at
* @sap over @transport with protocol version @version.
const rpcvers_t version,
struct timeval *timeout)
{
+ static const char *rpcb_pgmtbl[] = {
+ "rpcbind",
+ "portmap",
+ "portmapper",
+ "sunrpc",
+ NULL,
+ };
struct sockaddr_storage address;
struct sockaddr *saddr = (struct sockaddr *)&address;
- rpcprog_t rpcb_prog = nfs_getrpcbyname(RPCBPROG, nfs_gp_rpcb_pgmtbl);
+ rpcprog_t rpcb_prog = nfs_getrpcbyname(RPCBPROG, rpcb_pgmtbl);
memcpy(saddr, sap, (size_t)salen);
nfs_gp_set_port(saddr, nfs_gp_get_rpcb_port(transport));
* Returns a '\0'-terminated string if successful; otherwise NULL.
* rpc_createerr.cf_stat is set to reflect the error.
*/
-#ifdef HAVE_XDR_RPCB
+#ifdef HAVE_LIBTIRPC
static char *nfs_gp_get_netid(const sa_family_t family,
const unsigned short protocol)
return NULL;
}
-#endif /* HAVE_XDR_RPCB */
+#endif /* HAVE_LIBTIRPC */
/*
* Extract a port number from a universal address, and terminate the
return (int)(status == RPC_SUCCESS);
}
-#ifdef HAVE_XDR_RPCB
+#ifdef HAVE_LIBTIRPC
/*
* Initialize the rpcb argument for a GETADDR request.
return port;
}
-#endif /* HAVE_XDR_RPCB */
+#endif /* HAVE_LIBTIRPC */
/*
* Try GETPORT request via rpcbind version 2.
return port;
}
-#ifdef HAVE_XDR_RPCB
+#ifdef HAVE_LIBTIRPC
static unsigned short nfs_gp_getport_rpcb(CLIENT *client,
const struct sockaddr *sap,
return port;
}
-#endif /* HAVE_XDR_RPCB */
+#endif /* HAVE_LIBTIRPC */
static unsigned long nfs_gp_getport_pmap(CLIENT *client,
const rpcprog_t program,
struct timeval timeout)
{
switch (sap->sa_family) {
-#ifdef HAVE_XDR_RPCB
+#ifdef HAVE_LIBTIRPC
case AF_INET6:
return nfs_gp_getport_rpcb(client, sap, salen, program,
version, protocol, timeout);
-#endif /* HAVE_XDR_RPCB */
+#endif /* HAVE_LIBTIRPC */
case AF_INET:
return nfs_gp_getport_pmap(client, program, version,
protocol, timeout);
const rpcvers_t version,
const unsigned short protocol)
{
- struct addrinfo *gai_results;
- struct addrinfo gai_hint = {
- .ai_flags = AI_ADDRCONFIG,
- };
+ struct sockaddr_storage address;
+ struct sockaddr *lb_addr = (struct sockaddr *)&address;
+ socklen_t lb_len = sizeof(*lb_addr);
unsigned short port = 0;
- int error;
#ifdef NFS_GP_LOCAL
const struct sockaddr_un sun = {
#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,
+ if (nfs_gp_loopback_address(lb_addr, &lb_len)) {
+ port = nfs_getport(lb_addr, lb_len,
program, version, protocol);
- freeaddrinfo(gai_results);
} else
rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
}
* 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
+#ifdef HAVE_LIBTIRPC
unsigned short nfs_rpcb_getaddr(const struct sockaddr *sap,
const socklen_t salen,
return port;
}
-#else /* HAVE_XDR_RPCB */
+#else /* !HAVE_LIBTIRPC */
unsigned short nfs_rpcb_getaddr(const struct sockaddr *sap,
const socklen_t salen,
return 0;
}
-#endif /* HAVE_XDR_RPCB */
+#endif /* !HAVE_LIBTIRPC */
/**
* nfs_pmap_getport - query rpcbind via the portmap protocol (rpcbindv2)