summaryrefslogtreecommitdiffstats
path: root/support/export/hostname.c
diff options
context:
space:
mode:
Diffstat (limited to 'support/export/hostname.c')
-rw-r--r--support/export/hostname.c351
1 files changed, 351 insertions, 0 deletions
diff --git a/support/export/hostname.c b/support/export/hostname.c
index 8a23a89..79b9a8c 100644
--- a/support/export/hostname.c
+++ b/support/export/hostname.c
@@ -18,6 +18,8 @@
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
+#include <errno.h>
+
#include <xlog.h>
#ifdef TEST
#define xmalloc malloc
@@ -26,6 +28,13 @@
#include "misc.h"
#endif
+#include "sockaddr.h"
+#include "exportfs.h"
+
+#ifndef HAVE_DECL_AI_ADDRCONFIG
+#define AI_ADDRCONFIG 0
+#endif
+
#define ALIGNMENT sizeof (char *)
static int
@@ -158,6 +167,348 @@ hostent_dup (struct hostent *hp)
return cp;
}
+#ifdef HAVE_GETNAMEINFO
+static socklen_t
+sockaddr_size(const struct sockaddr *sap)
+{
+ if (sap->sa_family != AF_INET)
+ return 0;
+ return (socklen_t)sizeof(struct sockaddr_in);
+}
+#endif /* HAVE_GETNAMEINFO */
+
+/**
+ * host_ntop - generate presentation address given a sockaddr
+ * @sap: pointer to socket address
+ * @buf: working storage
+ * @buflen: size of @buf in bytes
+ *
+ * Returns a pointer to a @buf.
+ */
+#ifdef HAVE_GETNAMEINFO
+char *
+host_ntop(const struct sockaddr *sap, char *buf, const size_t buflen)
+{
+ socklen_t salen = sockaddr_size(sap);
+ int error;
+
+ memset(buf, 0, buflen);
+
+ if (salen == 0) {
+ (void)strncpy(buf, "bad family", buflen - 1);
+ return buf;
+ }
+
+ error = getnameinfo(sap, salen, buf, (socklen_t)buflen,
+ NULL, 0, NI_NUMERICHOST);
+ if (error != 0) {
+ buf[0] = '\0';
+ (void)strncpy(buf, "bad address", buflen - 1);
+ }
+
+ return buf;
+}
+#else /* !HAVE_GETNAMEINFO */
+char *
+host_ntop(const struct sockaddr *sap, char *buf, const size_t buflen)
+{
+ const struct sockaddr_in *sin = (const struct sockaddr_in *)(char *)sap;
+
+ memset(buf, 0, buflen);
+
+ if (sin->sin_family != AF_INET)
+ (void)strncpy(buf, "bad family", buflen - 1);
+ return buf;
+ }
+
+ if (inet_ntop(AF_INET, &sin->sin_addr.s_addr, buf, buflen) != NULL)
+ return buf;
+
+ buf[0] = '\0';
+ (void)strncpy(buf, "bad address", buflen - 1);
+ return buf;
+}
+#endif /* !HAVE_GETNAMEINFO */
+
+/**
+ * host_pton - return addrinfo for a given presentation address
+ * @paddr: pointer to a '\0'-terminated ASCII string containing an
+ * IP presentation address
+ *
+ * Returns address info structure, or NULL if an error occurs. Caller
+ * must free the returned structure with freeaddrinfo(3).
+ */
+__attribute_malloc__
+struct addrinfo *
+host_pton(const char *paddr)
+{
+ struct addrinfo *ai = NULL;
+ struct addrinfo hint = {
+ /* don't return duplicates */
+ .ai_protocol = (int)IPPROTO_UDP,
+ .ai_flags = AI_NUMERICHOST,
+ .ai_family = AF_UNSPEC,
+ };
+ struct sockaddr_in sin;
+ int error;
+
+ /*
+ * Although getaddrinfo(3) is easier to use and supports
+ * IPv6, it recognizes incomplete addresses like "10.4"
+ * as valid AF_INET addresses. It also accepts presentation
+ * addresses that end with a blank.
+ *
+ * inet_pton(3) is much stricter. Use it to be certain we
+ * have a real AF_INET presentation address, before invoking
+ * getaddrinfo(3) to generate the full addrinfo list.
+ */
+ if (inet_pton(AF_INET, paddr, &sin.sin_addr) == 0)
+ return NULL;
+
+ error = getaddrinfo(paddr, NULL, &hint, &ai);
+ switch (error) {
+ case 0:
+ return ai;
+ case EAI_NONAME:
+ if (paddr == NULL)
+ xlog(D_GENERAL, "%s: passed a NULL presentation address",
+ __func__);
+ break;
+ case EAI_SYSTEM:
+ xlog(D_GENERAL, "%s: failed to convert %s: (%d) %m",
+ __func__, paddr, errno);
+ break;
+ default:
+ xlog(D_GENERAL, "%s: failed to convert %s: %s",
+ __func__, paddr, gai_strerror(error));
+ break;
+ }
+
+ return NULL;
+}
+
+/**
+ * host_addrinfo - return addrinfo for a given hostname
+ * @hostname: pointer to a '\0'-terminated ASCII string containing a hostname
+ *
+ * Returns address info structure with ai_canonname filled in, or NULL
+ * if no information is available for @hostname. Caller must free the
+ * returned structure with freeaddrinfo(3).
+ */
+__attribute_malloc__
+struct addrinfo *
+host_addrinfo(const char *hostname)
+{
+ struct addrinfo *ai = NULL;
+ struct addrinfo hint = {
+ .ai_family = AF_INET,
+ /* don't return duplicates */
+ .ai_protocol = (int)IPPROTO_UDP,
+ .ai_flags = AI_ADDRCONFIG | AI_CANONNAME,
+ };
+ int error;
+
+ error = getaddrinfo(hostname, NULL, &hint, &ai);
+ switch (error) {
+ case 0:
+ return ai;
+ case EAI_SYSTEM:
+ xlog(D_GENERAL, "%s: failed to resolve %s: (%d) %m",
+ __func__, hostname, errno);
+ break;
+ default:
+ xlog(D_GENERAL, "%s: failed to resolve %s: %s",
+ __func__, hostname, gai_strerror(error));
+ break;
+ }
+
+ return NULL;
+}
+
+/**
+ * host_canonname - return canonical hostname bound to an address
+ * @sap: pointer to socket address to look up
+ *
+ * Discover the canonical hostname associated with the given socket
+ * address. The host's reverse mapping is verified in the process.
+ *
+ * Returns a '\0'-terminated ASCII string containing a hostname, or
+ * NULL if no hostname can be found for @sap. Caller must free
+ * the string.
+ */
+#ifdef HAVE_GETNAMEINFO
+__attribute_malloc__
+char *
+host_canonname(const struct sockaddr *sap)
+{
+ socklen_t salen = sockaddr_size(sap);
+ char buf[NI_MAXHOST];
+ int error;
+
+ if (salen == 0) {
+ xlog(D_GENERAL, "%s: unsupported address family %d",
+ __func__, sap->sa_family);
+ return NULL;
+ }
+
+ memset(buf, 0, sizeof(buf));
+ error = getnameinfo(sap, salen, buf, (socklen_t)sizeof(buf),
+ NULL, 0, NI_NAMEREQD);
+ switch (error) {
+ case 0:
+ break;
+ case EAI_SYSTEM:
+ xlog(D_GENERAL, "%s: getnameinfo(3) failed: (%d) %m",
+ __func__, errno);
+ return NULL;
+ default:
+ (void)getnameinfo(sap, salen, buf, (socklen_t)sizeof(buf),
+ NULL, 0, NI_NUMERICHOST);
+ xlog(D_GENERAL, "%s: failed to resolve %s: %s",
+ __func__, buf, gai_strerror(error));
+ return NULL;
+ }
+
+ return strdup(buf);
+}
+#else /* !HAVE_GETNAMEINFO */
+__attribute_malloc__
+char *
+host_canonname(const struct sockaddr *sap)
+{
+ const struct sockaddr_in *sin = (const struct sockaddr_in *)(char *)sap;
+ const struct in_addr *addr = &sin->sin_addr;
+ struct hostent *hp;
+
+ if (sap->sa_family != AF_INET)
+ return NULL;
+
+ hp = gethostbyaddr(addr, (socklen_t)sizeof(addr), AF_INET);
+ if (hp == NULL)
+ return NULL;
+
+ return strdup(hp->h_name);
+}
+#endif /* !HAVE_GETNAMEINFO */
+
+/**
+ * host_reliable_addrinfo - return addrinfo for a given address
+ * @sap: pointer to socket address to look up
+ *
+ * Reverse and forward lookups are performed to ensure the address has
+ * proper forward and reverse mappings.
+ *
+ * Returns address info structure with ai_canonname filled in, or NULL
+ * if no information is available for @sap. Caller must free the returned
+ * structure with freeaddrinfo(3).
+ */
+__attribute_malloc__
+struct addrinfo *
+host_reliable_addrinfo(const struct sockaddr *sap)
+{
+ struct addrinfo *ai;
+ char *hostname;
+
+ hostname = host_canonname(sap);
+ if (hostname == NULL)
+ return NULL;
+
+ ai = host_addrinfo(hostname);
+
+ free(hostname);
+ return ai;
+}
+
+/**
+ * host_numeric_addrinfo - return addrinfo without doing DNS queries
+ * @sap: pointer to socket address
+ *
+ * Returns address info structure, or NULL if an error occurred.
+ * Caller must free the returned structure with freeaddrinfo(3).
+ */
+#ifdef HAVE_GETNAMEINFO
+__attribute_malloc__
+struct addrinfo *
+host_numeric_addrinfo(const struct sockaddr *sap)
+{
+ socklen_t salen = sockaddr_size(sap);
+ char buf[INET_ADDRSTRLEN];
+ struct addrinfo *ai;
+ int error;
+
+ if (salen == 0) {
+ xlog(D_GENERAL, "%s: unsupported address family %d",
+ __func__, sap->sa_family);
+ return NULL;
+ }
+
+ memset(buf, 0, sizeof(buf));
+ error = getnameinfo(sap, salen, buf, (socklen_t)sizeof(buf),
+ NULL, 0, NI_NUMERICHOST);
+ switch (error) {
+ case 0:
+ break;
+ case EAI_SYSTEM:
+ xlog(D_GENERAL, "%s: getnameinfo(3) failed: (%d) %m",
+ __func__, errno);
+ return NULL;
+ default:
+ xlog(D_GENERAL, "%s: getnameinfo(3) failed: %s",
+ __func__, gai_strerror(error));
+ return NULL;
+ }
+
+ ai = host_pton(buf);
+
+ /*
+ * getaddrinfo(AI_NUMERICHOST) never fills in ai_canonname
+ */
+ if (ai != NULL) {
+ free(ai->ai_canonname); /* just in case */
+ ai->ai_canonname = strdup(buf);
+ if (ai->ai_canonname == NULL) {
+ freeaddrinfo(ai);
+ ai = NULL;
+ }
+ }
+
+ return ai;
+}
+#else /* !HAVE_GETNAMEINFO */
+__attribute_malloc__
+struct addrinfo *
+host_numeric_addrinfo(const struct sockaddr *sap)
+{
+ const struct sockaddr_in *sin = (const struct sockaddr_in *)sap;
+ const struct in_addr *addr = &sin->sin_addr;
+ char buf[INET_ADDRSTRLEN];
+ struct addrinfo *ai;
+
+ if (sap->sa_family != AF_INET)
+ return NULL;
+
+ memset(buf, 0, sizeof(buf));
+ if (inet_ntop(AF_INET, (char *)addr, buf,
+ (socklen_t)sizeof(buf)) == NULL)
+ return NULL;
+
+ ai = host_pton(buf);
+
+ /*
+ * getaddrinfo(AI_NUMERICHOST) never fills in ai_canonname
+ */
+ if (ai != NULL) {
+ ai->ai_canonname = strdup(buf);
+ if (ai->ai_canonname == NULL) {
+ freeaddrinfo(ai);
+ ai = NULL;
+ }
+ }
+
+ return ai;
+}
+#endif /* !HAVE_GETNAMEINFO */
+
static int
is_hostname(const char *sp)
{