]> git.decadent.org.uk Git - nfs-utils.git/blobdiff - support/nfs/getport.c
Imported Upstream version 1.2.3
[nfs-utils.git] / support / nfs / getport.c
index aa9c154386d4cc14c004616e9ce64e679684f561..d74400b0dcfe69eac02b8dbc2c531d01b16c9d79 100644 (file)
@@ -45,6 +45,7 @@
 #include <rpc/rpcb_prot.h>
 #endif
 
+#include "sockaddr.h"
 #include "nfsrpc.h"
 
 /*
@@ -65,6 +66,26 @@ static const rpcvers_t default_rpcb_version = RPCBVERS_4;
 static const rpcvers_t default_rpcb_version = PMAPVERS;
 #endif /* !HAVE_LIBTIRPC */
 
+/*
+ * Historical: Map TCP connect timeouts to timeout
+ * error code used by UDP.
+ */
+static void
+nfs_gp_map_tcp_errorcodes(const unsigned short protocol)
+{
+       if (protocol != IPPROTO_TCP)
+               return;
+
+       switch (rpc_createerr.cf_error.re_errno) {
+       case ETIMEDOUT:
+               rpc_createerr.cf_stat = RPC_TIMEDOUT;
+               break;
+       case ECONNREFUSED:
+               rpc_createerr.cf_stat = RPC_CANTRECV;
+               break;
+       }
+}
+
 /*
  * There's no easy way to tell how the local system's networking
  * and rpcbind is configured (ie. whether we want to use IPv6 or
@@ -92,24 +113,6 @@ static int nfs_gp_loopback_address(struct sockaddr *sap, socklen_t *salen)
        return ret;
 }
 
-/*
- * 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;
-       }
-}
-
 /*
  * Look up a network service in /etc/services and return the
  * network-order port number of that service.
@@ -188,13 +191,97 @@ static CLIENT *nfs_gp_get_rpcbclient(struct sockaddr *sap,
                NULL,
        };
        rpcprog_t rpcb_prog = nfs_getrpcbyname(RPCBPROG, rpcb_pgmtbl);
+       CLIENT *clnt;
 
-       nfs_gp_set_port(sap, nfs_gp_get_rpcb_port(transport));
-       return nfs_get_rpcclient(sap, salen, transport, rpcb_prog,
-                                       version, timeout);
+       nfs_set_port(sap, ntohs(nfs_gp_get_rpcb_port(transport)));
+       clnt = nfs_get_rpcclient(sap, salen, transport, rpcb_prog,
+                                                       version, timeout);
+       nfs_gp_map_tcp_errorcodes(transport);
+       return clnt;
 }
 
-/*
+/**
+ * nfs_get_proto - Convert a netid to an address family and protocol number
+ * @netid: C string containing a netid
+ * @family: OUT: address family
+ * @protocol: OUT: protocol number
+ *
+ * Returns 1 and fills in @protocol if the netid was recognized;
+ * otherwise zero is returned.
+ */
+#ifdef HAVE_LIBTIRPC
+int
+nfs_get_proto(const char *netid, sa_family_t *family, unsigned long *protocol)
+{
+       struct netconfig *nconf;
+       struct protoent *proto;
+
+       /*
+        * IANA does not define a protocol number for rdma netids,
+        * since "rdma" is not an IP protocol.
+        */
+       if (strcmp(netid, "rdma") == 0) {
+               *family = AF_INET;
+               *protocol = NFSPROTO_RDMA;
+               return 1;
+       }
+       if (strcmp(netid, "rdma6") == 0) {
+               *family = AF_INET6;
+               *protocol = NFSPROTO_RDMA;
+               return 1;
+       }
+
+       nconf = getnetconfigent(netid);
+       if (nconf == NULL)
+               return 0;
+
+       proto = getprotobyname(nconf->nc_proto);
+       if (proto == NULL) {
+               freenetconfigent(nconf);
+               return 0;
+       }
+
+       *family = AF_UNSPEC;
+       if (strcmp(nconf->nc_protofmly, NC_INET) == 0)
+               *family = AF_INET;
+       if (strcmp(nconf->nc_protofmly, NC_INET6) == 0)
+               *family = AF_INET6;
+       freenetconfigent(nconf);
+
+       *protocol = (unsigned long)proto->p_proto;
+       return 1;
+}
+#else  /* !HAVE_LIBTIRPC */
+int
+nfs_get_proto(const char *netid, sa_family_t *family, unsigned long *protocol)
+{
+       struct protoent *proto;
+
+       /*
+        * IANA does not define a protocol number for rdma netids,
+        * since "rdma" is not an IP protocol.
+        */
+       if (strcmp(netid, "rdma") == 0) {
+               *family = AF_INET;
+               *protocol = NFSPROTO_RDMA;
+               return 1;
+       }
+
+       proto = getprotobyname(netid);
+       if (proto == NULL)
+               return 0;
+
+       *family = AF_INET;
+       *protocol = (unsigned long)proto->p_proto;
+       return 1;
+}
+#endif /* !HAVE_LIBTIRPC */
+
+/**
+ * nfs_get_netid - Convert a protocol family and protocol name to a netid
+ * @family: protocol family
+ * @protocol: protocol number
+ *
  * 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.
@@ -208,13 +295,12 @@ static CLIENT *nfs_gp_get_rpcbclient(struct sockaddr *sap,
  * 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.
+ * 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_LIBTIRPC
-
-static char *nfs_gp_get_netid(const sa_family_t family,
-                             const unsigned short protocol)
+char *nfs_get_netid(const sa_family_t family, const unsigned long protocol)
 {
        char *nc_protofmly, *nc_proto, *nc_netid;
        struct netconfig *nconf;
@@ -250,6 +336,9 @@ static char *nfs_gp_get_netid(const sa_family_t family,
 
                nc_netid = strdup(nconf->nc_netid);
                endnetconfig(handle);
+
+               if (nc_netid == NULL)
+                       rpc_createerr.cf_stat = RPC_SYSTEMERROR;
                return nc_netid;
        }
        endnetconfig(handle);
@@ -258,8 +347,28 @@ out:
        rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
        return NULL;
 }
+#else  /* !HAVE_LIBTIRPC */
+char *nfs_get_netid(const sa_family_t family, const unsigned long protocol)
+{
+       struct protoent *proto;
+       char *netid;
 
-#endif /* HAVE_LIBTIRPC */
+       if (family != AF_INET)
+               goto out;
+       proto = getprotobynumber((int)protocol);
+       if (proto == NULL)
+               goto out;
+
+       netid = strdup(proto->p_name);
+       if (netid == NULL)
+               rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+       return netid;
+
+out:
+       rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
+       return NULL;
+}
+#endif /* !HAVE_LIBTIRPC */
 
 /*
  * Extract a port number from a universal address, and terminate the
@@ -393,6 +502,10 @@ static int nfs_gp_ping(CLIENT *client, struct timeval timeout)
                           (xdrproc_t)xdr_void, NULL,
                           timeout);
 
+       if (status != RPC_SUCCESS) {
+               rpc_createerr.cf_stat = status;
+               CLNT_GETERR(client, &rpc_createerr.cf_error);
+       }
        return (int)(status == RPC_SUCCESS);
 }
 
@@ -412,7 +525,7 @@ static int nfs_gp_init_rpcb_parms(const struct sockaddr *sap,
 {
        char *netid, *addr;
 
-       netid = nfs_gp_get_netid(sap->sa_family, protocol);
+       netid = nfs_get_netid(sap->sa_family, protocol);
        if (netid == NULL)
                return 0;
 
@@ -525,7 +638,7 @@ static unsigned long nfs_gp_pmap_getport(CLIENT *client,
 
        if (status != RPC_SUCCESS) {
                rpc_createerr.cf_stat = status;
-               clnt_geterr(client, &rpc_createerr.cf_error);
+               CLNT_GETERR(client, &rpc_createerr.cf_error);
                port = 0;
        } else if (port == 0)
                rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
@@ -618,8 +731,8 @@ 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)
 {
-       struct sockaddr_storage address;
-       struct sockaddr *saddr = (struct sockaddr *)&address;
+       union nfs_sockaddr address;
+       struct sockaddr *saddr = &address.sa;
        CLIENT *client;
        struct timeval tout = { -1, 0 };
        int result = 0;
@@ -634,6 +747,7 @@ int nfs_rpc_ping(const struct sockaddr *sap, const socklen_t salen,
                                                program, version, &tout);
        if (client != NULL) {
                result = nfs_gp_ping(client, tout);
+               nfs_gp_map_tcp_errorcodes(protocol);
                CLNT_DESTROY(client);
        }
 
@@ -686,8 +800,8 @@ unsigned short nfs_getport(const struct sockaddr *sap,
                           const rpcvers_t version,
                           const unsigned short protocol)
 {
-       struct sockaddr_storage address;
-       struct sockaddr *saddr = (struct sockaddr *)&address;
+       union nfs_sockaddr address;
+       struct sockaddr *saddr = &address.sa;
        struct timeval timeout = { -1, 0 };
        unsigned short port = 0;
        CLIENT *client;
@@ -745,11 +859,11 @@ int nfs_getport_ping(struct sockaddr *sap, const socklen_t salen,
        }
 
        if (port != 0) {
-               struct sockaddr_storage address;
-               struct sockaddr *saddr = (struct sockaddr *)&address;
+               union nfs_sockaddr address;
+               struct sockaddr *saddr = &address.sa;
 
                memcpy(saddr, sap, (size_t)salen);
-               nfs_gp_set_port(saddr, htons(port));
+               nfs_set_port(saddr, port);
 
                nfs_clear_rpc_createerr();
 
@@ -757,12 +871,13 @@ int nfs_getport_ping(struct sockaddr *sap, const socklen_t salen,
                                                program, version, &timeout);
                if (client != NULL) {
                        result = nfs_gp_ping(client, timeout);
+                       nfs_gp_map_tcp_errorcodes(protocol);
                        CLNT_DESTROY(client);
                }
        }
 
        if (result)
-               nfs_gp_set_port(sap, htons(port));
+               nfs_set_port(sap, port);
 
        return result;
 }
@@ -796,8 +911,8 @@ unsigned short nfs_getlocalport(const rpcprot_t program,
                                const rpcvers_t version,
                                const unsigned short protocol)
 {
-       struct sockaddr_storage address;
-       struct sockaddr *lb_addr = (struct sockaddr *)&address;
+       union nfs_sockaddr address;
+       struct sockaddr *lb_addr = &address.sa;
        socklen_t lb_len = sizeof(*lb_addr);
        unsigned short port = 0;
 
@@ -880,8 +995,8 @@ unsigned short nfs_rpcb_getaddr(const struct sockaddr *sap,
                                const unsigned short protocol,
                                const struct timeval *timeout)
 {
-       struct sockaddr_storage address;
-       struct sockaddr *saddr = (struct sockaddr *)&address;
+       union nfs_sockaddr address;
+       struct sockaddr *saddr = &address.sa;
        CLIENT *client;
        struct rpcb parms;
        struct timeval tout = { -1, 0 };