X-Git-Url: https://git.decadent.org.uk/gitweb/?p=nfs-utils.git;a=blobdiff_plain;f=utils%2Fmount%2Fnetwork.c;h=55b2cabd4f7cea8ae2d21b5200257fafa00ee3ce;hp=75354a7a62f044b2fec594beeb9a0c57ccddc1ec;hb=299a990de1b16ff769201fa0ed38249853254497;hpb=8508942e7244017325690e2d0c17429fa0cb9873 diff --git a/utils/mount/network.c b/utils/mount/network.c index 75354a7..55b2cab 100644 --- a/utils/mount/network.c +++ b/utils/mount/network.c @@ -36,6 +36,7 @@ #include #include +#include #include #include #include @@ -46,8 +47,27 @@ #include "nls.h" #include "nfs_mount.h" #include "mount_constants.h" +#include "nfsrpc.h" #include "network.h" +/* + * Earlier versions of glibc's /usr/include/netdb.h exclude these + * definitions because it was thought they were not part of a stable + * POSIX standard. However, they are defined by RFC 2553 and 3493 + * and in POSIX 1003.1-2001, so these definitions were added in later + * versions of netdb.h. + */ +#ifndef AI_V4MAPPED +#define AI_V4MAPPED 0x0008 /* IPv4-mapped addresses are acceptable. */ +#endif /* AI_V4MAPPED */ +#ifndef AI_ALL +#define AI_ALL 0x0010 /* Return both IPv4 and IPv6 addresses. */ +#endif /* AI_ALL */ +#ifndef AI_ADDRCONFIG +#define AI_ADDRCONFIG 0x0020 /* Use configuration of this host to choose \ + returned address type. */ +#endif /* AI_ADDRCONFIG */ + #define PMAP_TIMEOUT (10) #define CONNECT_TIMEOUT (20) #define MOUNT_TIMEOUT (30) @@ -60,6 +80,11 @@ extern int nfs_mount_data_version; extern char *progname; extern int verbose; +static const char *nfs_ns_pgmtbl[] = { + "status", + NULL, +}; + static const unsigned long nfs_to_mnt[] = { 0, 0, @@ -135,6 +160,21 @@ static const unsigned long probe_mnt3_first[] = { 0, }; +static void nfs_set_port(struct sockaddr *sap, const unsigned short port) +{ + switch (sap->sa_family) { + case AF_INET: + ((struct sockaddr_in *)sap)->sin_port = htons(port); + break; + case AF_INET6: + ((struct sockaddr_in6 *)sap)->sin6_port = htons(port); + break; + default: + nfs_error(_("%s: unrecognized address family in %s"), + progname, __func__); + } +} + /** * nfs_name_to_address - resolve hostname to an IPv4 or IPv6 socket address * @hostname: pointer to C string containing DNS hostname to resolve @@ -424,118 +464,73 @@ err_connect: return RPC_ANYSOCK; } -/* - * getport() is very similar to pmap_getport() with the exception that - * this version tries to use an ephemeral port, since reserved ports are - * not needed for GETPORT queries. This conserves the very limited - * reserved port space, which helps reduce failed socket binds - * during mount storms. - * - * A side effect of calling this function is that rpccreateerr is set. - */ -static unsigned short getport(struct sockaddr_in *saddr, - unsigned long program, - unsigned long version, - unsigned int proto) +static void nfs_pp_debug(const struct sockaddr *sap, const socklen_t salen, + const rpcprog_t program, const rpcvers_t version, + const unsigned short protocol, + const unsigned short port) { - 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); - - 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) - rpc_createerr.cf_stat = RPC_TIMEDOUT; - return 0; - } + char buf[NI_MAXHOST]; - switch (proto) { - case IPPROTO_UDP: - clnt = clntudp_bufcreate(&bind_saddr, - PMAPPROG, PMAPVERS, - RETRY_TIMEOUT, &socket, - RPCSMALLMSGSIZE, - RPCSMALLMSGSIZE); - break; - case IPPROTO_TCP: - clnt = clnttcp_create(&bind_saddr, - PMAPPROG, PMAPVERS, - &socket, - RPCSMALLMSGSIZE, RPCSMALLMSGSIZE); - break; - } - if (clnt != NULL) { - struct pmap parms = { - .pm_prog = program, - .pm_vers = version, - .pm_prot = proto, - }; - - stat = clnt_call(clnt, PMAPPROC_GETPORT, - (xdrproc_t)xdr_pmap, (caddr_t)&parms, - (xdrproc_t)xdr_u_short, (caddr_t)&port, - TIMEOUT); - if (stat) { - clnt_geterr(clnt, &rpc_createerr.cf_error); - rpc_createerr.cf_stat = stat; - } - clnt_destroy(clnt); - if (stat != RPC_SUCCESS) - port = 0; - else if (port == 0) - rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED; + if (!verbose) + return; + + if (nfs_present_sockaddr(sap, salen, buf, sizeof(buf)) == 0) { + buf[0] = '\0'; + strcat(buf, "unknown host"); } - close(socket); - return port; + fprintf(stderr, _("%s: trying %s prog %ld vers %ld prot %s port %d\n"), + progname, buf, program, version, + (protocol == IPPROTO_UDP ? _("UDP") : _("TCP")), + port); } /* * Use the portmapper to discover whether or not the service we want is * available. The lists 'versions' and 'protos' define ordered sequences * of service versions and udp/tcp protocols to probe for. + * + * Returns 1 if the requested service port is unambiguous and pingable; + * @pmap is filled in with the version, port, and transport protocol used + * during the successful ping. Note that if a port is already specified + * in @pmap and it matches the rpcbind query result, nfs_probe_port() does + * not perform an RPC ping. + * + * If an error occurs or the requested service isn't available, zero is + * returned; rpccreateerr.cf_stat is set to reflect the nature of the error. */ -static int probe_port(clnt_addr_t *server, const unsigned long *versions, - const unsigned int *protos) +static int nfs_probe_port(const struct sockaddr *sap, const socklen_t salen, + struct pmap *pmap, const unsigned long *versions, + const unsigned int *protos) { - struct sockaddr_in *saddr = &server->saddr; - struct pmap *pmap = &server->pmap; + struct sockaddr_storage address; + struct sockaddr *saddr = (struct sockaddr *)&address; const unsigned long prog = pmap->pm_prog, *p_vers; const unsigned int prot = (u_int)pmap->pm_prot, *p_prot; const u_short port = (u_short) pmap->pm_port; unsigned long vers = pmap->pm_vers; unsigned short p_port; + memcpy(saddr, sap, salen); p_prot = prot ? &prot : protos; p_vers = vers ? &vers : versions; rpc_createerr.cf_stat = 0; + for (;;) { - p_port = getport(saddr, prog, *p_vers, *p_prot); + p_port = nfs_getport(saddr, salen, prog, *p_vers, *p_prot); if (p_port) { if (!port || port == p_port) { - saddr->sin_port = htons(p_port); - if (verbose) { - printf(_("%s: trying %s prog %ld vers " - "%ld prot %s port %d\n"), - progname, - inet_ntoa(saddr->sin_addr), - prog, *p_vers, - *p_prot == IPPROTO_UDP ? - _("UDP") : _("TCP"), - p_port); - } - if (clnt_ping(saddr, prog, *p_vers, *p_prot, NULL)) + nfs_set_port(saddr, p_port); + nfs_pp_debug(saddr, salen, prog, *p_vers, + *p_prot, p_port); + if (nfs_rpc_ping(saddr, salen, prog, + *p_vers, *p_prot, NULL)) goto out_ok; } } 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; @@ -544,7 +539,8 @@ static int probe_port(clnt_addr_t *server, const unsigned long *versions, continue; p_prot = protos; } - if (rpc_createerr.cf_stat == RPC_TIMEDOUT) + if (rpc_createerr.cf_stat == RPC_TIMEDOUT || + rpc_createerr.cf_stat == RPC_CANTRECV) goto out_bad; if (vers || !*++p_vers) @@ -565,30 +561,58 @@ out_ok: return 1; } -static int probe_nfsport(clnt_addr_t *nfs_server) +/* + * Probe a server's NFS service to determine which versions and + * transport protocols are supported. + * + * Returns 1 if the requested service port is unambiguous and pingable; + * @pmap is filled in with the version, port, and transport protocol used + * during the successful ping. If all three are already specified, simply + * return success without an rpcbind query or RPC ping (we may be trying + * to mount an NFS service that is not advertised via rpcbind). + * + * If an error occurs or the requested service isn't available, zero is + * returned; rpccreateerr.cf_stat is set to reflect the nature of the error. + */ +static int nfs_probe_nfsport(const struct sockaddr *sap, const socklen_t salen, + struct pmap *pmap) { - struct pmap *pmap = &nfs_server->pmap; - if (pmap->pm_vers && pmap->pm_prot && pmap->pm_port) return 1; if (nfs_mount_data_version >= 4) - return probe_port(nfs_server, probe_nfs3_first, probe_tcp_first); + return nfs_probe_port(sap, salen, pmap, + probe_nfs3_first, probe_tcp_first); else - return probe_port(nfs_server, probe_nfs2_only, probe_udp_only); + return nfs_probe_port(sap, salen, pmap, + probe_nfs2_only, probe_udp_only); } -static int probe_mntport(clnt_addr_t *mnt_server) +/* + * Probe a server's mountd service to determine which versions and + * transport protocols are supported. + * + * Returns 1 if the requested service port is unambiguous and pingable; + * @pmap is filled in with the version, port, and transport protocol used + * during the successful ping. If all three are already specified, simply + * return success without an rpcbind query or RPC ping (we may be trying + * to mount an NFS service that is not advertised via rpcbind). + * + * If an error occurs or the requested service isn't available, zero is + * returned; rpccreateerr.cf_stat is set to reflect the nature of the error. + */ +static int nfs_probe_mntport(const struct sockaddr *sap, const socklen_t salen, + struct pmap *pmap) { - struct pmap *pmap = &mnt_server->pmap; - if (pmap->pm_vers && pmap->pm_prot && pmap->pm_port) return 1; if (nfs_mount_data_version >= 4) - return probe_port(mnt_server, probe_mnt3_first, probe_udp_first); + return nfs_probe_port(sap, salen, pmap, + probe_mnt3_first, probe_udp_first); else - return probe_port(mnt_server, probe_mnt1_first, probe_udp_only); + return nfs_probe_port(sap, salen, pmap, + probe_mnt1_first, probe_udp_only); } /** @@ -603,10 +627,13 @@ static int probe_mntport(clnt_addr_t *mnt_server) */ int probe_bothports(clnt_addr_t *mnt_server, clnt_addr_t *nfs_server) { + struct sockaddr *nfs_saddr = (struct sockaddr *)&nfs_server->saddr; + socklen_t nfs_salen = sizeof(nfs_server->saddr); + struct sockaddr *mnt_saddr = (struct sockaddr *)&mnt_server->saddr; + socklen_t mnt_salen = sizeof(mnt_server->saddr); struct pmap *nfs_pmap = &nfs_server->pmap; struct pmap *mnt_pmap = &mnt_server->pmap; struct pmap save_nfs, save_mnt; - int res; const unsigned long *probe_vers; if (mnt_pmap->pm_vers && !nfs_pmap->pm_vers) @@ -623,9 +650,9 @@ 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)) { + if (nfs_probe_nfsport(nfs_saddr, nfs_salen, nfs_pmap) != 0) { mnt_pmap->pm_vers = *probe_vers; - if ((res = probe_mntport(mnt_server)) != 0) + if (nfs_probe_mntport(mnt_saddr, mnt_salen, mnt_pmap) != 0) return 1; memcpy(mnt_pmap, &save_mnt, sizeof(*mnt_pmap)); } @@ -643,29 +670,21 @@ out_bad: return 0; version_fixed: - if (!probe_nfsport(nfs_server)) + if (!nfs_probe_nfsport(nfs_saddr, nfs_salen, nfs_pmap)) goto out_bad; - return probe_mntport(mnt_server); + return nfs_probe_mntport(mnt_saddr, mnt_salen, mnt_pmap); } -static int probe_statd(void) +static int nfs_probe_statd(void) { - struct sockaddr_in addr; - unsigned short port; - - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - port = getport(&addr, 100024, 1, IPPROTO_UDP); - - if (port == 0) - return 0; - addr.sin_port = htons(port); - - if (clnt_ping(&addr, 100024, 1, IPPROTO_UDP, NULL) <= 0) - return 0; + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = htonl(INADDR_LOOPBACK), + }; + rpcprog_t program = nfs_getrpcbyname(NSMPROG, nfs_ns_pgmtbl); - return 1; + return nfs_getport_ping((struct sockaddr *)&addr, sizeof(addr), + program, (rpcvers_t)1, IPPROTO_UDP); } /** @@ -679,14 +698,26 @@ int start_statd(void) struct stat stb; #endif - if (probe_statd()) + if (nfs_probe_statd()) return 1; #ifdef START_STATD if (stat(START_STATD, &stb) == 0) { if (S_ISREG(stb.st_mode) && (stb.st_mode & S_IXUSR)) { - system(START_STATD); - if (probe_statd()) + pid_t pid = fork(); + switch (pid) { + case 0: /* child */ + execl(START_STATD, START_STATD, NULL); + exit(1); + case -1: /* error */ + nfs_error(_("fork failed: %s"), + strerror(errno)); + break; + default: /* parent */ + waitpid(pid, NULL,0); + break; + } + if (nfs_probe_statd()) return 1; } } @@ -708,11 +739,14 @@ int start_statd(void) */ int nfs_call_umount(clnt_addr_t *mnt_server, dirpath *argp) { + struct sockaddr *sap = (struct sockaddr *)&mnt_server->saddr; + socklen_t salen = sizeof(mnt_server->saddr); + struct pmap *pmap = &mnt_server->pmap; CLIENT *clnt; enum clnt_stat res = 0; int msock; - if (!probe_mntport(mnt_server)) + if (!nfs_probe_mntport(sap, salen, pmap)) return 0; clnt = mnt_openclnt(mnt_server, &msock); if (!clnt) @@ -797,9 +831,9 @@ void mnt_closeclnt(CLIENT *clnt, int msock) * @prot: target RPC protocol * @caddr: filled in with our network address * - * Sigh... getport() doesn't actually check the version number. + * Sigh... GETPORT queries don'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 + * we're requesting, we open an RPC client, and fire off a NULL * RPC call. * * caddr is the network address that the server will use to call us back.