[Pkg-gnupg-commit] [gnupg2] 65/241: dirmngr: Add workaround for broken getaddrinfo.

Daniel Kahn Gillmor dkg at fifthhorseman.net
Wed Dec 9 20:31:55 UTC 2015


This is an automated email from the git hooks/post-receive script.

dkg pushed a commit to branch master
in repository gnupg2.

commit 5e7ac031f513ad3b60e4f092fa72b3bec0676515
Author: Werner Koch <wk at gnupg.org>
Date:   Sun Oct 25 16:38:07 2015 +0100

    dirmngr: Add workaround for broken getaddrinfo.
    
    * dirmngr/dns-stuff.c (resolve_name_standard): On failure retry by
    first resolving the CNAME.
    (get_dns_cname): New.
    
    * dirmngr/t-dns-stuff.c (main): Add option --cname.
    --
    
    At least the getaddrinfo implementation in glibc 2.19-13 from Debian
    returns EAI_NONAME if the CNAME points to a too long list of A/AAAA
    addresses.  Looking at the wire the data is correctly returned from
    the server but getaddrinfo seems to get confused by truncation and
    retry.  To fix this we resolve the CNAME again and call getaddrinfo
    again with the canonical name.
    
    Signed-off-by: Werner Koch <wk at gnupg.org>
---
 dirmngr/Makefile.am   |   4 +-
 dirmngr/dns-stuff.c   | 133 +++++++++++++++++++++++++++++++++++++++++++++++++-
 dirmngr/dns-stuff.h   |   3 ++
 dirmngr/t-dns-stuff.c |  23 +++++++++
 dirmngr/t-http.c      |   1 +
 5 files changed, 161 insertions(+), 3 deletions(-)

diff --git a/dirmngr/Makefile.am b/dirmngr/Makefile.am
index 0d4400f..009802a 100644
--- a/dirmngr/Makefile.am
+++ b/dirmngr/Makefile.am
@@ -140,9 +140,9 @@ t_ldap_parse_uri_SOURCES = \
         http.c dns-stuff.c \
         $(ldap_url) $(t_common_src)
 t_ldap_parse_uri_CFLAGS = -DWITHOUT_NPTH=1
-t_ldap_parse_uri_LDADD = $(ldaplibs) $(t_common_ldadd)
+t_ldap_parse_uri_LDADD = $(ldaplibs) $(t_common_ldadd) $(DNSLIBS)
 
 t_dns_stuff_SOURCES = t-dns-stuff.c dns-stuff.c
-t_dns_stuff_LDADD   = $(t_common_ldadd)
+t_dns_stuff_LDADD   = $(t_common_ldadd) $(DNSLIBS)
 
 $(PROGRAMS) : $(libcommon) $(libcommonpth)
diff --git a/dirmngr/dns-stuff.c b/dirmngr/dns-stuff.c
index d784ccf..f3b622d 100644
--- a/dirmngr/dns-stuff.c
+++ b/dirmngr/dns-stuff.c
@@ -163,7 +163,29 @@ resolve_name_standard (const char *name, unsigned short port,
     {
       aibuf = NULL;
       err = map_eai_to_gpg_error (ret);
-      goto leave;
+      if (gpg_err_code (err) == GPG_ERR_NO_NAME)
+        {
+          /* There seems to be a bug in the glibc getaddrinfo function
+             if the CNAME points to a long list of A and AAAA records
+             in which case the function return NO_NAME.  Let's do the
+             CNAME redirection again.  */
+          char *cname;
+
+          if (get_dns_cname (name, &cname))
+            goto leave; /* Still no success.  */
+
+          ret = getaddrinfo (cname, *portstr? portstr : NULL, &hints, &aibuf);
+          xfree (cname);
+          if (ret)
+            {
+              aibuf = NULL;
+              err = map_eai_to_gpg_error (ret);
+              goto leave;
+            }
+          err = 0; /* Yep, now it worked.  */
+        }
+      else
+        goto leave;
     }
 
   if (r_canonname && aibuf && aibuf->ai_canonname)
@@ -1011,3 +1033,112 @@ getsrv (const char *name,struct srventry **list)
   return -1;
 }
 #endif /*USE_DNS_SRV*/
+
+
+gpg_error_t
+get_dns_cname (const char *name, char **r_cname)
+{
+  gpg_error_t err;
+  int rc;
+
+  *r_cname = NULL;
+
+#ifdef USE_ADNS
+  {
+    adns_state state;
+    adns_answer *answer = NULL;
+
+    if (my_adns_init (&state))
+      return gpg_error (GPG_ERR_GENERAL);
+
+    rc = adns_synchronous (state, name, adns_r_cname, adns_qf_quoteok_query,
+                           &answer);
+    if (rc)
+      {
+        err = gpg_error_from_syserror ();
+        log_error ("DNS query failed: %s\n", gpg_strerror (err));
+        adns_finish (state);
+        return err;
+      }
+    if (answer->status != adns_s_ok
+        || answer->type != adns_r_cname || answer->nrrs != 1)
+      {
+        err = gpg_error (GPG_ERR_GENERAL);
+        log_error ("DNS query returned an error or no records: %s (%s)\n",
+                   adns_strerror (answer->status),
+                   adns_errabbrev (answer->status));
+        adns_free (answer);
+        adns_finish (state);
+        return err;
+      }
+    *r_cname = xtrystrdup (answer->rrs.str[0]);
+    if (!*r_cname)
+      err = gpg_error_from_syserror ();
+    else
+      err = 0;
+
+    adns_free (answer);
+    adns_finish (state);
+    return err;
+  }
+#else /*!USE_ADNS*/
+  {
+    unsigned char answer[2048];
+    HEADER *header = (HEADER *)answer;
+    unsigned char *pt, *emsg;
+    int r;
+    char *cname;
+    int cnamesize = 1025;
+    u16 count;
+
+    /* Do not allow a query using the standard resolver in Tor mode.  */
+    if (tor_mode)
+      return -1;
+
+    r = res_query (name, C_IN, T_CERT, answer, sizeof answer);
+    if (r < sizeof (HEADER) || r > sizeof answer)
+      return gpg_error (GPG_ERR_SERVER_FAILED);
+    if (header->rcode != NOERROR || !(count=ntohs (header->ancount)))
+      return gpg_error (GPG_ERR_NO_NAME); /* Error or no record found.  */
+    if (count != 1)
+      return gpg_error (GPG_ERR_SERVER_FAILED);
+
+    emsg = &answer[r];
+    pt = &answer[sizeof(HEADER)];
+    rc = dn_skipname (pt, emsg);
+    if (rc == -1)
+      return gpg_error (GPG_ERR_SERVER_FAILED);
+
+    pt += rc + QFIXEDSZ;
+    if (pt >= emsg)
+      return gpg_error (GPG_ERR_SERVER_FAILED);
+
+    rc = dn_skipname (pt, emsg);
+    if (rc == -1)
+      return gpg_error (GPG_ERR_SERVER_FAILED);
+    pt += rc + 2 + 2 + 4;
+    if (pt+2 >= emsg)
+      return gpg_error (GPG_ERR_SERVER_FAILED);
+    pt += 2;  /* Skip rdlen */
+
+    cname = xtrymalloc (cnamesize);
+    if (!cname)
+      return gpg_error_from_syserror ();
+
+    rc = dn_expand (answer, emsg, pt, cname, cnamesize -1);
+    if (rc == -1)
+      {
+        xfree (cname);
+        return gpg_error (GPG_ERR_SERVER_FAILED);
+      }
+    *r_cname = xtryrealloc (cname, strlen (cname)+1);
+    if (!*r_cname)
+      {
+        err = gpg_error_from_syserror ();
+        xfree (cname);
+        return err;
+      }
+    return 0;
+  }
+#endif /*!USE_ADNS*/
+}
diff --git a/dirmngr/dns-stuff.h b/dirmngr/dns-stuff.h
index fd1c43a..c3effad 100644
--- a/dirmngr/dns-stuff.h
+++ b/dirmngr/dns-stuff.h
@@ -110,6 +110,9 @@ gpg_error_t resolve_dns_addr (const struct sockaddr *addr, int addrlen,
 /* Return true if NAME is a numerical IP address.  */
 int is_ip_address (const char *name);
 
+/* Get the canonical name for NAME.  */
+gpg_error_t get_dns_cname (const char *name, char **r_cname);
+
 /* 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,
diff --git a/dirmngr/t-dns-stuff.c b/dirmngr/t-dns-stuff.c
index f216d06..4ecbd64 100644
--- a/dirmngr/t-dns-stuff.c
+++ b/dirmngr/t-dns-stuff.c
@@ -44,6 +44,7 @@ main (int argc, char **argv)
   int opt_cert = 0;
   int opt_srv = 0;
   int opt_bracket = 0;
+  int opt_cname = 0;
   char const *name = NULL;
 
   gpgrt_init ();
@@ -68,6 +69,7 @@ main (int argc, char **argv)
                  "  --bracket         enclose v6 addresses in brackets\n"
                  "  --cert            lookup a CERT RR\n"
                  "  --srv             lookup a SRV RR\n"
+                 "  --cname           lookup a CNAME RR\n"
                  , stdout);
           exit (0);
         }
@@ -102,6 +104,11 @@ main (int argc, char **argv)
           any_options = opt_srv = 1;
           argc--; argv++;
         }
+      else if (!strcmp (*argv, "--cname"))
+        {
+          any_options = opt_cname = 1;
+          argc--; argv++;
+        }
       else if (!strncmp (*argv, "--", 2))
         {
           fprintf (stderr, PGM ": unknown option '%s'\n", *argv);
@@ -177,6 +184,22 @@ main (int argc, char **argv)
       xfree (fpr);
       xfree (url);
     }
+  else if (opt_cname)
+    {
+      char *cname;
+
+      printf ("CNAME lookup on '%s'\n", name);
+      err = get_dns_cname (name, &cname);
+      if (err)
+        printf ("get_dns_cname failed: %s <%s>\n",
+                gpg_strerror (err), gpg_strsource (err));
+      else
+        {
+          printf ("CNAME found: '%s'\n", cname);
+        }
+
+      xfree (cname);
+    }
   else if (opt_srv)
     {
       struct srventry *srv;
diff --git a/dirmngr/t-http.c b/dirmngr/t-http.c
index 816b744..35858f6 100644
--- a/dirmngr/t-http.c
+++ b/dirmngr/t-http.c
@@ -36,6 +36,7 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
+#include <assuan.h>
 
 #include "util.h"
 #include "logging.h"

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-gnupg/gnupg2.git



More information about the Pkg-gnupg-commit mailing list