]> git.decadent.org.uk Git - nfs-utils.git/blobdiff - support/export/hostname.c
nfs: fix host_reliable_addrinfo
[nfs-utils.git] / support / export / hostname.c
index 79b9a8cba6081af5d91c93de78ac57757f275fe1..3e949a18fe52255b51a4d77713f28b1679f16b75 100644 (file)
 /*
 /*
- * support/export/hostname.c
+ * Copyright 2010 Oracle.  All rights reserved.
  *
  *
- * Functions for hostname.
+ * This file is part of nfs-utils.
  *
  *
+ * nfs-utils is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * nfs-utils is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with nfs-utils.  If not, see <http://www.gnu.org/licenses/>.
  */
 
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 #endif
 
  */
 
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 #endif
 
-/*
-#define TEST
-*/
-
 #include <string.h>
 #include <string.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
 #include <stdlib.h>
 #include <stdlib.h>
+#include <arpa/inet.h>
+#include <netdb.h>
 #include <errno.h>
 
 #include <errno.h>
 
-#include <xlog.h>
-#ifdef TEST
-#define xmalloc malloc
-#else
-#include "xmalloc.h"
-#include "misc.h"
-#endif
-
 #include "sockaddr.h"
 #include "exportfs.h"
 
 #include "sockaddr.h"
 #include "exportfs.h"
 
-#ifndef HAVE_DECL_AI_ADDRCONFIG
-#define AI_ADDRCONFIG  0
-#endif
-
-#define ALIGNMENT      sizeof (char *)
-
-static int
-align (int len, int al)
-{
-  int i;
-  i = len % al;
-  if (i)
-    len += al - i;
-  return len;
-}
-
-struct hostent *
-get_hostent (const char *addr, int len, int type)
-{
-  struct hostent *cp;
-  int len_ent;
-  const char *name;
-  int len_name;
-  int num_aliases = 1;
-  int len_aliases = sizeof (char *);
-  int num_addr_list = 1;
-  int len_addr_list = sizeof (char *);
-  int pos;
-  struct in_addr *ipv4;
-
-  switch (type)
-    {
-    case AF_INET:
-      ipv4 = (struct in_addr *) addr;
-      name = inet_ntoa (*ipv4);
-      break;
-
-    default:
-      return NULL;
-    }
-
-  len_ent = align (sizeof (*cp), ALIGNMENT);
-  len_name = align (strlen (name) + 1, ALIGNMENT);
-
-  num_addr_list++;
-  len_addr_list += align (len, ALIGNMENT) + sizeof (char *);
-
-  cp = (struct hostent *) xmalloc (len_ent + len_name + len_aliases
-                                  + len_addr_list);
-
-  cp->h_addrtype = type;
-  cp->h_length = len;
-  pos = len_ent;
-  cp->h_name = (char *) &(((char *) cp) [pos]);
-  strcpy (cp->h_name, name);
-
-  pos += len_name;
-  cp->h_aliases = (char **) &(((char *) cp) [pos]);
-  pos += num_aliases * sizeof (char *);
-  cp->h_aliases [0] = NULL;
-
-  pos = len_ent + len_name + len_aliases;
-  cp->h_addr_list = (char **) &(((char *) cp) [pos]);
-  pos += num_addr_list * sizeof (char *);
-  cp->h_addr_list [0] = (char *) &(((char *) cp) [pos]);
-  memcpy (cp->h_addr_list [0], addr, cp->h_length);
-  pos += align (cp->h_length, ALIGNMENT);
-  cp->h_addr_list [1] = NULL;
-
-  return cp;
-}
-
-struct hostent *
-hostent_dup (struct hostent *hp)
-{
-  int len_ent = align (sizeof (*hp), ALIGNMENT);
-  int len_name = align (strlen (hp->h_name) + 1, ALIGNMENT);
-  int num_aliases = 1;
-  int len_aliases = sizeof (char *);
-  int num_addr_list = 1;
-  int len_addr_list = sizeof (char *);
-  int pos, i;
-  char **sp;
-  struct hostent *cp;
-
-  for (sp = hp->h_aliases; sp && *sp; sp++)
-    {
-      num_aliases++;
-      len_aliases += align (strlen (*sp) + 1, ALIGNMENT)
-                    + sizeof (char *);
-    }
-
-  for (sp = hp->h_addr_list; *sp; sp++)
-    {
-      num_addr_list++;
-      len_addr_list += align (hp->h_length, ALIGNMENT)
-                      + sizeof (char *);
-    }
-
-  cp = (struct hostent *) xmalloc (len_ent + len_name + len_aliases
-                                  + len_addr_list);
-
-  *cp = *hp;
-  pos = len_ent;
-  cp->h_name = (char *) &(((char *) cp) [pos]);
-  strcpy (cp->h_name, hp->h_name);
-
-  pos += len_name;
-  cp->h_aliases = (char **) &(((char *) cp) [pos]);
-  pos += num_aliases * sizeof (char *);
-  for (sp = hp->h_aliases, i = 0; i < num_aliases; i++, sp++)
-    if (sp && *sp)
-      {
-       cp->h_aliases [i] = (char *) &(((char *) cp) [pos]);
-       strcpy (cp->h_aliases [i], *sp);
-       pos += align (strlen (*sp) + 1, ALIGNMENT);
-      }
-    else
-      cp->h_aliases [i] = NULL;
-
-  pos = len_ent + len_name + len_aliases;
-  cp->h_addr_list = (char **) &(((char *) cp) [pos]);
-  pos += num_addr_list * sizeof (char *);
-  for (sp = hp->h_addr_list, i = 0; i < num_addr_list; i++, sp++)
-    if (*sp)
-      {
-       cp->h_addr_list [i] = (char *) &(((char *) cp) [pos]);
-       memcpy (cp->h_addr_list [i], *sp, hp->h_length);
-       pos += align (hp->h_length, ALIGNMENT);
-      }
-    else
-      cp->h_addr_list [i] = *sp;
-
-  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
 /**
  * host_ntop - generate presentation address given a sockaddr
  * @sap: pointer to socket address
@@ -189,7 +42,7 @@ sockaddr_size(const struct sockaddr *sap)
 char *
 host_ntop(const struct sockaddr *sap, char *buf, const size_t buflen)
 {
 char *
 host_ntop(const struct sockaddr *sap, char *buf, const size_t buflen)
 {
-       socklen_t salen = sockaddr_size(sap);
+       socklen_t salen = nfs_sockaddr_length(sap);
        int error;
 
        memset(buf, 0, buflen);
        int error;
 
        memset(buf, 0, buflen);
@@ -250,7 +103,7 @@ host_pton(const char *paddr)
                .ai_family      = AF_UNSPEC,
        };
        struct sockaddr_in sin;
                .ai_family      = AF_UNSPEC,
        };
        struct sockaddr_in sin;
-       int error;
+       int error, inet4;
 
        /*
         * Although getaddrinfo(3) is easier to use and supports
 
        /*
         * Although getaddrinfo(3) is easier to use and supports
@@ -262,12 +115,17 @@ host_pton(const char *paddr)
         * have a real AF_INET presentation address, before invoking
         * getaddrinfo(3) to generate the full addrinfo list.
         */
         * have a real AF_INET presentation address, before invoking
         * getaddrinfo(3) to generate the full addrinfo list.
         */
+       inet4 = 1;
        if (inet_pton(AF_INET, paddr, &sin.sin_addr) == 0)
        if (inet_pton(AF_INET, paddr, &sin.sin_addr) == 0)
-               return NULL;
+               inet4 = 0;
 
        error = getaddrinfo(paddr, NULL, &hint, &ai);
        switch (error) {
        case 0:
 
        error = getaddrinfo(paddr, NULL, &hint, &ai);
        switch (error) {
        case 0:
+               if (!inet4 && ai->ai_addr->sa_family == AF_INET) {
+                       freeaddrinfo(ai);
+                       break;
+               }
                return ai;
        case EAI_NONAME:
                if (paddr == NULL)
                return ai;
        case EAI_NONAME:
                if (paddr == NULL)
@@ -301,10 +159,14 @@ host_addrinfo(const char *hostname)
 {
        struct addrinfo *ai = NULL;
        struct addrinfo hint = {
 {
        struct addrinfo *ai = NULL;
        struct addrinfo hint = {
+#ifdef IPV6_SUPPORTED
+               .ai_family      = AF_UNSPEC,
+#else
                .ai_family      = AF_INET,
                .ai_family      = AF_INET,
+#endif
                /* don't return duplicates */
                .ai_protocol    = (int)IPPROTO_UDP,
                /* don't return duplicates */
                .ai_protocol    = (int)IPPROTO_UDP,
-               .ai_flags       = AI_ADDRCONFIG | AI_CANONNAME,
+               .ai_flags       = AI_CANONNAME,
        };
        int error;
 
        };
        int error;
 
@@ -341,7 +203,7 @@ __attribute_malloc__
 char *
 host_canonname(const struct sockaddr *sap)
 {
 char *
 host_canonname(const struct sockaddr *sap)
 {
-       socklen_t salen = sockaddr_size(sap);
+       socklen_t salen = nfs_sockaddr_length(sap);
        char buf[NI_MAXHOST];
        int error;
 
        char buf[NI_MAXHOST];
        int error;
 
@@ -396,17 +258,19 @@ host_canonname(const struct sockaddr *sap)
  * @sap: pointer to socket address to look up
  *
  * Reverse and forward lookups are performed to ensure the address has
  * @sap: pointer to socket address to look up
  *
  * Reverse and forward lookups are performed to ensure the address has
- * proper forward and reverse mappings.
+ * matching 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).
+ * Returns addrinfo structure with just the provided address with
+ * ai_canonname filled in. If there is a problem with resolution or
+ * the resolved records don't match up properly then it returns NULL
+ *
+ * Caller must free the returned structure with freeaddrinfo(3).
  */
 __attribute_malloc__
 struct addrinfo *
 host_reliable_addrinfo(const struct sockaddr *sap)
 {
  */
 __attribute_malloc__
 struct addrinfo *
 host_reliable_addrinfo(const struct sockaddr *sap)
 {
-       struct addrinfo *ai;
+       struct addrinfo *ai, *a;
        char *hostname;
 
        hostname = host_canonname(sap);
        char *hostname;
 
        hostname = host_canonname(sap);
@@ -414,9 +278,31 @@ host_reliable_addrinfo(const struct sockaddr *sap)
                return NULL;
 
        ai = host_addrinfo(hostname);
                return NULL;
 
        ai = host_addrinfo(hostname);
+       if (!ai)
+               goto out_free_hostname;
+
+       /* make sure there's a matching address in the list */
+       for (a = ai; a; a = a->ai_next)
+               if (nfs_compare_sockaddr(a->ai_addr, sap))
+                       break;
+
+       freeaddrinfo(ai);
+       if (!a)
+               goto out_free_hostname;
+
+       /* get addrinfo with just the original address */
+       ai = host_numeric_addrinfo(sap);
+       if (!ai)
+               goto out_free_hostname;
+
+       /* and populate its ai_canonname field */
+       free(ai->ai_canonname);
+       ai->ai_canonname = hostname;
+       return ai;
 
 
+out_free_hostname:
        free(hostname);
        free(hostname);
-       return ai;
+       return NULL;
 }
 
 /**
 }
 
 /**
@@ -431,8 +317,8 @@ __attribute_malloc__
 struct addrinfo *
 host_numeric_addrinfo(const struct sockaddr *sap)
 {
 struct addrinfo *
 host_numeric_addrinfo(const struct sockaddr *sap)
 {
-       socklen_t salen = sockaddr_size(sap);
-       char buf[INET_ADDRSTRLEN];
+       socklen_t salen = nfs_sockaddr_length(sap);
+       char buf[INET6_ADDRSTRLEN];
        struct addrinfo *ai;
        int error;
 
        struct addrinfo *ai;
        int error;
 
@@ -508,159 +394,3 @@ host_numeric_addrinfo(const struct sockaddr *sap)
        return ai;
 }
 #endif /* !HAVE_GETNAMEINFO */
        return ai;
 }
 #endif /* !HAVE_GETNAMEINFO */
-
-static int
-is_hostname(const char *sp)
-{
-  if (*sp == '\0' || *sp == '@')
-    return 0;
-
-  for (; *sp; sp++)
-    {
-      if (*sp == '*' || *sp == '?' || *sp == '[' || *sp == '/')
-       return 0;
-      if (*sp == '\\' && sp[1])
-       sp++;
-    }
-
-  return 1;
-}
-
-int
-matchhostname (const char *h1, const char *h2)
-{
-  struct hostent *hp1, *hp2;
-  int status;
-
-  if (strcasecmp (h1, h2) == 0)
-    return 1;
-
-  if (!is_hostname (h1) || !is_hostname (h2))
-    return 0;
-
-  hp1 = gethostbyname (h1);
-  if (hp1 == NULL)
-    return 0;
-
-  hp1 = hostent_dup (hp1);
-
-  hp2 = gethostbyname (h2);
-  if (hp2)
-    {
-      if (strcasecmp (hp1->h_name, hp2->h_name) == 0)
-       status = 1;
-      else
-       {
-         char **ap1, **ap2;
-
-         status = 0;
-         for (ap1 = hp1->h_addr_list; *ap1 && status == 0; ap1++)
-           for (ap2 = hp2->h_addr_list; *ap2; ap2++)
-             if (memcmp (*ap1, *ap2, sizeof (struct in_addr)) == 0)
-               {
-                 status = 1;
-                 break;
-               }
-       }
-    }
-  else
-    status = 0;
-
-  free (hp1);
-  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)
-{
-  char **sp;
-
-  if (hp)
-    {
-      printf ("official hostname: %s\n", hp->h_name);
-      printf ("aliases:\n");
-      for (sp = hp->h_aliases; *sp; sp++)
-       printf ("  %s\n", *sp);
-      printf ("IP addresses:\n");
-      for (sp = hp->h_addr_list; *sp; sp++)
-       printf ("  %s\n", inet_ntoa (*(struct in_addr *) *sp));
-    }
-  else
-    printf ("Not host information\n");
-}
-
-int
-main (int argc, char **argv)
-{
-  struct hostent *hp = gethostbyname (argv [1]);
-  struct hostent *cp;
-  struct in_addr addr;
-
-  print_host (hp);
-
-  if (hp)
-    {
-      cp = hostent_dup (hp);
-      print_host (cp);
-      free (cp);
-    }
-  printf ("127.0.0.1 == %s: %d\n", argv [1],
-         matchhostname ("127.0.0.1", argv [1]));
-  addr.s_addr = inet_addr(argv [2]);
-  printf ("%s\n", inet_ntoa (addr));
-  cp = get_hostent ((const char *)&addr, sizeof(addr), AF_INET);
-  print_host (cp);
-  return 0;
-}
-#endif