X-Git-Url: https://git.decadent.org.uk/gitweb/?p=nfs-utils.git;a=blobdiff_plain;f=utils%2Fmount%2Fnetwork.c;h=afa47a4dd7e22cb4c1993cb5a0d1f12e01b94191;hp=0ba7bdb64acb940a4743ce7eebf652343d7e4fa4;hb=265f2708bdc6030250c13d46d70ed689c140c34e;hpb=ea0473feffd8071216c96217df3202a8deed2c65 diff --git a/utils/mount/network.c b/utils/mount/network.c index 0ba7bdb..afa47a4 100644 --- a/utils/mount/network.c +++ b/utils/mount/network.c @@ -50,24 +50,6 @@ #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) @@ -160,6 +142,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 @@ -449,113 +446,67 @@ 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, TRUE); - 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; } } @@ -592,56 +543,114 @@ 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); } -/** - * 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 +/* + * Probe a server's mountd service to determine which versions and + * transport protocols are supported. Invoked when the protocol + * version is already known for both the NFS and mountd service. * - * Returns 1 if successful, otherwise zero if some error occurred. - * Note that the arguments are both input and output arguments. + * Returns 1 and fills in both @pmap structs if the requested service + * ports are unambiguous and pingable. Otherwise zero is returned; + * rpccreateerr.cf_stat is set to reflect the nature of the error. + */ +static int nfs_probe_version_fixed(const struct sockaddr *mnt_saddr, + const socklen_t mnt_salen, + struct pmap *mnt_pmap, + const struct sockaddr *nfs_saddr, + const socklen_t nfs_salen, + struct pmap *nfs_pmap) +{ + if (!nfs_probe_nfsport(nfs_saddr, nfs_salen, nfs_pmap)) + return 0; + return nfs_probe_mntport(mnt_saddr, mnt_salen, mnt_pmap); +} + +/** + * nfs_probe_bothports - discover the RPC endpoints of mountd and NFS server + * @mnt_saddr: pointer to socket address of mountd server + * @mnt_salen: length of mountd server's address + * @mnt_pmap: IN: partially filled-in mountd RPC service tuple; + * OUT: fully filled-in mountd RPC service tuple + * @nfs_saddr: pointer to socket address of NFS server + * @nfs_salen: length of NFS server's address + * @nfs_pmap: IN: partially filled-in NFS RPC service tuple; + * OUT: fully filled-in NFS RPC service tuple * - * A side effect of calling this function is that rpccreateerr is set. + * Returns 1 and fills in both @pmap structs if the requested service + * ports are unambiguous and pingable. Otherwise zero is returned; + * rpccreateerr.cf_stat is set to reflect the nature of the error. */ -int probe_bothports(clnt_addr_t *mnt_server, clnt_addr_t *nfs_server) +int nfs_probe_bothports(const struct sockaddr *mnt_saddr, + const socklen_t mnt_salen, + struct pmap *mnt_pmap, + const struct sockaddr *nfs_saddr, + const socklen_t nfs_salen, + struct pmap *nfs_pmap) { - 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) nfs_pmap->pm_vers = mntvers_to_nfs(mnt_pmap->pm_vers); else if (nfs_pmap->pm_vers && !mnt_pmap->pm_vers) mnt_pmap->pm_vers = nfsvers_to_mnt(nfs_pmap->pm_vers); + if (nfs_pmap->pm_vers) - goto version_fixed; + return nfs_probe_version_fixed(mnt_saddr, mnt_salen, mnt_pmap, + nfs_saddr, nfs_salen, nfs_pmap); memcpy(&save_nfs, nfs_pmap, sizeof(save_nfs)); memcpy(&save_mnt, mnt_pmap, sizeof(save_mnt)); @@ -650,9 +659,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)); } @@ -661,18 +670,35 @@ int probe_bothports(clnt_addr_t *mnt_server, clnt_addr_t *nfs_server) case RPC_PROGNOTREGISTERED: break; default: - goto out_bad; + return 0; } memcpy(nfs_pmap, &save_nfs, sizeof(*nfs_pmap)); } -out_bad: return 0; +} -version_fixed: - if (!probe_nfsport(nfs_server)) - goto out_bad; - return probe_mntport(mnt_server); +/** + * 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 + * + * This is the legacy API that takes "clnt_addr_t" for both servers, + * but supports only AF_INET addresses. + * + * Returns 1 and fills in the pmap field in both clnt_addr_t structs + * if the requested service ports are unambiguous and pingable. + * Otherwise zero is returned; rpccreateerr.cf_stat is set to reflect + * the nature of the error. + */ +int probe_bothports(clnt_addr_t *mnt_server, clnt_addr_t *nfs_server) +{ + return nfs_probe_bothports((struct sockaddr *)&mnt_server->saddr, + sizeof(mnt_server->saddr), + &mnt_server->pmap, + (struct sockaddr *)&nfs_server->saddr, + sizeof(nfs_server->saddr), + &nfs_server->pmap); } static int nfs_probe_statd(void) @@ -710,7 +736,8 @@ int start_statd(void) execl(START_STATD, START_STATD, NULL); exit(1); case -1: /* error */ - perror("Fork failed"); + nfs_error(_("fork failed: %s"), + strerror(errno)); break; default: /* parent */ waitpid(pid, NULL,0); @@ -738,11 +765,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) @@ -827,9 +857,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.