resolved: enforce RefuseRecordTypes= also in proxy mode (#37902)

Fixes: #36491
This commit is contained in:
Yu Watanabe
2025-06-20 05:59:24 +09:00
committed by GitHub
3 changed files with 64 additions and 12 deletions
+39 -12
View File
@@ -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;
+4
View File
@@ -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);
+21
View File
@@ -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"