mirror of
https://github.com/morgan9e/systemd
synced 2026-04-14 00:14:32 +09:00
Merge pull request #19079 from poettering/resolved-ipv6-cache-fix
fix CNAME/DNAME following in combined A/AAAA replies
This commit is contained in:
@@ -433,6 +433,14 @@ int dns_query_new(
|
||||
} else {
|
||||
bool good = false;
|
||||
|
||||
/* This (primarily) checks two things:
|
||||
*
|
||||
* 1. That the question is not empty
|
||||
* 2. That all RR keys in the question objects are for the same domain
|
||||
*
|
||||
* Or in other words, a single DnsQuery object may be used to look up A+AAAA combination for
|
||||
* the same domain name, or SRV+TXT (for DNS-SD services), but not for unrelated lookups. */
|
||||
|
||||
if (dns_question_size(question_utf8) > 0) {
|
||||
r = dns_question_is_valid_for_query(question_utf8);
|
||||
if (r < 0)
|
||||
@@ -982,12 +990,12 @@ static int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname)
|
||||
r = dns_question_cname_redirect(q->question_idna, cname, &nq_idna);
|
||||
if (r < 0)
|
||||
return r;
|
||||
else if (r > 0)
|
||||
if (r > 0)
|
||||
log_debug("Following CNAME/DNAME %s → %s.", dns_question_first_name(q->question_idna), dns_question_first_name(nq_idna));
|
||||
|
||||
k = dns_question_is_equal(q->question_idna, q->question_utf8);
|
||||
if (k < 0)
|
||||
return r;
|
||||
return k;
|
||||
if (k > 0) {
|
||||
/* Same question? Shortcut new question generation */
|
||||
nq_utf8 = dns_question_ref(nq_idna);
|
||||
@@ -996,7 +1004,7 @@ static int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname)
|
||||
k = dns_question_cname_redirect(q->question_utf8, cname, &nq_utf8);
|
||||
if (k < 0)
|
||||
return k;
|
||||
else if (k > 0)
|
||||
if (k > 0)
|
||||
log_debug("Following UTF8 CNAME/DNAME %s → %s.", dns_question_first_name(q->question_utf8), dns_question_first_name(nq_utf8));
|
||||
}
|
||||
|
||||
@@ -1032,6 +1040,8 @@ int dns_query_process_cname(DnsQuery *q) {
|
||||
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *cname = NULL;
|
||||
DnsQuestion *question;
|
||||
DnsResourceRecord *rr;
|
||||
bool full_match = true;
|
||||
DnsResourceKey *k;
|
||||
int r;
|
||||
|
||||
assert(q);
|
||||
@@ -1041,13 +1051,44 @@ int dns_query_process_cname(DnsQuery *q) {
|
||||
|
||||
question = dns_query_question_for_protocol(q, q->answer_protocol);
|
||||
|
||||
DNS_ANSWER_FOREACH(rr, q->answer) {
|
||||
r = dns_question_matches_rr(question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
return DNS_QUERY_MATCH; /* The answer matches directly, no need to follow cnames */
|
||||
/* Small reminder: our question will consist of one or more RR keys that match in name, but not in
|
||||
* record type. Specifically, when we do an address lookup the question will typically consist of one
|
||||
* A and one AAAA key lookup for the same domain name. When we get a response from a server we need
|
||||
* to check if the answer answers all our questions to use it. Note that a response of CNAME/DNAME
|
||||
* can answer both an A and the AAAA question for us, but an A/AAAA response only the relevant
|
||||
* type.
|
||||
*
|
||||
* Hence we first check of the answers we collected are sufficient to answer all our questions
|
||||
* directly. If one question wasn't answered we go on, waiting for more replies. However, if there's
|
||||
* a CNAME/DNAME response we use it, and redirect to it, regardless if it was a response to the A or
|
||||
* the AAAA query.*/
|
||||
|
||||
DNS_QUESTION_FOREACH(k, question) {
|
||||
bool match = false;
|
||||
|
||||
DNS_ANSWER_FOREACH(rr, q->answer) {
|
||||
r = dns_resource_key_match_rr(k, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0) {
|
||||
match = true; /* Yay, we found an RR that matches the key we are looking for */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!match) {
|
||||
/* Hmm. :-( there's no response for this key. This doesn't match. */
|
||||
full_match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (full_match)
|
||||
return DNS_QUERY_MATCH; /* The answer can answer our question in full, no need to follow CNAMEs/DNAMEs */
|
||||
|
||||
/* Let's see if there is a CNAME/DNAME to match. This case is simpler: we accept the CNAME/DNAME that
|
||||
* matches any of our questions. */
|
||||
DNS_ANSWER_FOREACH(rr, q->answer) {
|
||||
r = dns_question_matches_cname_or_dname(question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
|
||||
if (r < 0)
|
||||
return r;
|
||||
@@ -1056,7 +1097,7 @@ int dns_query_process_cname(DnsQuery *q) {
|
||||
}
|
||||
|
||||
if (!cname)
|
||||
return DNS_QUERY_NOMATCH; /* No match and no cname to follow */
|
||||
return DNS_QUERY_NOMATCH; /* No match and no CNAME/DNAME to follow */
|
||||
|
||||
if (q->flags & SD_RESOLVED_NO_CNAME)
|
||||
return -ELOOP;
|
||||
|
||||
@@ -45,7 +45,14 @@ struct DnsQuery {
|
||||
* that even on classic DNS some labels might use UTF8 encoding. Specifically, DNS-SD service names
|
||||
* (in contrast to their domain suffixes) use UTF-8 encoding even on DNS. Thus, the difference
|
||||
* between these two fields is mostly relevant only for explicit *hostname* lookups as well as the
|
||||
* domain suffixes of service lookups. */
|
||||
* domain suffixes of service lookups.
|
||||
*
|
||||
* Note that questions may consist of multiple RR keys at once, but they must be for the same domain
|
||||
* name. This is used for A+AAAA and TXT+SRV lookups: we'll allocate a single DnsQuery object for
|
||||
* them instead of two separate ones. That allows us minor optimizations with response handling:
|
||||
* CNAME/DNAMEs of the first reply we get can already be used to follow the CNAME/DNAME chain for
|
||||
* both, and we can take benefit of server replies that oftentimes put A responses into AAAA queries
|
||||
* and vice versa (in the additional section). */
|
||||
DnsQuestion *question_idna;
|
||||
DnsQuestion *question_utf8;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user