]> git.decadent.org.uk Git - nfs-utils.git/commitdiff
libexport.a: Add helpers to manage DNS lookups
authorChuck Lever <chuck.lever@oracle.com>
Tue, 22 Jun 2010 14:41:03 +0000 (10:41 -0400)
committerSteve Dickson <steved@redhat.com>
Tue, 22 Jun 2010 20:04:53 +0000 (16:04 -0400)
Introduce DNS query helpers based on getaddrinfo(3) and
getnameinfo(3).  These will eventually replace the existing
hostent-based functions in support/export/hostname.c.

Put some of these new helpers to immediate use, where convenient.

As they are part of libexport.a, I've added the forward declarations
for these new functions in exportfs.h rather than misc.h, where the
hostent-based forward declarations are currently.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Steve Dickson <steved@redhat.com>
support/export/hostname.c
support/export/nfsctl.c
support/include/exportfs.h
utils/exportfs/exportfs.c
utils/mountd/cache.c
utils/mountd/rmtab.c

index 8a23a89b7874466b8146946e5ec65a15b96d57b8..79b9a8cba6081af5d91c93de78ac57757f275fe1 100644 (file)
@@ -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
 #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)
 {
index ae357c75053a0cc52c24727de23da5ceeee78fdf..3b9876adb8edbcff6590dd00cd0b20fd512eb979 100644 (file)
@@ -79,7 +79,7 @@ cltsetup(struct nfsctl_client *cltarg, nfs_client *clp)
 
        j = 0;
        for (i = 0; i < cltarg->cl_naddr && i < NFSCLNT_ADDRMAX; i++) {
-               struct sockaddr_in *sin = get_addrlist_in(clp, i);
+               const struct sockaddr_in *sin = get_addrlist_in(clp, i);
                if (sin->sin_family == AF_INET)
                        cltarg->cl_addrlist[j++] = sin->sin_addr;
        }
index 70bdd571684e035a57944fd787424f6c79fe4905..97bb68ef203cdb9992e522b5ad79ac551941e333 100644 (file)
@@ -146,6 +146,19 @@ void                               xtab_append(nfs_export *);
 
 int                            secinfo_addflavor(struct flav_info *, struct exportent *);
 
+char *                         host_ntop(const struct sockaddr *sap,
+                                               char *buf, const size_t buflen);
+__attribute_malloc__
+struct addrinfo *              host_pton(const char *paddr);
+__attribute_malloc__
+struct addrinfo *              host_addrinfo(const char *hostname);
+__attribute_malloc__
+char *                         host_canonname(const struct sockaddr *sap);
+__attribute_malloc__
+struct addrinfo *              host_reliable_addrinfo(const struct sockaddr *sap);
+__attribute_malloc__
+struct addrinfo *              host_numeric_addrinfo(const struct sockaddr *sap);
+
 int                            rmtab_read(void);
 
 struct nfskey *                        key_lookup(char *hname);
index 331e57e8cf4b5efd8a5c296f16abc4d1db64fc69..83d00a0637a9ab819c406c202ba1f1dfa5d5b3d1 100644 (file)
@@ -287,7 +287,7 @@ static void
 unexportfs(char *arg, int verbose)
 {
        nfs_export      *exp;
-       struct hostent  *hp = NULL;
+       struct addrinfo *ai = NULL;
        char            *path;
        char            *hname = arg;
        int             htype;
@@ -302,10 +302,9 @@ unexportfs(char *arg, int verbose)
        }
 
        if ((htype = client_gettype(hname)) == MCL_FQDN) {
-               if ((hp = gethostbyname(hname)) != 0) {
-                       hp = hostent_dup (hp);
-                       hname = (char *) hp->h_name;
-               }
+               ai = host_addrinfo(hname);
+               if (ai)
+                       hname = ai->ai_canonname;
        }
 
        for (exp = exportlist[htype].p_head; exp; exp = exp->m_next) {
@@ -341,7 +340,7 @@ unexportfs(char *arg, int verbose)
                exp->m_mayexport = 0;
        }
 
-       if (hp) free (hp);
+       freeaddrinfo(ai);
 }
 
 static int can_test(void)
index 85cd8296759ad242ac0718a57cb7e577ccb0d926..1176c624e1a75960d1738633daf4d3376e7643bc 100644 (file)
@@ -912,7 +912,7 @@ int cache_export_ent(char *domain, struct exportent *exp, char *path)
 
 int cache_export(nfs_export *exp, char *path)
 {
-       struct sockaddr_in *sin = get_addrlist_in(exp->m_client, 0);
+       char buf[INET_ADDRSTRLEN];
        int err;
        FILE *f;
 
@@ -920,8 +920,10 @@ int cache_export(nfs_export *exp, char *path)
        if (!f)
                return -1;
 
+
        qword_print(f, "nfsd");
-       qword_print(f, inet_ntoa(sin->sin_addr));
+       qword_print(f, 
+               host_ntop(get_addrlist(exp->m_client, 0), buf, sizeof(buf)));
        qword_printint(f, time(0)+30*60);
        qword_print(f, exp->m_client->m_hostname);
        err = qword_eol(f);
index 19b22eea280d2028dd607ae5911c9687e91fc588..ba0fcf6f6814ec9bb105f62022454fad34149d60 100644 (file)
@@ -133,8 +133,7 @@ mountlist_del(char *hname, const char *path)
 void
 mountlist_del_all(struct sockaddr_in *sin)
 {
-       struct in_addr  addr = sin->sin_addr;
-       struct hostent  *hp;
+       char            *hostname;
        struct rmtabent *rep;
        nfs_export      *exp;
        FILE            *fp;
@@ -142,11 +141,13 @@ mountlist_del_all(struct sockaddr_in *sin)
 
        if ((lockid = xflock(_PATH_RMTABLCK, "w")) < 0)
                return;
-       if (!(hp = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET))) {
-               xlog(L_ERROR, "can't get hostname of %s", inet_ntoa(addr));
+       hostname = host_canonname((struct sockaddr *)sin);
+       if (hostname == NULL) {
+               char buf[INET_ADDRSTRLEN];
+               xlog(L_ERROR, "can't get hostname of %s",
+                       host_ntop((struct sockaddr *)sin, buf, sizeof(buf)));
                goto out_unlock;
        }
-       hp = hostent_dup (hp);
 
        if (!setrmtabent("r"))
                goto out_free;
@@ -155,7 +156,7 @@ mountlist_del_all(struct sockaddr_in *sin)
                goto out_close;
 
        while ((rep = getrmtabent(1, NULL)) != NULL) {
-               if (strcmp(rep->r_client, hp->h_name) == 0 &&
+               if (strcmp(rep->r_client, hostname) == 0 &&
                    (exp = auth_authenticate("umountall", sin, rep->r_path)))
                        continue;
                fputrmtabent(fp, rep, NULL);
@@ -168,7 +169,7 @@ mountlist_del_all(struct sockaddr_in *sin)
 out_close:
        endrmtabent();  /* close & unlink */
 out_free:
-       free (hp);
+       free(hostname);
 out_unlock:
        xfunlock(lockid);
 }