[Pkg-gnupg-commit] [gnupg2] 52/185: dirmngr: Implement querying nameservers over IPv6.

Daniel Kahn Gillmor dkg at fifthhorseman.net
Mon Aug 7 11:55:19 UTC 2017


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

dkg pushed a commit to branch experimental
in repository gnupg2.

commit 15d2a009931f44a60b9df6325f837add208459d6
Author: Justus Winter <justus at g10code.com>
Date:   Tue Jun 13 11:33:06 2017 +0200

    dirmngr: Implement querying nameservers over IPv6.
    
    * dirmngr/dns.c (dns_so_check): Reinitialize sockets on address family
    mismatch.
    (enum dns_res_state): New states for querying over IPv6.
    (dns_res_exec): Implement the new states by copying and modifying the
    IPv4 variants.  Branch to their respective counterparts if the current
    list of resolvers using the current address family is exhausted.
    --
    
    This allows dirmngr to resolve names on systems where the nameservers
    are only reachable via IPv6.
    
    GnuPG-bug-id: 2990
    Signed-off-by: Justus Winter <justus at g10code.com>
---
 dirmngr/dns.c | 180 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 179 insertions(+), 1 deletion(-)

diff --git a/dirmngr/dns.c b/dirmngr/dns.c
index c660cf5..a89411b 100644
--- a/dirmngr/dns.c
+++ b/dirmngr/dns.c
@@ -7583,6 +7583,22 @@ int dns_so_check(struct dns_socket *so) {
 retry:
 	switch (so->state) {
 	case DNS_SO_UDP_INIT:
+		if (so->remote.ss_family != so->local.ss_family) {
+			/* Family mismatch.  Reinitialize.  */
+			if ((error = dns_so_closefd(so, &so->udp)))
+				goto error;
+			if ((error = dns_so_closefd(so, &so->tcp)))
+				goto error;
+
+			/* If the user supplied an interface
+			   statement, that is gone now.  Sorry.  */
+			memset(&so->local, 0, sizeof so->local);
+			so->local.ss_family = so->remote.ss_family;
+
+			if (-1 == (so->udp = dns_socket((struct sockaddr *)&so->local, SOCK_DGRAM, &error)))
+				goto error;
+		}
+
 		so->state++;
 	case DNS_SO_UDP_CONN:
 		error = dns_connect(so->udp, (struct sockaddr *)&so->remote, dns_sa_len(&so->remote));
@@ -7621,6 +7637,19 @@ retry:
 
 		so->state++;
 	case DNS_SO_TCP_INIT:
+		if (so->remote.ss_family != so->local.ss_family) {
+			/* Family mismatch.  Reinitialize.  */
+			if ((error = dns_so_closefd(so, &so->udp)))
+				goto error;
+			if ((error = dns_so_closefd(so, &so->tcp)))
+				goto error;
+
+			/* If the user supplied an interface
+			   statement, that is gone now.  Sorry.  */
+			memset(&so->local, 0, sizeof so->local);
+			so->local.ss_family = so->remote.ss_family;
+		}
+
 		if (dns_so_tcp_keep(so)) {
 			so->state = DNS_SO_TCP_SEND;
 
@@ -8072,6 +8101,8 @@ enum dns_res_state {
 	DNS_R_RESOLV1_NS,	/* Epilog: Inspect answer */
 	DNS_R_FOREACH_A,
 	DNS_R_QUERY_A,
+	DNS_R_FOREACH_AAAA,
+	DNS_R_QUERY_AAAA,
 	DNS_R_CNAME0_A,
 	DNS_R_CNAME1_A,
 
@@ -8731,8 +8762,22 @@ exec:
 		F->hints_j.section	= DNS_S_ALL & ~DNS_S_QD;
 
 		if (!dns_rr_grep(&rr, 1, &F->hints_j, F->hints, &error)) {
-			if (!dns_rr_i_count(&F->hints_j))
+			if (!dns_rr_i_count(&F->hints_j)) {
+				/* Check if we have in fact servers
+				   with an IPv6 address.  */
+				dns_rr_i_init(&F->hints_j, F->hints);
+				F->hints_j.name		= u.ns.host;
+				F->hints_j.type		= DNS_T_AAAA;
+				F->hints_j.section	= DNS_S_ALL & ~DNS_S_QD;
+				if (dns_rr_grep(&rr, 1, &F->hints_j, F->hints, &error)) {
+					/* We do.  Reinitialize
+					   iterator and handle it.  */
+					dns_rr_i_init(&F->hints_j, F->hints);
+					dgoto(R->sp, DNS_R_FOREACH_AAAA);
+				}
+
 				dgoto(R->sp, DNS_R_RESOLV0_NS);
+			}
 
 			dgoto(R->sp, DNS_R_FOREACH_NS);
 		}
@@ -8833,6 +8878,139 @@ exec:
 		/* XXX: Should we copy F->answer to R->nodata? */
 
 		dgoto(R->sp, DNS_R_FOREACH_A);
+	case DNS_R_FOREACH_AAAA: {
+		struct dns_aaaa aaaa;
+		struct sockaddr_in6 sin6;
+
+		/*
+		 * NOTE: Iterator initialized in DNS_R_FOREACH_NS because
+		 * this state is re-entrant, but we need to reset
+		 * .name to a valid pointer each time.
+		 */
+		if ((error = dns_ns_parse(&u.ns, &F->hints_ns, F->hints)))
+			goto error;
+
+		F->hints_j.name		= u.ns.host;
+		F->hints_j.type		= DNS_T_AAAA;
+		F->hints_j.section	= DNS_S_ALL & ~DNS_S_QD;
+
+		if (!dns_rr_grep(&rr, 1, &F->hints_j, F->hints, &error)) {
+			if (!dns_rr_i_count(&F->hints_j)) {
+				/* Check if we have in fact servers
+				   with an IPv4 address.  */
+				dns_rr_i_init(&F->hints_j, F->hints);
+				F->hints_j.name		= u.ns.host;
+				F->hints_j.type		= DNS_T_A;
+				F->hints_j.section	= DNS_S_ALL & ~DNS_S_QD;
+				if (dns_rr_grep(&rr, 1, &F->hints_j, F->hints, &error)) {
+					/* We do.  Reinitialize
+					   iterator and handle it.  */
+					dns_rr_i_init(&F->hints_j, F->hints);
+					dgoto(R->sp, DNS_R_FOREACH_A);
+				}
+
+				dgoto(R->sp, DNS_R_RESOLV0_NS);
+			}
+
+			dgoto(R->sp, DNS_R_FOREACH_NS);
+		}
+
+		if ((error = dns_aaaa_parse(&aaaa, &rr, F->hints)))
+			goto error;
+
+		memset(&sin6, '\0', sizeof sin6); /* NB: silence valgrind */
+		sin6.sin6_family	= AF_INET6;
+		sin6.sin6_addr	= aaaa.addr;
+		if (R->sp == 0)
+			sin6.sin6_port = dns_hints_port(R->hints, AF_INET, &sin6.sin6_addr);
+		else
+			sin6.sin6_port = htons(53);
+
+		if (DNS_DEBUG) {
+			char addr[INET6_ADDRSTRLEN + 1];
+			dns_aaaa_print(addr, sizeof addr, &aaaa);
+			dns_header(F->query)->qid = dns_so_mkqid(&R->so);
+			DNS_SHOW(F->query, "ASKING: %s/%s @ DEPTH: %u)", u.ns.host, addr, R->sp);
+		}
+
+		dns_trace_setcname(R->trace, u.ns.host, (struct sockaddr *)&sin6);
+
+		if ((error = dns_so_submit(&R->so, F->query, (struct sockaddr *)&sin6)))
+			goto error;
+
+		F->state++;
+	}
+	case DNS_R_QUERY_AAAA:
+		if (dns_so_elapsed(&R->so) >= dns_resconf_timeout(R->resconf))
+			dgoto(R->sp, DNS_R_FOREACH_AAAA);
+
+		if ((error = dns_so_check(&R->so)))
+			goto error;
+
+		if (!dns_p_setptr(&F->answer, dns_so_fetch(&R->so, &error)))
+			goto error;
+
+		if (DNS_DEBUG) {
+			DNS_SHOW(F->answer, "ANSWER @ DEPTH: %u)", R->sp);
+		}
+
+		if (dns_p_rcode(F->answer) == DNS_RC_FORMERR ||
+		    dns_p_rcode(F->answer) == DNS_RC_NOTIMP ||
+		    dns_p_rcode(F->answer) == DNS_RC_BADVERS) {
+			/* Temporarily disable EDNS0 and try again. */
+			if (F->qflags & DNS_Q_EDNS0) {
+				F->qflags &= ~DNS_Q_EDNS0;
+				if ((error = dns_q_remake(&F->query, F->qflags)))
+					goto error;
+
+				dgoto(R->sp, DNS_R_FOREACH_AAAA);
+			}
+		}
+
+		if ((error = dns_rr_parse(&rr, 12, F->query)))
+			goto error;
+
+		if (!(len = dns_d_expand(u.name, sizeof u.name, rr.dn.p, F->query, &error)))
+			goto error;
+		else if (len >= sizeof u.name)
+			goto toolong;
+
+		dns_rr_foreach(&rr, F->answer, .section = DNS_S_AN, .name = u.name, .type = rr.type) {
+			dgoto(R->sp, DNS_R_FINISH);	/* Found */
+		}
+
+		dns_rr_foreach(&rr, F->answer, .section = DNS_S_AN, .name = u.name, .type = DNS_T_CNAME) {
+			F->ans_cname	= rr;
+
+			dgoto(R->sp, DNS_R_CNAME0_A);
+		}
+
+		/*
+		 * XXX: The condition here should probably check whether
+		 * R->sp == 0, because DNS_R_SEARCH runs regardless of
+		 * options.recurse. See DNS_R_BIND.
+		 */
+		if (!R->resconf->options.recurse) {
+			/* Make first answer our tentative answer */
+			if (!R->nodata)
+				dns_p_movptr(&R->nodata, &F->answer);
+
+			dgoto(R->sp, DNS_R_SEARCH);
+		}
+
+		dns_rr_foreach(&rr, F->answer, .section = DNS_S_NS, .type = DNS_T_NS) {
+			dns_p_movptr(&F->hints, &F->answer);
+
+			dgoto(R->sp, DNS_R_ITERATE);
+		}
+
+		/* XXX: Should this go further up? */
+		if (dns_header(F->answer)->aa)
+			dgoto(R->sp, DNS_R_FINISH);
+
+		/* XXX: Should we copy F->answer to R->nodata? */
+
+		dgoto(R->sp, DNS_R_FOREACH_AAAA);
 	case DNS_R_CNAME0_A:
 		if (&F[1] >= endof(R->stack))
 			dgoto(R->sp, DNS_R_FINISH);

-- 
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