diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c index 3fd87d4b90..dee771bb44 100644 --- a/src/resolve/resolved-dns-query.c +++ b/src/resolve/resolved-dns-query.c @@ -528,6 +528,32 @@ DnsQuery *dns_query_free(DnsQuery *q) { return mfree(q); } +typedef enum RefuseRecordTypeResult { + REFUSE_BAD, + REFUSE_GOOD, + REFUSE_PARTIAL, +} RefuseRecordTypeResult; + +static RefuseRecordTypeResult test_refuse_record_types(Set *refuse_record_types, DnsQuestion *question) { + bool has_good = false, has_bad = false; + DnsResourceKey *key; + + DNS_QUESTION_FOREACH(key, question) + if (set_contains(refuse_record_types, INT_TO_PTR(key->type))) + has_bad = true; + else + has_good = true; + + if (has_bad && !has_good) + return REFUSE_BAD; + if (!has_bad) { + assert(has_good); /* The question should have at least one key. */ + return REFUSE_GOOD; + } + + return REFUSE_PARTIAL; +} + static int manager_validate_and_mangle_question(Manager *manager, DnsQuestion **question, DnsQuestion **ret_allocated) { int r; @@ -535,7 +561,7 @@ static int manager_validate_and_mangle_question(Manager *manager, DnsQuestion ** assert(question); assert(ret_allocated); - if (!*question) { + if (dns_question_isempty(*question)) { *ret_allocated = NULL; return 0; } @@ -545,22 +571,16 @@ static int manager_validate_and_mangle_question(Manager *manager, DnsQuestion ** return 0; /* No filtering configured. Let's shortcut. */ } - bool has_good = false, has_bad = false; - DnsResourceKey *key; - DNS_QUESTION_FOREACH(key, *question) - if (set_contains(manager->refuse_record_types, INT_TO_PTR(key->type))) - has_bad = true; - else - has_good = true; - - if (has_bad && !has_good) + RefuseRecordTypeResult result = test_refuse_record_types(manager->refuse_record_types, *question); + if (result == REFUSE_BAD) return -ENOANO; /* All bad, refuse.*/ - if (!has_bad) { - assert(has_good); /* The question should have at least one key. */ + if (result == REFUSE_GOOD) { *ret_allocated = NULL; return 0; /* All good. Not necessary to filter. */ } + assert(result == REFUSE_PARTIAL); + /* Mangle the question suppressing bad entries, leaving good entries */ _cleanup_(dns_question_unrefp) DnsQuestion *new_question = dns_question_new(dns_question_size(*question)); if (!new_question) @@ -612,6 +632,13 @@ int dns_query_new( if (question_utf8 || question_idna) return -EINVAL; + assert(dns_question_size(question_bypass->question) == 1); + + /* In bypass mode we'll never mangle the question, but only deny or allow. (In bypass mode + * there's only going to be one entry in the query, hence there's no point in mangling + * questions, i.e. leaving some entries in and removing others.) */ + if (test_refuse_record_types(m->refuse_record_types, question_bypass->question) != REFUSE_GOOD) + return -ENOANO; } else { bool good = false; diff --git a/src/resolve/test-dns-query.c b/src/resolve/test-dns-query.c index 083cc6d700..3bcb6e0e78 100644 --- a/src/resolve/test-dns-query.c +++ b/src/resolve/test-dns-query.c @@ -128,6 +128,8 @@ TEST(dns_query_new_bypass_ok) { ASSERT_NOT_NULL(question); ASSERT_OK(dns_packet_append_question(packet, question)); + DNS_PACKET_HEADER(packet)->qdcount = htobe16(dns_question_size(question)); + ASSERT_OK(dns_packet_extract(packet)); ASSERT_OK(dns_query_new(&manager, &query, NULL, NULL, packet, 1, 0)); ASSERT_NOT_NULL(query); @@ -146,6 +148,8 @@ TEST(dns_query_new_bypass_conflict) { ASSERT_NOT_NULL(question); ASSERT_OK(dns_packet_append_question(packet, question)); + DNS_PACKET_HEADER(packet)->qdcount = htobe16(dns_question_size(question)); + ASSERT_OK(dns_packet_extract(packet)); ASSERT_OK(dns_question_new_address(&extra_q, AF_INET, "www.example.com", false)); ASSERT_NOT_NULL(extra_q); diff --git a/test/units/TEST-75-RESOLVED.sh b/test/units/TEST-75-RESOLVED.sh index 823e404e28..c4ed53ec6c 100755 --- a/test/units/TEST-75-RESOLVED.sh +++ b/test/units/TEST-75-RESOLVED.sh @@ -1143,15 +1143,27 @@ testcase_14_refuse_record_types() { run dig localhost -t AAAA grep -qF "status: REFUSED" "$RUN_OUT" + run dig localhost @127.0.0.54 -t AAAA + grep -qF "status: REFUSED" "$RUN_OUT" + run dig localhost -t SRV grep -qF "status: REFUSED" "$RUN_OUT" + run dig localhost @127.0.0.54 -t SRV + grep -qF "status: REFUSED" "$RUN_OUT" + run dig localhost -t TXT grep -qF "status: REFUSED" "$RUN_OUT" + run dig localhost @127.0.0.54 -t TXT + grep -qF "status: REFUSED" "$RUN_OUT" + run dig localhost -t A grep -qF "status: NOERROR" "$RUN_OUT" + run dig localhost @127.0.0.54 -t A + grep -qF "status: NOERROR" "$RUN_OUT" + run resolvectl query localhost5 grep -qF "127.128.0.5" "$RUN_OUT" @@ -1180,12 +1192,21 @@ testcase_14_refuse_record_types() { run dig localhost -t SRV grep -qF "status: NOERROR" "$RUN_OUT" + run dig localhost @127.0.0.54 -t SRV + grep -qF "status: NOERROR" "$RUN_OUT" + run dig localhost -t TXT grep -qF "status: NOERROR" "$RUN_OUT" + run dig localhost @127.0.0.54 -t TXT + grep -qF "status: NOERROR" "$RUN_OUT" + run dig localhost -t AAAA grep -qF "status: REFUSED" "$RUN_OUT" + run dig localhost @127.0.0.54 -t AAAA + grep -qF "status: REFUSED" "$RUN_OUT" + (! run resolvectl query localhost5 --type=SRV) grep -qF "does not have any RR of the requested type" "$RUN_OUT"