]> git.decadent.org.uk Git - nfs-utils.git/blobdiff - utils/mount/network.c
mount.nfs: If port= specifies an unregistered port, retry, then fail
[nfs-utils.git] / utils / mount / network.c
index 91a005cc9fc5cac052a5876d79dc8d041048ecf5..73d64a2b8782221b601f132cdb18aac450efc4f0 100644 (file)
@@ -185,39 +185,32 @@ static void nfs_set_port(struct sockaddr *sap, const unsigned short port)
        }
 }
 
-#ifdef HAVE_DECL_AI_ADDRCONFIG
-/**
- * nfs_name_to_address - resolve hostname to an IPv4 or IPv6 socket address
- * @hostname: pointer to C string containing DNS hostname to resolve
- * @af_hint: hint to restrict resolution to one address family
- * @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)
+static int nfs_lookup(const char *hostname, const sa_family_t family,
+                     struct sockaddr *sap, socklen_t *salen)
 {
        struct addrinfo *gai_results;
        struct addrinfo gai_hint = {
-               .ai_family      = af_hint,
+#ifdef HAVE_DECL_AI_ADDRCONFIG
                .ai_flags       = AI_ADDRCONFIG,
+#endif /* HAVE_DECL_AI_ADDRCONFIG */
+               .ai_family      = family,
        };
        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) {
+       switch (error) {
+       case 0:
+               break;
+       case EAI_SYSTEM:
                nfs_error(_("%s: DNS resolution failed for %s: %s"),
-                       progname, hostname, (error == EAI_SYSTEM ?
-                               strerror(errno) : gai_strerror(error)));
+                       progname, hostname, strerror(errno));
+               return ret;
+       default:
+               nfs_error(_("%s: DNS resolution failed for %s: %s"),
+                       progname, hostname, gai_strerror(error));
                return ret;
        }
 
@@ -240,61 +233,25 @@ int nfs_name_to_address(const char *hostname,
        freeaddrinfo(gai_results);
        return ret;
 }
-#else  /* HAVE_DECL_AI_ADDRCONFIG */
+
 /**
- * nfs_name_to_address - resolve hostname to an IPv4 socket address
+ * nfs_name_to_address - resolve hostname to an IPv4 or IPv6 socket address
  * @hostname: pointer to C string containing DNS hostname to resolve
- * @af_hint: hint to restrict resolution to one address family
  * @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.
- *
- * Some older getaddrinfo(3) implementations don't support
- * AI_ADDRCONFIG or AI_V4MAPPED properly.  For those cases, a DNS
- * resolver based on the traditional gethostbyname(3) is provided.
  */
 int nfs_name_to_address(const char *hostname,
-                       const sa_family_t af_hint,
                        struct sockaddr *sap, socklen_t *salen)
 {
-       struct sockaddr_in *sin = (struct sockaddr_in *)sap;
-       socklen_t len = *salen;
-       struct hostent *hp;
-
-       *salen = 0;
-
-       if (af_hint != AF_INET) {
-               nfs_error(_("%s: address family not supported by DNS resolver\n"),
-                               progname, hostname);
-               return 0;
-       }
-
-       sin->sin_family = AF_INET;
-       if (inet_aton(hostname, &sin->sin_addr)) {
-               *salen = sizeof(*sin);
-               return 1;
-       }
-
-       hp = gethostbyname(hostname);
-       if (hp == NULL) {
-               nfs_error(_("%s: DNS resolution failed for %s: %s"),
-                               progname, hostname, hstrerror(h_errno));
-               return 0;
-       }
-
-       if (hp->h_length > len) {
-               nfs_error(_("%s: DNS resolution results too long for buffer\n"),
-                               progname);
-               return 0;
-       }
-
-       memcpy(&sin->sin_addr, hp->h_addr, hp->h_length);
-       *salen = hp->h_length;
-       return 1;
+#ifdef IPV6_SUPPORTED
+       return nfs_lookup(hostname, AF_UNSPEC, sap, salen);
+#else  /* !IPV6_SUPPORTED */
+       return nfs_lookup(hostname, AF_INET, sap, salen);
+#endif /* !IPV6_SUPPORTED */
 }
-#endif /* HAVE_DECL_AI_ADDRCONFIG */
 
 /**
  * nfs_gethostbyname - resolve a hostname to an IPv4 address
@@ -307,8 +264,7 @@ 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);
+       return nfs_lookup(hostname, AF_INET, (struct sockaddr *)sin, &len);
 }
 
 /**
@@ -546,12 +502,28 @@ static void nfs_pp_debug(const struct sockaddr *sap, const socklen_t salen,
                strcat(buf, "unknown host");
        }
 
-       fprintf(stderr, _("%s: trying %s prog %ld vers %ld prot %s port %d\n"),
-                       progname, buf, program, version,
+       fprintf(stderr, _("%s: trying %s prog %lu vers %lu prot %s port %d\n"),
+                       progname, buf, (unsigned long)program,
+                       (unsigned long)version,
                        (protocol == IPPROTO_UDP ? _("UDP") : _("TCP")),
                        port);
 }
 
+static void nfs_pp_debug2(const char *str)
+{
+       if (!verbose)
+               return;
+
+       if (rpc_createerr.cf_error.re_status == RPC_CANTRECV ||
+           rpc_createerr.cf_error.re_status == RPC_CANTSEND)
+               nfs_error(_("%s: portmap query %s%s - %s"),
+                               progname, str, clnt_spcreateerror(""),
+                               strerror(rpc_createerr.cf_error.re_errno));
+       else
+               nfs_error(_("%s: portmap query %s%s"),
+                               progname, str, clnt_spcreateerror(""));
+}
+
 /*
  * Use the portmapper to discover whether or not the service we want is
  * available. The lists 'versions' and 'protos' define ordered sequences
@@ -581,9 +553,11 @@ static int nfs_probe_port(const struct sockaddr *sap, const socklen_t salen,
        memcpy(saddr, sap, salen);
        p_prot = prot ? &prot : protos;
        p_vers = vers ? &vers : versions;
-       rpc_createerr.cf_stat = 0;
 
        for (;;) {
+               if (verbose)
+                       printf(_("%s: prog %lu, trying vers=%lu, prot=%u\n"),
+                               progname, prog, *p_vers, *p_prot);
                p_port = nfs_getport(saddr, salen, prog, *p_vers, *p_prot);
                if (p_port) {
                        if (!port || port == p_port) {
@@ -593,28 +567,31 @@ static int nfs_probe_port(const struct sockaddr *sap, const socklen_t salen,
                                if (nfs_rpc_ping(saddr, salen, prog,
                                                        *p_vers, *p_prot, NULL))
                                        goto out_ok;
-                       }
+                       } else
+                               rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
                }
                if (rpc_createerr.cf_stat != RPC_PROGNOTREGISTERED &&
                    rpc_createerr.cf_stat != RPC_TIMEDOUT &&
                    rpc_createerr.cf_stat != RPC_CANTRECV &&
                    rpc_createerr.cf_stat != RPC_PROGVERSMISMATCH)
-                       goto out_bad;
+                       break;
 
                if (!prot) {
-                       if (*++p_prot)
+                       if (*++p_prot) {
+                               nfs_pp_debug2("retrying");
                                continue;
+                       }
                        p_prot = protos;
                }
                if (rpc_createerr.cf_stat == RPC_TIMEDOUT ||
                    rpc_createerr.cf_stat == RPC_CANTRECV)
-                       goto out_bad;
+                       break;
 
                if (vers || !*++p_vers)
                        break;
        }
 
-out_bad:
+       nfs_pp_debug2("failed");
        return 0;
 
 out_ok:
@@ -624,7 +601,7 @@ out_ok:
                pmap->pm_prot = *p_prot;
        if (!port)
                pmap->pm_port = p_port;
-       rpc_createerr.cf_stat = 0;
+       nfs_clear_rpc_createerr();
        return 1;
 }
 
@@ -837,6 +814,68 @@ int start_statd(void)
        return 0;
 }
 
+/**
+ * nfs_advise_umount - ask the server to remove a share from it's rmtab
+ * @sap: pointer to IP address of server to call
+ * @salen: length of server address
+ * @pmap: partially filled-in mountd RPC service tuple
+ * @argp: directory path of share to "unmount"
+ *
+ * Returns one if the unmount call succeeded; zero if the unmount
+ * failed for any reason;  rpccreateerr.cf_stat is set to reflect
+ * the nature of the error.
+ *
+ * We use a fast timeout since this call is advisory only.
+ */
+int nfs_advise_umount(const struct sockaddr *sap, const socklen_t salen,
+                     const struct pmap *pmap, const dirpath *argp)
+{
+       struct sockaddr_storage address;
+       struct sockaddr *saddr = (struct sockaddr *)&address;
+       struct pmap mnt_pmap = *pmap;
+       struct timeval timeout = {
+               .tv_sec         = MOUNT_TIMEOUT >> 3,
+       };
+       CLIENT *client;
+       enum clnt_stat res = 0;
+
+       memcpy(saddr, sap, salen);
+       if (nfs_probe_mntport(saddr, salen, &mnt_pmap) == 0) {
+               if (verbose)
+                       nfs_error(_("%s: Failed to discover mountd port%s"),
+                               progname, clnt_spcreateerror(""));
+               return 0;
+       }
+       nfs_set_port(saddr, mnt_pmap.pm_port);
+
+       client = nfs_get_priv_rpcclient(saddr, salen, mnt_pmap.pm_prot,
+                                       mnt_pmap.pm_prog, mnt_pmap.pm_vers,
+                                       &timeout);
+       if (client == NULL) {
+               if (verbose)
+                       nfs_error(_("%s: Failed to create RPC client%s"),
+                               progname, clnt_spcreateerror(""));
+               return 0;
+       }
+
+       client->cl_auth = authunix_create_default();
+
+       res = CLNT_CALL(client, MOUNTPROC_UMNT,
+                       (xdrproc_t)xdr_dirpath, (caddr_t)argp,
+                       (xdrproc_t)xdr_void, NULL,
+                       timeout);
+       if (verbose && res != RPC_SUCCESS)
+               nfs_error(_("%s: UMNT call failed: %s"),
+                       progname, clnt_sperrno(res));
+
+       auth_destroy(client->cl_auth);
+       CLNT_DESTROY(client);
+
+       if (res != RPC_SUCCESS)
+               return 0;
+       return 1;
+}
+
 /**
  * nfs_call_umount - ask the server to remove a share from it's rmtab
  * @mnt_server: address of RPC MNT program server
@@ -1168,16 +1207,16 @@ static rpcvers_t nfs_nfs_version(struct mount_options *options)
        long tmp;
 
        switch (po_rightmost(options, nfs_version_opttbl)) {
-       case 1: /* v2 */
+       case 0: /* v2 */
                return 2;
-       case 2: /* v3 */
+       case 1: /* v3 */
                return 3;
-       case 3: /* vers */
+       case 2: /* vers */
                if (po_get_numeric(options, "vers", &tmp) == PO_FOUND)
                        if (tmp >= 2 && tmp <= 3)
                                return tmp;
                break;
-       case 4: /* nfsvers */
+       case 3: /* nfsvers */
                if (po_get_numeric(options, "nfsvers", &tmp) == PO_FOUND)
                        if (tmp >= 2 && tmp <= 3)
                                return tmp;
@@ -1198,11 +1237,9 @@ static unsigned short nfs_nfs_protocol(struct mount_options *options)
        char *option;
 
        switch (po_rightmost(options, nfs_transport_opttbl)) {
-       case 1: /* udp */
-               return IPPROTO_UDP;
-       case 2: /* tcp */
+       case 1: /* tcp */
                return IPPROTO_TCP;
-       case 3: /* proto */
+       case 2: /* proto */
                option = po_get(options, "proto");
                if (option) {
                        if (strcmp(option, "tcp") == 0)
@@ -1211,6 +1248,7 @@ static unsigned short nfs_nfs_protocol(struct mount_options *options)
                                return IPPROTO_UDP;
                }
        }
+
        return IPPROTO_UDP;
 }
 
@@ -1284,7 +1322,7 @@ static unsigned short nfs_mount_protocol(struct mount_options *options)
                        return IPPROTO_UDP;
        }
 
-       return nfs_nfs_version(options);
+       return nfs_nfs_protocol(options);
 }
 
 /*