]> git.decadent.org.uk Git - nfs-utils.git/blobdiff - support/export/hostname.c
libexport.a: Add helpers to manage DNS lookups
[nfs-utils.git] / support / export / hostname.c
index 8a23a89b7874466b8146946e5ec65a15b96d57b8..79b9a8cba6081af5d91c93de78ac57757f275fe1 100644 (file)
@@ -18,6 +18,8 @@
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <stdlib.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <stdlib.h>
+#include <errno.h>
+
 #include <xlog.h>
 #ifdef TEST
 #define xmalloc malloc
 #include <xlog.h>
 #ifdef TEST
 #define xmalloc malloc
 #include "misc.h"
 #endif
 
 #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
 #define ALIGNMENT      sizeof (char *)
 
 static int
@@ -158,6 +167,348 @@ hostent_dup (struct hostent *hp)
   return cp;
 }
 
   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)
 {
 static int
 is_hostname(const char *sp)
 {