]> 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 299fe9937b4d03606dcbc14a749cae3bd7e057a7..79b9a8cba6081af5d91c93de78ac57757f275fe1 100644 (file)
@@ -5,7 +5,9 @@
  *
  */
 
-#include "config.h"
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
 
 /*
 #define TEST
@@ -16,6 +18,9 @@
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <stdlib.h>
+#include <errno.h>
+
+#include <xlog.h>
 #ifdef TEST
 #define xmalloc malloc
 #else
 #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
@@ -117,7 +129,7 @@ hostent_dup (struct hostent *hp)
       len_addr_list += align (hp->h_length, ALIGNMENT)
                       + sizeof (char *);
     }
-  
+
   cp = (struct hostent *) xmalloc (len_ent + len_name + len_aliases
                                   + len_addr_list);
 
@@ -155,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)
 {
@@ -216,6 +570,56 @@ matchhostname (const char *h1, const char *h2)
   return status;
 }
 
+
+/* Map IP to hostname, and then map back to addr to make sure it is a
+ * reliable hostname
+ */
+struct hostent *
+get_reliable_hostbyaddr(const char *addr, int len, int type)
+{
+       struct hostent *hp = NULL;
+
+       struct hostent *reverse;
+       struct hostent *forward;
+       char **sp;
+
+       reverse = gethostbyaddr (addr, len, type);
+       if (!reverse)
+               return NULL;
+
+       /* must make sure the hostent is authorative. */
+
+       reverse = hostent_dup (reverse);
+       forward = gethostbyname (reverse->h_name);
+
+       if (forward) {
+               /* now make sure the "addr" is in the list */
+               for (sp = forward->h_addr_list ; *sp ; sp++) {
+                       if (memcmp (*sp, addr, forward->h_length) == 0)
+                               break;
+               }
+
+               if (*sp) {
+                       /* it's valid */
+                       hp = hostent_dup (forward);
+               }
+               else {
+                       /* it was a FAKE */
+                       xlog (L_WARNING, "Fake hostname %s for %s - forward lookup doesn't match reverse",
+                             reverse->h_name, inet_ntoa(*(struct in_addr*)addr));
+               }
+       }
+       else {
+               /* never heard of it. misconfigured DNS? */
+               xlog (L_WARNING, "Fake hostname %s for %s - forward lookup doesn't exist",
+                     reverse->h_name, inet_ntoa(*(struct in_addr*)addr));
+       }
+
+       free (reverse);
+       return hp;
+}
+
+
 #ifdef TEST
 void
 print_host (struct hostent *hp)
@@ -224,7 +628,7 @@ print_host (struct hostent *hp)
 
   if (hp)
     {
-      printf ("official hostname: %s\n", hp->h_name); 
+      printf ("official hostname: %s\n", hp->h_name);
       printf ("aliases:\n");
       for (sp = hp->h_aliases; *sp; sp++)
        printf ("  %s\n", *sp);