resolved: enforce RefuseRecordTypes= also in proxy mode (#37902)
Fixes: #36491
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user