From 792a73862500883386b3f1ab3c470c671819fa37 Mon Sep 17 00:00:00 2001 From: James Coglan Date: Thu, 20 Jun 2024 10:13:21 +0100 Subject: [PATCH 01/12] resolved: tests for dns_query_new() --- src/resolve/meson.build | 11 +++ src/resolve/test-dns-query.c | 169 +++++++++++++++++++++++++++++++++++ 2 files changed, 180 insertions(+) create mode 100644 src/resolve/test-dns-query.c diff --git a/src/resolve/meson.build b/src/resolve/meson.build index b139afda6a..cf49532319 100644 --- a/src/resolve/meson.build +++ b/src/resolve/meson.build @@ -232,6 +232,17 @@ executables += [ ], 'include_directories' : resolve_includes, }, + test_template + { + 'sources' : [ + files('test-dns-query.c'), + basic_dns_sources, + systemd_resolved_sources, + ], + 'dependencies' : [ + systemd_resolved_dependencies, + ], + 'include_directories' : resolve_includes, + }, test_template + { 'sources' : [ files('test-resolved-stream.c'), diff --git a/src/resolve/test-dns-query.c b/src/resolve/test-dns-query.c new file mode 100644 index 0000000000..85a7892904 --- /dev/null +++ b/src/resolve/test-dns-query.c @@ -0,0 +1,169 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "log.h" +#include "resolved-dns-query.h" +#include "resolved-dns-rr.h" +#include "resolved-manager.h" +#include "tests.h" + +/* ================================================================ + * dns_query_new() + * ================================================================ */ + +TEST(dns_query_new_single_question) { + Manager manager = {}; + _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; + _cleanup_(dns_query_freep) DnsQuery *query = NULL; + + ASSERT_OK(dns_question_new_address(&question, AF_INET, "www.example.com", false)); + ASSERT_NOT_NULL(question); + + ASSERT_OK(dns_query_new(&manager, &query, question, NULL, NULL, 1, 0)); + ASSERT_NOT_NULL(query); +} + +TEST(dns_query_new_multi_question_same_domain) { + Manager manager = {}; + _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; + _cleanup_(dns_query_freep) DnsQuery *query = NULL; + DnsResourceKey *key = NULL; + + question = dns_question_new(2); + ASSERT_NOT_NULL(question); + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); + ASSERT_NOT_NULL(key); + ASSERT_OK(dns_question_add(question, key, 0)); + dns_resource_key_unref(key); + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, "www.example.com"); + ASSERT_NOT_NULL(key); + ASSERT_OK(dns_question_add(question, key, 0)); + dns_resource_key_unref(key); + + ASSERT_OK(dns_query_new(&manager, &query, question, NULL, NULL, 1, 0)); + ASSERT_NOT_NULL(query); +} + +TEST(dns_query_new_multi_question_different_domain) { + Manager manager = {}; + _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; + _cleanup_(dns_query_freep) DnsQuery *query = NULL; + DnsResourceKey *key = NULL; + + question = dns_question_new(2); + ASSERT_NOT_NULL(question); + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "ns1.example.com"); + ASSERT_NOT_NULL(key); + ASSERT_OK(dns_question_add(question, key, 0)); + dns_resource_key_unref(key); + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, "ns2.example.com"); + ASSERT_NOT_NULL(key); + ASSERT_OK(dns_question_add(question, key, 0)); + dns_resource_key_unref(key); + + ASSERT_ERROR(dns_query_new(&manager, &query, question, NULL, NULL, 1, 0), EINVAL); + ASSERT_NULL(query); +} + +#if HAVE_LIBIDN || HAVE_LIBIDN2 +TEST(dns_query_new_same_utf8_and_idna) { + Manager manager = {}; + _cleanup_(dns_question_unrefp) DnsQuestion *q_utf8 = NULL, *q_idna = NULL; + _cleanup_(dns_query_freep) DnsQuery *query = NULL; + + ASSERT_OK(dns_question_new_address(&q_utf8, AF_INET, "www.\xF0\x9F\x98\xB1.com", false)); + ASSERT_NOT_NULL(q_utf8); + + ASSERT_OK(dns_question_new_address(&q_idna, AF_INET, "www.\xF0\x9F\x98\xB1.com", true)); + ASSERT_NOT_NULL(q_idna); + + ASSERT_OK(dns_query_new(&manager, &query, q_utf8, q_idna, NULL, 1, 0)); + ASSERT_NOT_NULL(query); +} + +TEST(dns_query_new_different_utf8_and_idna) { + Manager manager = {}; + _cleanup_(dns_question_unrefp) DnsQuestion *q_utf8 = NULL, *q_idna = NULL; + _cleanup_(dns_query_freep) DnsQuery *query = NULL; + + ASSERT_OK(dns_question_new_address(&q_utf8, AF_INET, "www.\xF0\x9F\x98\xB1.com", false)); + ASSERT_NOT_NULL(q_utf8); + + ASSERT_OK(dns_question_new_address(&q_idna, AF_INET, "www.\xF0\x9F\x8E\xBC.com", true)); + ASSERT_NOT_NULL(q_idna); + + ASSERT_OK(dns_query_new(&manager, &query, q_utf8, q_idna, NULL, 1, 0)); + ASSERT_NOT_NULL(query); +} +#endif + +TEST(dns_query_new_bypass_ok) { + Manager manager = {}; + _cleanup_(dns_query_freep) DnsQuery *query = NULL; + _cleanup_(dns_packet_unrefp) DnsPacket *packet = NULL; + _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; + + ASSERT_OK(dns_packet_new_query(&packet, DNS_PROTOCOL_DNS, 0, false)); + ASSERT_NOT_NULL(packet); + + ASSERT_OK(dns_question_new_address(&question, AF_INET, "www.example.com", false)); + ASSERT_NOT_NULL(question); + + ASSERT_OK(dns_packet_append_question(packet, question)); + + ASSERT_OK(dns_query_new(&manager, &query, NULL, NULL, packet, 1, 0)); + ASSERT_NOT_NULL(query); +} + +TEST(dns_query_new_bypass_conflict) { + Manager manager = {}; + _cleanup_(dns_query_freep) DnsQuery *query = NULL; + _cleanup_(dns_packet_unrefp) DnsPacket *packet = NULL; + _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL, *extra_q = NULL; + + ASSERT_OK(dns_packet_new_query(&packet, DNS_PROTOCOL_DNS, 0, false)); + ASSERT_NOT_NULL(packet); + + ASSERT_OK(dns_question_new_address(&question, AF_INET, "www.example.com", false)); + ASSERT_NOT_NULL(question); + + ASSERT_OK(dns_packet_append_question(packet, question)); + + ASSERT_OK(dns_question_new_address(&extra_q, AF_INET, "www.example.com", false)); + ASSERT_NOT_NULL(extra_q); + + ASSERT_ERROR(dns_query_new(&manager, &query, extra_q, NULL, packet, 1, 0), EINVAL); + ASSERT_NULL(query); +} + +#define MAX_QUERIES 2048 + +TEST(dns_query_new_too_many_questions) { + Manager manager = {}; + DnsQuestion *question = NULL; + DnsQuery *queries[MAX_QUERIES + 1]; + + for (size_t i = 0; i < MAX_QUERIES; i++) { + ASSERT_OK(dns_question_new_address(&question, AF_INET, "www.example.com", false)); + ASSERT_NOT_NULL(question); + + ASSERT_OK(dns_query_new(&manager, &queries[i], question, NULL, NULL, 1, 0)); + ASSERT_NOT_NULL(queries[i]); + + dns_question_unref(question); + } + + ASSERT_OK(dns_question_new_address(&question, AF_INET, "www.example.com", false)); + ASSERT_NOT_NULL(question); + + ASSERT_ERROR(dns_query_new(&manager, &queries[MAX_QUERIES], question, NULL, NULL, 1, 0), EBUSY); + dns_question_unref(question); + + for (size_t i = 0; i < MAX_QUERIES; i++) + dns_query_free(queries[i]); +} + +DEFINE_TEST_MAIN(LOG_DEBUG); From e5d246b488dcab865b25a28f6151bb0a7e33ccdc Mon Sep 17 00:00:00 2001 From: James Coglan Date: Thu, 20 Jun 2024 14:10:14 +0100 Subject: [PATCH 02/12] resolved: tests for dns_query_make_auxiliary() --- src/resolve/test-dns-query.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/resolve/test-dns-query.c b/src/resolve/test-dns-query.c index 85a7892904..69275877f6 100644 --- a/src/resolve/test-dns-query.c +++ b/src/resolve/test-dns-query.c @@ -166,4 +166,39 @@ TEST(dns_query_new_too_many_questions) { dns_query_free(queries[i]); } +/* ================================================================ + * dns_query_make_auxiliary() + * ================================================================ */ + +TEST(dns_query_make_auxiliary) { + Manager manager = {}; + _cleanup_(dns_question_unrefp) DnsQuestion *qn1 = NULL, *qn2 = NULL, *qn3 = NULL; + _cleanup_(dns_query_freep) DnsQuery *q1 = NULL, *q2 = NULL, *q3 = NULL; + + ASSERT_OK(dns_question_new_address(&qn1, AF_INET, "www.example.com", false)); + ASSERT_NOT_NULL(qn1); + ASSERT_OK(dns_query_new(&manager, &q1, qn1, NULL, NULL, 1, 0)); + ASSERT_NOT_NULL(q1); + + ASSERT_OK(dns_question_new_address(&qn2, AF_INET, "www.example.net", false)); + ASSERT_NOT_NULL(qn2); + ASSERT_OK(dns_query_new(&manager, &q2, qn2, NULL, NULL, 1, 0)); + ASSERT_NOT_NULL(q2); + + ASSERT_OK(dns_question_new_address(&qn3, AF_INET, "www.example.org", false)); + ASSERT_NOT_NULL(qn3); + ASSERT_OK(dns_query_new(&manager, &q3, qn3, NULL, NULL, 1, 0)); + ASSERT_NOT_NULL(q3); + + ASSERT_OK(dns_query_make_auxiliary(q2, q1)); + ASSERT_OK(dns_query_make_auxiliary(q3, q1)); + + ASSERT_EQ(q1->n_auxiliary_queries, 2u); + ASSERT_TRUE(q1->auxiliary_queries == q3); + ASSERT_TRUE(q1->auxiliary_queries->auxiliary_queries_next == q2); + + ASSERT_TRUE(q2->auxiliary_for == q1); + ASSERT_TRUE(q3->auxiliary_for == q1); +} + DEFINE_TEST_MAIN(LOG_DEBUG); From 47eb7e0bf23e262191656f9310675a6937fe4b2f Mon Sep 17 00:00:00 2001 From: James Coglan Date: Thu, 20 Jun 2024 16:24:58 +0100 Subject: [PATCH 03/12] resolved: tests for common usage of dns_query_process_cname_one() --- src/resolve/test-dns-query.c | 139 +++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) diff --git a/src/resolve/test-dns-query.c b/src/resolve/test-dns-query.c index 69275877f6..11bb2fe5a0 100644 --- a/src/resolve/test-dns-query.c +++ b/src/resolve/test-dns-query.c @@ -201,4 +201,143 @@ TEST(dns_query_make_auxiliary) { ASSERT_TRUE(q3->auxiliary_for == q1); } +/* ================================================================ + * dns_query_process_cname_one() + * ================================================================ */ + +TEST(dns_query_process_cname_one_null) { + Manager manager = {}; + _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; + _cleanup_(dns_query_freep) DnsQuery *query = NULL; + + ASSERT_OK(dns_question_new_address(&question, AF_INET, "www.example.com", false)); + ASSERT_NOT_NULL(question); + + ASSERT_OK(dns_query_new(&manager, &query, NULL, question, NULL, 1, 0)); + ASSERT_NOT_NULL(query); + + ASSERT_EQ(dns_query_process_cname_one(query), DNS_QUERY_MATCH); +} + +TEST(dns_query_process_cname_one_success_exact_match) { + Manager manager = {}; + _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; + _cleanup_(dns_query_freep) DnsQuery *query = NULL; + DnsResourceRecord *rr = NULL; + + ASSERT_OK(dns_question_new_address(&question, AF_INET, "www.example.com", false)); + ASSERT_NOT_NULL(question); + + ASSERT_OK(dns_query_new(&manager, &query, NULL, question, NULL, 1, 0)); + ASSERT_NOT_NULL(query); + + query->state = DNS_TRANSACTION_SUCCESS; + query->answer_protocol = DNS_PROTOCOL_DNS; + query->answer_family = AF_INET; + query->answer = dns_answer_new(1); + ASSERT_NOT_NULL(query->answer); + + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); + ASSERT_NOT_NULL(rr); + rr->ttl = 3600; + rr->a.in_addr.s_addr = htobe32(0xc0a8017f); + dns_answer_add(query->answer, rr, 1, 0, NULL); + dns_resource_record_unref(rr); + + ASSERT_EQ(dns_query_process_cname_one(query), DNS_QUERY_MATCH); + + ASSERT_EQ(query->n_cname_redirects, 0u); +} + +TEST(dns_query_process_cname_one_success_match_cname) { + Manager manager = {}; + _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; + _cleanup_(dns_query_freep) DnsQuery *query = NULL; + DnsResourceRecord *rr = NULL; + DnsResourceKey *key = NULL; + + ASSERT_OK(dns_question_new_address(&question, AF_INET, "www.example.com", false)); + ASSERT_NOT_NULL(question); + + ASSERT_OK(dns_query_new(&manager, &query, NULL, question, NULL, 1, 0)); + ASSERT_NOT_NULL(query); + + query->state = DNS_TRANSACTION_SUCCESS; + query->answer_protocol = DNS_PROTOCOL_DNS; + query->answer_family = AF_INET; + query->answer_query_flags = SD_RESOLVED_FROM_NETWORK; + query->answer = dns_answer_new(1); + ASSERT_NOT_NULL(query->answer); + + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_CNAME, "www.example.com"); + ASSERT_NOT_NULL(rr); + rr->ttl = 3600; + rr->cname.name = strdup("example.com"); + ASSERT_NOT_NULL(rr->cname.name); + dns_answer_add(query->answer, rr, 1, 0, NULL); + dns_resource_record_unref(rr); + + ASSERT_EQ(dns_query_process_cname_one(query), DNS_QUERY_CNAME); + + ASSERT_FALSE(dns_query_fully_authenticated(query)); + ASSERT_FALSE(dns_query_fully_confidential(query)); + ASSERT_FALSE(dns_query_fully_authoritative(query)); + + ASSERT_GT(query->flags & SD_RESOLVED_NO_SEARCH, 0u); + + ASSERT_EQ(query->n_cname_redirects, 1u); + + ASSERT_NULL(query->question_utf8); + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); + ASSERT_NOT_NULL(key); + ASSERT_EQ(dns_question_size(query->collected_questions), 1u); + ASSERT_TRUE(dns_question_contains_key(query->collected_questions, key)); + dns_resource_key_unref(key); + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "example.com"); + ASSERT_NOT_NULL(key); + ASSERT_EQ(dns_question_size(query->question_idna), 1u); + ASSERT_TRUE(dns_question_contains_key(query->question_idna, key)); + dns_resource_key_unref(key); +} + +TEST(dns_query_process_cname_one_success_flags) { + Manager manager = {}; + _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; + _cleanup_(dns_query_freep) DnsQuery *query = NULL; + DnsResourceRecord *rr = NULL; + + ASSERT_OK(dns_question_new_address(&question, AF_INET, "www.example.com", false)); + ASSERT_NOT_NULL(question); + + ASSERT_OK(dns_query_new(&manager, &query, NULL, question, NULL, 1, 0)); + ASSERT_NOT_NULL(query); + + query->state = DNS_TRANSACTION_SUCCESS; + query->answer_protocol = DNS_PROTOCOL_DNS; + query->answer_family = AF_INET; + query->answer = dns_answer_new(1); + ASSERT_NOT_NULL(query->answer); + + query->answer_query_flags = SD_RESOLVED_FROM_NETWORK | + SD_RESOLVED_AUTHENTICATED | + SD_RESOLVED_CONFIDENTIAL | + SD_RESOLVED_SYNTHETIC; + + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_CNAME, "www.example.com"); + ASSERT_NOT_NULL(rr); + rr->ttl = 3600; + rr->cname.name = strdup("example.com"); + ASSERT_NOT_NULL(rr->cname.name); + dns_answer_add(query->answer, rr, 1, 0, NULL); + dns_resource_record_unref(rr); + + ASSERT_EQ(dns_query_process_cname_one(query), DNS_QUERY_CNAME); + + ASSERT_TRUE(dns_query_fully_authenticated(query)); + ASSERT_TRUE(dns_query_fully_confidential(query)); + ASSERT_TRUE(dns_query_fully_authoritative(query)); +} + DEFINE_TEST_MAIN(LOG_DEBUG); From 9add77fafb5094f017f03ef938148d9597cabc15 Mon Sep 17 00:00:00 2001 From: James Coglan Date: Thu, 20 Jun 2024 17:03:33 +0100 Subject: [PATCH 04/12] resolved: tests for dns_query_process_cname_many() --- src/resolve/test-dns-query.c | 103 +++++++++++++++++++++++++++++++++-- 1 file changed, 97 insertions(+), 6 deletions(-) diff --git a/src/resolve/test-dns-query.c b/src/resolve/test-dns-query.c index 11bb2fe5a0..b5c72a3073 100644 --- a/src/resolve/test-dns-query.c +++ b/src/resolve/test-dns-query.c @@ -6,6 +6,12 @@ #include "resolved-manager.h" #include "tests.h" +static char* checked_strdup(const char *str) { + char *copy = strdup(str); + ASSERT_NOT_NULL(copy); + return copy; +} + /* ================================================================ * dns_query_new() * ================================================================ */ @@ -272,8 +278,7 @@ TEST(dns_query_process_cname_one_success_match_cname) { rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_CNAME, "www.example.com"); ASSERT_NOT_NULL(rr); rr->ttl = 3600; - rr->cname.name = strdup("example.com"); - ASSERT_NOT_NULL(rr->cname.name); + rr->cname.name = checked_strdup("example.com"); dns_answer_add(query->answer, rr, 1, 0, NULL); dns_resource_record_unref(rr); @@ -287,17 +292,17 @@ TEST(dns_query_process_cname_one_success_match_cname) { ASSERT_EQ(query->n_cname_redirects, 1u); + ASSERT_EQ(dns_question_size(query->collected_questions), 1u); ASSERT_NULL(query->question_utf8); + ASSERT_EQ(dns_question_size(query->question_idna), 1u); key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); ASSERT_NOT_NULL(key); - ASSERT_EQ(dns_question_size(query->collected_questions), 1u); ASSERT_TRUE(dns_question_contains_key(query->collected_questions, key)); dns_resource_key_unref(key); key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "example.com"); ASSERT_NOT_NULL(key); - ASSERT_EQ(dns_question_size(query->question_idna), 1u); ASSERT_TRUE(dns_question_contains_key(query->question_idna, key)); dns_resource_key_unref(key); } @@ -328,8 +333,7 @@ TEST(dns_query_process_cname_one_success_flags) { rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_CNAME, "www.example.com"); ASSERT_NOT_NULL(rr); rr->ttl = 3600; - rr->cname.name = strdup("example.com"); - ASSERT_NOT_NULL(rr->cname.name); + rr->cname.name = checked_strdup("example.com"); dns_answer_add(query->answer, rr, 1, 0, NULL); dns_resource_record_unref(rr); @@ -340,4 +344,91 @@ TEST(dns_query_process_cname_one_success_flags) { ASSERT_TRUE(dns_query_fully_authoritative(query)); } +/* ================================================================ + * dns_query_process_cname_many() + * ================================================================ */ + +TEST(dns_query_process_cname_many_success_match_multiple_cname) { + Manager manager = {}; + _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; + _cleanup_(dns_query_freep) DnsQuery *query = NULL; + DnsResourceRecord *rr = NULL; + DnsResourceKey *key = NULL; + + ASSERT_OK(dns_question_new_address(&question, AF_INET, "www.example.com", false)); + ASSERT_NOT_NULL(question); + + ASSERT_OK(dns_query_new(&manager, &query, NULL, question, NULL, 1, 0)); + ASSERT_NOT_NULL(query); + + query->state = DNS_TRANSACTION_SUCCESS; + query->answer_protocol = DNS_PROTOCOL_DNS; + query->answer_family = AF_INET; + query->answer_query_flags = SD_RESOLVED_FROM_NETWORK; + query->answer = dns_answer_new(4); + ASSERT_NOT_NULL(query->answer); + + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, "example.com"); + ASSERT_NOT_NULL(rr); + rr->ttl = 3600; + rr->a.in_addr.s_addr = htobe32(0xc0a8017f); + dns_answer_add(query->answer, rr, 1, 0, NULL); + dns_resource_record_unref(rr); + + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_CNAME, "www.example.com"); + ASSERT_NOT_NULL(rr); + rr->ttl = 3600; + rr->cname.name = checked_strdup("tmp1.example.com"); + dns_answer_add(query->answer, rr, 1, 0, NULL); + dns_resource_record_unref(rr); + + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_CNAME, "tmp2.example.com"); + ASSERT_NOT_NULL(rr); + rr->ttl = 3600; + rr->cname.name = checked_strdup("example.com"); + dns_answer_add(query->answer, rr, 1, 0, NULL); + dns_resource_record_unref(rr); + + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_CNAME, "tmp1.example.com"); + ASSERT_NOT_NULL(rr); + rr->ttl = 3600; + rr->cname.name = checked_strdup("tmp2.example.com"); + dns_answer_add(query->answer, rr, 1, 0, NULL); + dns_resource_record_unref(rr); + + ASSERT_EQ(dns_query_process_cname_many(query), DNS_QUERY_MATCH); + + ASSERT_FALSE(dns_query_fully_authenticated(query)); + ASSERT_FALSE(dns_query_fully_confidential(query)); + ASSERT_FALSE(dns_query_fully_authoritative(query)); + + ASSERT_GT(query->flags & SD_RESOLVED_NO_SEARCH, 0u); + + ASSERT_EQ(query->n_cname_redirects, 3u); + + ASSERT_EQ(dns_question_size(query->collected_questions), 3u); + ASSERT_NULL(query->question_utf8); + ASSERT_EQ(dns_question_size(query->question_idna), 1u); + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); + ASSERT_NOT_NULL(key); + ASSERT_TRUE(dns_question_contains_key(query->collected_questions, key)); + dns_resource_key_unref(key); + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "tmp1.example.com"); + ASSERT_NOT_NULL(key); + ASSERT_TRUE(dns_question_contains_key(query->collected_questions, key)); + dns_resource_key_unref(key); + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "tmp2.example.com"); + ASSERT_NOT_NULL(key); + ASSERT_TRUE(dns_question_contains_key(query->collected_questions, key)); + dns_resource_key_unref(key); + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "example.com"); + ASSERT_NOT_NULL(key); + ASSERT_TRUE(dns_question_contains_key(query->question_idna, key)); + dns_resource_key_unref(key); +} + DEFINE_TEST_MAIN(LOG_DEBUG); From 7d5ad4f88ecacec85f1c7e4b2f05547d4c205e8a Mon Sep 17 00:00:00 2001 From: James Coglan Date: Thu, 20 Jun 2024 17:25:50 +0100 Subject: [PATCH 05/12] resolved: tests for dns_query_process_cname_one(); no match, DNAME, utf-8 handling --- src/resolve/test-dns-query.c | 183 +++++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) diff --git a/src/resolve/test-dns-query.c b/src/resolve/test-dns-query.c index b5c72a3073..0f917a3e45 100644 --- a/src/resolve/test-dns-query.c +++ b/src/resolve/test-dns-query.c @@ -255,6 +255,36 @@ TEST(dns_query_process_cname_one_success_exact_match) { ASSERT_EQ(query->n_cname_redirects, 0u); } +TEST(dns_query_process_cname_one_success_no_match) { + Manager manager = {}; + _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; + _cleanup_(dns_query_freep) DnsQuery *query = NULL; + DnsResourceRecord *rr = NULL; + + ASSERT_OK(dns_question_new_address(&question, AF_INET, "www.example.com", false)); + ASSERT_NOT_NULL(question); + + ASSERT_OK(dns_query_new(&manager, &query, NULL, question, NULL, 1, 0)); + ASSERT_NOT_NULL(query); + + query->state = DNS_TRANSACTION_SUCCESS; + query->answer_protocol = DNS_PROTOCOL_DNS; + query->answer_family = AF_INET; + query->answer = dns_answer_new(1); + ASSERT_NOT_NULL(query->answer); + + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, "tmp.example.com"); + ASSERT_NOT_NULL(rr); + rr->ttl = 3600; + rr->a.in_addr.s_addr = htobe32(0xc0a8017f); + dns_answer_add(query->answer, rr, 1, 0, NULL); + dns_resource_record_unref(rr); + + ASSERT_EQ(dns_query_process_cname_one(query), DNS_QUERY_NOMATCH); + + ASSERT_EQ(query->n_cname_redirects, 0u); +} + TEST(dns_query_process_cname_one_success_match_cname) { Manager manager = {}; _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; @@ -344,6 +374,159 @@ TEST(dns_query_process_cname_one_success_flags) { ASSERT_TRUE(dns_query_fully_authoritative(query)); } +TEST(dns_query_process_cname_one_success_match_dname) { + Manager manager = {}; + _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; + _cleanup_(dns_query_freep) DnsQuery *query = NULL; + DnsResourceRecord *rr = NULL; + DnsResourceKey *key = NULL; + + ASSERT_OK(dns_question_new_address(&question, AF_INET, "www.example.com", false)); + ASSERT_NOT_NULL(question); + + ASSERT_OK(dns_query_new(&manager, &query, NULL, question, NULL, 1, 0)); + ASSERT_NOT_NULL(query); + + query->state = DNS_TRANSACTION_SUCCESS; + query->answer_protocol = DNS_PROTOCOL_DNS; + query->answer_family = AF_INET; + query->answer = dns_answer_new(1); + ASSERT_NOT_NULL(query->answer); + + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNAME, "example.com"); + ASSERT_NOT_NULL(rr); + rr->ttl = 3600; + rr->dname.name = checked_strdup("v2.example.com"); + dns_answer_add(query->answer, rr, 1, 0, NULL); + dns_resource_record_unref(rr); + + ASSERT_EQ(dns_query_process_cname_one(query), DNS_QUERY_CNAME); + + ASSERT_EQ(query->n_cname_redirects, 1u); + + ASSERT_EQ(dns_question_size(query->collected_questions), 1u); + ASSERT_NULL(query->question_utf8); + ASSERT_EQ(dns_question_size(query->question_idna), 1u); + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); + ASSERT_NOT_NULL(key); + ASSERT_TRUE(dns_question_contains_key(query->collected_questions, key)); + dns_resource_key_unref(key); + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.v2.example.com"); + ASSERT_NOT_NULL(key); + ASSERT_TRUE(dns_question_contains_key(query->question_idna, key)); + dns_resource_key_unref(key); +} + +#if HAVE_LIBIDN || HAVE_LIBIDN2 +TEST(dns_query_process_cname_one_success_match_dname_utf8_same) { + Manager manager = {}; + _cleanup_(dns_question_unrefp) DnsQuestion *q_utf8 = NULL, *q_idna = NULL; + _cleanup_(dns_query_freep) DnsQuery *query = NULL; + DnsResourceRecord *rr = NULL; + DnsResourceKey *key = NULL; + + ASSERT_OK(dns_question_new_address(&q_utf8, AF_INET, "www.xn--tl8h.com", false)); + ASSERT_NOT_NULL(q_utf8); + + ASSERT_OK(dns_question_new_address(&q_idna, AF_INET, "www.\xF0\x9F\x8E\xBC.com", true)); + ASSERT_NOT_NULL(q_idna); + + ASSERT_OK(dns_query_new(&manager, &query, q_utf8, q_idna, NULL, 1, 0)); + ASSERT_NOT_NULL(query); + + query->state = DNS_TRANSACTION_SUCCESS; + query->answer_protocol = DNS_PROTOCOL_DNS; + query->answer_family = AF_INET; + query->answer = dns_answer_new(1); + ASSERT_NOT_NULL(query->answer); + + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNAME, "xn--tl8h.com"); + ASSERT_NOT_NULL(rr); + rr->ttl = 3600; + rr->dname.name = checked_strdup("v2.xn--tl8h.com"); + dns_answer_add(query->answer, rr, 1, 0, NULL); + dns_resource_record_unref(rr); + + ASSERT_EQ(dns_query_process_cname_one(query), DNS_QUERY_CNAME); + + ASSERT_EQ(query->n_cname_redirects, 1u); + + ASSERT_EQ(dns_question_size(query->collected_questions), 1u); + ASSERT_EQ(dns_question_size(query->question_utf8), 1u); + ASSERT_EQ(dns_question_size(query->question_idna), 1u); + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.xn--tl8h.com"); + ASSERT_NOT_NULL(key); + ASSERT_TRUE(dns_question_contains_key(query->collected_questions, key)); + dns_resource_key_unref(key); + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.v2.xn--tl8h.com"); + ASSERT_NOT_NULL(key); + ASSERT_TRUE(dns_question_contains_key(query->question_utf8, key)); + dns_resource_key_unref(key); + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.v2.xn--tl8h.com"); + ASSERT_NOT_NULL(key); + ASSERT_TRUE(dns_question_contains_key(query->question_idna, key)); + dns_resource_key_unref(key); +} + +TEST(dns_query_process_cname_one_success_match_dname_utf8_different) { + Manager manager = {}; + _cleanup_(dns_question_unrefp) DnsQuestion *q_utf8 = NULL, *q_idna = NULL; + _cleanup_(dns_query_freep) DnsQuery *query = NULL; + DnsResourceRecord *rr = NULL; + DnsResourceKey *key = NULL; + + ASSERT_OK(dns_question_new_address(&q_utf8, AF_INET, "www.\xF0\x9F\x98\xB1.com", false)); + ASSERT_NOT_NULL(q_utf8); + + ASSERT_OK(dns_question_new_address(&q_idna, AF_INET, "www.\xF0\x9F\x8E\xBC.com", true)); + ASSERT_NOT_NULL(q_idna); + + ASSERT_OK(dns_query_new(&manager, &query, q_utf8, q_idna, NULL, 1, 0)); + ASSERT_NOT_NULL(query); + + query->state = DNS_TRANSACTION_SUCCESS; + query->answer_protocol = DNS_PROTOCOL_DNS; + query->answer_family = AF_INET; + query->answer = dns_answer_new(1); + ASSERT_NOT_NULL(query->answer); + + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNAME, "xn--tl8h.com"); + ASSERT_NOT_NULL(rr); + rr->ttl = 3600; + rr->dname.name = checked_strdup("v2.xn--tl8h.com"); + dns_answer_add(query->answer, rr, 1, 0, NULL); + dns_resource_record_unref(rr); + + ASSERT_EQ(dns_query_process_cname_one(query), DNS_QUERY_CNAME); + + ASSERT_EQ(query->n_cname_redirects, 1u); + + ASSERT_EQ(dns_question_size(query->collected_questions), 2u); + ASSERT_NULL(query->question_utf8); + ASSERT_EQ(dns_question_size(query->question_idna), 1u); + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.\xF0\x9F\x98\xB1.com"); + ASSERT_NOT_NULL(key); + ASSERT_TRUE(dns_question_contains_key(query->collected_questions, key)); + dns_resource_key_unref(key); + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.xn--tl8h.com"); + ASSERT_NOT_NULL(key); + ASSERT_TRUE(dns_question_contains_key(query->collected_questions, key)); + dns_resource_key_unref(key); + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.v2.xn--tl8h.com"); + ASSERT_NOT_NULL(key); + ASSERT_TRUE(dns_question_contains_key(query->question_idna, key)); + dns_resource_key_unref(key); +} +#endif + /* ================================================================ * dns_query_process_cname_many() * ================================================================ */ From 16078b817287978655b2f63941614d8168abb337 Mon Sep 17 00:00:00 2001 From: James Coglan Date: Fri, 21 Jun 2024 15:40:20 +0100 Subject: [PATCH 06/12] resolved: tests for dns_query_go() -- with and without network link --- src/resolve/test-dns-query.c | 102 +++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/src/resolve/test-dns-query.c b/src/resolve/test-dns-query.c index 0f917a3e45..4f6d70f1c1 100644 --- a/src/resolve/test-dns-query.c +++ b/src/resolve/test-dns-query.c @@ -3,6 +3,9 @@ #include "log.h" #include "resolved-dns-query.h" #include "resolved-dns-rr.h" +#include "resolved-dns-scope.h" +#include "resolved-dns-server.h" +#include "resolved-link.h" #include "resolved-manager.h" #include "tests.h" @@ -614,4 +617,103 @@ TEST(dns_query_process_cname_many_success_match_multiple_cname) { dns_resource_key_unref(key); } +/* ================================================================ + * dns_query_go() + * ================================================================ */ + +/* Testing this function is somewhat problematic since, in addition to setting up the state for query + * candidates, their scopes and transactions, it also directly initiates I/O to files and the network. In + * particular: + * + * - The very first thing it does is try to respond to the query by reading the system /etc/hosts file, which + * may be symlinked to a SystemD resource. Ideally we could test this without accessing global files. + * + * - dns_scope_get_dns_server() calls manager_get_dns_server(), which tries to read /etc/resolv.conf. + * + * - A potential solution to these issues would be to let these file paths be configured instead of + * hard-coded into the source. + * + * - dns_scope_good_domain(), by checking dns_scope_get_dns_server(), will not match with a scope that does + * not have a server configured, either on the scope's link (if it has one) or the manager's main/fallback + * server. Configuring a server means that dns_query_candidate_go() and then dns_transaction_go() will send + * UDP/TCP traffic to that server. Ideally we'd like to test that we can set up all the candidate and + * transaction state without actually causing any requests to be sent. + */ + +static void dns_scope_freep(DnsScope **s) { + dns_scope_free(*s); +} + +typedef struct GoConfig { + bool use_link; +} GoConfig; + +static GoConfig mk_go_config(void) { + return (GoConfig) { + .use_link = false + }; +} + +static void exercise_dns_query_go(GoConfig *cfg) { + Manager manager = {}; + Link *link = NULL; + _cleanup_(dns_server_unrefp) DnsServer *server = NULL; + _cleanup_(dns_scope_freep) DnsScope *scope = NULL; + + _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; + _cleanup_(dns_query_freep) DnsQuery *query = NULL; + + DnsProtocol protocol = DNS_PROTOCOL_DNS; + int family = AF_INET; + int flags = SD_RESOLVED_FLAGS_MAKE(protocol, family, false, false); + int ifindex; + + union in_addr_union server_addr = { .in.s_addr = htobe32(0x7f000001) }; + const char *server_name = "localhost"; + uint16_t port = 53; + DnsServerType type; + + if (cfg->use_link) { + ifindex = 1; + ASSERT_OK(link_new(&manager, &link, ifindex)); + ASSERT_NOT_NULL(link); + type = DNS_SERVER_LINK; + } else { + ifindex = 0; + link = NULL; + type = DNS_SERVER_FALLBACK; + } + + ASSERT_OK(sd_event_new(&manager.event)); + ASSERT_NOT_NULL(manager.event); + + ASSERT_OK(dns_server_new(&manager, &server, type, link, family, &server_addr, + port, ifindex, server_name, RESOLVE_CONFIG_SOURCE_DBUS)); + + ASSERT_NOT_NULL(server); + + ASSERT_OK(dns_scope_new(&manager, &scope, link, protocol, family)); + ASSERT_NOT_NULL(scope); + + ASSERT_OK(dns_question_new_address(&question, AF_INET, "www.example.com", false)); + ASSERT_NOT_NULL(question); + + ASSERT_OK(dns_query_new(&manager, &query, question, question, NULL, ifindex, flags)); + ASSERT_NOT_NULL(query); + + ASSERT_OK(dns_query_go(query)); + + dns_server_unref(server); + sd_event_unref(manager.event); +} + +TEST(dns_query_go) { + GoConfig cfg1 = mk_go_config(); + exercise_dns_query_go(&cfg1); + + GoConfig cfg2 = mk_go_config(); + cfg2.use_link = true; + exercise_dns_query_go(&cfg2); +} + DEFINE_TEST_MAIN(LOG_DEBUG); From 5d5f86431ff61f7aea11418ad9d4620f4c5f0c93 Mon Sep 17 00:00:00 2001 From: James Coglan Date: Fri, 21 Jun 2024 17:01:01 +0100 Subject: [PATCH 07/12] resolved: tests for dns_query_go() using a bypass query --- src/resolve/test-dns-query.c | 38 ++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/src/resolve/test-dns-query.c b/src/resolve/test-dns-query.c index 4f6d70f1c1..6d5e3a760a 100644 --- a/src/resolve/test-dns-query.c +++ b/src/resolve/test-dns-query.c @@ -646,11 +646,13 @@ static void dns_scope_freep(DnsScope **s) { typedef struct GoConfig { bool use_link; + bool use_bypass; } GoConfig; static GoConfig mk_go_config(void) { return (GoConfig) { - .use_link = false + .use_link = false, + .use_bypass = false }; } @@ -661,6 +663,7 @@ static void exercise_dns_query_go(GoConfig *cfg) { _cleanup_(dns_scope_freep) DnsScope *scope = NULL; _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; + _cleanup_(dns_packet_unrefp) DnsPacket *packet = NULL; _cleanup_(dns_query_freep) DnsQuery *query = NULL; DnsProtocol protocol = DNS_PROTOCOL_DNS; @@ -698,9 +701,22 @@ static void exercise_dns_query_go(GoConfig *cfg) { ASSERT_OK(dns_question_new_address(&question, AF_INET, "www.example.com", false)); ASSERT_NOT_NULL(question); - ASSERT_OK(dns_query_new(&manager, &query, question, question, NULL, ifindex, flags)); - ASSERT_NOT_NULL(query); + if (cfg->use_bypass) { + ASSERT_OK(dns_packet_new_query(&packet, protocol, 0, false)); + ASSERT_NOT_NULL(packet); + DNS_PACKET_HEADER(packet)->qdcount = htobe16(1); + packet->question = dns_question_ref(question); + ASSERT_OK(dns_packet_append_question(packet, question)); + /* search domains must be turned off for bypass queries, otherwise dns_query_add_candidate() + * tries to extract the domain name from question_idna which cannot exist on bypasses. */ + flags |= SD_RESOLVED_NO_SEARCH; + ASSERT_OK(dns_query_new(&manager, &query, NULL, NULL, packet, ifindex, flags)); + } else { + ASSERT_OK(dns_query_new(&manager, &query, question, question, NULL, ifindex, flags)); + } + + ASSERT_NOT_NULL(query); ASSERT_OK(dns_query_go(query)); dns_server_unref(server); @@ -708,12 +724,18 @@ static void exercise_dns_query_go(GoConfig *cfg) { } TEST(dns_query_go) { - GoConfig cfg1 = mk_go_config(); - exercise_dns_query_go(&cfg1); + GoConfig cfg; - GoConfig cfg2 = mk_go_config(); - cfg2.use_link = true; - exercise_dns_query_go(&cfg2); + cfg = mk_go_config(); + exercise_dns_query_go(&cfg); + + cfg = mk_go_config(); + cfg.use_link = true; + exercise_dns_query_go(&cfg); + + cfg = mk_go_config(); + cfg.use_bypass = true; + exercise_dns_query_go(&cfg); } DEFINE_TEST_MAIN(LOG_DEBUG); From cc3bad9d70b2f4f2680bf999eb8b8edf73c15030 Mon Sep 17 00:00:00 2001 From: James Coglan Date: Fri, 21 Jun 2024 17:43:16 +0100 Subject: [PATCH 08/12] resolved: tests for dns_query_go() when no scopes are available --- src/resolve/test-dns-query.c | 38 ++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/src/resolve/test-dns-query.c b/src/resolve/test-dns-query.c index 6d5e3a760a..21792b0147 100644 --- a/src/resolve/test-dns-query.c +++ b/src/resolve/test-dns-query.c @@ -645,18 +645,20 @@ static void dns_scope_freep(DnsScope **s) { } typedef struct GoConfig { + bool has_scope; bool use_link; bool use_bypass; } GoConfig; static GoConfig mk_go_config(void) { return (GoConfig) { + .has_scope = true, .use_link = false, .use_bypass = false }; } -static void exercise_dns_query_go(GoConfig *cfg) { +static void exercise_dns_query_go(GoConfig *cfg, void (*check_query)(DnsQuery *query)) { Manager manager = {}; Link *link = NULL; _cleanup_(dns_server_unrefp) DnsServer *server = NULL; @@ -690,13 +692,15 @@ static void exercise_dns_query_go(GoConfig *cfg) { ASSERT_OK(sd_event_new(&manager.event)); ASSERT_NOT_NULL(manager.event); - ASSERT_OK(dns_server_new(&manager, &server, type, link, family, &server_addr, - port, ifindex, server_name, RESOLVE_CONFIG_SOURCE_DBUS)); + if (cfg->has_scope) { + ASSERT_OK(dns_server_new(&manager, &server, type, link, family, &server_addr, + port, ifindex, server_name, RESOLVE_CONFIG_SOURCE_DBUS)); - ASSERT_NOT_NULL(server); + ASSERT_NOT_NULL(server); - ASSERT_OK(dns_scope_new(&manager, &scope, link, protocol, family)); - ASSERT_NOT_NULL(scope); + ASSERT_OK(dns_scope_new(&manager, &scope, link, protocol, family)); + ASSERT_NOT_NULL(scope); + } ASSERT_OK(dns_question_new_address(&question, AF_INET, "www.example.com", false)); ASSERT_NOT_NULL(question); @@ -717,25 +721,39 @@ static void exercise_dns_query_go(GoConfig *cfg) { } ASSERT_NOT_NULL(query); - ASSERT_OK(dns_query_go(query)); + ASSERT_TRUE(dns_query_go(query)); + + if (check_query != NULL) + check_query(query); dns_server_unref(server); sd_event_unref(manager.event); } +static void check_query_no_servers(DnsQuery *query) { + ASSERT_NOT_NULL(query); + + ASSERT_EQ(dns_answer_size(query->answer), 0u); + ASSERT_EQ(query->answer_rcode, DNS_RCODE_SUCCESS); +} + TEST(dns_query_go) { GoConfig cfg; cfg = mk_go_config(); - exercise_dns_query_go(&cfg); + exercise_dns_query_go(&cfg, NULL); cfg = mk_go_config(); cfg.use_link = true; - exercise_dns_query_go(&cfg); + exercise_dns_query_go(&cfg, NULL); cfg = mk_go_config(); cfg.use_bypass = true; - exercise_dns_query_go(&cfg); + exercise_dns_query_go(&cfg, NULL); + + cfg = mk_go_config(); + cfg.has_scope = false; + exercise_dns_query_go(&cfg, check_query_no_servers); } DEFINE_TEST_MAIN(LOG_DEBUG); From 8501f445634d3ec8b8a7bac98d5a7c81c644ac58 Mon Sep 17 00:00:00 2001 From: James Coglan Date: Fri, 21 Jun 2024 17:56:22 +0100 Subject: [PATCH 09/12] resolved: tests for dns_query_go() when configured with search domains --- src/resolve/test-dns-query.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/resolve/test-dns-query.c b/src/resolve/test-dns-query.c index 21792b0147..6df06745f6 100644 --- a/src/resolve/test-dns-query.c +++ b/src/resolve/test-dns-query.c @@ -648,13 +648,15 @@ typedef struct GoConfig { bool has_scope; bool use_link; bool use_bypass; + bool use_search_domain; } GoConfig; static GoConfig mk_go_config(void) { return (GoConfig) { .has_scope = true, .use_link = false, - .use_bypass = false + .use_bypass = false, + .use_search_domain = false }; } @@ -663,6 +665,7 @@ static void exercise_dns_query_go(GoConfig *cfg, void (*check_query)(DnsQuery *q Link *link = NULL; _cleanup_(dns_server_unrefp) DnsServer *server = NULL; _cleanup_(dns_scope_freep) DnsScope *scope = NULL; + _cleanup_(dns_search_domain_unrefp) DnsSearchDomain *sd = NULL; _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; _cleanup_(dns_packet_unrefp) DnsPacket *packet = NULL; @@ -702,7 +705,19 @@ static void exercise_dns_query_go(GoConfig *cfg, void (*check_query)(DnsQuery *q ASSERT_NOT_NULL(scope); } - ASSERT_OK(dns_question_new_address(&question, AF_INET, "www.example.com", false)); + if (cfg->use_search_domain) { + if (link == NULL) + dns_search_domain_new(&manager, &sd, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "local"); + else + dns_search_domain_new(&manager, &sd, DNS_SEARCH_DOMAIN_LINK, link, "local"); + + /* search domains trigger on single-label domains */ + ASSERT_OK(dns_question_new_address(&question, AF_INET, "berlin", false)); + flags &= ~SD_RESOLVED_NO_SEARCH; + } else { + ASSERT_OK(dns_question_new_address(&question, AF_INET, "www.example.com", false)); + } + ASSERT_NOT_NULL(question); if (cfg->use_bypass) { @@ -754,6 +769,10 @@ TEST(dns_query_go) { cfg = mk_go_config(); cfg.has_scope = false; exercise_dns_query_go(&cfg, check_query_no_servers); + + cfg = mk_go_config(); + cfg.use_search_domain = true; + exercise_dns_query_go(&cfg, NULL); } DEFINE_TEST_MAIN(LOG_DEBUG); From 0a547aea969e4cce693bf5a51a49178fdce8c69f Mon Sep 17 00:00:00 2001 From: James Coglan Date: Mon, 24 Jun 2024 08:51:38 +0100 Subject: [PATCH 10/12] resolved: refactor environment management in dns_query_go() tests --- src/resolve/test-dns-query.c | 142 +++++++++++++++++++++-------------- 1 file changed, 87 insertions(+), 55 deletions(-) diff --git a/src/resolve/test-dns-query.c b/src/resolve/test-dns-query.c index 6df06745f6..388c54ef50 100644 --- a/src/resolve/test-dns-query.c +++ b/src/resolve/test-dns-query.c @@ -640,10 +640,6 @@ TEST(dns_query_process_cname_many_success_match_multiple_cname) { * transaction state without actually causing any requests to be sent. */ -static void dns_scope_freep(DnsScope **s) { - dns_scope_free(*s); -} - typedef struct GoConfig { bool has_scope; bool use_link; @@ -660,68 +656,107 @@ static GoConfig mk_go_config(void) { }; } -static void exercise_dns_query_go(GoConfig *cfg, void (*check_query)(DnsQuery *query)) { - Manager manager = {}; - Link *link = NULL; - _cleanup_(dns_server_unrefp) DnsServer *server = NULL; - _cleanup_(dns_scope_freep) DnsScope *scope = NULL; - _cleanup_(dns_search_domain_unrefp) DnsSearchDomain *sd = NULL; +typedef struct GoEnvironment { + Manager manager; + DnsProtocol protocol; + int family; + int ifindex; + Link *link; + DnsScope *scope; + DnsServerType server_type; + union in_addr_union server_addr; + char *server_name; + uint16_t server_port; + DnsServer *server; + DnsSearchDomain *search_domain; +} GoEnvironment; +static void go_env_teardown(GoEnvironment *env) { + ASSERT_NOT_NULL(env); + + dns_search_domain_unref(env->search_domain); + dns_server_unref(env->server); + dns_server_unref(env->server); + free(env->server_name); + dns_scope_free(env->scope); + sd_event_unref(env->manager.event); +} + +static void go_env_setup(GoEnvironment *env, GoConfig *cfg) { + ASSERT_NOT_NULL(env); + ASSERT_NOT_NULL(cfg); + + env->manager = (Manager) {}; + env->protocol = DNS_PROTOCOL_DNS; + env->family = AF_INET; + + ASSERT_OK(sd_event_new(&env->manager.event)); + ASSERT_NOT_NULL(env->manager.event); + + if (cfg->use_link) { + env->ifindex = 1; + ASSERT_OK(link_new(&env->manager, &env->link, env->ifindex)); + ASSERT_NOT_NULL(env->link); + env->server_type = DNS_SERVER_LINK; + } else { + env->ifindex = 0; + env->link = NULL; + env->server_type = DNS_SERVER_FALLBACK; + } + + if (cfg->has_scope) { + ASSERT_OK(dns_scope_new(&env->manager, &env->scope, env->link, env->protocol, env->family)); + ASSERT_NOT_NULL(env->scope); + + env->server_addr.in.s_addr = htobe32(0x7f000001); + env->server_name = checked_strdup("localhost"); + env->server_port = 53; + + ASSERT_OK(dns_server_new(&env->manager, &env->server, env->server_type, + env->link, env->family, &env->server_addr, env->server_port, + env->ifindex, env->server_name, RESOLVE_CONFIG_SOURCE_DBUS)); + + ASSERT_NOT_NULL(env->server); + } else { + env->scope = NULL; + env->server_name = NULL; + env->server = NULL; + } + + if (cfg->use_search_domain) { + if (env->link == NULL) + dns_search_domain_new(&env->manager, &env->search_domain, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "local"); + else + dns_search_domain_new(&env->manager, &env->search_domain, DNS_SEARCH_DOMAIN_LINK, env->link, "local"); + + ASSERT_NOT_NULL(env->search_domain); + } else { + env->search_domain = NULL; + } +} + +static void exercise_dns_query_go(GoConfig *cfg, void (*check_query)(DnsQuery *query)) { + _cleanup_(go_env_teardown) GoEnvironment env = {}; _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; _cleanup_(dns_packet_unrefp) DnsPacket *packet = NULL; _cleanup_(dns_query_freep) DnsQuery *query = NULL; - DnsProtocol protocol = DNS_PROTOCOL_DNS; - int family = AF_INET; - int flags = SD_RESOLVED_FLAGS_MAKE(protocol, family, false, false); - int ifindex; + go_env_setup(&env, cfg); - union in_addr_union server_addr = { .in.s_addr = htobe32(0x7f000001) }; - const char *server_name = "localhost"; - uint16_t port = 53; - DnsServerType type; - - if (cfg->use_link) { - ifindex = 1; - ASSERT_OK(link_new(&manager, &link, ifindex)); - ASSERT_NOT_NULL(link); - type = DNS_SERVER_LINK; - } else { - ifindex = 0; - link = NULL; - type = DNS_SERVER_FALLBACK; - } - - ASSERT_OK(sd_event_new(&manager.event)); - ASSERT_NOT_NULL(manager.event); - - if (cfg->has_scope) { - ASSERT_OK(dns_server_new(&manager, &server, type, link, family, &server_addr, - port, ifindex, server_name, RESOLVE_CONFIG_SOURCE_DBUS)); - - ASSERT_NOT_NULL(server); - - ASSERT_OK(dns_scope_new(&manager, &scope, link, protocol, family)); - ASSERT_NOT_NULL(scope); - } + int flags = SD_RESOLVED_FLAGS_MAKE(env.protocol, env.family, false, false); if (cfg->use_search_domain) { - if (link == NULL) - dns_search_domain_new(&manager, &sd, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "local"); - else - dns_search_domain_new(&manager, &sd, DNS_SEARCH_DOMAIN_LINK, link, "local"); - /* search domains trigger on single-label domains */ - ASSERT_OK(dns_question_new_address(&question, AF_INET, "berlin", false)); + ASSERT_OK(dns_question_new_address(&question, env.family, "berlin", false)); flags &= ~SD_RESOLVED_NO_SEARCH; } else { - ASSERT_OK(dns_question_new_address(&question, AF_INET, "www.example.com", false)); + ASSERT_OK(dns_question_new_address(&question, env.family, "www.example.com", false)); } ASSERT_NOT_NULL(question); if (cfg->use_bypass) { - ASSERT_OK(dns_packet_new_query(&packet, protocol, 0, false)); + ASSERT_OK(dns_packet_new_query(&packet, env.protocol, 0, false)); ASSERT_NOT_NULL(packet); DNS_PACKET_HEADER(packet)->qdcount = htobe16(1); packet->question = dns_question_ref(question); @@ -730,9 +765,9 @@ static void exercise_dns_query_go(GoConfig *cfg, void (*check_query)(DnsQuery *q /* search domains must be turned off for bypass queries, otherwise dns_query_add_candidate() * tries to extract the domain name from question_idna which cannot exist on bypasses. */ flags |= SD_RESOLVED_NO_SEARCH; - ASSERT_OK(dns_query_new(&manager, &query, NULL, NULL, packet, ifindex, flags)); + ASSERT_OK(dns_query_new(&env.manager, &query, NULL, NULL, packet, env.ifindex, flags)); } else { - ASSERT_OK(dns_query_new(&manager, &query, question, question, NULL, ifindex, flags)); + ASSERT_OK(dns_query_new(&env.manager, &query, question, question, NULL, env.ifindex, flags)); } ASSERT_NOT_NULL(query); @@ -740,9 +775,6 @@ static void exercise_dns_query_go(GoConfig *cfg, void (*check_query)(DnsQuery *q if (check_query != NULL) check_query(query); - - dns_server_unref(server); - sd_event_unref(manager.event); } static void check_query_no_servers(DnsQuery *query) { From c080825f1443c67991979f081aabebea6ee2885e Mon Sep 17 00:00:00 2001 From: James Coglan Date: Mon, 24 Jun 2024 09:22:17 +0100 Subject: [PATCH 11/12] resolved: tests for dns_query_go(); multiple search domains for dns_query_candidate_notify() --- src/resolve/test-dns-query.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/resolve/test-dns-query.c b/src/resolve/test-dns-query.c index 388c54ef50..db69dedb94 100644 --- a/src/resolve/test-dns-query.c +++ b/src/resolve/test-dns-query.c @@ -644,7 +644,7 @@ typedef struct GoConfig { bool has_scope; bool use_link; bool use_bypass; - bool use_search_domain; + size_t n_search_domains; } GoConfig; static GoConfig mk_go_config(void) { @@ -652,7 +652,7 @@ static GoConfig mk_go_config(void) { .has_scope = true, .use_link = false, .use_bypass = false, - .use_search_domain = false + .n_search_domains = 0 }; } @@ -668,13 +668,16 @@ typedef struct GoEnvironment { char *server_name; uint16_t server_port; DnsServer *server; - DnsSearchDomain *search_domain; + size_t n_search_domains; + DnsSearchDomain *search_domains[4]; } GoEnvironment; static void go_env_teardown(GoEnvironment *env) { ASSERT_NOT_NULL(env); - dns_search_domain_unref(env->search_domain); + for (size_t i = 0 ; i < env->n_search_domains; i++) + dns_search_domain_unref(env->search_domains[i]); + dns_server_unref(env->server); dns_server_unref(env->server); free(env->server_name); @@ -682,6 +685,8 @@ static void go_env_teardown(GoEnvironment *env) { sd_event_unref(env->manager.event); } +static const char *SEARCH_DOMAINS[] = { "local", "example.com", "org", "edu" }; + static void go_env_setup(GoEnvironment *env, GoConfig *cfg) { ASSERT_NOT_NULL(env); ASSERT_NOT_NULL(cfg); @@ -723,15 +728,12 @@ static void go_env_setup(GoEnvironment *env, GoConfig *cfg) { env->server = NULL; } - if (cfg->use_search_domain) { - if (env->link == NULL) - dns_search_domain_new(&env->manager, &env->search_domain, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "local"); - else - dns_search_domain_new(&env->manager, &env->search_domain, DNS_SEARCH_DOMAIN_LINK, env->link, "local"); + env->n_search_domains = cfg->n_search_domains; - ASSERT_NOT_NULL(env->search_domain); - } else { - env->search_domain = NULL; + for (size_t i = 0 ; i < env->n_search_domains; i++) { + DnsSearchDomainType type = (env->link == NULL) ? DNS_SEARCH_DOMAIN_SYSTEM : DNS_SEARCH_DOMAIN_LINK; + ASSERT_OK(dns_search_domain_new(&env->manager, &env->search_domains[i], type, env->link, SEARCH_DOMAINS[i])); + ASSERT_NOT_NULL(env->search_domains[i]); } } @@ -745,7 +747,7 @@ static void exercise_dns_query_go(GoConfig *cfg, void (*check_query)(DnsQuery *q int flags = SD_RESOLVED_FLAGS_MAKE(env.protocol, env.family, false, false); - if (cfg->use_search_domain) { + if (cfg->n_search_domains > 0) { /* search domains trigger on single-label domains */ ASSERT_OK(dns_question_new_address(&question, env.family, "berlin", false)); flags &= ~SD_RESOLVED_NO_SEARCH; @@ -803,7 +805,7 @@ TEST(dns_query_go) { exercise_dns_query_go(&cfg, check_query_no_servers); cfg = mk_go_config(); - cfg.use_search_domain = true; + cfg.n_search_domains = 2; exercise_dns_query_go(&cfg, NULL); } From 538a3dc298c28ac27563ab54db93b4e91ae320af Mon Sep 17 00:00:00 2001 From: James Coglan Date: Mon, 24 Jun 2024 10:16:02 +0100 Subject: [PATCH 12/12] resolved: tests for dns_query_string() --- src/resolve/test-dns-query.c | 71 ++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/src/resolve/test-dns-query.c b/src/resolve/test-dns-query.c index db69dedb94..53c4588474 100644 --- a/src/resolve/test-dns-query.c +++ b/src/resolve/test-dns-query.c @@ -617,6 +617,77 @@ TEST(dns_query_process_cname_many_success_match_multiple_cname) { dns_resource_key_unref(key); } +/* ================================================================ + * dns_query_string() + * ================================================================ */ + +TEST(dns_query_string_question_utf8) { + Manager manager = {}; + _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; + _cleanup_(dns_query_freep) DnsQuery *query = NULL; + + ASSERT_OK(dns_question_new_address(&question, AF_INET, "utf8.example.com", false)); + ASSERT_NOT_NULL(question); + + ASSERT_OK(dns_query_new(&manager, &query, question, NULL, NULL, 1, 0)); + ASSERT_NOT_NULL(query); + + const char *str = dns_query_string(query); + ASSERT_STREQ(str, "utf8.example.com"); +} + +TEST(dns_query_string_question_idna) { + Manager manager = {}; + _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; + _cleanup_(dns_query_freep) DnsQuery *query = NULL; + + ASSERT_OK(dns_question_new_address(&question, AF_INET, "idna.example.com", false)); + ASSERT_NOT_NULL(question); + + ASSERT_OK(dns_query_new(&manager, &query, NULL, question, NULL, 1, 0)); + ASSERT_NOT_NULL(query); + + const char *str = dns_query_string(query); + ASSERT_STREQ(str, "idna.example.com"); +} + +TEST(dns_query_string_question_bypass) { + Manager manager = {}; + _cleanup_(dns_query_freep) DnsQuery *query = NULL; + _cleanup_(dns_packet_unrefp) DnsPacket * packet = NULL; + + ASSERT_OK(dns_packet_new_query(&packet, DNS_PROTOCOL_DNS, 0, false)); + ASSERT_NOT_NULL(packet); + + ASSERT_OK(dns_question_new_address(&packet->question, AF_INET, "bypass.example.com", false)); + ASSERT_NOT_NULL(packet->question); + + ASSERT_OK(dns_query_new(&manager, &query, NULL, NULL, packet, 1, 0)); + ASSERT_NOT_NULL(query); + + const char *str = dns_query_string(query); + ASSERT_STREQ(str, "bypass.example.com"); +} + +TEST(dns_query_string_request_address) { + Manager manager = {}; + _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; + _cleanup_(dns_query_freep) DnsQuery *query = NULL; + + ASSERT_OK(dns_question_new_address(&question, AF_INET, "www.example.com", false)); + ASSERT_NOT_NULL(question); + + ASSERT_OK(dns_query_new(&manager, &query, question, NULL, NULL, 1, 0)); + ASSERT_NOT_NULL(query); + + query->request_family = AF_INET; + query->request_address.in.s_addr = htobe32(0x7f000001); + query->request_address_valid = true; + + const char *str = dns_query_string(query); + ASSERT_STREQ(str, "127.0.0.1"); +} + /* ================================================================ * dns_query_go() * ================================================================ */