diff --git a/dirmngr/Makefile.am b/dirmngr/Makefile.am index 0d4400fad..009802ad6 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 d784ccf97..f3b622de3 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 fd1c43acb..c3effad83 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 f216d06eb..4ecbd64f0 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 816b74490..35858f649 100644 --- a/dirmngr/t-http.c +++ b/dirmngr/t-http.c @@ -36,6 +36,7 @@ #include #include #include +#include #include "util.h" #include "logging.h"