aboutsummaryrefslogtreecommitdiffstats
path: root/dirmngr/dns-stuff.c
diff options
context:
space:
mode:
Diffstat (limited to 'dirmngr/dns-stuff.c')
-rw-r--r--dirmngr/dns-stuff.c133
1 files changed, 132 insertions, 1 deletions
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*/
+}