2008-02-18 18:36:34

by Chuck Lever III

[permalink] [raw]
Subject: [PATCH 14/17] mount: Introduce IPv6-aware DNS resolver API function

To support mounting IPv6 servers, introduce a function in network.c that
can resolve hostnames to either IPv4 or IPv6 addresses.

Signed-off-by: Chuck Lever <[email protected]>
---

configure.ac | 2 +
utils/mount/network.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++++
utils/mount/network.h | 1 +
3 files changed, 73 insertions(+), 0 deletions(-)

diff --git a/configure.ac b/configure.ac
index 3e600b4..4372fbe 100644
--- a/configure.ac
+++ b/configure.ac
@@ -298,6 +298,8 @@ have_bindresvport6=none
if test "$enable_ipv6" = yes; then
AC_CHECK_FUNC(bindresvport6, [have_bindresvport6=lib])
AC_CHECK_FUNC(inet_ntop, , , AC_MSG_ERROR(Function 'inet_ntop' not found.))
+ AC_CHECK_FUNC(getaddrinfo, , ,
+ AC_MSG_ERROR(Function 'getaddrinfo' not found.))
fi
AM_CONDITIONAL(CONFIG_BRP6, [test "$have_bindresvport6" = "none"])

diff --git a/utils/mount/network.c b/utils/mount/network.c
index 6c29e7c..2ffaf49 100644
--- a/utils/mount/network.c
+++ b/utils/mount/network.c
@@ -175,6 +175,76 @@ int nfs_gethostbyname(const char *hostname, struct sockaddr_in *saddr)
return 1;
}

+/**
+ * nfs_name_to_address - convert hostname to an IPv4 or IPv6 socket address
+ * @hostname: pointer to C string containing DNS hostname to resolve
+ * @hint: which address family to prioritize in the results
+ * @sap: pointer to buffer to fill with result
+ *
+ * Returns zero if successful, or -1 if an error occurred.
+ *
+ * Assumes the sap argument points to a large enough buffer.
+ */
+#ifdef IPV6_SUPPORTED
+int nfs_name_to_address(const char *hostname, const sa_family_t hint,
+ struct sockaddr *sap)
+{
+ struct addrinfo *result, *results;
+ int error;
+
+ error = getaddrinfo(hostname, NULL, NULL, &results);
+ if (error) {
+ gai_strerror(error);
+ goto out_err;
+ }
+ if (!results) {
+ nfs_error(_("%s: no results from getaddrinfo\n"), progname);
+ goto out_err;
+ }
+
+ /*
+ * Try to find a result whose family matches "hint". If there
+ * isn't one, just return the address in the first item. If
+ * the caller sets "hint" to AF_UNSPEC, it will never match any
+ * of the results, and the first one will be returned by default.
+ */
+ for (result = results; result; result = result->ai_next)
+ if (result->ai_family == hint)
+ break;
+ if (!result)
+ result = results;
+
+ switch (result->ai_family) {
+ case AF_INET:
+ memcpy(sap, result->ai_addr, sizeof(struct sockaddr_in));
+ break;
+ case AF_INET6:
+ memcpy(sap, result->ai_addr, sizeof(struct sockaddr_in6));
+ break;
+ default:
+ nfs_error(_("%s: unrecognized address family: %u\n"),
+ ((struct sockaddr *)result->ai_addr)->sa_family);
+ error = EAFNOSUPPORT;
+ }
+
+ freeaddrinfo(results);
+
+ if (!error)
+ return 0;
+
+out_err:
+ nfs_error(_("%s: hostname resolution failed for %s\n"),
+ progname, hostname);
+ return -1;
+}
+#else
+int nfs_name_to_address(const char *hostname, const sa_family_t hint,
+ struct sockaddr *sap)
+{
+ return nfs_gethostbyname(hostname, (struct sockaddr_in *)sap);
+}
+#endif
+
static int __nfs_gs_err_done(const int error)
{
rpc_createerr.cf_stat = RPC_SYSTEMERROR;
diff --git a/utils/mount/network.h b/utils/mount/network.h
index 7cabb16..97538b6 100644
--- a/utils/mount/network.h
+++ b/utils/mount/network.h
@@ -48,6 +48,7 @@ static const struct timeval RETRY_TIMEOUT = { 3, 0 };

int probe_bothports(clnt_addr_t *, clnt_addr_t *);
int nfs_gethostbyname(const char *, struct sockaddr_in *);
+int nfs_name_to_address(const char *, const sa_family_t, struct sockaddr *);
int nfs_client_address(const struct sockaddr *, struct sockaddr *, socklen_t *);
int nfs_call_umount(clnt_addr_t *, dirpath *);
int clnt_ping(struct sockaddr_in *, const unsigned long,