diff --git a/configure.ac b/configure.ac index f196e1bbd..f3576c7e0 100644 --- a/configure.ac +++ b/configure.ac @@ -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.]) + # Find the system resolver which can always be enabled with + # the dirmngr option --standard-resolver. - if test x"$use_dns_srv" = xyes ; then - AC_DEFINE(USE_DNS_SRV,1) - fi + # 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)) - 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. + # macOS renames dn_skipname into res_9_dn_skipname in , + # 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 , 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))) - # 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 , - # 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 , 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 + AC_MSG_CHECKING([whether the resolver is usable]) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include #include #include #include ]], @@ -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 #include #include @@ -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 diff --git a/dirmngr/dirmngr.c b/dirmngr/dirmngr.c index 191f7724f..c26a46895 100644 --- a/dirmngr/dirmngr.c +++ b/dirmngr/dirmngr.c @@ -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); diff --git a/dirmngr/dns-stuff.c b/dirmngr/dns-stuff.c index 6849af4be..7924fd333 100644 --- a/dirmngr/dns-stuff.c +++ b/dirmngr/dns-stuff.c @@ -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. * @@ -36,9 +36,11 @@ # endif # include #else -# include -# include -# include +# if HAVE_SYSTEM_RESOLVER +# include +# include +# include +# endif # include #endif #include @@ -101,6 +103,8 @@ /* 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) -{ - int srvcount=0; - u16 count; - int i, rc; - - *list = NULL; - #ifdef USE_ADNS - { - adns_state state; - adns_answer *answer = NULL; +/* ADNS based helper for getsrv. */ +static int +getsrv_adns (const char *name, struct srventry **list) +{ + int srvcount = 0; + u16 count; + int rc; + adns_state state; + adns_answer *answer = NULL; - if (my_adns_init (&state)) + if (my_adns_init (&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; - - 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; + 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; - } + 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++; + 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); - } + 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); + } - 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; + adns_free (answer); + adns_finish (state); - /* Do not allow a query using the standard resolver in Tor mode. */ - if (tor_mode) - return -1; + return srvcount; +} +#endif /*USE_ADNS*/ - 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)]; +/* 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; - /* Skip over the query */ - rc = dn_skipname (pt, emsg); - if (rc == -1) - goto fail; + /* Do not allow a query using the standard resolver in Tor mode. */ + if (tor_mode) + return -1; - pt += rc + QFIXEDSZ; + 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. */ - while (count-- > 0 && pt < emsg) - { - struct srventry *srv=NULL; - u16 type,class; - struct srventry *newlist; + emsg = &answer[r]; + pt = &answer[sizeof(HEADER)]; - 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++; + /* Skip over the query */ + rc = dn_skipname (pt, emsg); + if (rc == -1) + goto fail; - rc = dn_skipname(pt,emsg); /* the name we just queried for */ - if (rc == -1) - goto fail; - pt+=rc; + pt += rc + QFIXEDSZ; - /* Truncated message? */ - if((emsg-pt)<16) - goto fail; + while (count-- > 0 && pt < emsg) + { + struct srventry *srv = NULL; + u16 type, class; + struct srventry *newlist; - type = buf16_to_u16 (pt); - pt += 2; - /* We asked for SRV and got something else !? */ - if(type!=T_SRV) - goto fail; + 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++; - class = buf16_to_u16 (pt); - pt += 2; - /* We asked for IN and got something else !? */ - if(class!=C_IN) - goto fail; + rc = dn_skipname (pt, emsg); /* The name we just queried for. */ + if (rc == -1) + goto fail; + pt += rc; - pt += 4; /* ttl */ - dlen = buf16_to_u16 (pt); - pt += 2; + /* Truncated message? */ + if ((emsg-pt) < 16) + goto fail; - srv->priority = buf16_to_ushort (pt); - pt += 2; - srv->weight = buf16_to_ushort (pt); - pt += 2; - srv->port = buf16_to_ushort (pt); - pt += 2; + type = buf16_to_u16 (pt); + pt += 2; + /* We asked for SRV and got something else !? */ + if (type != T_SRV) + goto fail; - /* 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; - } - } + 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_adns (const char *name, char **r_cname) +{ + gpg_error_t err; + int rc; + adns_state state; + adns_answer *answer = NULL; + + if (my_adns_init (&state)) + return gpg_error (GPG_ERR_GENERAL); + + 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; +} +#endif /*USE_ADNS*/ + + +/* 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; + + /* 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; + +#else /*!HAVE_SYSTEM_RESOLVER*/ + + (void)name; + (void)r_cname; + return -1; + +#endif /*!HAVE_SYSTEM_RESOLVER*/ +} 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); - - 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; - - 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; - } + if (!standard_resolver) + return get_dns_cname_adns (name, r_cname); #endif /*!USE_ADNS*/ + + return get_dns_cname_standard (name, r_cname); } diff --git a/dirmngr/dns-stuff.h b/dirmngr/dns-stuff.h index 10e6d8d91..c3c094611 100644 --- a/dirmngr/dns-stuff.h +++ b/dirmngr/dns-stuff.h @@ -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); diff --git a/dirmngr/http.c b/dirmngr/http.c index bc62c820b..1078be91c 100644 --- a/dirmngr/http.c +++ b/dirmngr/http.c @@ -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) { diff --git a/dirmngr/ks-engine-hkp.c b/dirmngr/ks-engine-hkp.c index 3b5e75d62..8f5343243 100644 --- a/dirmngr/ks-engine-hkp.c +++ b/dirmngr/ks-engine-hkp.c @@ -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. */ diff --git a/dirmngr/server.c b/dirmngr/server.c index 008849823..f1d762d4f 100644 --- a/dirmngr/server.c +++ b/dirmngr/server.c @@ -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 diff --git a/dirmngr/t-dns-stuff.c b/dirmngr/t-dns-stuff.c index 5e8bf228a..8d2cba6f3 100644 --- a/dirmngr/t-dns-stuff.c +++ b/dirmngr/t-dns-stuff.c @@ -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; diff --git a/doc/dirmngr.texi b/doc/dirmngr.texi index 963dff855..62a41b674 100644 --- a/doc/dirmngr.texi +++ b/doc/dirmngr.texi @@ -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