dirmngr: Implement a getnameinfo wrapper.
authorWerner Koch <wk@gnupg.org>
Sat, 24 Oct 2015 14:27:47 +0000 (16:27 +0200)
committerWerner Koch <wk@gnupg.org>
Sun, 25 Oct 2015 15:47:11 +0000 (16:47 +0100)
* dirmngr/dns-stuff.h (DNS_NUMERICHOST): New.
(DNS_WITHBRACKET): New.
* dirmngr/dns-stuff.c (resolve_name_standard): Factor code out to...
(map_eai_to_gpg_error): new.
(resolve_addr_standard): New.
(resolve_dns_addr): New.

* dirmngr/ks-engine-hkp.c (is_ip_address): Move to ...
* dirmngr/dns-stuff.c (is_ip_address): here.  Add support for non
bracketed v6 addresses.

* dirmngr/t-dns-stuff.c: Remove header netdb.h.
(main): Add option --bracket.  Use resolve_dns_name instead of
getnameinfo.

Signed-off-by: Werner Koch <wk@gnupg.org>
dirmngr/dns-stuff.c
dirmngr/dns-stuff.h
dirmngr/ks-engine-hkp.c
dirmngr/t-dns-stuff.c

index 300d086..dc5cb89 100644 (file)
 #include "host2net.h"
 #include "dns-stuff.h"
 
+
+#if AF_UNSPEC != 0
+# error AF_UNSPEC does not have the value 0
+#endif
+
 /* Not every installation has gotten around to supporting SRVs or
    CERTs yet... */
 #ifndef T_SRV
@@ -96,6 +101,30 @@ free_dns_addrinfo (dns_addrinfo_t ai)
 }
 
 
+static gpg_error_t
+map_eai_to_gpg_error (int ec)
+{
+  gpg_error_t err;
+
+  switch (ec)
+    {
+    case EAI_AGAIN:     err = gpg_error (GPG_ERR_EAGAIN); break;
+    case EAI_BADFLAGS:  err = gpg_error (GPG_ERR_INV_FLAG); break;
+    case EAI_FAIL:      err = gpg_error (GPG_ERR_SERVER_FAILED); break;
+    case EAI_MEMORY:    err = gpg_error (GPG_ERR_ENOMEM); break;
+    case EAI_NODATA:    err = gpg_error (GPG_ERR_NO_DATA); break;
+    case EAI_NONAME:    err = gpg_error (GPG_ERR_NO_NAME); break;
+    case EAI_SERVICE:   err = gpg_error (GPG_ERR_NOT_SUPPORTED); break;
+    case EAI_ADDRFAMILY:err = gpg_error (GPG_ERR_EADDRNOTAVAIL); break;
+    case EAI_FAMILY:    err = gpg_error (GPG_ERR_EAFNOSUPPORT); break;
+    case EAI_SOCKTYPE:  err = gpg_error (GPG_ERR_ESOCKTNOSUPPORT); break;
+    case EAI_SYSTEM:    err = gpg_error_from_syserror (); break;
+    default:            err = gpg_error (GPG_ERR_UNKNOWN_ERRNO); break;
+    }
+  return err;
+}
+
+
 /* Resolve a name using the standard system function.  */
 static gpg_error_t
 resolve_name_standard (const char *name, unsigned short port,
@@ -132,21 +161,7 @@ resolve_name_standard (const char *name, unsigned short port,
   if (ret)
     {
       aibuf = NULL;
-      switch (ret)
-        {
-        case EAI_AGAIN:     err = gpg_error (GPG_ERR_EAGAIN); break;
-        case EAI_BADFLAGS:  err = gpg_error (GPG_ERR_INV_FLAG); break;
-        case EAI_FAIL:      err = gpg_error (GPG_ERR_SERVER_FAILED); break;
-        case EAI_MEMORY:    err = gpg_error (GPG_ERR_ENOMEM); break;
-        case EAI_NODATA:    err = gpg_error (GPG_ERR_NO_DATA); break;
-        case EAI_NONAME:    err = gpg_error (GPG_ERR_NO_NAME); break;
-        case EAI_SERVICE:   err = gpg_error (GPG_ERR_NOT_SUPPORTED); break;
-        case EAI_ADDRFAMILY:err = gpg_error (GPG_ERR_EADDRNOTAVAIL); break;
-        case EAI_FAMILY:    err = gpg_error (GPG_ERR_EAFNOSUPPORT); break;
-        case EAI_SOCKTYPE:  err = gpg_error (GPG_ERR_ESOCKTNOSUPPORT); break;
-        case EAI_SYSTEM:    err = gpg_error_from_syserror (); break;
-        default:            err = gpg_error (GPG_ERR_UNKNOWN_ERRNO); break;
-        }
+      err = map_eai_to_gpg_error (ret);
       goto leave;
     }
 
@@ -193,6 +208,66 @@ resolve_name_standard (const char *name, unsigned short port,
 }
 
 
+/* Resolve an address using the standard system function.  */
+static gpg_error_t
+resolve_addr_standard (const struct sockaddr *addr, int addrlen,
+                       unsigned int flags, char **r_name)
+{
+  gpg_error_t err;
+  int ec;
+  char *buffer, *p;
+  int buflen;
+
+  *r_name = NULL;
+
+  buflen = NI_MAXHOST;
+  buffer = xtrymalloc (buflen + 2 + 1);
+  if (!buffer)
+    return gpg_error_from_syserror ();
+
+  if ((flags & DNS_NUMERICHOST) || tor_mode)
+    ec = EAI_NONAME;
+  else
+    ec = getnameinfo (addr, addrlen, buffer, buflen, NULL, 0, NI_NAMEREQD);
+
+  if (!ec && *buffer == '[')
+    ec = EAI_FAIL;  /* A name may never start with a bracket.  */
+  else if (ec == EAI_NONAME)
+    {
+      p = buffer;
+      if (addr->sa_family == AF_INET6 && (flags & DNS_WITHBRACKET))
+        {
+          *p++ = '[';
+          buflen -= 2;
+        }
+      ec = getnameinfo (addr, addrlen, p, buflen, NULL, 0, NI_NUMERICHOST);
+      if (!ec && addr->sa_family == AF_INET6 && (flags & DNS_WITHBRACKET))
+        strcat (buffer, "]");
+    }
+
+  if (ec)
+    err = map_eai_to_gpg_error (ec);
+  else
+    {
+      p = xtryrealloc (buffer, strlen (buffer)+1);
+      if (!p)
+        err = gpg_error_from_syserror ();
+      else
+        {
+          buffer = p;
+          err = 0;
+        }
+    }
+
+  if (err)
+    xfree (buffer);
+  else
+    *r_name = buffer;
+
+  return err;
+}
+
+
 /* This a wrapper around getaddrinfo with slighly different semantics.
    NAME is the name to resolve.
    PORT is the requested port or 0.
@@ -219,6 +294,85 @@ resolve_dns_name (const char *name, unsigned short port,
 }
 
 
+gpg_error_t
+resolve_dns_addr (const struct sockaddr *addr, int addrlen,
+                  unsigned int flags, char **r_name)
+{
+#ifdef USE_ADNS_disabled_for_now
+  return resolve_addr_adns (addr, addrlen, flags, r_name);
+#else
+  return resolve_addr_standard (addr, addrlen, flags, r_name);
+#endif
+}
+
+
+/* Check whether NAME is an IP address.  Returns true if it is either
+   an IPv6 or IPv4 numerical address.  */
+int
+is_ip_address (const char *name)
+{
+  const char *s;
+  int ndots, dblcol, n;
+
+  if (*name == '[')
+    return 1; /* yes: A legal DNS name may not contain this character;
+                 this mut be bracketed v6 address.  */
+  if (*name == '.')
+    return 0; /* No.  A leading dot is not a valid IP address.  */
+
+  /* Check whether this is a v6 address.  */
+  ndots = n = dblcol = 0;
+  for (s=name; *s; s++)
+    {
+      if (*s == ':')
+        {
+          ndots++;
+          if (s[1] == ':')
+            {
+              ndots++;
+              if (dblcol)
+                return 0; /* No: Only one "::" allowed.  */
+              dblcol++;
+              if (s[1])
+                s++;
+            }
+          n = 0;
+        }
+      else if (*s == '.')
+        goto legacy;
+      else if (!strchr ("0123456789abcdefABCDEF", *s))
+        return 0; /* No: Not a hex digit.  */
+      else if (++n > 4)
+        return 0; /* To many digits in a group.  */
+    }
+  if (ndots > 7)
+    return 0; /* No: Too many colons.  */
+  else if (ndots > 1)
+    return 1; /* Yes: At least 2 colons indicate an v6 address.  */
+
+ legacy:
+  /* Check whether it is legacy IP address.  */
+  ndots = n = 0;
+  for (s=name; *s; s++)
+    {
+      if (*s == '.')
+        {
+          if (s[1] == '.')
+            return 0; /* No:  Douple dot. */
+          if (atoi (s+1) > 255)
+            return 0; /* No:  Ipv4 byte value too large.  */
+          ndots++;
+          n = 0;
+        }
+      else if (!strchr ("0123456789", *s))
+        return 0; /* No: Not a digit.  */
+      else if (++n > 3)
+        return 0; /* No: More than 3 digits.  */
+    }
+  return !!(ndots == 3);
+}
+
+
 #ifdef USE_ADNS
 /* Init ADNS and store the new state at R_STATE.  Returns 0 on
    success; prints an error message and returns an error code on
index 13c47df..fd1c43a 100644 (file)
 # include <sys/socket.h>
 #endif
 
+/*
+ * Flags used with resolve_dns_addr.
+ */
+#define DNS_NUMERICHOST        1  /* Force numeric output format.  */
+#define DNS_WITHBRACKET        2  /* Put brackets around numeric v6
+                                     addresses.  */
 
+/*
+ * Constants for use with get_dns_cert.
+ */
 #define DNS_CERTTYPE_ANY       0 /* Internal catch all type. */
 /* Certificate types according to RFC-4398:  */
 #define DNS_CERTTYPE_PKIX      1 /* X.509 as per PKIX. */
@@ -58,6 +67,8 @@
 #define DNS_CERTTYPE_RRBASE 1024 /* Base of special constants.  */
 #define DNS_CERTTYPE_RR61   (DNS_CERTTYPE_RRBASE + 61)
 
+
+
 struct dns_addrinfo_s;
 typedef struct dns_addrinfo_s *dns_addrinfo_t;
 struct dns_addrinfo_s
@@ -87,17 +98,25 @@ gpg_error_t enable_dns_tormode (void);
 
 void free_dns_addrinfo (dns_addrinfo_t ai);
 
-/* Provide function similar to getaddrinfo.  */
+/* Function similar to getaddrinfo.  */
 gpg_error_t resolve_dns_name (const char *name, unsigned short port,
                               int want_family, int want_socktype,
                               dns_addrinfo_t *r_dai, char **r_canonname);
 
+/* Function similar to getnameinfo.  */
+gpg_error_t resolve_dns_addr (const struct sockaddr *addr, int addrlen,
+                              unsigned int flags, char **r_name);
+
+/* Return true if NAME is a numerical IP address.  */
+int is_ip_address (const char *name);
+
 /* Return a CERT record or an arbitray RR.  */
 gpg_error_t get_dns_cert (const char *name, int want_certtype,
                           void **r_key, size_t *r_keylen,
                           unsigned char **r_fpr, size_t *r_fprlen,
                           char **r_url);
 
+
 int getsrv (const char *name,struct srventry **list);
 
 
index 444f305..809204d 100644 (file)
@@ -280,38 +280,6 @@ my_getnameinfo (dns_addrinfo_t ai, char *host, size_t hostlen,
 }
 
 
-/* Check whether NAME is an IP address.  */
-static int
-is_ip_address (const char *name)
-{
-  int ndots, n;
-
-  if (*name == '[')
-    return 1;
-  /* Check whether it is legacy IP address.  */
-  if (*name == '.')
-    return 0; /* No.  */
-  ndots = n = 0;
-  for (; *name; name++)
-    {
-      if (*name == '.')
-        {
-          if (name[1] == '.')
-            return 0; /* No. */
-          if (atoi (name+1) > 255)
-            return 0; /* Value too large.  */
-          ndots++;
-          n = 0;
-        }
-      else if (!strchr ("012345678", *name))
-        return 0; /* Not a digit.  */
-      else if (++n > 3)
-        return 0; /* More than 3 digits.  */
-    }
-  return !!(ndots == 3);
-}
-
-
 /* Map the host name NAME to the actual to be used host name.  This
    allows us to manage round robin DNS names.  We use our own strategy
    to choose one of the hosts.  For example we skip those hosts which
index 63d4cdd..0511f93 100644 (file)
@@ -22,9 +22,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <assert.h>
-#ifndef HAVE_W32_SYSTEM
-# include <netdb.h>
-#endif
+
 
 #include "util.h"
 #include "dns-stuff.h"
@@ -45,6 +43,7 @@ main (int argc, char **argv)
   int opt_tor = 0;
   int opt_cert = 0;
   int opt_srv = 0;
+  int opt_bracket = 0;
   char const *name = NULL;
 
   gpgrt_init ();
@@ -66,6 +65,7 @@ main (int argc, char **argv)
                  "  --verbose         print timings etc.\n"
                  "  --debug           flyswatter\n"
                  "  --use-tor         use Tor\n"
+                 "  --bracket         enclose v6 addresses in brackets\n"
                  "  --cert            lookup a CERT RR\n"
                  "  --srv             lookup a SRV RR\n"
                  , stdout);
@@ -87,6 +87,11 @@ main (int argc, char **argv)
           opt_tor = 1;
           argc--; argv++;
         }
+      else if (!strcmp (*argv, "--bracket"))
+        {
+          opt_bracket = 1;
+          argc--; argv++;
+        }
       else if (!strcmp (*argv, "--cert"))
         {
           any_options = opt_cert = 1;
@@ -194,8 +199,7 @@ main (int argc, char **argv)
     {
       char *cname;
       dns_addrinfo_t aibuf, ai;
-      int ret;
-      char hostbuf[1025];
+      char *host;
 
       printf ("Lookup on '%s'\n", name);
 
@@ -216,14 +220,30 @@ main (int argc, char **argv)
                   ai->family == AF_INET?  "inet4" : "?    ",
                   ai->socktype, ai->protocol);
 
-          ret = getnameinfo (ai->addr, ai->addrlen,
-                             hostbuf, sizeof hostbuf,
-                             NULL, 0,
-                             NI_NUMERICHOST);
-          if (ret)
-            printf ("[getnameinfo failed: %s]\n", gai_strerror (ret));
+          err = resolve_dns_addr (ai->addr, ai->addrlen,
+                                  (DNS_NUMERICHOST
+                                   | (opt_bracket? DNS_WITHBRACKET:0)),
+                                  &host);
+          if (err)
+            printf ("[getnameinfo failed: %s]", gpg_strerror (err));
+          else
+            {
+              printf ("%s", host);
+              xfree (host);
+            }
+
+          err = resolve_dns_addr (ai->addr, ai->addrlen,
+                                  (opt_bracket? DNS_WITHBRACKET:0),
+                                  &host);
+          if (err)
+            printf ("[getnameinfo failed (2): %s]", gpg_strerror (err));
           else
-            printf ("%s\n", hostbuf);
+            {
+              if (!is_ip_address (host))
+                printf ("  (%s)", host);
+              xfree (host);
+            }
+          putchar ('\n');
         }
       xfree (cname);
       free_dns_addrinfo (aibuf);