Zbigniew Jędrzejewski-Szmek 6384ab
From 4cba52cc7a2191d0b38e605801c60d8648bc67e2 Mon Sep 17 00:00:00 2001
Zbigniew Jędrzejewski-Szmek 6384ab
From: Lennart Poettering <lennart@poettering.net>
Zbigniew Jędrzejewski-Szmek 6384ab
Date: Mon, 22 Mar 2021 18:27:36 +0100
Zbigniew Jędrzejewski-Szmek 6384ab
Subject: [PATCH 1/2] resolved: propagate correct error variable
Zbigniew Jędrzejewski-Szmek 6384ab
Zbigniew Jędrzejewski-Szmek 6384ab
---
Zbigniew Jędrzejewski-Szmek 6384ab
 src/resolve/resolved-dns-query.c | 6 +++---
Zbigniew Jędrzejewski-Szmek 6384ab
 1 file changed, 3 insertions(+), 3 deletions(-)
Zbigniew Jędrzejewski-Szmek 6384ab
Zbigniew Jędrzejewski-Szmek 6384ab
diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c
Zbigniew Jędrzejewski-Szmek 6384ab
index e4386c402ac..c5805111d21 100644
Zbigniew Jędrzejewski-Szmek 6384ab
--- a/src/resolve/resolved-dns-query.c
Zbigniew Jędrzejewski-Szmek 6384ab
+++ b/src/resolve/resolved-dns-query.c
Zbigniew Jędrzejewski-Szmek 6384ab
@@ -982,12 +982,12 @@ static int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname)
Zbigniew Jędrzejewski-Szmek 6384ab
         r = dns_question_cname_redirect(q->question_idna, cname, &nq_idna);
Zbigniew Jędrzejewski-Szmek 6384ab
         if (r < 0)
Zbigniew Jędrzejewski-Szmek 6384ab
                 return r;
Zbigniew Jędrzejewski-Szmek 6384ab
-        else if (r > 0)
Zbigniew Jędrzejewski-Szmek 6384ab
+        if (r > 0)
Zbigniew Jędrzejewski-Szmek 6384ab
                 log_debug("Following CNAME/DNAME %s → %s.", dns_question_first_name(q->question_idna), dns_question_first_name(nq_idna));
Zbigniew Jędrzejewski-Szmek 6384ab
 
Zbigniew Jędrzejewski-Szmek 6384ab
         k = dns_question_is_equal(q->question_idna, q->question_utf8);
Zbigniew Jędrzejewski-Szmek 6384ab
         if (k < 0)
Zbigniew Jędrzejewski-Szmek 6384ab
-                return r;
Zbigniew Jędrzejewski-Szmek 6384ab
+                return k;
Zbigniew Jędrzejewski-Szmek 6384ab
         if (k > 0) {
Zbigniew Jędrzejewski-Szmek 6384ab
                 /* Same question? Shortcut new question generation */
Zbigniew Jędrzejewski-Szmek 6384ab
                 nq_utf8 = dns_question_ref(nq_idna);
Zbigniew Jędrzejewski-Szmek 6384ab
@@ -996,7 +996,7 @@ static int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname)
Zbigniew Jędrzejewski-Szmek 6384ab
                 k = dns_question_cname_redirect(q->question_utf8, cname, &nq_utf8);
Zbigniew Jędrzejewski-Szmek 6384ab
                 if (k < 0)
Zbigniew Jędrzejewski-Szmek 6384ab
                         return k;
Zbigniew Jędrzejewski-Szmek 6384ab
-                else if (k > 0)
Zbigniew Jędrzejewski-Szmek 6384ab
+                if (k > 0)
Zbigniew Jędrzejewski-Szmek 6384ab
                         log_debug("Following UTF8 CNAME/DNAME %s → %s.", dns_question_first_name(q->question_utf8), dns_question_first_name(nq_utf8));
Zbigniew Jędrzejewski-Szmek 6384ab
         }
Zbigniew Jędrzejewski-Szmek 6384ab
 
Zbigniew Jędrzejewski-Szmek 6384ab
Zbigniew Jędrzejewski-Szmek 6384ab
From 1a71fe4ee5248140f2395a7daedfad8f8b9ad291 Mon Sep 17 00:00:00 2001
Zbigniew Jędrzejewski-Szmek 6384ab
From: Lennart Poettering <lennart@poettering.net>
Zbigniew Jędrzejewski-Szmek 6384ab
Date: Mon, 22 Mar 2021 18:27:46 +0100
Zbigniew Jędrzejewski-Szmek 6384ab
Subject: [PATCH 2/2] resolved: don't accept responses to query unless they
Zbigniew Jędrzejewski-Szmek 6384ab
 completely answer our questions
Zbigniew Jędrzejewski-Szmek 6384ab
Zbigniew Jędrzejewski-Szmek 6384ab
When we checking if the responses we collected for a DnsQuery are
Zbigniew Jędrzejewski-Szmek 6384ab
sufficient to complete it we previously only check if one of the
Zbigniew Jędrzejewski-Szmek 6384ab
collected response RRs matches at least one of the question RR keys.
Zbigniew Jędrzejewski-Szmek 6384ab
Zbigniew Jędrzejewski-Szmek 6384ab
This changes the logic to require that there must be at least one
Zbigniew Jędrzejewski-Szmek 6384ab
response RR matched *each* of the question RR keys before considering
Zbigniew Jędrzejewski-Szmek 6384ab
the answer complete.
Zbigniew Jędrzejewski-Szmek 6384ab
Zbigniew Jędrzejewski-Szmek 6384ab
Otherwise we might end up accepting an A reply as complete answer for an
Zbigniew Jędrzejewski-Szmek 6384ab
A/AAAA query and vice versa, but we want to make sure we wait until we
Zbigniew Jędrzejewski-Szmek 6384ab
get a reply on both types before returning this to the user in all
Zbigniew Jędrzejewski-Szmek 6384ab
cases.
Zbigniew Jędrzejewski-Szmek 6384ab
Zbigniew Jędrzejewski-Szmek 6384ab
This has been broken for basically forever, but didn't surface until
Zbigniew Jędrzejewski-Szmek 6384ab
b1eea703e01da1e280e179fb119449436a0c9b8e since until then we'd basically
Zbigniew Jędrzejewski-Szmek 6384ab
ignore the auxiliary RRs included in CNAME/DNAME replies. Once that
Zbigniew Jędrzejewski-Szmek 6384ab
commit was made we'd start using the auxiliary RRs included in
Zbigniew Jędrzejewski-Szmek 6384ab
CNAME/DNAME replies but those typically included only A or only AAAA
Zbigniew Jędrzejewski-Szmek 6384ab
which we then took for complete.
Zbigniew Jędrzejewski-Szmek 6384ab
Zbigniew Jędrzejewski-Szmek 6384ab
Fixe: #19049
Zbigniew Jędrzejewski-Szmek 6384ab
---
Zbigniew Jędrzejewski-Szmek 6384ab
 src/resolve/resolved-dns-query.c | 55 ++++++++++++++++++++++++++++----
Zbigniew Jędrzejewski-Szmek 6384ab
 src/resolve/resolved-dns-query.h |  9 +++++-
Zbigniew Jędrzejewski-Szmek 6384ab
 2 files changed, 56 insertions(+), 8 deletions(-)
Zbigniew Jędrzejewski-Szmek 6384ab
Zbigniew Jędrzejewski-Szmek 6384ab
diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c
Zbigniew Jędrzejewski-Szmek 6384ab
index c5805111d21..8bc06079830 100644
Zbigniew Jędrzejewski-Szmek 6384ab
--- a/src/resolve/resolved-dns-query.c
Zbigniew Jędrzejewski-Szmek 6384ab
+++ b/src/resolve/resolved-dns-query.c
Zbigniew Jędrzejewski-Szmek 6384ab
@@ -433,6 +433,14 @@ int dns_query_new(
Zbigniew Jędrzejewski-Szmek 6384ab
         } else {
Zbigniew Jędrzejewski-Szmek 6384ab
                 bool good = false;
Zbigniew Jędrzejewski-Szmek 6384ab
 
Zbigniew Jędrzejewski-Szmek 6384ab
+                /* This (primarily) checks two things:
Zbigniew Jędrzejewski-Szmek 6384ab
+                 *
Zbigniew Jędrzejewski-Szmek 6384ab
+                 * 1. That the question is not empty
Zbigniew Jędrzejewski-Szmek 6384ab
+                 * 2. That all RR keys in the question objects are for the same domain
Zbigniew Jędrzejewski-Szmek 6384ab
+                 *
Zbigniew Jędrzejewski-Szmek 6384ab
+                 * Or in other words, a single DnsQuery object may be used to look up A+AAAA combination for
Zbigniew Jędrzejewski-Szmek 6384ab
+                 * the same domain name, or SRV+TXT (for DNS-SD services), but not for unrelated lookups. */
Zbigniew Jędrzejewski-Szmek 6384ab
+
Zbigniew Jędrzejewski-Szmek 6384ab
                 if (dns_question_size(question_utf8) > 0) {
Zbigniew Jędrzejewski-Szmek 6384ab
                         r = dns_question_is_valid_for_query(question_utf8);
Zbigniew Jędrzejewski-Szmek 6384ab
                         if (r < 0)
Zbigniew Jędrzejewski-Szmek 6384ab
@@ -1032,6 +1040,8 @@ int dns_query_process_cname(DnsQuery *q) {
Zbigniew Jędrzejewski-Szmek 6384ab
         _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *cname = NULL;
Zbigniew Jędrzejewski-Szmek 6384ab
         DnsQuestion *question;
Zbigniew Jędrzejewski-Szmek 6384ab
         DnsResourceRecord *rr;
Zbigniew Jędrzejewski-Szmek 6384ab
+        bool full_match = true;
Zbigniew Jędrzejewski-Szmek 6384ab
+        DnsResourceKey *k;
Zbigniew Jędrzejewski-Szmek 6384ab
         int r;
Zbigniew Jędrzejewski-Szmek 6384ab
 
Zbigniew Jędrzejewski-Szmek 6384ab
         assert(q);
Zbigniew Jędrzejewski-Szmek 6384ab
@@ -1041,13 +1051,44 @@ int dns_query_process_cname(DnsQuery *q) {
Zbigniew Jędrzejewski-Szmek 6384ab
 
Zbigniew Jędrzejewski-Szmek 6384ab
         question = dns_query_question_for_protocol(q, q->answer_protocol);
Zbigniew Jędrzejewski-Szmek 6384ab
 
Zbigniew Jędrzejewski-Szmek 6384ab
-        DNS_ANSWER_FOREACH(rr, q->answer) {
Zbigniew Jędrzejewski-Szmek 6384ab
-                r = dns_question_matches_rr(question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
Zbigniew Jędrzejewski-Szmek 6384ab
-                if (r < 0)
Zbigniew Jędrzejewski-Szmek 6384ab
-                        return r;
Zbigniew Jędrzejewski-Szmek 6384ab
-                if (r > 0)
Zbigniew Jędrzejewski-Szmek 6384ab
-                        return DNS_QUERY_MATCH; /* The answer matches directly, no need to follow cnames */
Zbigniew Jędrzejewski-Szmek 6384ab
+        /* Small reminder: our question will consist of one or more RR keys that match in name, but not in
Zbigniew Jędrzejewski-Szmek 6384ab
+         * record type. Specifically, when we do an address lookup the question will typically consist of one
Zbigniew Jędrzejewski-Szmek 6384ab
+         * A and one AAAA key lookup for the same domain name. When we get a response from a server we need
Zbigniew Jędrzejewski-Szmek 6384ab
+         * to check if the answer answers all our questions to use it. Note that a response of CNAME/DNAME
Zbigniew Jędrzejewski-Szmek 6384ab
+         * can answer both an A and the AAAA question for us, but an A/AAAA response only the relevant
Zbigniew Jędrzejewski-Szmek 6384ab
+         * type.
Zbigniew Jędrzejewski-Szmek 6384ab
+         *
Zbigniew Jędrzejewski-Szmek 6384ab
+         * Hence we first check of the answers we collected are sufficient to answer all our questions
Zbigniew Jędrzejewski-Szmek 6384ab
+         * directly. If one question wasn't answered we go on, waiting for more replies. However, if there's
Zbigniew Jędrzejewski-Szmek 6384ab
+         * a CNAME/DNAME response we use it, and redirect to it, regardless if it was a response to the A or
Zbigniew Jędrzejewski-Szmek 6384ab
+         * the AAAA query.*/
Zbigniew Jędrzejewski-Szmek 6384ab
+
Zbigniew Jędrzejewski-Szmek 6384ab
+        DNS_QUESTION_FOREACH(k, question) {
Zbigniew Jędrzejewski-Szmek 6384ab
+                bool match = false;
Zbigniew Jędrzejewski-Szmek 6384ab
+
Zbigniew Jędrzejewski-Szmek 6384ab
+                DNS_ANSWER_FOREACH(rr, q->answer) {
Zbigniew Jędrzejewski-Szmek 6384ab
+                        r = dns_resource_key_match_rr(k, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
Zbigniew Jędrzejewski-Szmek 6384ab
+                        if (r < 0)
Zbigniew Jędrzejewski-Szmek 6384ab
+                                return r;
Zbigniew Jędrzejewski-Szmek 6384ab
+                        if (r > 0) {
Zbigniew Jędrzejewski-Szmek 6384ab
+                                match = true; /* Yay, we found an RR that matches the key we are looking for */
Zbigniew Jędrzejewski-Szmek 6384ab
+                                break;
Zbigniew Jędrzejewski-Szmek 6384ab
+                        }
Zbigniew Jędrzejewski-Szmek 6384ab
+                }
Zbigniew Jędrzejewski-Szmek 6384ab
+
Zbigniew Jędrzejewski-Szmek 6384ab
+                if (!match) {
Zbigniew Jędrzejewski-Szmek 6384ab
+                        /* Hmm. :-( there's no response for this key. This doesn't match. */
Zbigniew Jędrzejewski-Szmek 6384ab
+                        full_match = false;
Zbigniew Jędrzejewski-Szmek 6384ab
+                        break;
Zbigniew Jędrzejewski-Szmek 6384ab
+                }
Zbigniew Jędrzejewski-Szmek 6384ab
+        }
Zbigniew Jędrzejewski-Szmek 6384ab
 
Zbigniew Jędrzejewski-Szmek 6384ab
+        if (full_match)
Zbigniew Jędrzejewski-Szmek 6384ab
+                return DNS_QUERY_MATCH; /* The answer can answer our question in full, no need to follow CNAMEs/DNAMEs */
Zbigniew Jędrzejewski-Szmek 6384ab
+
Zbigniew Jędrzejewski-Szmek 6384ab
+        /* Let's see if there is a CNAME/DNAME to match. This case is simpler: we accept the CNAME/DNAME that
Zbigniew Jędrzejewski-Szmek 6384ab
+         * matches any of our questions. */
Zbigniew Jędrzejewski-Szmek 6384ab
+        DNS_ANSWER_FOREACH(rr, q->answer) {
Zbigniew Jędrzejewski-Szmek 6384ab
                 r = dns_question_matches_cname_or_dname(question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
Zbigniew Jędrzejewski-Szmek 6384ab
                 if (r < 0)
Zbigniew Jędrzejewski-Szmek 6384ab
                         return r;
Zbigniew Jędrzejewski-Szmek 6384ab
@@ -1056,7 +1097,7 @@ int dns_query_process_cname(DnsQuery *q) {
Zbigniew Jędrzejewski-Szmek 6384ab
         }
Zbigniew Jędrzejewski-Szmek 6384ab
 
Zbigniew Jędrzejewski-Szmek 6384ab
         if (!cname)
Zbigniew Jędrzejewski-Szmek 6384ab
-                return DNS_QUERY_NOMATCH; /* No match and no cname to follow */
Zbigniew Jędrzejewski-Szmek 6384ab
+                return DNS_QUERY_NOMATCH; /* No match and no CNAME/DNAME to follow */
Zbigniew Jędrzejewski-Szmek 6384ab
 
Zbigniew Jędrzejewski-Szmek 6384ab
         if (q->flags & SD_RESOLVED_NO_CNAME)
Zbigniew Jędrzejewski-Szmek 6384ab
                 return -ELOOP;
Zbigniew Jędrzejewski-Szmek 6384ab
diff --git a/src/resolve/resolved-dns-query.h b/src/resolve/resolved-dns-query.h
Zbigniew Jędrzejewski-Szmek 6384ab
index 5d12171b0a1..5d96cc06f84 100644
Zbigniew Jędrzejewski-Szmek 6384ab
--- a/src/resolve/resolved-dns-query.h
Zbigniew Jędrzejewski-Szmek 6384ab
+++ b/src/resolve/resolved-dns-query.h
Zbigniew Jędrzejewski-Szmek 6384ab
@@ -45,7 +45,14 @@ struct DnsQuery {
Zbigniew Jędrzejewski-Szmek 6384ab
          * that even on classic DNS some labels might use UTF8 encoding. Specifically, DNS-SD service names
Zbigniew Jędrzejewski-Szmek 6384ab
          * (in contrast to their domain suffixes) use UTF-8 encoding even on DNS. Thus, the difference
Zbigniew Jędrzejewski-Szmek 6384ab
          * between these two fields is mostly relevant only for explicit *hostname* lookups as well as the
Zbigniew Jędrzejewski-Szmek 6384ab
-         * domain suffixes of service lookups. */
Zbigniew Jędrzejewski-Szmek 6384ab
+         * domain suffixes of service lookups.
Zbigniew Jędrzejewski-Szmek 6384ab
+         *
Zbigniew Jędrzejewski-Szmek 6384ab
+         * Note that questions may consist of multiple RR keys at once, but they must be for the same domain
Zbigniew Jędrzejewski-Szmek 6384ab
+         * name. This is used for A+AAAA and TXT+SRV lookups: we'll allocate a single DnsQuery object for
Zbigniew Jędrzejewski-Szmek 6384ab
+         * them instead of two separate ones. That allows us minor optimizations with response handling:
Zbigniew Jędrzejewski-Szmek 6384ab
+         * CNAME/DNAMEs of the first reply we get can already be used to follow the CNAME/DNAME chain for
Zbigniew Jędrzejewski-Szmek 6384ab
+         * both, and we can take benefit of server replies that oftentimes put A responses into AAAA queries
Zbigniew Jędrzejewski-Szmek 6384ab
+         * and vice versa (in the additional section). */
Zbigniew Jędrzejewski-Szmek 6384ab
         DnsQuestion *question_idna;
Zbigniew Jędrzejewski-Szmek 6384ab
         DnsQuestion *question_utf8;
Zbigniew Jędrzejewski-Szmek 6384ab