]> git.decadent.org.uk Git - nfs-utils.git/blobdiff - support/nfs/getport.c
nfs-utils: replace function-specific switches with HAVE_LIBTIRPC
[nfs-utils.git] / support / nfs / getport.c
index 25dca6c0a9da38372332df67a0c057d49f280f2e..2255b7d1bc86f344bd2f51ac175325bdad1468b6 100644 (file)
@@ -40,7 +40,7 @@
 #include <rpc/rpc.h>
 #include <rpc/pmap_prot.h>
 
-#ifdef HAVE_TIRPC_NETCONFIG_H
+#ifdef HAVE_LIBTIRPC
 #include <tirpc/netconfig.h>
 #include <tirpc/rpc/rpcb_prot.h>
 #endif
  * 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.
@@ -121,12 +134,64 @@ static void nfs_gp_set_port(struct sockaddr *sap, const in_port_t port)
        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.
@@ -142,9 +207,16 @@ static CLIENT *nfs_gp_get_rpcbclient(const struct sockaddr *sap,
                                     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));
@@ -170,7 +242,7 @@ static CLIENT *nfs_gp_get_rpcbclient(const struct sockaddr *sap,
  * 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)
@@ -218,7 +290,7 @@ out:
        return NULL;
 }
 
-#endif /* HAVE_XDR_RPCB */
+#endif /* HAVE_LIBTIRPC */
 
 /*
  * Extract a port number from a universal address, and terminate the
@@ -381,7 +453,7 @@ static int nfs_gp_ping(CLIENT *client, struct timeval timeout)
        return (int)(status == RPC_SUCCESS);
 }
 
-#ifdef HAVE_XDR_RPCB
+#ifdef HAVE_LIBTIRPC
 
 /*
  * Initialize the rpcb argument for a GETADDR request.
@@ -493,7 +565,7 @@ static unsigned short nfs_gp_rpcb_getaddr(CLIENT *client,
        return port;
 }
 
-#endif /* HAVE_XDR_RPCB */
+#endif /* HAVE_LIBTIRPC */
 
 /*
  * Try GETPORT request via rpcbind version 2.
@@ -523,7 +595,7 @@ static unsigned long nfs_gp_pmap_getport(CLIENT *client,
        return port;
 }
 
-#ifdef HAVE_XDR_RPCB
+#ifdef HAVE_LIBTIRPC
 
 static unsigned short nfs_gp_getport_rpcb(CLIENT *client,
                                          const struct sockaddr *sap,
@@ -545,7 +617,7 @@ static unsigned short nfs_gp_getport_rpcb(CLIENT *client,
        return port;
 }
 
-#endif /* HAVE_XDR_RPCB */
+#endif /* HAVE_LIBTIRPC */
 
 static unsigned long nfs_gp_getport_pmap(CLIENT *client,
                                         const rpcprog_t program,
@@ -580,11 +652,11 @@ static unsigned short nfs_gp_getport(CLIENT *client,
                                     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);
@@ -780,12 +852,10 @@ 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,
-       };
+       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 = {
@@ -811,12 +881,9 @@ unsigned short nfs_getlocalport(const rpcprot_t program,
 #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;
        }
@@ -855,7 +922,7 @@ unsigned short nfs_getlocalport(const rpcprot_t program,
  * 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,
@@ -888,7 +955,7 @@ unsigned short nfs_rpcb_getaddr(const struct sockaddr *sap,
        return port;
 }
 
-#else  /* HAVE_XDR_RPCB */
+#else  /* !HAVE_LIBTIRPC */
 
 unsigned short nfs_rpcb_getaddr(const struct sockaddr *sap,
                                const socklen_t salen,
@@ -904,7 +971,7 @@ unsigned short nfs_rpcb_getaddr(const struct sockaddr *sap,
        return 0;
 }
 
-#endif /* HAVE_XDR_RPCB */
+#endif /* !HAVE_LIBTIRPC */
 
 /**
  * nfs_pmap_getport - query rpcbind via the portmap protocol (rpcbindv2)