]> git.decadent.org.uk Git - nfs-utils.git/blobdiff - utils/mount/network.c
Introduce a new DNS resolver function in utils/mount/network.c that uses
[nfs-utils.git] / utils / mount / network.c
index 5a56c23ce20f9b9ca6333d9f1e08f837a8d18925..30a4d40ef78c7216e149e6cd58a0dc7685d05bfb 100644 (file)
  *
  */
 
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
 #include <ctype.h>
 #include <unistd.h>
 #include <stdio.h>
@@ -139,26 +143,73 @@ static const unsigned long probe_mnt3_first[] = {
        0,
 };
 
-int nfs_gethostbyname(const char *hostname, struct sockaddr_in *saddr)
+/**
+ * nfs_name_to_address - resolve hostname to an IPv4 or IPv6 socket address
+ * @hostname: pointer to C string containing DNS hostname to resolve
+ * @sap: pointer to buffer to fill with socket address
+ * @len: IN: size of buffer to fill; OUT: size of socket address
+ *
+ * Returns 1 and places a socket address at @sap if successful;
+ * otherwise zero.
+ */
+int nfs_name_to_address(const char *hostname,
+                       const sa_family_t af_hint,
+                       struct sockaddr *sap, socklen_t *salen)
 {
-       struct hostent *hp;
-
-       saddr->sin_family = AF_INET;
-       if (!inet_aton(hostname, &saddr->sin_addr)) {
-               if ((hp = gethostbyname(hostname)) == NULL) {
-                       nfs_error(_("%s: can't get address for %s\n"),
-                                       progname, hostname);
-                       return 0;
-               } else {
-                       if (hp->h_length > sizeof(*saddr)) {
-                               nfs_error(_("%s: got bad hp->h_length\n"),
-                                               progname);
-                               hp->h_length = sizeof(*saddr);
-                       }
-                       memcpy(&saddr->sin_addr, hp->h_addr, hp->h_length);
+       struct addrinfo *gai_results;
+       struct addrinfo gai_hint = {
+               .ai_family      = af_hint,
+               .ai_flags       = AI_ADDRCONFIG,
+       };
+       socklen_t len = *salen;
+       int error, ret = 0;
+
+       if (af_hint == AF_INET6)
+               gai_hint.ai_flags |= AI_V4MAPPED|AI_ALL;
+
+       *salen = 0;
+
+       error = getaddrinfo(hostname, NULL, &gai_hint, &gai_results);
+       if (error) {
+               nfs_error(_("%s: DNS resolution failed for %s: %s"),
+                       progname, hostname, (error == EAI_SYSTEM ?
+                               strerror(errno) : gai_strerror(error)));
+               return ret;
+       }
+
+       switch (gai_results->ai_addr->sa_family) {
+       case AF_INET:
+       case AF_INET6:
+               if (len >= gai_results->ai_addrlen) {
+                       *salen = gai_results->ai_addrlen;
+                       memcpy(sap, gai_results->ai_addr, *salen);
+                       ret = 1;
                }
+               break;
+       default:
+               /* things are really broken if we get here, so warn */
+               nfs_error(_("%s: unrecognized DNS resolution results for %s"),
+                               progname, hostname);
+               break;
        }
-       return 1;
+
+       freeaddrinfo(gai_results);
+       return ret;
+}
+
+/**
+ * nfs_gethostbyname - resolve a hostname to an IPv4 address
+ * @hostname: pointer to a C string containing a DNS hostname
+ * @saddr: returns an IPv4 address 
+ *
+ * Returns 1 if successful, otherwise zero.
+ */
+int nfs_gethostbyname(const char *hostname, struct sockaddr_in *sin)
+{
+       socklen_t len = sizeof(*sin);
+
+       return nfs_name_to_address(hostname, AF_INET,
+                                       (struct sockaddr *)sin, &len);
 }
 
 /*
@@ -303,14 +354,16 @@ static unsigned short getport(struct sockaddr_in *saddr,
                                unsigned long version,
                                unsigned int proto)
 {
+       struct sockaddr_in bind_saddr;
        unsigned short port = 0;
        int socket;
        CLIENT *clnt = NULL;
        enum clnt_stat stat;
+       bind_saddr = *saddr;
+       bind_saddr.sin_port = htons(PMAPPORT);
 
-       saddr->sin_port = htons(PMAPPORT);
-
-       socket = get_socket(saddr, proto, PMAP_TIMEOUT, FALSE, FALSE);
+       socket = get_socket(&bind_saddr, proto, PMAP_TIMEOUT, FALSE, FALSE);
        if (socket == RPC_ANYSOCK) {
                if (proto == IPPROTO_TCP &&
                    rpc_createerr.cf_error.re_errno == ETIMEDOUT)
@@ -320,14 +373,16 @@ static unsigned short getport(struct sockaddr_in *saddr,
 
        switch (proto) {
        case IPPROTO_UDP:
-               clnt = clntudp_bufcreate(saddr,
+               clnt = clntudp_bufcreate(&bind_saddr,
                                         PMAPPROG, PMAPVERS,
                                         RETRY_TIMEOUT, &socket,
                                         RPCSMALLMSGSIZE,
                                         RPCSMALLMSGSIZE);
                break;
        case IPPROTO_TCP:
-               clnt = clnttcp_create(saddr, PMAPPROG, PMAPVERS, &socket,
+               clnt = clnttcp_create(&bind_saddr,
+                                     PMAPPROG, PMAPVERS,
+                                     &socket,
                                      RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
                break;
        }
@@ -388,7 +443,7 @@ static int probe_port(clnt_addr_t *server, const unsigned long *versions,
                                                inet_ntoa(saddr->sin_addr),
                                                prog, *p_vers,
                                                *p_prot == IPPROTO_UDP ?
-                                                       "udp" : "tcp",
+                                                       _("UDP") : _("TCP"),
                                                p_port);
                                 }
                                if (clnt_ping(saddr, prog, *p_vers, *p_prot, NULL))
@@ -397,7 +452,8 @@ static int probe_port(clnt_addr_t *server, const unsigned long *versions,
                                        goto out_bad;
                        }
                }
-               if (rpc_createerr.cf_stat != RPC_PROGNOTREGISTERED)
+               if (rpc_createerr.cf_stat != RPC_PROGNOTREGISTERED &&
+                   rpc_createerr.cf_stat != RPC_PROGVERSMISMATCH)
                        goto out_bad;
 
                if (!prot) {
@@ -405,10 +461,6 @@ static int probe_port(clnt_addr_t *server, const unsigned long *versions,
                                continue;
                        p_prot = protos;
                }
-               if (vers == pmap->pm_vers) {
-                       p_vers = versions;
-                       vers = 0;
-               }
                if (vers || !*++p_vers)
                        break;
        }
@@ -453,6 +505,16 @@ static int probe_mntport(clnt_addr_t *mnt_server)
                return probe_port(mnt_server, probe_mnt1_first, probe_udp_only);
 }
 
+/**
+ * probe_bothports - discover the RPC endpoints of mountd and NFS server
+ * @mnt_server: pointer to address and pmap argument for mountd results
+ * @nfs_server: pointer to address and pmap argument for NFS server
+ *
+ * Returns 1 if successful, otherwise zero if some error occurred.
+ * Note that the arguments are both input and output arguments.
+ *
+ * A side effect of calling this function is that rpccreateerr is set.
+ */
 int probe_bothports(clnt_addr_t *mnt_server, clnt_addr_t *nfs_server)
 {
        struct pmap *nfs_pmap = &nfs_server->pmap;
@@ -476,7 +538,7 @@ int probe_bothports(clnt_addr_t *mnt_server, clnt_addr_t *nfs_server)
        for (; *probe_vers; probe_vers++) {
                nfs_pmap->pm_vers = mntvers_to_nfs(*probe_vers);
                if ((res = probe_nfsport(nfs_server) != 0)) {
-                       mnt_pmap->pm_vers = nfsvers_to_mnt(nfs_pmap->pm_vers);
+                       mnt_pmap->pm_vers = *probe_vers;
                        if ((res = probe_mntport(mnt_server)) != 0)
                                return 1;
                        memcpy(mnt_pmap, &save_mnt, sizeof(*mnt_pmap));
@@ -520,8 +582,10 @@ static int probe_statd(void)
        return 1;
 }
 
-/*
- * Attempt to start rpc.statd
+/**
+ * start_statd - attempt to start rpc.statd
+ *
+ * Returns 1 if statd is running; otherwise zero.
  */
 int start_statd(void)
 {
@@ -545,7 +609,7 @@ int start_statd(void)
        return 0;
 }
 
-/*
+/**
  * nfs_call_umount - ask the server to remove a share from it's rmtab
  * @mnt_server: address of RPC MNT program server
  * @argp: directory path of share to "unmount"
@@ -562,33 +626,29 @@ int nfs_call_umount(clnt_addr_t *mnt_server, dirpath *argp)
        enum clnt_stat res = 0;
        int msock;
 
-       switch (mnt_server->pmap.pm_vers) {
-       case 3:
-       case 2:
-       case 1:
-               if (!probe_mntport(mnt_server))
-                       return 0;
-               clnt = mnt_openclnt(mnt_server, &msock);
-               if (!clnt)
-                       return 0;
-               res = clnt_call(clnt, MOUNTPROC_UMNT,
-                               (xdrproc_t)xdr_dirpath, (caddr_t)argp,
-                               (xdrproc_t)xdr_void, NULL,
-                               TIMEOUT);
-               mnt_closeclnt(clnt, msock);
-               if (res == RPC_SUCCESS)
-                       return 1;
-               break;
-       default:
-               res = RPC_SUCCESS;
-               break;
-       }
+       if (!probe_mntport(mnt_server))
+               return 0;
+       clnt = mnt_openclnt(mnt_server, &msock);
+       if (!clnt)
+               return 0;
+       res = clnt_call(clnt, MOUNTPROC_UMNT,
+                       (xdrproc_t)xdr_dirpath, (caddr_t)argp,
+                       (xdrproc_t)xdr_void, NULL,
+                       TIMEOUT);
+       mnt_closeclnt(clnt, msock);
 
        if (res == RPC_SUCCESS)
                return 1;
        return 0;
 }
 
+/**
+ * mnt_openclnt - get a handle for a remote mountd service
+ * @mnt_server: address and pmap arguments of mountd service
+ * @msock: returns a file descriptor of the underlying transport socket
+ *
+ * Returns an active handle for the remote's mountd service
+ */
 CLIENT *mnt_openclnt(clnt_addr_t *mnt_server, int *msock)
 {
        struct sockaddr_in *mnt_saddr = &mnt_server->saddr;
@@ -630,6 +690,12 @@ CLIENT *mnt_openclnt(clnt_addr_t *mnt_server, int *msock)
        return NULL;
 }
 
+/**
+ * mnt_closeclnt - terminate a handle for a remote mountd service
+ * @clnt: pointer to an active handle for a remote mountd service
+ * @msock: file descriptor of the underlying transport socket
+ *
+ */
 void mnt_closeclnt(CLIENT *clnt, int msock)
 {
        auth_destroy(clnt->cl_auth);
@@ -637,11 +703,24 @@ void mnt_closeclnt(CLIENT *clnt, int msock)
        close(msock);
 }
 
-/*
+/**
+ * clnt_ping - send an RPC ping to the remote RPC service endpoint
+ * @saddr: server's address
+ * @prog: target RPC program number
+ * @vers: target RPC version number
+ * @prot: target RPC protocol
+ * @caddr: filled in with our network address
+ *
  * Sigh... getport() doesn't actually check the version number.
  * In order to make sure that the server actually supports the service
  * we're requesting, we open and RPC client, and fire off a NULL
  * RPC call.
+ *
+ * caddr is the network address that the server will use to call us back.
+ * On multi-homed clients, this address depends on which NIC we use to
+ * route requests to the server.
+ *
+ * Returns one if successful, otherwise zero.
  */
 int clnt_ping(struct sockaddr_in *saddr, const unsigned long prog,
                const unsigned long vers, const unsigned int prot,
@@ -711,3 +790,36 @@ int clnt_ping(struct sockaddr_in *saddr, const unsigned long prog,
        else
                return 0;
 }
+
+/**
+ * get_client_address - acquire our local network address
+ * @saddr: server's address
+ * @caddr: filled in with our network address
+ *
+ * Discover a network address that the server will use to call us back.
+ * On multi-homed clients, this address depends on which NIC we use to
+ * route requests to the server.
+ *
+ * Use a connected datagram socket so as not to leave a socket in TIME_WAIT.
+ *
+ * Returns one if successful, otherwise zero.
+ */
+int get_client_address(struct sockaddr_in *saddr, struct sockaddr_in *caddr)
+{
+       socklen_t len = sizeof(*caddr);
+       int socket, err;
+
+       socket = get_socket(saddr, IPPROTO_UDP, CONNECT_TIMEOUT, FALSE, TRUE);
+       if (socket == RPC_ANYSOCK)
+               return 0;
+
+       err = getsockname(socket, caddr, &len);
+       close(socket);
+
+       if (err && verbose) {
+               nfs_error(_("%s: getsockname failed: %s"),
+                               progname, strerror(errno));
+               return 0;
+       }
+       return 1;
+}