X-Git-Url: https://git.decadent.org.uk/gitweb/?p=nfs-utils.git;a=blobdiff_plain;f=utils%2Fmount%2Fnetwork.c;h=3f2721b802450134acd165b673f059615cc6f35f;hp=2b09457524e9fa0754c3e93115456c6b5ca82407;hb=ffb42f63d41542bd2dab4570248d18642e39ed48;hpb=d4b159620b26dce8f83d41358d618ccc82fa82d4 diff --git a/utils/mount/network.c b/utils/mount/network.c index 2b09457..3f2721b 100644 --- a/utils/mount/network.c +++ b/utils/mount/network.c @@ -21,6 +21,10 @@ * */ +#ifdef HAVE_CONFIG_H +#include +#endif + #include #include #include @@ -29,10 +33,13 @@ #include #include #include + +#include +#include +#include #include #include #include -#include #include "xcommon.h" #include "mount.h" @@ -139,6 +146,60 @@ static const unsigned long probe_mnt3_first[] = { 0, }; +/** + * 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 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; + } + + freeaddrinfo(gai_results); + return ret; +} + /** * nfs_gethostbyname - resolve a hostname to an IPv4 address * @hostname: pointer to a C string containing a DNS hostname @@ -146,26 +207,104 @@ static const unsigned long probe_mnt3_first[] = { * * Returns 1 if successful, otherwise zero. */ -int nfs_gethostbyname(const char *hostname, struct sockaddr_in *saddr) +int nfs_gethostbyname(const char *hostname, struct sockaddr_in *sin) { - 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); + socklen_t len = sizeof(*sin); + + return nfs_name_to_address(hostname, AF_INET, + (struct sockaddr *)sin, &len); +} + +/** + * nfs_string_to_sockaddr - convert string address to sockaddr + * @address: pointer to presentation format address to convert + * @addrlen: length of presentation address + * @sap: pointer to socket address buffer to fill in + * @salen: IN: length of address buffer + * OUT: length of converted socket address + * + * Convert a presentation format address string to a socket address. + * Similar to nfs_name_to_address(), but the DNS query is squelched, + * and won't make any noise if the getaddrinfo() call fails. + * + * Returns 1 and fills in @sap and @salen if successful; otherwise zero. + * + * See RFC 4038 section 5.1 or RFC 3513 section 2.2 for more details + * on presenting IPv6 addresses as text strings. + */ +int nfs_string_to_sockaddr(const char *address, const size_t addrlen, + struct sockaddr *sap, socklen_t *salen) +{ + struct addrinfo *gai_results; + struct addrinfo gai_hint = { + .ai_flags = AI_NUMERICHOST, + }; + socklen_t len = *salen; + int ret = 0; + + *salen = 0; + + if (getaddrinfo(address, NULL, &gai_hint, &gai_results) == 0) { + 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; } - memcpy(&saddr->sin_addr, hp->h_addr, hp->h_length); + break; } + freeaddrinfo(gai_results); } - return 1; + + return ret; +} + +/** + * nfs_present_sockaddr - convert sockaddr to string + * @sap: pointer to socket address to convert + * @salen: length of socket address + * @buf: pointer to buffer to fill in + * @buflen: length of buffer + * + * Convert the passed-in sockaddr-style address to presentation format. + * The presentation format address is placed in @buf and is + * '\0'-terminated. + * + * Returns 1 if successful; otherwise zero. + * + * See RFC 4038 section 5.1 or RFC 3513 section 2.2 for more details + * on presenting IPv6 addresses as text strings. + */ +int nfs_present_sockaddr(const struct sockaddr *sap, const socklen_t salen, + char *buf, const size_t buflen) +{ +#ifdef HAVE_GETNAMEINFO + int result; + + result = getnameinfo(sap, salen, buf, buflen, + NULL, 0, NI_NUMERICHOST); + if (!result) + return 1; + + nfs_error(_("%s: invalid server address: %s"), progname, + gai_strerror(result)); + return 0; +#else /* HAVE_GETNAMEINFO */ + char *addr; + + if (sap->sa_family == AF_INET) { + addr = inet_ntoa(((struct sockaddr_in *)sap)->sin_addr); + if (addr && strlen(addr) < buflen) { + strcpy(buf, addr); + return 1; + } + } + + nfs_error(_("%s: invalid server address"), progname); + return 0; +#endif /* HAVE_GETNAMEINFO */ } /* @@ -494,7 +633,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));