X-Git-Url: https://git.decadent.org.uk/gitweb/?p=nfs-utils.git;a=blobdiff_plain;f=utils%2Fmount%2Fnetwork.c;h=a82c338a2cb06eb801e35110b211c02b184fef7b;hp=0c68993c0e06a7e287483e6abcc53ee65b0b2ea9;hb=632650fa1a0b358f9d8d617cfd115a334c4b9b66;hpb=b8711a0665b9ecff9d59ee36d756f50823242f64 diff --git a/utils/mount/network.c b/utils/mount/network.c index 0c68993..a82c338 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,22 @@ 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__); + } +} + +#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 @@ -213,6 +211,61 @@ 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 + * @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; +} +#endif /* HAVE_DECL_AI_ADDRCONFIG */ /** * nfs_gethostbyname - resolve a hostname to an IPv4 address @@ -474,27 +527,38 @@ static void nfs_pp_debug(const struct sockaddr *sap, const socklen_t salen, * 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) { - const struct sockaddr *saddr = (struct sockaddr *)&server->saddr; - const socklen_t salen = sizeof(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 = nfs_getport(saddr, salen, prog, *p_vers, *p_prot); if (p_port) { if (!port || port == p_port) { - server->saddr.sin_port = htons(p_port); + 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, @@ -535,56 +599,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)); @@ -593,9 +715,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)); } @@ -604,18 +726,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) @@ -682,11 +821,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)