dirmngr: Implement a getaddrinfo wrapper.
authorWerner Koch <wk@gnupg.org>
Wed, 21 Oct 2015 15:55:56 +0000 (17:55 +0200)
committerWerner Koch <wk@gnupg.org>
Wed, 21 Oct 2015 15:56:56 +0000 (17:56 +0200)
* dirmngr/dns-stuff.h: Include some header files.
(dns_addinfo_t, dns_addrinfo_s): New.
* dirmngr/dns-stuff.c: Always include DNS related headers.
(free_dns_addrinfo): New.
(resolve_name_standard): New.
(resolve_dns_name): New.

* dirmngr/t-dns-stuff.c: Include netdb.h.
(main): Keep old default mode with no args but else print outout of
resolve_dns_name.  Revamp option parser.
--

This wrapper allows us to switch to ADNS and thus Tor for standard
name resultion.

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

index c2d40c9..ba4ab8f 100644 (file)
 
 #include <config.h>
 #include <sys/types.h>
-#ifdef USE_DNS_CERT
-# ifdef HAVE_W32_SYSTEM
-#  ifdef HAVE_WINSOCK2_H
-#   include <winsock2.h>
-#  endif
-#  include <windows.h>
-# else
-#  include <netinet/in.h>
-#  include <arpa/nameser.h>
-#  include <resolv.h>
+#ifdef HAVE_W32_SYSTEM
+# ifdef HAVE_WINSOCK2_H
+#  include <winsock2.h>
 # endif
-# include <string.h>
+# include <windows.h>
+#else
+# include <netinet/in.h>
+# include <arpa/nameser.h>
+# include <resolv.h>
+# include <netdb.h>
 #endif
+#include <string.h>
 #ifdef USE_ADNS
 # include <adns.h>
 #endif
@@ -77,6 +76,142 @@ enable_dns_tormode (void)
   return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
 }
 
+/* Free an addressinfo linked list as returned by resolve_dns_name.  */
+void
+free_dns_addrinfo (dns_addrinfo_t ai)
+{
+  while (ai)
+    {
+      dns_addrinfo_t next = ai->next;
+      xfree (ai);
+      ai = next;
+    }
+}
+
+
+/* Resolve a name using the standard system function.  */
+static gpg_error_t
+resolve_name_standard (const char *name, unsigned short port,
+                       int want_family, int want_socktype,
+                       dns_addrinfo_t *r_dai, char **r_canonname)
+{
+  gpg_error_t err = 0;
+  dns_addrinfo_t daihead = NULL;
+  dns_addrinfo_t dai;
+  struct addrinfo *aibuf = NULL;
+  struct addrinfo hints, *ai;
+  char portstr[21];
+  int ret;
+
+  *r_dai = NULL;
+  if (r_canonname)
+    *r_canonname = NULL;
+
+  memset (&hints, 0, sizeof hints);
+  hints.ai_family = want_family;
+  hints.ai_socktype = want_socktype;
+  if (r_canonname)
+    hints.ai_flags = AI_CANONNAME;
+
+  if (port)
+    snprintf (portstr, sizeof portstr, "%hu", port);
+  else
+    *portstr = 0;
+
+  /* We can't use the the AI_IDN flag because that does the conversion
+     using the current locale.  However, GnuPG always used UTF-8.  To
+     support IDN we would need to make use of the libidn API.  */
+  ret = getaddrinfo (name, *portstr? portstr : NULL, &hints, &aibuf);
+  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;
+        }
+      goto leave;
+    }
+
+  if (r_canonname && aibuf && aibuf->ai_canonname)
+    {
+      *r_canonname = xtrystrdup (aibuf->ai_canonname);
+      if (!*r_canonname)
+        {
+          err = gpg_error_from_syserror ();
+          goto leave;
+        }
+    }
+
+  for (ai = aibuf; ai; ai = ai->ai_next)
+    {
+      if (ai->ai_family != AF_INET6 && ai->ai_family != AF_INET)
+        continue;
+
+      dai = xtrymalloc (sizeof *dai + ai->ai_addrlen - 1);
+      dai->family = ai->ai_family;
+      dai->socktype = ai->ai_socktype;
+      dai->protocol = ai->ai_protocol;
+      dai->addrlen = ai->ai_addrlen;
+      memcpy (dai->addr, ai->ai_addr, ai->ai_addrlen);
+      dai->next = daihead;
+      daihead = dai;
+    }
+
+ leave:
+  if (aibuf)
+    freeaddrinfo (aibuf);
+  if (err)
+    {
+      if (r_canonname)
+        {
+          xfree (*r_canonname);
+          *r_canonname = NULL;
+        }
+      free_dns_addrinfo (daihead);
+    }
+  else
+    *r_dai = daihead;
+  return err;
+}
+
+
+/* This a wrapper around getaddrinfo with slighly different semantics.
+   NAME is the name to resolve.
+   PORT is the requested port or 0.
+   WANT_FAMILY is either 0 (AF_UNSPEC), AF_INET6, or AF_INET4.
+   WANT_SOCKETTYPE is either SOCK_STREAM or SOCK_DGRAM.
+
+   On success the result is stored in a linked list with the head
+   stored at the address R_AI; the caller must call gpg_addrinfo_free
+   on this.  If R_CANONNAME is not NULL the official name of the host
+   is stored there as a malloced string; if that name is not available
+   NULL is stored.  */
+gpg_error_t
+resolve_dns_name (const char *name, unsigned short port,
+                  int want_family, int want_socktype,
+                  dns_addrinfo_t *r_ai, char **r_canonname)
+{
+#ifdef USE_ADNS_disabled_for_now
+  return resolve_name_adns (name, port, want_family, want_socktype,
+                            r_ai, r_canonname);
+#else
+  return resolve_name_standard (name, port, want_family, want_socktype,
+                                r_ai, r_canonname);
+#endif
+}
+
+
 /* Returns 0 on success or an error code.  If a PGP CERT record was
    found, the malloced data is returned at (R_KEY, R_KEYLEN) and
    the other return parameters are set to NULL/0.  If an IPGP CERT
index aea0e69..090e79b 100644 (file)
 #ifndef GNUPG_DIRMNGR_DNS_STUFF_H
 #define GNUPG_DIRMNGR_DNS_STUFF_H
 
+#ifdef HAVE_W32_SYSTEM
+# ifdef HAVE_WINSOCK2_H
+#  include <winsock2.h>
+# endif
+# include <windows.h>
+#else
+# include <sys/types.h>
+# include <sys/socket.h>
+#endif
+
 
 #define DNS_CERTTYPE_ANY       0 /* Internal catch all type. */
 /* Certificate types according to RFC-4398:  */
 #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
+{
+  dns_addrinfo_t next;
+  int family;
+  int socktype;
+  int protocol;
+  int addrlen;
+  struct sockaddr addr[1];
+};
+
+
+
 /* Calling this function switches the DNS code into Tor mode if
    possibe.  Return 0 on success.  */
 gpg_error_t enable_dns_tormode (void);
 
+void free_dns_addrinfo (dns_addrinfo_t ai);
+
+/* Provide 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);
+
 /* 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,
index e34f809..e8a74ea 100644 (file)
 #include <stdio.h>
 #include <stdlib.h>
 #include <assert.h>
+#ifndef HAVE_W32_SYSTEM
+# include <netdb.h>
+#endif
 
 #include "util.h"
 #include "dns-stuff.h"
 
+#define PGM "t-dns-stuff"
+
+static int verbose;
+static int debug;
+
+
 
 int
 main (int argc, char **argv)
 {
+  int last_argc = -1;
   gpg_error_t err;
-  unsigned char *fpr;
-  size_t fpr_len;
-  char *url;
-  void *key;
-  size_t keylen;
+  int opt_cert = 0;
   char const *name;
 
+  gpgrt_init ();
   if (argc)
+    { argc--; argv++; }
+  while (argc && last_argc != argc )
     {
-      argc--;
-      argv++;
+      last_argc = argc;
+      if (!strcmp (*argv, "--"))
+        {
+          argc--; argv++;
+          break;
+        }
+      else if (!strcmp (*argv, "--help"))
+        {
+          fputs ("usage: " PGM " [HOST]\n"
+                 "Options:\n"
+                 "  --verbose         print timings etc.\n"
+                 "  --debug           flyswatter\n"
+                 "  --cert            lookup a CERT RR\n"
+                 , stdout);
+          exit (0);
+        }
+      else if (!strcmp (*argv, "--verbose"))
+        {
+          verbose++;
+          argc--; argv++;
+        }
+      else if (!strcmp (*argv, "--debug"))
+        {
+          verbose += 2;
+          debug++;
+          argc--; argv++;
+        }
+      else if (!strcmp (*argv, "--cert"))
+        {
+          opt_cert = 1;
+          argc--; argv++;
+        }
+      else if (!strncmp (*argv, "--", 2))
+        {
+          fprintf (stderr, PGM ": unknown option '%s'\n", *argv);
+          exit (1);
+        }
     }
 
   if (!argc)
-    name = "simon.josefsson.org";
+    {
+      opt_cert = 1;
+      name = "simon.josefsson.org";
+    }
   else if (argc == 1)
     name = *argv;
   else
     {
-      fputs ("usage: t-dns-stuff [name]\n", stderr);
-      return 1;
+      fprintf (stderr, PGM ": too many host names given\n");
+      exit (1);
     }
 
-  printf ("CERT lookup on '%s'\n", name);
-
-  err = get_dns_cert (name, DNS_CERTTYPE_ANY, &key, &keylen,
-                      &fpr, &fpr_len, &url);
-  if (err)
-    printf ("get_dns_cert failed: %s <%s>\n",
-            gpg_strerror (err), gpg_strsource (err));
-  else if (key)
+  if (opt_cert)
     {
-      printf ("Key found (%u bytes)\n", (unsigned int)keylen);
+      unsigned char *fpr;
+      size_t fpr_len;
+      char *url;
+      void *key;
+      size_t keylen;
+
+      printf ("CERT lookup on '%s'\n", name);
+
+      err = get_dns_cert (name, DNS_CERTTYPE_ANY, &key, &keylen,
+                          &fpr, &fpr_len, &url);
+      if (err)
+        printf ("get_dns_cert failed: %s <%s>\n",
+                gpg_strerror (err), gpg_strsource (err));
+      else if (key)
+        {
+          printf ("Key found (%u bytes)\n", (unsigned int)keylen);
+        }
+      else
+        {
+          if (fpr)
+            {
+              int i;
+
+              printf ("Fingerprint found (%d bytes): ", (int)fpr_len);
+              for (i = 0; i < fpr_len; i++)
+                printf ("%02X", fpr[i]);
+              putchar ('\n');
+            }
+          else
+            printf ("No fingerprint found\n");
+
+          if (url)
+            printf ("URL found: %s\n", url);
+          else
+            printf ("No URL found\n");
+
+        }
+
+      xfree (key);
+      xfree (fpr);
+      xfree (url);
     }
-  else
+  else /* Standard lookup.  */
     {
-      if (fpr)
-       {
-         int i;
-
-         printf ("Fingerprint found (%d bytes): ", (int)fpr_len);
-         for (i = 0; i < fpr_len; i++)
-           printf ("%02X", fpr[i]);
-         putchar ('\n');
-       }
-      else
-       printf ("No fingerprint found\n");
+      char *cname;
+      dns_addrinfo_t aibuf, ai;
+      int ret;
+      char hostbuf[1025];
 
-      if (url)
-       printf ("URL found: %s\n", url);
-      else
-       printf ("No URL found\n");
+      printf ("Lookup on '%s'\n", name);
+
+      err = resolve_dns_name (name, 0, 0, SOCK_STREAM, &aibuf, &cname);
+      if (err)
+        {
+          fprintf (stderr, PGM": resolving '%s' failed: %s\n",
+                   name, gpg_strerror (err));
+          exit (1);
+        }
+
+      if (cname)
+        printf ("cname: %s\n", cname);
+      for (ai = aibuf; ai; ai = ai->next)
+        {
+          printf ("%s %3d %3d   ",
+                  ai->family == AF_INET6? "inet6" :
+                  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));
+          else
+            printf ("%s\n", hostbuf);
+        }
+      xfree (cname);
+      free_dns_addrinfo (aibuf);
     }
 
-  xfree (key);
-  xfree (fpr);
-  xfree (url);
 
   return 0;
 }