dirmngr: Add option --standard-resolver.
authorWerner Koch <wk@gnupg.org>
Thu, 1 Dec 2016 14:45:51 +0000 (15:45 +0100)
committerWerner Koch <wk@gnupg.org>
Thu, 1 Dec 2016 15:40:32 +0000 (16:40 +0100)
* dirmngr/dirmngr.c (oStandardResolver): New constant.
(opts): New option --standard-resolver.
(parse_rereadable_options): Set option.
* dirmngr/dns-stuff.c: Refactor all code to support the new option.
(standard_resolver): New var.
(enable_standard_resolver, standard_resolver_p): New func.
* dirmngr/http.c (connect_server): Remove USE_DNS_SRV build
conditional.
* dirmngr/ks-engine-hkp.c (map_host): Ditto.
* dirmngr/server.c (cmd_getinfo) <dnsinfo>: Take care of new option
* configure.ac (HAVE_ADNS_IF_TORMODE): Remove var ADNSLIB.  ac_define
USE_ADNS in the adns checking code.  Remove options --disable-dns-srv
and --disable-dns-cert.  Always look for the system resolver.  Print
warning if no system resolver was found.
(USE_DNS_CERT, USE_DNS_SRV): Remove ac_defines.
(HAVE_SYSTEM_RESOLVER): New ac_define.
(USE_DNS_SRV): Remove am_conditional; not used anyway.
--

This option allows for runtime switching to the system's standard
resolver.  This is mainly useful to work around possible bugs in the
optional resolver libraries (as of now ADNS).  Note that on Windows
there is no code to use systems's resolver and thus for full
functionality dirmngr must be build with a separate resolver.

This patch also does way with configure options to disable the use of
CERT and SRV records.

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

index f196e1b..f3576c7 100644 (file)
@@ -103,6 +103,7 @@ have_sqlite=no
 have_npth=no
 have_libusb=no
 have_adns=no
+have_system_resolver=no
 gnupg_have_ldap="n/a"
 
 use_zip=yes
@@ -1025,58 +1026,39 @@ fi
 CPPFLAGS=${_cppflags}
 LDFLAGS=${_ldflags}
 if test "$have_adns" = "yes"; then
-  ADNSLIBS="-ladns"
+  DNSLIBS="-ladns"
+  AC_DEFINE(USE_ADNS,1,[Use ADNS as resolver library.])
+else
+  DNSLIBS=""
 fi
 
 
 #
-# Now try for the resolver functions so we can use DNS for SRV, PA and CERT.
+# Check standard resolver functions.
 #
-AC_ARG_ENABLE(dns-srv,
-              AC_HELP_STRING([--disable-dns-srv],
-                             [disable the use of DNS SRV in HKP and HTTP]),
-              use_dns_srv=$enableval,use_dns_srv=yes)
-
-AC_ARG_ENABLE(dns-cert,
-   AC_HELP_STRING([--disable-dns-cert],
-       [disable the use of CERT records in DNS]),
-   use_dns_cert=$enableval,use_dns_cert=yes)
-
-if test x"$use_dns_srv" = xyes || test x"$use_dns_cert" = xyes; then
+if test "$build_dirmngr" = "yes"; then
   _dns_save_libs=$LIBS
   LIBS=""
 
-  if test x"$have_adns" = xyes ; then
-    # We prefer ADNS.
-    DNSLIBS="$ADNSLIBS"
-    AC_DEFINE(USE_ADNS,1,[Use ADNS as resolver library.])
-
-    if test x"$use_dns_srv" = xyes ; then
-        AC_DEFINE(USE_DNS_SRV,1)
-    fi
-
-    if test x"$use_dns_cert" = xyes ; then
-        AC_DEFINE(USE_DNS_CERT,1,[define to use DNS CERT])
-    fi
-  else
-    # With no ADNS find the system resolver.
+  # Find the system resolver which can always be enabled with
+  # the dirmngr option --standard-resolver.
 
-    # the double underscore thing is a glibc-ism?
-    AC_SEARCH_LIBS(res_query,resolv bind,,
-                   AC_SEARCH_LIBS(__res_query,resolv bind,,have_resolver=no))
-    AC_SEARCH_LIBS(dn_expand,resolv bind,,
-                   AC_SEARCH_LIBS(__dn_expand,resolv bind,,have_resolver=no))
+  # the double underscore thing is a glibc-ism?
+  AC_SEARCH_LIBS(res_query,resolv bind,,
+                 AC_SEARCH_LIBS(__res_query,resolv bind,,have_resolver=no))
+  AC_SEARCH_LIBS(dn_expand,resolv bind,,
+                 AC_SEARCH_LIBS(__dn_expand,resolv bind,,have_resolver=no))
 
-    # macOS renames dn_skipname into res_9_dn_skipname in <resolv.h>,
-    # and for some reason fools us into believing we don't need
-    # -lresolv even if we do.  Since the test program checking for the
-    # symbol does not include <resolv.h>, we need to check for the
-    # renamed symbol explicitly.
-    AC_SEARCH_LIBS(res_9_dn_skipname,resolv bind,,
-        AC_SEARCH_LIBS(dn_skipname,resolv bind,,
-            AC_SEARCH_LIBS(__dn_skipname,resolv bind,,have_resolver=no)))
+  # macOS renames dn_skipname into res_9_dn_skipname in <resolv.h>,
+  # and for some reason fools us into believing we don't need
+  # -lresolv even if we do.  Since the test program checking for the
+  # symbol does not include <resolv.h>, we need to check for the
+  # renamed symbol explicitly.
+  AC_SEARCH_LIBS(res_9_dn_skipname,resolv bind,,
+      AC_SEARCH_LIBS(dn_skipname,resolv bind,,
+          AC_SEARCH_LIBS(__dn_skipname,resolv bind,,have_resolver=no)))
 
-    if test x"$have_resolver" != xno ; then
+  if test x"$have_resolver" != xno ; then
 
       # Make sure that the BIND 4 resolver interface is workable before
       # enabling any code that calls it.  At some point I'll rewrite the
@@ -1084,8 +1066,8 @@ if test x"$use_dns_srv" = xyes || test x"$use_dns_cert" = xyes; then
       # We might also want to use adns instead.  Problem with ADNS is that
       # it does not support v6.
 
-      AC_MSG_CHECKING([whether the resolver is usable])
-      AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <sys/types.h>
+    AC_MSG_CHECKING([whether the resolver is usable])
+    AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <sys/types.h>
 #include <netinet/in.h>
 #include <arpa/nameser.h>
 #include <resolv.h>]],
@@ -1094,15 +1076,15 @@ if test x"$use_dns_srv" = xyes || test x"$use_dns_cert" = xyes; then
   dn_skipname(0,0);
   dn_expand(0,0,0,0,0);
 ]])],have_resolver=yes,have_resolver=no)
-      AC_MSG_RESULT($have_resolver)
+    AC_MSG_RESULT($have_resolver)
 
-      # This is Apple-specific and somewhat bizarre as they changed the
-      # define in bind 8 for some reason.
+    # This is Apple-specific and somewhat bizarre as they changed the
+    # define in bind 8 for some reason.
 
-      if test x"$have_resolver" != xyes ; then
-         AC_MSG_CHECKING(
-             [whether I can make the resolver usable with BIND_8_COMPAT])
-        AC_LINK_IFELSE([AC_LANG_PROGRAM([[#define BIND_8_COMPAT
+    if test x"$have_resolver" != xyes ; then
+      AC_MSG_CHECKING(
+           [whether I can make the resolver usable with BIND_8_COMPAT])
+      AC_LINK_IFELSE([AC_LANG_PROGRAM([[#define BIND_8_COMPAT
 #include <sys/types.h>
 #include <netinet/in.h>
 #include <arpa/nameser.h>
@@ -1111,28 +1093,23 @@ if test x"$use_dns_srv" = xyes || test x"$use_dns_cert" = xyes; then
   res_query("foo.bar",C_IN,T_A,answer,PACKETSZ);
   dn_skipname(0,0); dn_expand(0,0,0,0,0);
 ]])],[have_resolver=yes ; need_compat=yes])
-         AC_MSG_RESULT($have_resolver)
-      fi
+      AC_MSG_RESULT($have_resolver)
     fi
+  fi
 
-    if test x"$have_resolver" = xyes ; then
-      DNSLIBS=$LIBS
-
-      if test x"$use_dns_srv" = xyes ; then
-         AC_DEFINE(USE_DNS_SRV,1,[define to use DNS SRV])
-      fi
-
-      if test x"$use_dns_cert" = xyes ; then
-        AC_DEFINE(USE_DNS_CERT,1,[define to use DNS CERT])
-      fi
-
-      if test x"$need_compat" = xyes ; then
-        AC_DEFINE(BIND_8_COMPAT,1,[an Apple OSXism])
-      fi
-    else
-      use_dns_srv=no
-      use_dns_cert=no
+  if test x"$have_resolver" = xyes ; then
+    AC_DEFINE(HAVE_SYSTEM_RESOLVER,1,[The system's resolver is usable.])
+    DNSLIBS="$DNSLIBS $LIBS"
+    if test x"$need_compat" = xyes ; then
+      AC_DEFINE(BIND_8_COMPAT,1,[an Apple OSXism])
     fi
+  else
+    AC_MSG_WARN([[
+***
+*** The system's DNS resolver is not usable.
+*** Dirmngr functionality is limited.
+***]])
+   show_tor_support="${show_tor_support} (no system resolver)"
   fi
 
   LIBS=$_dns_save_libs
@@ -1140,8 +1117,6 @@ fi
 
 AC_SUBST(DNSLIBS)
 
-AM_CONDITIONAL(USE_DNS_SRV, test x"$use_dns_srv" = xyes)
-
 
 #
 # Check for LDAP
@@ -1963,7 +1938,6 @@ echo "
         Dirmngr auto start:  $dirmngr_auto_start
         Readline support:    $gnupg_cv_have_readline
         LDAP support:        $gnupg_have_ldap
-        DNS SRV support:     $use_dns_srv
         TLS support:         $use_tls_library
         TOFU support:        $use_tofu
         Tor support:         $show_tor_support
index 191f772..c26a468 100644 (file)
@@ -140,6 +140,7 @@ enum cmd_and_opt_values {
   oKeyServer,
   oNameServer,
   oDisableCheckOwnSocket,
+  oStandardResolver,
   aTest
 };
 
@@ -236,6 +237,7 @@ static ARGPARSE_OPTS opts[] = {
   ARGPARSE_s_s (oHTTPWrapperProgram, "http-wrapper-program", "@"),
   ARGPARSE_s_n (oHonorHTTPProxy, "honor-http-proxy", "@"),
   ARGPARSE_s_s (oIgnoreCertExtension,"ignore-cert-extension", "@"),
+  ARGPARSE_s_n (oStandardResolver, "standard-resolver", "@"),
 
   ARGPARSE_group (302,N_("@\n(See the \"info\" manual for a complete listing "
                          "of all commands and options)\n")),
@@ -543,6 +545,7 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
       FREE_STRLIST (opt.keyserver);
       /* Note: We do not allow resetting of opt.use_tor at runtime.  */
       disable_check_own_socket = 0;
+      enable_standard_resolver (0);
       return 1;
     }
 
@@ -617,6 +620,8 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
 
     case oUseTor: opt.use_tor = 1; break;
 
+    case oStandardResolver: enable_standard_resolver (1); break;
+
     case oKeyServer:
       if (*pargs->r.ret_str)
         add_to_strlist (&opt.keyserver, pargs->r.ret_str);
index 6849af4..7924fd3 100644 (file)
@@ -1,6 +1,6 @@
 /* dns-stuff.c - DNS related code including CERT RR (rfc-4398)
  * Copyright (C) 2003, 2005, 2006, 2009 Free Software Foundation, Inc.
- * Copyright (C) 2005, 2006, 2009, 2015 Werner Koch
+ * Copyright (C) 2005, 2006, 2009, 2015. 2016 Werner Koch
  *
  * This file is part of GnuPG.
  *
 # endif
 # include <windows.h>
 #else
-# include <netinet/in.h>
-# include <arpa/nameser.h>
-# include <resolv.h>
+# if HAVE_SYSTEM_RESOLVER
+#  include <netinet/in.h>
+#  include <arpa/nameser.h>
+#  include <resolv.h>
+# endif
 # include <netdb.h>
 #endif
 #include <string.h>
 /* The default nameserver used with ADNS in Tor mode.  */
 #define DEFAULT_NAMESERVER "8.8.8.8"
 
+/* If set force the use of the standard resolver.  */
+static int standard_resolver;
 
 /* If set Tor mode shall be used.  */
 static int tor_mode;
@@ -114,6 +118,25 @@ static char tor_nameserver[40+20];
 static char tor_credentials[50];
 #endif
 
+
+/* Calling this function with YES set to True forces the use of the
+ * standard resolver even if dirmngr has been built with support for
+ * an alternative resolver.  */
+void
+enable_standard_resolver (int yes)
+{
+  standard_resolver = yes;
+}
+
+
+/* Return true if the standard resolver is used.  */
+int
+standard_resolver_p (void)
+{
+  return standard_resolver;
+}
+
+
 /* Sets the module in Tor mode.  Returns 0 is this is possible or an
    error code.  */
 gpg_error_t
@@ -121,7 +144,7 @@ enable_dns_tormode (int new_circuit)
 {
   (void) new_circuit;
 
-#if defined(USE_DNS_CERT) && defined(USE_ADNS)
+#ifdef USE_ADNS
 # if HAVE_ADNS_IF_TORMODE
    if (!*tor_credentials || new_circuit)
      {
@@ -367,7 +390,6 @@ resolve_name_adns (const char *name, unsigned short port,
 #endif /*USE_ADNS*/
 
 
-#ifndef USE_ADNS
 /* Resolve a name using the standard system function.  */
 static gpg_error_t
 resolve_name_standard (const char *name, unsigned short port,
@@ -472,7 +494,6 @@ resolve_name_standard (const char *name, unsigned short port,
     *r_dai = daihead;
   return err;
 }
-#endif /*!USE_ADNS*/
 
 
 /* Resolve an address using the standard system function.  */
@@ -552,12 +573,12 @@ resolve_dns_name (const char *name, unsigned short port,
                   dns_addrinfo_t *r_ai, char **r_canonname)
 {
 #ifdef USE_ADNS
-  return resolve_name_adns (name, port, want_family, want_socktype,
-                            r_ai, r_canonname);
-#else
+  if (!standard_resolver)
+    return resolve_name_adns (name, port, want_family, want_socktype,
+                              r_ai, r_canonname);
+#endif
   return resolve_name_standard (name, port, want_family, want_socktype,
                                 r_ai, r_canonname);
-#endif
 }
 
 
@@ -565,11 +586,7 @@ 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
 }
 
 
@@ -654,23 +671,13 @@ is_onion_address (const char *name)
 }
 
 
-/* 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
-   record was found the fingerprint is stored as an allocated block at
-   R_FPR and its length at R_FPRLEN; an URL is is allocated as a
-   string and returned at R_URL.  If WANT_CERTTYPE is 0 this function
-   returns the first CERT found with a supported type; it is expected
-   that only one CERT record is used.  If WANT_CERTTYPE is one of the
-   supported certtypes only records with this certtype are considered
-   and the first found is returned.  (R_KEY,R_KEYLEN) are optional. */
-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)
-{
-#ifdef USE_DNS_CERT
 #ifdef USE_ADNS
+/* ADNS version of get_dns_cert.  */
+static gpg_error_t
+get_dns_cert_adns (const char *name, int want_certtype,
+                   void **r_key, size_t *r_keylen,
+                   unsigned char **r_fpr, size_t *r_fprlen, char **r_url)
+{
   gpg_error_t err;
   int ret;
   adns_state state;
@@ -678,14 +685,6 @@ get_dns_cert (const char *name, int want_certtype,
   unsigned int ctype;
   int count;
 
-  if (r_key)
-    *r_key = NULL;
-  if (r_keylen)
-    *r_keylen = 0;
-  *r_fpr = NULL;
-  *r_fprlen = 0;
-  *r_url = NULL;
-
   err = my_adns_init (&state);
   if (err)
     return err;
@@ -812,22 +811,22 @@ get_dns_cert (const char *name, int want_certtype,
   adns_free (answer);
   adns_finish (state);
   return err;
+}
+#endif /*!USE_ADNS */
 
-#else /*!USE_ADNS*/
 
+/* Standard resolver version of get_dns_cert.  */
+static gpg_error_t
+get_dns_cert_standard (const char *name, int want_certtype,
+                       void **r_key, size_t *r_keylen,
+                       unsigned char **r_fpr, size_t *r_fprlen, char **r_url)
+{
+#ifdef HAVE_SYSTEM_RESOLVER
   gpg_error_t err;
   unsigned char *answer;
   int r;
   u16 count;
 
-  if (r_key)
-    *r_key = NULL;
-  if (r_keylen)
-    *r_keylen = 0;
-  *r_fpr = NULL;
-  *r_fprlen = 0;
-  *r_url = NULL;
-
   /* Allocate a 64k buffer which is the limit for an DNS response.  */
   answer = xtrymalloc (65536);
   if (!answer)
@@ -1004,9 +1003,36 @@ get_dns_cert (const char *name, int want_certtype,
   xfree (answer);
   return err;
 
-#endif /*!USE_ADNS */
-#else /* !USE_DNS_CERT */
+#else /*!HAVE_SYSTEM_RESOLVER*/
+
   (void)name;
+  (void)want_certtype;
+  (void)r_key;
+  (void)r_keylen;
+  (void)r_fpr;
+  (void)r_fprlen;
+  (void)r_url;
+  return gpg_error (GPG_ERR_NOT_SUPPORTED);
+
+#endif /*!HAVE_SYSTEM_RESOLVER*/
+}
+
+
+/* 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
+   record was found the fingerprint is stored as an allocated block at
+   R_FPR and its length at R_FPRLEN; an URL is is allocated as a
+   string and returned at R_URL.  If WANT_CERTTYPE is 0 this function
+   returns the first CERT found with a supported type; it is expected
+   that only one CERT record is used.  If WANT_CERTTYPE is one of the
+   supported certtypes only records with this certtype are considered
+   and the first found is returned.  (R_KEY,R_KEYLEN) are optional. */
+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)
+{
   if (r_key)
     *r_key = NULL;
   if (r_keylen)
@@ -1015,11 +1041,16 @@ get_dns_cert (const char *name, int want_certtype,
   *r_fprlen = 0;
   *r_url = NULL;
 
-  return gpg_error (GPG_ERR_NOT_SUPPORTED);
-#endif
+#ifdef USE_ADNS
+  if (!standard_resolver)
+    return get_dns_cert_adns (name, want_certtype, r_key, r_keylen,
+                              r_fpr, r_fprlen, r_url);
+#endif /*!USE_ADNS */
+  return get_dns_cert_standard (name, want_certtype, r_key, r_keylen,
+                                r_fpr, r_fprlen, r_url);
 }
 
-#ifdef USE_DNS_SRV
+
 static int
 priosort(const void *a,const void *b)
 {
@@ -1033,170 +1064,215 @@ priosort(const void *a,const void *b)
 }
 
 
-int
-getsrv (const char *name,struct srventry **list)
+#ifdef USE_ADNS
+/* ADNS based helper for getsrv.  */
+static int
+getsrv_adns (const char *name, struct srventry **list)
 {
-  int srvcount=0;
+  int srvcount = 0;
   u16 count;
-  int i, rc;
-
-  *list = NULL;
+  int rc;
+  adns_state state;
+  adns_answer *answer = NULL;
 
-#ifdef USE_ADNS
-  {
-    adns_state state;
-    adns_answer *answer = NULL;
+  if (my_adns_init (&state))
+    return -1;
 
-    if (my_adns_init (&state))
+  my_unprotect ();
+  rc = adns_synchronous (state, name, adns_r_srv, adns_qf_quoteok_query,
+                         &answer);
+  my_protect ();
+  if (rc)
+    {
+      log_error ("DNS query failed: %s\n", strerror (rc));
+      adns_finish (state);
       return -1;
-
-    my_unprotect ();
-    rc = adns_synchronous (state, name, adns_r_srv, adns_qf_quoteok_query,
-                           &answer);
-    my_protect ();
-    if (rc)
-      {
-        log_error ("DNS query failed: %s\n", strerror (rc));
-        adns_finish (state);
-        return -1;
-      }
-    if (answer->status != adns_s_ok
-        || answer->type != adns_r_srv || !answer->nrrs)
-      {
+    }
+  if (answer->status != adns_s_ok
+      || answer->type != adns_r_srv || !answer->nrrs)
+    {
         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 0;
-      }
+    }
 
-    for (count = 0; count < answer->nrrs; count++)
-      {
-        struct srventry *srv = NULL;
-        struct srventry *newlist;
-
-        if (strlen (answer->rrs.srvha[count].ha.host) >= sizeof srv->target)
-          {
-            log_info ("hostname in SRV record too long - skipped\n");
-            continue;
-          }
-
-        newlist = xtryrealloc (*list, (srvcount+1)*sizeof(struct srventry));
-        if (!newlist)
-          goto fail;
-        *list = newlist;
-        memset (&(*list)[srvcount], 0, sizeof(struct srventry));
-        srv = &(*list)[srvcount];
-        srvcount++;
-
-        srv->priority = answer->rrs.srvha[count].priority;
-        srv->weight   = answer->rrs.srvha[count].weight;
-        srv->port     = answer->rrs.srvha[count].port;
-        strcpy (srv->target, answer->rrs.srvha[count].ha.host);
-      }
+  for (count = 0; count < answer->nrrs; count++)
+    {
+      struct srventry *srv = NULL;
+      struct srventry *newlist;
 
-    adns_free (answer);
-    adns_finish (state);
-  }
-#else /*!USE_ADNS*/
-  {
-    union {
-      unsigned char ans[2048];
-      HEADER header[1];
-    } res;
-    unsigned char *answer = res.ans;
-    HEADER *header = res.header;
-    unsigned char *pt, *emsg;
-    int r;
-    u16 dlen;
-
-    /* Do not allow a query using the standard resolver in Tor mode.  */
-    if (tor_mode)
-      return -1;
+      if (strlen (answer->rrs.srvha[count].ha.host) >= sizeof srv->target)
+        {
+          log_info ("hostname in SRV record too long - skipped\n");
+          continue;
+        }
 
-    my_unprotect ();
-    r = res_query (name, C_IN, T_SRV, answer, sizeof answer);
-    my_protect ();
-    if (r < sizeof (HEADER) || r > sizeof answer
-        || header->rcode != NOERROR || !(count=ntohs (header->ancount)))
-      return 0; /* Error or no record found.  */
+      newlist = xtryrealloc (*list, (srvcount+1)*sizeof(struct srventry));
+      if (!newlist)
+        {
+          xfree (*list);
+          *list = NULL;
+          return -1;
+        }
+      *list = newlist;
+      memset (&(*list)[srvcount], 0, sizeof(struct srventry));
+      srv = &(*list)[srvcount];
+      srvcount++;
+
+      srv->priority = answer->rrs.srvha[count].priority;
+      srv->weight   = answer->rrs.srvha[count].weight;
+      srv->port     = answer->rrs.srvha[count].port;
+      strcpy (srv->target, answer->rrs.srvha[count].ha.host);
+    }
 
-    emsg = &answer[r];
-    pt = &answer[sizeof(HEADER)];
+  adns_free (answer);
+  adns_finish (state);
 
-    /* Skip over the query */
-    rc = dn_skipname (pt, emsg);
-    if (rc == -1)
-      goto fail;
+  return srvcount;
+}
+#endif /*USE_ADNS*/
 
-    pt += rc + QFIXEDSZ;
 
-    while (count-- > 0 && pt < emsg)
-      {
-        struct srventry *srv=NULL;
-        u16 type,class;
-        struct srventry *newlist;
-
-        newlist = xtryrealloc (*list, (srvcount+1)*sizeof(struct srventry));
-        if (!newlist)
-          goto fail;
-        *list = newlist;
-        memset(&(*list)[srvcount],0,sizeof(struct srventry));
-        srv=&(*list)[srvcount];
-        srvcount++;
-
-        rc = dn_skipname(pt,emsg); /* the name we just queried for */
-        if (rc == -1)
-          goto fail;
-        pt+=rc;
-
-        /* Truncated message? */
-        if((emsg-pt)<16)
-          goto fail;
-
-        type = buf16_to_u16 (pt);
-        pt += 2;
-        /* We asked for SRV and got something else !? */
-        if(type!=T_SRV)
-          goto fail;
-
-        class = buf16_to_u16 (pt);
-        pt += 2;
-        /* We asked for IN and got something else !? */
-        if(class!=C_IN)
-          goto fail;
-
-        pt += 4; /* ttl */
-        dlen = buf16_to_u16 (pt);
-        pt += 2;
-
-        srv->priority = buf16_to_ushort (pt);
-        pt += 2;
-        srv->weight = buf16_to_ushort (pt);
-        pt += 2;
-        srv->port = buf16_to_ushort (pt);
-        pt += 2;
-
-        /* Get the name.  2782 doesn't allow name compression, but
-           dn_expand still works to pull the name out of the
-           packet. */
-        rc = dn_expand(answer,emsg,pt,srv->target, sizeof srv->target);
-        if (rc == 1 && srv->target[0] == 0) /* "." */
-          {
-            xfree(*list);
-            *list = NULL;
-            return 0;
-          }
-        if (rc == -1)
-          goto fail;
-        pt += rc;
-        /* Corrupt packet? */
-        if (dlen != rc+6)
-          goto fail;
-      }
-  }
+/* Standard resolver based helper for getsrv.  */
+static int
+getsrv_standard (const char *name, struct srventry **list)
+{
+#ifdef HAVE_SYSTEM_RESOLVER
+  union {
+    unsigned char ans[2048];
+    HEADER header[1];
+  } res;
+  unsigned char *answer = res.ans;
+  HEADER *header = res.header;
+  unsigned char *pt, *emsg;
+  int r, rc;
+  u16 dlen;
+  int srvcount=0;
+  u16 count;
+
+  /* Do not allow a query using the standard resolver in Tor mode.  */
+  if (tor_mode)
+    return -1;
+
+  my_unprotect ();
+  r = res_query (name, C_IN, T_SRV, answer, sizeof answer);
+  my_protect ();
+  if (r < sizeof (HEADER) || r > sizeof answer
+      || header->rcode != NOERROR || !(count=ntohs (header->ancount)))
+    return 0; /* Error or no record found.  */
+
+  emsg = &answer[r];
+  pt = &answer[sizeof(HEADER)];
+
+  /* Skip over the query */
+  rc = dn_skipname (pt, emsg);
+  if (rc == -1)
+    goto fail;
+
+  pt += rc + QFIXEDSZ;
+
+  while (count-- > 0 && pt < emsg)
+    {
+      struct srventry *srv = NULL;
+      u16 type, class;
+      struct srventry *newlist;
+
+      newlist = xtryrealloc (*list, (srvcount+1)*sizeof(struct srventry));
+      if (!newlist)
+        goto fail;
+      *list = newlist;
+      memset (&(*list)[srvcount], 0, sizeof(struct srventry));
+      srv = &(*list)[srvcount];
+      srvcount++;
+
+      rc = dn_skipname (pt, emsg); /* The name we just queried for.  */
+      if (rc == -1)
+        goto fail;
+      pt += rc;
+
+      /* Truncated message? */
+      if ((emsg-pt) < 16)
+        goto fail;
+
+      type = buf16_to_u16 (pt);
+      pt += 2;
+      /* We asked for SRV and got something else !? */
+      if (type != T_SRV)
+        goto fail;
+
+      class = buf16_to_u16 (pt);
+      pt += 2;
+      /* We asked for IN and got something else !? */
+      if (class != C_IN)
+        goto fail;
+
+      pt += 4; /* ttl */
+      dlen = buf16_to_u16 (pt);
+      pt += 2;
+
+      srv->priority = buf16_to_ushort (pt);
+      pt += 2;
+      srv->weight = buf16_to_ushort (pt);
+      pt += 2;
+      srv->port = buf16_to_ushort (pt);
+      pt += 2;
+
+      /* Get the name.  2782 doesn't allow name compression, but
+       * dn_expand still works to pull the name out of the packet. */
+      rc = dn_expand (answer, emsg, pt, srv->target, sizeof srv->target);
+      if (rc == 1 && srv->target[0] == 0) /* "." */
+        {
+          xfree(*list);
+          *list = NULL;
+          return 0;
+        }
+      if (rc == -1)
+        goto fail;
+      pt += rc;
+      /* Corrupt packet? */
+      if (dlen != rc+6)
+        goto fail;
+    }
+
+  return srvcount;
+
+ fail:
+  xfree (*list);
+  *list = NULL;
+  return -1;
+
+#else /*!HAVE_SYSTEM_RESOLVER*/
+
+  (void)name;
+  (void)list;
+  return -1;
+
+#endif /*!HAVE_SYSTEM_RESOLVER*/
+}
+
+
+int
+getsrv (const char *name, struct srventry **list)
+{
+  int srvcount;
+  int i;
+
+  *list = NULL;
+
+  if (0)
+    ;
+#ifdef USE_ADNS
+  else if (!standard_resolver)
+    srvcount = getsrv_adns (name, list);
 #endif /*!USE_ADNS*/
+  else
+    srvcount = getsrv_standard (name, list);
+
+  if (srvcount <= 0)
+    return srvcount;
 
   /* Now we have an array of all the srv records. */
 
@@ -1272,125 +1348,144 @@ getsrv (const char *name,struct srventry **list)
     }
 
   return srvcount;
-
- fail:
-  xfree(*list);
-  *list=NULL;
-  return -1;
 }
-#endif /*USE_DNS_SRV*/
 
 
+#ifdef USE_ADNS
+/* ADNS version of get_dns_cname.  */
 gpg_error_t
-get_dns_cname (const char *name, char **r_cname)
+get_dns_cname_adns (const char *name, char **r_cname)
 {
   gpg_error_t err;
   int rc;
+  adns_state state;
+  adns_answer *answer = NULL;
 
-  *r_cname = NULL;
+  if (my_adns_init (&state))
+    return gpg_error (GPG_ERR_GENERAL);
 
-#ifdef USE_ADNS
-  {
-    adns_state state;
-    adns_answer *answer = NULL;
+  my_unprotect ();
+  rc = adns_synchronous (state, name, adns_r_cname, adns_qf_quoteok_query,
+                         &answer);
+  my_protect ();
+  if (rc)
+    {
+      err = gpg_error (gpg_err_code_from_errno (rc));
+      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 = map_adns_status_to_gpg_error (answer->status);
+      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;
 
-    if (my_adns_init (&state))
-      return gpg_error (GPG_ERR_GENERAL);
+  adns_free (answer);
+  adns_finish (state);
+  return err;
+}
+#endif /*USE_ADNS*/
 
-    my_unprotect ();
-    rc = adns_synchronous (state, name, adns_r_cname, adns_qf_quoteok_query,
-                           &answer);
-    my_protect ();
-    if (rc)
-      {
-        err = gpg_error (gpg_err_code_from_errno (rc));
-        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 = map_adns_status_to_gpg_error (answer->status);
-        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*/
-  {
-    union {
-      unsigned char ans[2048];
-      HEADER header[1];
-    } res;
-    unsigned char *answer = res.ans;
-    HEADER *header = res.header;
-    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;
+/* Standard resolver version of get_dns_cname.  */
+gpg_error_t
+get_dns_cname_standard (const char *name, char **r_cname)
+{
+#ifdef HAVE_SYSTEM_RESOLVER
+  gpg_error_t err;
+  int rc;
+  union {
+    unsigned char ans[2048];
+    HEADER header[1];
+  } res;
+  unsigned char *answer = res.ans;
+  HEADER *header = res.header;
+  unsigned char *pt, *emsg;
+  int r;
+  char *cname;
+  int cnamesize = 1025;
+  u16 count;
 
-    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);
+  /* Do not allow a query using the standard resolver in Tor mode.  */
+  if (tor_mode)
+    return -1;
 
-    emsg = &answer[r];
-    pt = &answer[sizeof(HEADER)];
-    rc = dn_skipname (pt, emsg);
-    if (rc == -1)
-      return gpg_error (GPG_ERR_SERVER_FAILED);
+  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 ();
 
-    pt += rc + QFIXEDSZ;
-    if (pt >= emsg)
+  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;
 
-    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 */
+#else /*!HAVE_SYSTEM_RESOLVER*/
 
-    cname = xtrymalloc (cnamesize);
-    if (!cname)
-      return gpg_error_from_syserror ();
+  (void)name;
+  (void)r_cname;
+  return -1;
 
-    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 /*!HAVE_SYSTEM_RESOLVER*/
+}
+
+
+gpg_error_t
+get_dns_cname (const char *name, char **r_cname)
+{
+  *r_cname = NULL;
+
+#ifdef USE_ADNS
+  if (!standard_resolver)
+    return get_dns_cname_adns (name, r_cname);
 #endif /*!USE_ADNS*/
+
+  return get_dns_cname_standard (name, r_cname);
 }
index 10e6d8d..c3c0946 100644 (file)
@@ -92,6 +92,14 @@ struct srventry
 };
 
 
+/* Calling this function with YES set to True forces the use of the
+ * standard resolver even if dirmngr has been built with support for
+ * an alternative resolver.  */
+void enable_standard_resolver (int yes);
+
+/* Return true if the standard resolver is used.  */
+int standard_resolver_p (void);
+
 /* Calling this function switches the DNS code into Tor mode if
    possibe.  Return 0 on success.  */
 gpg_error_t enable_dns_tormode (int new_circuit);
index bc62c82..1078be9 100644 (file)
@@ -2323,7 +2323,6 @@ connect_server (const char *server, unsigned short port,
 #endif /*!HASSUAN_SOCK_TOR*/
     }
 
-#ifdef USE_DNS_SRV
   /* Do the SRV thing */
   if (srvtag)
     {
@@ -2347,10 +2346,6 @@ connect_server (const char *server, unsigned short port,
             }
        }
     }
-#else
-  (void)flags;
-  (void)srvtag;
-#endif /*USE_DNS_SRV*/
 
   if (!serverlist)
     {
index 3b5e75d..8f53432 100644 (file)
@@ -426,11 +426,9 @@ map_host (ctrl_t ctrl, const char *name, int force_reselect,
       int refidx;
       int is_pool = 0;
       char *cname;
-#ifdef USE_DNS_SRV
       char *srvrecord;
       struct srventry *srvs;
       int srvscount;
-#endif /* USE_DNS_SRV */
 
       reftblsize = 100;
       reftbl = xtrymalloc (reftblsize * sizeof *reftbl);
@@ -447,7 +445,6 @@ map_host (ctrl_t ctrl, const char *name, int force_reselect,
         }
       hi = hosttable[idx];
 
-#ifdef USE_DNS_SRV
       if (!is_ip_address (name))
         {
           /* Check for SRV records.  */
@@ -488,7 +485,6 @@ map_host (ctrl_t ctrl, const char *name, int force_reselect,
               xfree (srvs);
             }
         }
-#endif /* USE_DNS_SRV */
 
       /* Find all A records for this entry and put them into the pool
          list - if any.  */
index 0088498..f1d762d 100644 (file)
@@ -2309,13 +2309,19 @@ cmd_getinfo (assuan_context_t ctx, char *line)
     }
   else if (!strcmp (line, "dnsinfo"))
     {
+      if (standard_resolver_p ())
+        assuan_set_okay_line
+          (ctx, "- Forced use of System resolver (w/o Tor support)");
+      else
+        {
 #if USE_ADNS && HAVE_ADNS_IF_TORMODE
-      assuan_set_okay_line (ctx, "- ADNS with Tor support");
+          assuan_set_okay_line (ctx, "- ADNS with Tor support");
 #elif USE_ADNS
-      assuan_set_okay_line (ctx, "- ADNS w/o Tor support");
+          assuan_set_okay_line (ctx, "- ADNS w/o Tor support");
 #else
-      assuan_set_okay_line (ctx, "- System resolver w/o Tor support");
+          assuan_set_okay_line (ctx, "- System resolver (w/o Tor support)");
 #endif
+        }
       err = 0;
     }
   else
index 5e8bf22..8d2cba6 100644 (file)
@@ -64,14 +64,15 @@ main (int argc, char **argv)
         {
           fputs ("usage: " PGM " [HOST]\n"
                  "Options:\n"
-                 "  --verbose         print timings etc.\n"
-                 "  --debug           flyswatter\n"
-                 "  --use-tor         use Tor\n"
-                 "  --new-circuit     use a new Tor circuit\n"
-                 "  --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"
+                 "  --verbose           print timings etc.\n"
+                 "  --debug             flyswatter\n"
+                 "  --standard-resolver use the system's resolver\n"
+                 "  --use-tor           use Tor\n"
+                 "  --new-circuit       use a new Tor circuit\n"
+                 "  --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);
         }
@@ -96,6 +97,11 @@ main (int argc, char **argv)
           opt_new_circuit = 1;
           argc--; argv++;
         }
+      else if (!strcmp (*argv, "--standard-resolver"))
+        {
+          enable_standard_resolver (1);
+          argc--; argv++;
+        }
       else if (!strcmp (*argv, "--bracket"))
         {
           opt_bracket = 1;
index 963dff8..62a41b6 100644 (file)
@@ -244,6 +244,13 @@ this still leaks the DNS queries; e.g. to lookup the hosts in a
 keyserver pool.  Certain other features are disabled if this mode is
 active.
 
+@item --standard-resolver
+@opindex standard-resolver
+This option forces the use of the system's standard DNS resolver code.
+This is mainly used for debugging.  Note that on Windows a standard
+resolver is not used and all DNS access will return the error ``Not
+Implemented'' if this function is used.
+
 @item --allow-version-check
 @opindex allow-version-check
 Allow Dirmngr to connect to @code{https://versions.gnupg.org} to get