From 29c15d9d7bd62c524132463e07d3c299e9852c55 Mon Sep 17 00:00:00 2001 From: James Coglan Date: Fri, 17 May 2024 11:22:34 +0100 Subject: [PATCH 01/20] resolved: tests for dns_cache_put(); successful A query Co-Authored-By: jan@neighbourhood.ie --- src/resolve/meson.build | 6 ++ src/resolve/test-dns-cache.c | 155 +++++++++++++++++++++++++++++++++++ 2 files changed, 161 insertions(+) create mode 100644 src/resolve/test-dns-cache.c diff --git a/src/resolve/meson.build b/src/resolve/meson.build index b139afda6a2..33adc8ca935 100644 --- a/src/resolve/meson.build +++ b/src/resolve/meson.build @@ -165,6 +165,12 @@ executables += [ resolve_test_template + { 'sources' : files('test-dns-answer.c'), }, + resolve_test_template + { + 'sources' : files( + 'test-dns-cache.c', + 'resolved-dns-cache.c' + ), + }, resolve_test_template + { 'sources' : files('test-dns-packet.c'), }, diff --git a/src/resolve/test-dns-cache.c b/src/resolve/test-dns-cache.c new file mode 100644 index 00000000000..d6568a9a5c9 --- /dev/null +++ b/src/resolve/test-dns-cache.c @@ -0,0 +1,155 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include + +#include "dns-type.h" +#include "log.h" +#include "resolve-util.h" +#include "resolved-def.h" +#include "resolved-dns-answer.h" +#include "resolved-dns-cache.h" +#include "resolved-dns-dnssec.h" +#include "resolved-dns-packet.h" +#include "resolved-dns-rr.h" +#include "tests.h" + +static DnsCache new_cache(void) { + return (DnsCache) {}; +} + +typedef struct PutArgs { + DnsCacheMode cache_mode; + DnsProtocol protocol; + DnsResourceKey *key; + int rcode; + DnsAnswer *answer; + DnsPacket *full_packet; + uint64_t query_flags; + DnssecResult dnssec_result; + uint32_t nsec_ttl; + int owner_family; + const union in_addr_union owner_address; + usec_t stale_retention_usec; +} PutArgs; + +static PutArgs mk_put_args(void) { + return (PutArgs) { + .cache_mode = DNS_CACHE_MODE_YES, + .protocol = DNS_PROTOCOL_DNS, + .key = NULL, + .rcode = DNS_RCODE_SUCCESS, + .answer = NULL, + .full_packet = NULL, + .query_flags = SD_RESOLVED_AUTHENTICATED | SD_RESOLVED_CONFIDENTIAL, + .dnssec_result = DNSSEC_UNSIGNED, + .nsec_ttl = 3600, + .owner_family = AF_INET, + .owner_address = { .in.s_addr = htobe32(0x01020304) }, + .stale_retention_usec = 0 + }; +} + +static int cache_put(DnsCache *cache, PutArgs *args) { + ASSERT_NOT_NULL(cache); + ASSERT_NOT_NULL(args); + + return dns_cache_put(cache, + args->cache_mode, + args->protocol, + args->key, + args->rcode, + args->answer, + args->full_packet, + args->query_flags, + args->dnssec_result, + args->nsec_ttl, + args->owner_family, + &args->owner_address, + args->stale_retention_usec); +} + +static void dns_cache_unrefp(DnsCache *cache) { + dns_cache_flush(cache); +} + +static void put_args_unrefp(PutArgs *args) { + ASSERT_NOT_NULL(args); + + dns_resource_key_unref(args->key); + dns_answer_unref(args->answer); + dns_packet_unref(args->full_packet); +} + +TEST(dns_a_success_is_cached) { + _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); + _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + DnsAnswerFlags flags; + + put_args.key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); + ASSERT_NOT_NULL(put_args.key); + put_args.rcode = DNS_RCODE_SUCCESS; + + put_args.answer = dns_answer_new(1); + ASSERT_NOT_NULL(put_args.answer); + + rr = dns_resource_record_new(put_args.key); + ASSERT_NOT_NULL(rr); + rr->a.in_addr.s_addr = htobe32(0xc0a8017f); + rr->ttl = 3600; + flags = DNS_ANSWER_CACHEABLE; + dns_answer_add(put_args.answer, rr, 1, flags, NULL); + + ASSERT_OK(cache_put(&cache, &put_args)); + ASSERT_FALSE(dns_cache_is_empty(&cache)); +} + +TEST(dns_a_success_zero_ttl_is_not_cached) { + _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); + _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + DnsAnswerFlags flags; + + put_args.key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); + ASSERT_NOT_NULL(put_args.key); + put_args.rcode = DNS_RCODE_SUCCESS; + + put_args.answer = dns_answer_new(1); + ASSERT_NOT_NULL(put_args.answer); + + rr = dns_resource_record_new(put_args.key); + ASSERT_NOT_NULL(rr); + rr->a.in_addr.s_addr = htobe32(0xc0a8017f); + rr->ttl = 0; + flags = DNS_ANSWER_CACHEABLE; + dns_answer_add(put_args.answer, rr, 1, flags, NULL); + + ASSERT_OK(cache_put(&cache, &put_args)); + ASSERT_TRUE(dns_cache_is_empty(&cache)); +} + +TEST(dns_a_success_not_cacheable_is_not_cached) { + _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); + _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + DnsAnswerFlags flags; + + put_args.key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); + ASSERT_NOT_NULL(put_args.key); + put_args.rcode = DNS_RCODE_SUCCESS; + + put_args.answer = dns_answer_new(1); + ASSERT_NOT_NULL(put_args.answer); + + rr = dns_resource_record_new(put_args.key); + ASSERT_NOT_NULL(rr); + rr->a.in_addr.s_addr = htobe32(0xc0a8017f); + rr->ttl = 3600; + flags = 0; + dns_answer_add(put_args.answer, rr, 1, flags, NULL); + + ASSERT_OK(cache_put(&cache, &put_args)); + ASSERT_TRUE(dns_cache_is_empty(&cache)); +} + +DEFINE_TEST_MAIN(LOG_DEBUG); From c5df8d53a915a2fc91960198549cb049a0de07cc Mon Sep 17 00:00:00 2001 From: James Coglan Date: Tue, 21 May 2024 14:30:30 +0100 Subject: [PATCH 02/20] resolved: tests for dns_cache_put() with different RCODEs --- src/resolve/test-dns-cache.c | 44 ++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/resolve/test-dns-cache.c b/src/resolve/test-dns-cache.c index d6568a9a5c9..c9f084d3def 100644 --- a/src/resolve/test-dns-cache.c +++ b/src/resolve/test-dns-cache.c @@ -104,6 +104,50 @@ TEST(dns_a_success_is_cached) { ASSERT_FALSE(dns_cache_is_empty(&cache)); } +TEST(dns_a_nxdomain_is_cached) { + _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); + _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); + + put_args.key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); + ASSERT_NOT_NULL(put_args.key); + put_args.rcode = DNS_RCODE_NXDOMAIN; + + put_args.answer = dns_answer_new(1); + ASSERT_NOT_NULL(put_args.answer); + dns_answer_add_soa(put_args.answer, "example.com", 3600, 0); + + ASSERT_OK(cache_put(&cache, &put_args)); + ASSERT_FALSE(dns_cache_is_empty(&cache)); +} + +TEST(dns_a_servfail_is_cached) { + _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); + _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); + + put_args.key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); + ASSERT_NOT_NULL(put_args.key); + put_args.rcode = DNS_RCODE_SERVFAIL; + put_args.answer = dns_answer_new(0); + ASSERT_NOT_NULL(put_args.answer); + + ASSERT_OK(cache_put(&cache, &put_args)); + ASSERT_FALSE(dns_cache_is_empty(&cache)); +} + +TEST(dns_a_refused_is_not_cached) { + _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); + _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); + + put_args.key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); + ASSERT_NOT_NULL(put_args.key); + put_args.rcode = DNS_RCODE_REFUSED; + put_args.answer = dns_answer_new(0); + ASSERT_NOT_NULL(put_args.answer); + + ASSERT_OK(cache_put(&cache, &put_args)); + ASSERT_TRUE(dns_cache_is_empty(&cache)); +} + TEST(dns_a_success_zero_ttl_is_not_cached) { _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); From 4f4c06f02d4d2e6b645bb88ffc1e031aa45812d3 Mon Sep 17 00:00:00 2001 From: James Coglan Date: Tue, 21 May 2024 14:39:26 +0100 Subject: [PATCH 03/20] resolved: test for dns_cache_put() with empty answer --- src/resolve/test-dns-cache.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/resolve/test-dns-cache.c b/src/resolve/test-dns-cache.c index c9f084d3def..d708afef7b6 100644 --- a/src/resolve/test-dns-cache.c +++ b/src/resolve/test-dns-cache.c @@ -104,6 +104,20 @@ TEST(dns_a_success_is_cached) { ASSERT_FALSE(dns_cache_is_empty(&cache)); } +TEST(dns_a_success_empty_answer_is_not_cached) { + _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); + _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); + + put_args.key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); + ASSERT_NOT_NULL(put_args.key); + put_args.rcode = DNS_RCODE_SUCCESS; + put_args.answer = dns_answer_new(0); + ASSERT_NOT_NULL(put_args.answer); + + ASSERT_OK(cache_put(&cache, &put_args)); + ASSERT_TRUE(dns_cache_is_empty(&cache)); +} + TEST(dns_a_nxdomain_is_cached) { _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); From bbcd3bc38137a0d778145815c1c27fcb789eebcd Mon Sep 17 00:00:00 2001 From: James Coglan Date: Tue, 21 May 2024 14:49:40 +0100 Subject: [PATCH 04/20] resolved: refactor DNS answer construction for cache tests --- src/resolve/test-dns-cache.c | 65 ++++++++++-------------------------- 1 file changed, 18 insertions(+), 47 deletions(-) diff --git a/src/resolve/test-dns-cache.c b/src/resolve/test-dns-cache.c index d708afef7b6..51559ffa330 100644 --- a/src/resolve/test-dns-cache.c +++ b/src/resolve/test-dns-cache.c @@ -33,12 +33,12 @@ typedef struct PutArgs { } PutArgs; static PutArgs mk_put_args(void) { - return (PutArgs) { + PutArgs put_args = { .cache_mode = DNS_CACHE_MODE_YES, .protocol = DNS_PROTOCOL_DNS, .key = NULL, .rcode = DNS_RCODE_SUCCESS, - .answer = NULL, + .answer = dns_answer_new(0), .full_packet = NULL, .query_flags = SD_RESOLVED_AUTHENTICATED | SD_RESOLVED_CONFIDENTIAL, .dnssec_result = DNSSEC_UNSIGNED, @@ -47,6 +47,9 @@ static PutArgs mk_put_args(void) { .owner_address = { .in.s_addr = htobe32(0x01020304) }, .stale_retention_usec = 0 }; + + ASSERT_NOT_NULL(put_args.answer); + return put_args; } static int cache_put(DnsCache *cache, PutArgs *args) { @@ -80,25 +83,24 @@ static void put_args_unrefp(PutArgs *args) { dns_packet_unref(args->full_packet); } +static void answer_add_a(PutArgs *args, DnsResourceKey *key, int addr, int ttl, DnsAnswerFlags flags) { + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + + rr = dns_resource_record_new(key); + ASSERT_NOT_NULL(rr); + rr->a.in_addr.s_addr = htobe32(addr); + rr->ttl = ttl; + dns_answer_add(args->answer, rr, 1, flags, NULL); +} + TEST(dns_a_success_is_cached) { _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); - _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; - DnsAnswerFlags flags; put_args.key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); ASSERT_NOT_NULL(put_args.key); put_args.rcode = DNS_RCODE_SUCCESS; - - put_args.answer = dns_answer_new(1); - ASSERT_NOT_NULL(put_args.answer); - - rr = dns_resource_record_new(put_args.key); - ASSERT_NOT_NULL(rr); - rr->a.in_addr.s_addr = htobe32(0xc0a8017f); - rr->ttl = 3600; - flags = DNS_ANSWER_CACHEABLE; - dns_answer_add(put_args.answer, rr, 1, flags, NULL); + answer_add_a(&put_args, put_args.key, 0xc0a8017f, 3600, DNS_ANSWER_CACHEABLE); ASSERT_OK(cache_put(&cache, &put_args)); ASSERT_FALSE(dns_cache_is_empty(&cache)); @@ -111,8 +113,6 @@ TEST(dns_a_success_empty_answer_is_not_cached) { put_args.key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); ASSERT_NOT_NULL(put_args.key); put_args.rcode = DNS_RCODE_SUCCESS; - put_args.answer = dns_answer_new(0); - ASSERT_NOT_NULL(put_args.answer); ASSERT_OK(cache_put(&cache, &put_args)); ASSERT_TRUE(dns_cache_is_empty(&cache)); @@ -125,9 +125,6 @@ TEST(dns_a_nxdomain_is_cached) { put_args.key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); ASSERT_NOT_NULL(put_args.key); put_args.rcode = DNS_RCODE_NXDOMAIN; - - put_args.answer = dns_answer_new(1); - ASSERT_NOT_NULL(put_args.answer); dns_answer_add_soa(put_args.answer, "example.com", 3600, 0); ASSERT_OK(cache_put(&cache, &put_args)); @@ -141,8 +138,6 @@ TEST(dns_a_servfail_is_cached) { put_args.key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); ASSERT_NOT_NULL(put_args.key); put_args.rcode = DNS_RCODE_SERVFAIL; - put_args.answer = dns_answer_new(0); - ASSERT_NOT_NULL(put_args.answer); ASSERT_OK(cache_put(&cache, &put_args)); ASSERT_FALSE(dns_cache_is_empty(&cache)); @@ -155,8 +150,6 @@ TEST(dns_a_refused_is_not_cached) { put_args.key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); ASSERT_NOT_NULL(put_args.key); put_args.rcode = DNS_RCODE_REFUSED; - put_args.answer = dns_answer_new(0); - ASSERT_NOT_NULL(put_args.answer); ASSERT_OK(cache_put(&cache, &put_args)); ASSERT_TRUE(dns_cache_is_empty(&cache)); @@ -165,22 +158,11 @@ TEST(dns_a_refused_is_not_cached) { TEST(dns_a_success_zero_ttl_is_not_cached) { _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); - _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; - DnsAnswerFlags flags; put_args.key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); ASSERT_NOT_NULL(put_args.key); put_args.rcode = DNS_RCODE_SUCCESS; - - put_args.answer = dns_answer_new(1); - ASSERT_NOT_NULL(put_args.answer); - - rr = dns_resource_record_new(put_args.key); - ASSERT_NOT_NULL(rr); - rr->a.in_addr.s_addr = htobe32(0xc0a8017f); - rr->ttl = 0; - flags = DNS_ANSWER_CACHEABLE; - dns_answer_add(put_args.answer, rr, 1, flags, NULL); + answer_add_a(&put_args, put_args.key, 0xc0a8017f, 0, DNS_ANSWER_CACHEABLE); ASSERT_OK(cache_put(&cache, &put_args)); ASSERT_TRUE(dns_cache_is_empty(&cache)); @@ -189,22 +171,11 @@ TEST(dns_a_success_zero_ttl_is_not_cached) { TEST(dns_a_success_not_cacheable_is_not_cached) { _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); - _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; - DnsAnswerFlags flags; put_args.key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); ASSERT_NOT_NULL(put_args.key); put_args.rcode = DNS_RCODE_SUCCESS; - - put_args.answer = dns_answer_new(1); - ASSERT_NOT_NULL(put_args.answer); - - rr = dns_resource_record_new(put_args.key); - ASSERT_NOT_NULL(rr); - rr->a.in_addr.s_addr = htobe32(0xc0a8017f); - rr->ttl = 3600; - flags = 0; - dns_answer_add(put_args.answer, rr, 1, flags, NULL); + answer_add_a(&put_args, put_args.key, 0xc0a8017f, 3600, 0); ASSERT_OK(cache_put(&cache, &put_args)); ASSERT_TRUE(dns_cache_is_empty(&cache)); From f44ed739f1f1dc2a1f265140af60d80cb36f4a4d Mon Sep 17 00:00:00 2001 From: James Coglan Date: Tue, 21 May 2024 15:37:56 +0100 Subject: [PATCH 05/20] resolved: tests for dns_cache_put() with non-matching class, type, name --- src/resolve/test-dns-cache.c | 51 ++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/src/resolve/test-dns-cache.c b/src/resolve/test-dns-cache.c index 51559ffa330..9ec5abd0d30 100644 --- a/src/resolve/test-dns-cache.c +++ b/src/resolve/test-dns-cache.c @@ -106,6 +106,57 @@ TEST(dns_a_success_is_cached) { ASSERT_FALSE(dns_cache_is_empty(&cache)); } +TEST(dns_a_success_non_matching_type_is_cached) { + _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); + _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); + _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; + + put_args.key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); + ASSERT_NOT_NULL(put_args.key); + put_args.rcode = DNS_RCODE_SUCCESS; + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, "www.example.com"); + ASSERT_NOT_NULL(key); + answer_add_a(&put_args, key, 0xc0a8017f, 3600, DNS_ANSWER_CACHEABLE); + + ASSERT_OK(cache_put(&cache, &put_args)); + ASSERT_FALSE(dns_cache_is_empty(&cache)); +} + +TEST(dns_a_success_non_matching_name_is_cached) { + _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); + _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); + _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; + + put_args.key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); + ASSERT_NOT_NULL(put_args.key); + put_args.rcode = DNS_RCODE_SUCCESS; + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "example.com"); + ASSERT_NOT_NULL(key); + answer_add_a(&put_args, key, 0xc0a8017f, 3600, DNS_ANSWER_CACHEABLE); + + ASSERT_OK(cache_put(&cache, &put_args)); + ASSERT_FALSE(dns_cache_is_empty(&cache)); +} + +TEST(dns_a_success_escaped_key_returns_error) { + _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); + _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); + _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; + + put_args.key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); + ASSERT_NOT_NULL(put_args.key); + put_args.rcode = DNS_RCODE_SUCCESS; + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.\\example.com"); + ASSERT_NOT_NULL(key); + answer_add_a(&put_args, key, 0xc0a8017f, 3600, DNS_ANSWER_CACHEABLE); + + ASSERT_ERROR(cache_put(&cache, &put_args), EINVAL); + ASSERT_TRUE(dns_cache_is_empty(&cache)); +} + TEST(dns_a_success_empty_answer_is_not_cached) { _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); From 186184ad14ca8aa685ae7da2d06b47134d474580 Mon Sep 17 00:00:00 2001 From: James Coglan Date: Tue, 21 May 2024 16:26:31 +0100 Subject: [PATCH 06/20] resolved: tests for dns_cache_put(); CNAME success and name error --- src/resolve/test-dns-cache.c | 45 ++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/resolve/test-dns-cache.c b/src/resolve/test-dns-cache.c index 9ec5abd0d30..f8d88403ac2 100644 --- a/src/resolve/test-dns-cache.c +++ b/src/resolve/test-dns-cache.c @@ -93,6 +93,17 @@ static void answer_add_a(PutArgs *args, DnsResourceKey *key, int addr, int ttl, dns_answer_add(args->answer, rr, 1, flags, NULL); } +static void answer_add_cname(PutArgs *args, DnsResourceKey *key, const char *alias, int ttl, DnsAnswerFlags flags) { + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + + rr = dns_resource_record_new(key); + ASSERT_NOT_NULL(rr); + rr->cname.name = strdup(alias); + ASSERT_NOT_NULL(rr->cname.name); + rr->ttl = ttl; + dns_answer_add(args->answer, rr, 1, flags, NULL); +} + TEST(dns_a_success_is_cached) { _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); @@ -232,4 +243,38 @@ TEST(dns_a_success_not_cacheable_is_not_cached) { ASSERT_TRUE(dns_cache_is_empty(&cache)); } +TEST(dns_a_to_cname_success_is_cached) { + _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); + _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); + _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; + + put_args.key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); + ASSERT_NOT_NULL(put_args.key); + put_args.rcode = DNS_RCODE_SUCCESS; + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_CNAME, "www.example.com"); + ASSERT_NOT_NULL(key); + answer_add_cname(&put_args, key, "example.com", 3600, DNS_ANSWER_CACHEABLE); + + ASSERT_OK(cache_put(&cache, &put_args)); + ASSERT_FALSE(dns_cache_is_empty(&cache)); +} + +TEST(dns_a_to_cname_success_escaped_name_returns_error) { + _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); + _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); + _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; + + put_args.key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); + ASSERT_NOT_NULL(put_args.key); + put_args.rcode = DNS_RCODE_SUCCESS; + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_CNAME, "www.\\example.com"); + ASSERT_NOT_NULL(key); + answer_add_cname(&put_args, key, "example.com", 3600, DNS_ANSWER_CACHEABLE); + + ASSERT_ERROR(cache_put(&cache, &put_args), EINVAL); + ASSERT_TRUE(dns_cache_is_empty(&cache)); +} + DEFINE_TEST_MAIN(LOG_DEBUG); From 2bec28864af50d3d9f0f8b888040384bfa00e9f9 Mon Sep 17 00:00:00 2001 From: James Coglan Date: Thu, 23 May 2024 15:57:05 +0100 Subject: [PATCH 07/20] resolved: check that adding an expired response removes cache entry --- src/resolve/test-dns-cache.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/resolve/test-dns-cache.c b/src/resolve/test-dns-cache.c index f8d88403ac2..11dea7002f4 100644 --- a/src/resolve/test-dns-cache.c +++ b/src/resolve/test-dns-cache.c @@ -230,6 +230,27 @@ TEST(dns_a_success_zero_ttl_is_not_cached) { ASSERT_TRUE(dns_cache_is_empty(&cache)); } +TEST(dns_a_success_zero_ttl_removes_existing_entry) { + _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); + _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); + + put_args.key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); + ASSERT_NOT_NULL(put_args.key); + put_args.rcode = DNS_RCODE_SUCCESS; + answer_add_a(&put_args, put_args.key, 0xc0a8017f, 3600, DNS_ANSWER_CACHEABLE); + + ASSERT_OK(cache_put(&cache, &put_args)); + ASSERT_FALSE(dns_cache_is_empty(&cache)); + + dns_answer_unref(put_args.answer); + put_args.answer = dns_answer_new(1); + ASSERT_NOT_NULL(put_args.answer); + answer_add_a(&put_args, put_args.key, 0xc0a8017f, 0, DNS_ANSWER_CACHEABLE); + + ASSERT_OK(cache_put(&cache, &put_args)); + ASSERT_TRUE(dns_cache_is_empty(&cache)); +} + TEST(dns_a_success_not_cacheable_is_not_cached) { _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); From f8da0642f3e57821b7875a7776abb9c2353c9b9b Mon Sep 17 00:00:00 2001 From: James Coglan Date: Wed, 22 May 2024 15:54:36 +0100 Subject: [PATCH 08/20] resolved: tests for dns_cache_dump_to_json() --- src/resolve/test-dns-cache.c | 60 ++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/src/resolve/test-dns-cache.c b/src/resolve/test-dns-cache.c index 11dea7002f4..2789549b248 100644 --- a/src/resolve/test-dns-cache.c +++ b/src/resolve/test-dns-cache.c @@ -104,6 +104,15 @@ static void answer_add_cname(PutArgs *args, DnsResourceKey *key, const char *ali dns_answer_add(args->answer, rr, 1, flags, NULL); } +#define BY_IDX(json, idx) sd_json_variant_by_index(json, idx) +#define BY_KEY(json, key) sd_json_variant_by_key(json, key) +#define INTVAL(json) sd_json_variant_integer(json) +#define STRVAL(json) sd_json_variant_string(json) + +/* ================================================================ + * dns_cache_put() + * ================================================================ */ + TEST(dns_a_success_is_cached) { _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); @@ -298,4 +307,55 @@ TEST(dns_a_to_cname_success_escaped_name_returns_error) { ASSERT_TRUE(dns_cache_is_empty(&cache)); } +/* ================================================================ + * dns_cache_dump_to_json() + * ================================================================ */ + +TEST(dns_cache_dump_json_basic) { + _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); + _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); + _cleanup_(sd_json_variant_unrefp) sd_json_variant *json = NULL, *expected = NULL; + sd_json_variant *item = NULL, *rr = NULL; + _cleanup_free_ char *str = calloc(256, sizeof(char)); + + ASSERT_NOT_NULL(str); + + put_args.key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); + ASSERT_NOT_NULL(put_args.key); + put_args.rcode = DNS_RCODE_SUCCESS; + answer_add_a(&put_args, put_args.key, 0xc0a8017f, 3600, DNS_ANSWER_CACHEABLE); + cache_put(&cache, &put_args); + + ASSERT_EQ(dns_cache_size(&cache), 1u); + + ASSERT_OK(dns_cache_dump_to_json(&cache, &json)); + ASSERT_NOT_NULL(json); + + ASSERT_TRUE(sd_json_variant_is_array(json)); + ASSERT_EQ(sd_json_variant_elements(json), 1u); + + item = BY_IDX(json, 0); + ASSERT_NOT_NULL(item); + + sprintf(str, "{ \"class\": %d, \"type\": %d, \"name\": \"www.example.com\" }", DNS_CLASS_IN, DNS_TYPE_A); + ASSERT_OK(sd_json_parse(str, 0, &expected, NULL, NULL)); + ASSERT_TRUE(sd_json_variant_equal(BY_KEY(item, "key"), expected)); + + ASSERT_TRUE(sd_json_variant_is_array(BY_KEY(item, "rrs"))); + ASSERT_EQ(sd_json_variant_elements(BY_KEY(item, "rrs")), 1u); + + rr = BY_KEY(BY_IDX(BY_KEY(item, "rrs"), 0), "rr"); + ASSERT_NOT_NULL(rr); + ASSERT_TRUE(sd_json_variant_equal(BY_KEY(rr, "key"), expected)); + + sd_json_variant_unref(expected); + + sprintf(str, "[192, 168, 1, 127]"); + ASSERT_OK(sd_json_parse(str, 0, &expected, NULL, NULL)); + ASSERT_TRUE(sd_json_variant_equal(BY_KEY(rr, "address"), expected)); + + ASSERT_TRUE(sd_json_variant_is_string(BY_KEY(BY_IDX(BY_KEY(item, "rrs"), 0), "raw"))); + ASSERT_TRUE(sd_json_variant_is_integer(BY_KEY(item, "until"))); +} + DEFINE_TEST_MAIN(LOG_DEBUG); From 76f832a4f63cd4deb2379ee9a0e4337161733d85 Mon Sep 17 00:00:00 2001 From: James Coglan Date: Wed, 22 May 2024 16:49:18 +0100 Subject: [PATCH 09/20] resolved: first test for dns_cache_lookup() --- src/resolve/test-dns-cache.c | 41 ++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/resolve/test-dns-cache.c b/src/resolve/test-dns-cache.c index 2789549b248..ab76bf50630 100644 --- a/src/resolve/test-dns-cache.c +++ b/src/resolve/test-dns-cache.c @@ -307,6 +307,47 @@ TEST(dns_a_to_cname_success_escaped_name_returns_error) { ASSERT_TRUE(dns_cache_is_empty(&cache)); } +/* ================================================================ + * dns_cache_lookup() + * ================================================================ */ + +TEST(dns_cache_lookup_single) { + _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); + _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); + _cleanup_(dns_answer_unrefp) DnsAnswer *ret_answer = NULL; + _cleanup_(dns_packet_unrefp) DnsPacket *ret_full_packet = NULL; + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; + int query_flags, ret_rcode; + uint64_t ret_query_flags; + + put_args.key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); + ASSERT_NOT_NULL(put_args.key); + put_args.rcode = DNS_RCODE_SUCCESS; + answer_add_a(&put_args, put_args.key, 0xc0a8017f, 3600, DNS_ANSWER_CACHEABLE); + cache_put(&cache, &put_args); + + ASSERT_EQ(dns_cache_size(&cache), 1u); + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); + ASSERT_NOT_NULL(key); + query_flags = 0; + ASSERT_TRUE(dns_cache_lookup(&cache, key, query_flags, &ret_rcode, &ret_answer, &ret_full_packet, &ret_query_flags, NULL)); + + ASSERT_EQ(cache.n_hit, 1u); + ASSERT_EQ(cache.n_miss, 0u); + + ASSERT_EQ(ret_rcode, DNS_RCODE_SUCCESS); + ASSERT_EQ(ret_query_flags, SD_RESOLVED_CONFIDENTIAL); + + ASSERT_EQ(dns_answer_size(ret_answer), 1u); + + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); + ASSERT_NOT_NULL(rr); + rr->a.in_addr.s_addr = htobe32(0xc0a8017f); + ASSERT_TRUE(dns_answer_contains(ret_answer, rr)); +} + /* ================================================================ * dns_cache_dump_to_json() * ================================================================ */ From 25ef0fc7c812a8864ab988651cde025550502c2b Mon Sep 17 00:00:00 2001 From: James Coglan Date: Thu, 23 May 2024 09:53:42 +0100 Subject: [PATCH 10/20] resolved: tests for dns_cache_lookup() for NXDOMAIN --- src/resolve/test-dns-cache.c | 54 ++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/src/resolve/test-dns-cache.c b/src/resolve/test-dns-cache.c index ab76bf50630..4d87fd85b3e 100644 --- a/src/resolve/test-dns-cache.c +++ b/src/resolve/test-dns-cache.c @@ -83,6 +83,12 @@ static void put_args_unrefp(PutArgs *args) { dns_packet_unref(args->full_packet); } +static char* checked_strdup(const char *str) { + char *copy = strdup(str); + ASSERT_NOT_NULL(copy); + return copy; +} + static void answer_add_a(PutArgs *args, DnsResourceKey *key, int addr, int ttl, DnsAnswerFlags flags) { _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; @@ -98,8 +104,7 @@ static void answer_add_cname(PutArgs *args, DnsResourceKey *key, const char *ali rr = dns_resource_record_new(key); ASSERT_NOT_NULL(rr); - rr->cname.name = strdup(alias); - ASSERT_NOT_NULL(rr->cname.name); + rr->cname.name = checked_strdup(alias); rr->ttl = ttl; dns_answer_add(args->answer, rr, 1, flags, NULL); } @@ -311,7 +316,7 @@ TEST(dns_a_to_cname_success_escaped_name_returns_error) { * dns_cache_lookup() * ================================================================ */ -TEST(dns_cache_lookup_single) { +TEST(dns_cache_lookup_success) { _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); _cleanup_(dns_answer_unrefp) DnsAnswer *ret_answer = NULL; @@ -348,6 +353,49 @@ TEST(dns_cache_lookup_single) { ASSERT_TRUE(dns_answer_contains(ret_answer, rr)); } +TEST(dns_cache_lookup_nxdomain) { + _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); + _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); + _cleanup_(dns_answer_unrefp) DnsAnswer *ret_answer = NULL; + _cleanup_(dns_packet_unrefp) DnsPacket *ret_full_packet = NULL; + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; + int query_flags, ret_rcode; + uint64_t ret_query_flags; + + put_args.key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); + ASSERT_NOT_NULL(put_args.key); + put_args.rcode = DNS_RCODE_NXDOMAIN; + dns_answer_add_soa(put_args.answer, "example.com", 3600, 0); + cache_put(&cache, &put_args); + + ASSERT_EQ(dns_cache_size(&cache), 1u); + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); + ASSERT_NOT_NULL(key); + query_flags = 0; + ASSERT_TRUE(dns_cache_lookup(&cache, key, query_flags, &ret_rcode, &ret_answer, &ret_full_packet, &ret_query_flags, NULL)); + + ASSERT_EQ(cache.n_hit, 1u); + ASSERT_EQ(cache.n_miss, 0u); + + ASSERT_EQ(ret_rcode, DNS_RCODE_NXDOMAIN); + ASSERT_EQ(ret_query_flags, (SD_RESOLVED_AUTHENTICATED | SD_RESOLVED_CONFIDENTIAL)); + + ASSERT_EQ(dns_answer_size(ret_answer), 1u); + + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_SOA, "example.com"); + ASSERT_NOT_NULL(rr); + rr->soa.mname = checked_strdup("example.com"); + rr->soa.rname = checked_strdup("root.example.com"); + rr->soa.serial = 1; + rr->soa.refresh = 1; + rr->soa.retry = 1; + rr->soa.expire = 1; + rr->soa.minimum = 3600; + ASSERT_TRUE(dns_answer_contains(ret_answer, rr)); +} + /* ================================================================ * dns_cache_dump_to_json() * ================================================================ */ From e936ec5e4ffedaab2bd249135bbf71724282ae34 Mon Sep 17 00:00:00 2001 From: James Coglan Date: Thu, 23 May 2024 10:26:41 +0100 Subject: [PATCH 11/20] resolved: test cache misses --- src/resolve/test-dns-cache.c | 53 ++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/resolve/test-dns-cache.c b/src/resolve/test-dns-cache.c index 4d87fd85b3e..f94a405d04b 100644 --- a/src/resolve/test-dns-cache.c +++ b/src/resolve/test-dns-cache.c @@ -316,6 +316,28 @@ TEST(dns_a_to_cname_success_escaped_name_returns_error) { * dns_cache_lookup() * ================================================================ */ +TEST(dns_cache_lookup_miss) { + _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); + _cleanup_(dns_answer_unrefp) DnsAnswer *ret_answer = NULL; + _cleanup_(dns_packet_unrefp) DnsPacket *ret_full_packet = NULL; + _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; + int query_flags, ret_rcode; + uint64_t ret_query_flags; + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); + ASSERT_NOT_NULL(key); + query_flags = 0; + ASSERT_FALSE(dns_cache_lookup(&cache, key, query_flags, &ret_rcode, &ret_answer, &ret_full_packet, &ret_query_flags, NULL)); + + ASSERT_EQ(cache.n_hit, 0u); + ASSERT_EQ(cache.n_miss, 1u); + + ASSERT_EQ(ret_rcode, DNS_RCODE_SUCCESS); + ASSERT_EQ(ret_query_flags, 0u); + + ASSERT_EQ(dns_answer_size(ret_answer), 0u); +} + TEST(dns_cache_lookup_success) { _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); @@ -396,6 +418,37 @@ TEST(dns_cache_lookup_nxdomain) { ASSERT_TRUE(dns_answer_contains(ret_answer, rr)); } +TEST(dns_cache_lookup_any_always_misses) { + _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); + _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); + _cleanup_(dns_answer_unrefp) DnsAnswer *ret_answer = NULL; + _cleanup_(dns_packet_unrefp) DnsPacket *ret_full_packet = NULL; + _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; + int query_flags, ret_rcode; + uint64_t ret_query_flags; + + put_args.key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); + ASSERT_NOT_NULL(put_args.key); + put_args.rcode = DNS_RCODE_SUCCESS; + answer_add_a(&put_args, put_args.key, 0xc0a8017f, 3600, DNS_ANSWER_CACHEABLE); + cache_put(&cache, &put_args); + + ASSERT_EQ(dns_cache_size(&cache), 1u); + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_ANY, "www.example.com"); + ASSERT_NOT_NULL(key); + query_flags = 0; + ASSERT_FALSE(dns_cache_lookup(&cache, key, query_flags, &ret_rcode, &ret_answer, &ret_full_packet, &ret_query_flags, NULL)); + + ASSERT_EQ(cache.n_hit, 0u); + ASSERT_EQ(cache.n_miss, 1u); + + ASSERT_EQ(ret_rcode, DNS_RCODE_SUCCESS); + ASSERT_EQ(ret_query_flags, 0u); + + ASSERT_EQ(dns_answer_size(ret_answer), 0u); +} + /* ================================================================ * dns_cache_dump_to_json() * ================================================================ */ From 6dd377ad708317aba2e0c3cab37db6313098c2c9 Mon Sep 17 00:00:00 2001 From: James Coglan Date: Fri, 24 May 2024 10:58:30 +0100 Subject: [PATCH 12/20] resolved: tests for dns_cache_dump() --- src/resolve/test-dns-cache.c | 90 ++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/src/resolve/test-dns-cache.c b/src/resolve/test-dns-cache.c index f94a405d04b..150b118e520 100644 --- a/src/resolve/test-dns-cache.c +++ b/src/resolve/test-dns-cache.c @@ -3,6 +3,9 @@ #include #include "dns-type.h" +#include "fd-util.h" +#include "fileio.h" +#include "fs-util.h" #include "log.h" #include "resolve-util.h" #include "resolved-def.h" @@ -12,6 +15,7 @@ #include "resolved-dns-packet.h" #include "resolved-dns-rr.h" #include "tests.h" +#include "tmpfile-util.h" static DnsCache new_cache(void) { return (DnsCache) {}; @@ -449,6 +453,92 @@ TEST(dns_cache_lookup_any_always_misses) { ASSERT_EQ(dns_answer_size(ret_answer), 0u); } +/* ================================================================ + * dns_cache_dump() + * ================================================================ */ + +static int cmpstring(const void *a, const void *b) { + ASSERT_NOT_NULL(a); + ASSERT_NOT_NULL(b); + + return strcmp(*(const char **)a, *(const char **)b); +} + +static void check_dump_contents(FILE *f, const char **expected, size_t n) { + char *actual[n]; + rewind(f); + + for (size_t i = 0; i < n; i++) { + size_t length = read_line(f, 1024, &actual[i]); + ASSERT_GT(length, 0u); + } + + qsort(actual, n, sizeof(char *), cmpstring); + + for (size_t i = 0; i < n; i++) + ASSERT_STREQ(actual[i], expected[i]); + + for (size_t i = 0; i < n; i++) + free(actual[i]); +} + +TEST(dns_cache_dump_single_a) { + _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); + _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); + + put_args.key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); + ASSERT_NOT_NULL(put_args.key); + put_args.rcode = DNS_RCODE_SUCCESS; + answer_add_a(&put_args, put_args.key, 0xc0a8017f, 3600, DNS_ANSWER_CACHEABLE); + cache_put(&cache, &put_args); + + ASSERT_EQ(dns_cache_size(&cache), 1u); + + _cleanup_(unlink_tempfilep) char p[] = "/tmp/dns-cache-dump-single-a-XXXXXX"; + _cleanup_fclose_ FILE *f = NULL; + fmkostemp_safe(p, "r+", &f); + dns_cache_dump(&cache, f); + + const char *expected[] = { + "\twww.example.com IN A 192.168.1.127" + }; + check_dump_contents(f, expected, 1); +} + +TEST(dns_cache_dump_a_with_cname) { + _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); + _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); + _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; + + put_args.key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); + ASSERT_NOT_NULL(put_args.key); + put_args.rcode = DNS_RCODE_SUCCESS; + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_CNAME, "www.example.com"); + ASSERT_NOT_NULL(key); + answer_add_cname(&put_args, key, "example.com", 3600, DNS_ANSWER_CACHEABLE); + + dns_resource_key_unref(key); + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "example.com"); + ASSERT_NOT_NULL(key); + answer_add_a(&put_args, key, 0xc0a8017f, 3600, DNS_ANSWER_CACHEABLE); + + cache_put(&cache, &put_args); + + ASSERT_EQ(dns_cache_size(&cache), 2u); + + _cleanup_(unlink_tempfilep) char p[] = "/tmp/dns-cache-dump-a-with-cname-XXXXXX"; + _cleanup_fclose_ FILE *f = NULL; + fmkostemp_safe(p, "r+", &f); + dns_cache_dump(&cache, f); + + const char *expected[] = { + "\texample.com IN A 192.168.1.127", + "\twww.example.com IN CNAME example.com" + }; + check_dump_contents(f, expected, 2); +} + /* ================================================================ * dns_cache_dump_to_json() * ================================================================ */ From 043a29b13969da21afb5584d5f8847af06176f82 Mon Sep 17 00:00:00 2001 From: James Coglan Date: Tue, 18 Jun 2024 15:02:30 +0100 Subject: [PATCH 13/20] resolved: tests for dns_cache_lookup() returning the most recent input --- src/resolve/test-dns-cache.c | 95 ++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/src/resolve/test-dns-cache.c b/src/resolve/test-dns-cache.c index 150b118e520..e0f99d51c14 100644 --- a/src/resolve/test-dns-cache.c +++ b/src/resolve/test-dns-cache.c @@ -379,6 +379,101 @@ TEST(dns_cache_lookup_success) { ASSERT_TRUE(dns_answer_contains(ret_answer, rr)); } +TEST(dns_cache_lookup_returns_most_recent_response) { + _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); + _cleanup_(put_args_unrefp) PutArgs args1 = mk_put_args(), args2 = mk_put_args(); + _cleanup_(dns_answer_unrefp) DnsAnswer *ret_answer = NULL; + _cleanup_(dns_packet_unrefp) DnsPacket *ret_full_packet = NULL; + _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; + DnsResourceRecord *rr = NULL; + int query_flags, ret_rcode; + uint64_t ret_query_flags; + + args1.key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); + ASSERT_NOT_NULL(args1.key); + args1.rcode = DNS_RCODE_SUCCESS; + answer_add_a(&args1, args1.key, 0xc0a8017f, 3600, DNS_ANSWER_CACHEABLE); + cache_put(&cache, &args1); + + args2.key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); + ASSERT_NOT_NULL(args2.key); + args2.rcode = DNS_RCODE_SUCCESS; + answer_add_a(&args2, args2.key, 0x7f01a8c0, 2400, DNS_ANSWER_CACHEABLE); + cache_put(&cache, &args2); + + ASSERT_EQ(dns_cache_size(&cache), 1u); + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); + ASSERT_NOT_NULL(key); + query_flags = 0; + ASSERT_TRUE(dns_cache_lookup(&cache, key, query_flags, &ret_rcode, &ret_answer, &ret_full_packet, &ret_query_flags, NULL)); + + ASSERT_EQ(cache.n_hit, 1u); + ASSERT_EQ(cache.n_miss, 0u); + + ASSERT_EQ(ret_rcode, DNS_RCODE_SUCCESS); + ASSERT_EQ(ret_query_flags, SD_RESOLVED_CONFIDENTIAL); + + ASSERT_EQ(dns_answer_size(ret_answer), 1u); + + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); + ASSERT_NOT_NULL(rr); + rr->a.in_addr.s_addr = htobe32(0x7f01a8c0); + ASSERT_TRUE(dns_answer_contains(ret_answer, rr)); + dns_resource_record_unref(rr); + + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); + ASSERT_NOT_NULL(rr); + rr->a.in_addr.s_addr = htobe32(0xc0a8017f); + ASSERT_FALSE(dns_answer_contains(ret_answer, rr)); + dns_resource_record_unref(rr); +} + +TEST(dns_cache_lookup_retains_multiple_answers_from_one_response) { + _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); + _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); + _cleanup_(dns_answer_unrefp) DnsAnswer *ret_answer = NULL; + _cleanup_(dns_packet_unrefp) DnsPacket *ret_full_packet = NULL; + _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; + DnsResourceRecord *rr = NULL; + int query_flags, ret_rcode; + uint64_t ret_query_flags; + + put_args.key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); + ASSERT_NOT_NULL(put_args.key); + put_args.rcode = DNS_RCODE_SUCCESS; + answer_add_a(&put_args, put_args.key, 0xc0a8017f, 3600, DNS_ANSWER_CACHEABLE); + answer_add_a(&put_args, put_args.key, 0x7f01a8cc, 3600, DNS_ANSWER_CACHEABLE); + cache_put(&cache, &put_args); + + ASSERT_EQ(dns_cache_size(&cache), 1u); + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); + ASSERT_NOT_NULL(key); + query_flags = 0; + ASSERT_TRUE(dns_cache_lookup(&cache, key, query_flags, &ret_rcode, &ret_answer, &ret_full_packet, &ret_query_flags, NULL)); + + ASSERT_EQ(cache.n_hit, 1u); + ASSERT_EQ(cache.n_miss, 0u); + + ASSERT_EQ(ret_rcode, DNS_RCODE_SUCCESS); + ASSERT_EQ(ret_query_flags, SD_RESOLVED_CONFIDENTIAL); + + ASSERT_EQ(dns_answer_size(ret_answer), 2u); + + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); + ASSERT_NOT_NULL(rr); + rr->a.in_addr.s_addr = htobe32(0x7f01a8cc); + ASSERT_TRUE(dns_answer_contains(ret_answer, rr)); + dns_resource_record_unref(rr); + + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); + ASSERT_NOT_NULL(rr); + rr->a.in_addr.s_addr = htobe32(0xc0a8017f); + ASSERT_TRUE(dns_answer_contains(ret_answer, rr)); + dns_resource_record_unref(rr); +} + TEST(dns_cache_lookup_nxdomain) { _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); From 5b61b34ad05fdb16cfc2b65faeccb25fe8a6d7a7 Mon Sep 17 00:00:00 2001 From: James Coglan Date: Tue, 18 Jun 2024 16:19:07 +0100 Subject: [PATCH 14/20] resolved: tests for dns_cache_lookup(); mDNS and multiple matching entries --- src/resolve/test-dns-cache.c | 204 +++++++++++++++++++++++++++++++++++ 1 file changed, 204 insertions(+) diff --git a/src/resolve/test-dns-cache.c b/src/resolve/test-dns-cache.c index e0f99d51c14..eb21aa56204 100644 --- a/src/resolve/test-dns-cache.c +++ b/src/resolve/test-dns-cache.c @@ -169,6 +169,98 @@ TEST(dns_a_success_non_matching_name_is_cached) { ASSERT_FALSE(dns_cache_is_empty(&cache)); } +TEST(dns_a_success_mdns_no_key_is_cached) { + _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); + _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); + _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; + + put_args.protocol = DNS_PROTOCOL_MDNS; + put_args.rcode = DNS_RCODE_SUCCESS; + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "example.com"); + ASSERT_NOT_NULL(key); + answer_add_a(&put_args, key, 0xc0a8017f, 3600, DNS_ANSWER_CACHEABLE); + + ASSERT_OK(cache_put(&cache, &put_args)); + ASSERT_FALSE(dns_cache_is_empty(&cache)); +} + +TEST(dns_a_success_mdns_update_existing) { + _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); + _cleanup_(put_args_unrefp) PutArgs args1 = mk_put_args(), args2 = mk_put_args(); + DnsResourceKey *key = NULL; + + args1.protocol = DNS_PROTOCOL_MDNS; + args1.rcode = DNS_RCODE_SUCCESS; + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "example.com"); + ASSERT_NOT_NULL(key); + answer_add_a(&args1, key, 0xc0a8017f, 3600, DNS_ANSWER_CACHEABLE | DNS_ANSWER_SHARED_OWNER); + dns_resource_key_unref(key); + + ASSERT_OK(cache_put(&cache, &args1)); + + args2.protocol = DNS_PROTOCOL_MDNS; + args2.rcode = DNS_RCODE_SUCCESS; + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "example.com"); + ASSERT_NOT_NULL(key); + answer_add_a(&args2, key, 0xc0a8017f, 2400, DNS_ANSWER_CACHEABLE | DNS_ANSWER_SHARED_OWNER); + dns_resource_key_unref(key); + + ASSERT_OK(cache_put(&cache, &args2)); + + ASSERT_EQ(dns_cache_size(&cache), 1u); +} + +TEST(dns_a_success_mdns_zero_ttl_removes_existing) { + _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); + _cleanup_(put_args_unrefp) PutArgs args1 = mk_put_args(), args2 = mk_put_args(); + DnsResourceKey *key = NULL; + + args1.protocol = DNS_PROTOCOL_MDNS; + args1.rcode = DNS_RCODE_SUCCESS; + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "example.com"); + ASSERT_NOT_NULL(key); + answer_add_a(&args1, key, 0xc0a8017f, 3600, DNS_ANSWER_CACHEABLE | DNS_ANSWER_SHARED_OWNER); + dns_resource_key_unref(key); + + ASSERT_OK(cache_put(&cache, &args1)); + + args2.protocol = DNS_PROTOCOL_MDNS; + args2.rcode = DNS_RCODE_SUCCESS; + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "example.com"); + ASSERT_NOT_NULL(key); + answer_add_a(&args2, key, 0xc0a8017f, 0, DNS_ANSWER_CACHEABLE | DNS_ANSWER_SHARED_OWNER); + dns_resource_key_unref(key); + + ASSERT_OK(cache_put(&cache, &args2)); + + ASSERT_TRUE(dns_cache_is_empty(&cache)); +} + +TEST(dns_a_success_mdns_same_key_different_payloads) { + _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); + _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); + DnsResourceKey *key = NULL; + + put_args.protocol = DNS_PROTOCOL_MDNS; + put_args.rcode = DNS_RCODE_SUCCESS; + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "example.com"); + ASSERT_NOT_NULL(key); + answer_add_a(&put_args, key, 0xc0a8017f, 3600, DNS_ANSWER_CACHEABLE | DNS_ANSWER_SHARED_OWNER); + dns_resource_key_unref(key); + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "example.com"); + ASSERT_NOT_NULL(key); + answer_add_a(&put_args, key, 0x7f01a8cc, 2400, DNS_ANSWER_CACHEABLE | DNS_ANSWER_SHARED_OWNER); + dns_resource_key_unref(key); + + ASSERT_EQ(dns_answer_size(put_args.answer), 2u); + + ASSERT_OK(cache_put(&cache, &put_args)); + ASSERT_EQ(dns_cache_size(&cache), 1u); +} + TEST(dns_a_success_escaped_key_returns_error) { _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); @@ -548,6 +640,118 @@ TEST(dns_cache_lookup_any_always_misses) { ASSERT_EQ(dns_answer_size(ret_answer), 0u); } +TEST(dns_cache_lookup_mdns_multiple_shared_responses_are_cached) { + _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); + _cleanup_(put_args_unrefp) PutArgs args1 = mk_put_args(), args2 = mk_put_args(); + _cleanup_(dns_answer_unrefp) DnsAnswer *ret_answer = NULL; + _cleanup_(dns_packet_unrefp) DnsPacket *ret_full_packet = NULL; + DnsResourceKey *key = NULL; + DnsResourceRecord *rr = NULL; + int query_flags, ret_rcode; + uint64_t ret_query_flags; + + args1.protocol = DNS_PROTOCOL_MDNS; + args1.rcode = DNS_RCODE_SUCCESS; + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "example.com"); + ASSERT_NOT_NULL(key); + answer_add_a(&args1, key, 0xc0a8017f, 3600, DNS_ANSWER_CACHEABLE | DNS_ANSWER_SHARED_OWNER); + ASSERT_OK(cache_put(&cache, &args1)); + dns_resource_key_unref(key); + + args2.protocol = DNS_PROTOCOL_MDNS; + args2.rcode = DNS_RCODE_SUCCESS; + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "example.com"); + ASSERT_NOT_NULL(key); + answer_add_a(&args2, key, 0x7f01a8cc, 3600, DNS_ANSWER_CACHEABLE | DNS_ANSWER_SHARED_OWNER); + ASSERT_OK(cache_put(&cache, &args2)); + dns_resource_key_unref(key); + + ASSERT_FALSE(dns_cache_is_empty(&cache)); + ASSERT_EQ(dns_cache_size(&cache), 1u); + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "example.com"); + ASSERT_NOT_NULL(key); + query_flags = 0; + ASSERT_TRUE(dns_cache_lookup(&cache, key, query_flags, &ret_rcode, &ret_answer, &ret_full_packet, &ret_query_flags, NULL)); + dns_resource_key_unref(key); + + ASSERT_EQ(cache.n_hit, 1u); + ASSERT_EQ(cache.n_miss, 0u); + + ASSERT_EQ(ret_rcode, DNS_RCODE_SUCCESS); + ASSERT_EQ(ret_query_flags, SD_RESOLVED_CONFIDENTIAL); + + ASSERT_EQ(dns_answer_size(ret_answer), 2u); + + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, "example.com"); + ASSERT_NOT_NULL(rr); + rr->a.in_addr.s_addr = htobe32(0xc0a8017f); + ASSERT_TRUE(dns_answer_contains(ret_answer, rr)); + dns_resource_record_unref(rr); + + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, "example.com"); + ASSERT_NOT_NULL(rr); + rr->a.in_addr.s_addr = htobe32(0x7f01a8cc); + ASSERT_TRUE(dns_answer_contains(ret_answer, rr)); + dns_resource_record_unref(rr); +} + +TEST(dns_cache_lookup_mdns_multiple_unshared_responses_are_not_cached) { + _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); + _cleanup_(put_args_unrefp) PutArgs args1 = mk_put_args(), args2 = mk_put_args(); + _cleanup_(dns_answer_unrefp) DnsAnswer *ret_answer = NULL; + _cleanup_(dns_packet_unrefp) DnsPacket *ret_full_packet = NULL; + DnsResourceKey *key = NULL; + DnsResourceRecord *rr = NULL; + int query_flags, ret_rcode; + uint64_t ret_query_flags; + + args1.protocol = DNS_PROTOCOL_MDNS; + args1.rcode = DNS_RCODE_SUCCESS; + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "example.com"); + ASSERT_NOT_NULL(key); + answer_add_a(&args1, key, 0xc0a8017f, 3600, DNS_ANSWER_CACHEABLE); + ASSERT_OK(cache_put(&cache, &args1)); + dns_resource_key_unref(key); + + args2.protocol = DNS_PROTOCOL_MDNS; + args2.rcode = DNS_RCODE_SUCCESS; + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "example.com"); + ASSERT_NOT_NULL(key); + answer_add_a(&args2, key, 0x7f01a8cc, 3600, DNS_ANSWER_CACHEABLE); + ASSERT_OK(cache_put(&cache, &args2)); + dns_resource_key_unref(key); + + ASSERT_FALSE(dns_cache_is_empty(&cache)); + ASSERT_EQ(dns_cache_size(&cache), 1u); + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "example.com"); + ASSERT_NOT_NULL(key); + query_flags = 0; + ASSERT_TRUE(dns_cache_lookup(&cache, key, query_flags, &ret_rcode, &ret_answer, &ret_full_packet, &ret_query_flags, NULL)); + dns_resource_key_unref(key); + + ASSERT_EQ(cache.n_hit, 1u); + ASSERT_EQ(cache.n_miss, 0u); + + ASSERT_EQ(ret_rcode, DNS_RCODE_SUCCESS); + ASSERT_EQ(ret_query_flags, SD_RESOLVED_CONFIDENTIAL); + + ASSERT_EQ(dns_answer_size(ret_answer), 1u); + + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, "example.com"); + ASSERT_NOT_NULL(rr); + rr->a.in_addr.s_addr = htobe32(0xc0a8017f); + ASSERT_FALSE(dns_answer_contains(ret_answer, rr)); + dns_resource_record_unref(rr); + + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, "example.com"); + ASSERT_NOT_NULL(rr); + rr->a.in_addr.s_addr = htobe32(0x7f01a8cc); + ASSERT_TRUE(dns_answer_contains(ret_answer, rr)); + dns_resource_record_unref(rr); +} + /* ================================================================ * dns_cache_dump() * ================================================================ */ From 42547022fd4eed72d4ce26b2c15d89219ab723b4 Mon Sep 17 00:00:00 2001 From: James Coglan Date: Tue, 18 Jun 2024 17:57:55 +0100 Subject: [PATCH 15/20] resolved: tests for dns_cache_export_shared_to_packet() --- src/resolve/test-dns-cache.c | 136 +++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) diff --git a/src/resolve/test-dns-cache.c b/src/resolve/test-dns-cache.c index eb21aa56204..e3dd89b9e8c 100644 --- a/src/resolve/test-dns-cache.c +++ b/src/resolve/test-dns-cache.c @@ -752,6 +752,142 @@ TEST(dns_cache_lookup_mdns_multiple_unshared_responses_are_not_cached) { dns_resource_record_unref(rr); } +/* ================================================================ + * dns_cache_export_shared_to_packet() + * ================================================================ */ + +TEST(dns_cache_export_shared_to_packet) { + _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); + _cleanup_(put_args_unrefp) PutArgs args1 = mk_put_args(), args2 = mk_put_args(); + _cleanup_(dns_packet_unrefp) DnsPacket *packet = NULL; + DnsResourceKey *key = NULL; + + args1.protocol = DNS_PROTOCOL_MDNS; + args1.rcode = DNS_RCODE_SUCCESS; + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "shared.example.com"); + ASSERT_NOT_NULL(key); + answer_add_a(&args1, key, 0xc0a8017f, 3600, DNS_ANSWER_CACHEABLE | DNS_ANSWER_SHARED_OWNER); + dns_resource_key_unref(key); + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "unshared.example.com"); + ASSERT_NOT_NULL(key); + answer_add_a(&args1, key, 0xa87f01c0, 2400, DNS_ANSWER_CACHEABLE); + dns_resource_key_unref(key); + + cache_put(&cache, &args1); + + args2.protocol = DNS_PROTOCOL_DNS; + args2.key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "dns.example.com"); + ASSERT_NOT_NULL(args2.key); + args2.rcode = DNS_RCODE_SUCCESS; + answer_add_a(&args2, args2.key, 0xa9fe0100, 2400, DNS_ANSWER_CACHEABLE); + cache_put(&cache, &args2); + + dns_packet_new(&packet, DNS_PROTOCOL_MDNS, 0, DNS_PACKET_SIZE_MAX); + ASSERT_NOT_NULL(packet); + ASSERT_OK(dns_cache_export_shared_to_packet(&cache, packet, 0, 50)); + + const uint8_t data[] = { + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + + /* name */ 0x06, 's', 'h', 'a', 'r', 'e', 'd', + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + /* A */ 0x00, 0x01, + /* IN */ 0x00, 0x01, + /* ttl */ 0x00, 0x00, 0x0e, 0x10, + /* rdata */ 0x00, 0x04, + /* ip */ 0xc0, 0xa8, 0x01, 0x7f + }; + + ASSERT_EQ(packet->size, sizeof(data)); + ASSERT_EQ(memcmp(DNS_PACKET_DATA(packet), data, sizeof(data)), 0); +} + +TEST(dns_cache_export_shared_to_packet_multi) { + _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); + _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); + _cleanup_(dns_packet_unrefp) DnsPacket *packet = NULL; + DnsResourceKey *key = NULL; + + put_args.protocol = DNS_PROTOCOL_MDNS; + put_args.rcode = DNS_RCODE_SUCCESS; + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "shared1.example.com"); + ASSERT_NOT_NULL(key); + answer_add_a(&put_args, key, 0xc0a8017f, 3600, DNS_ANSWER_CACHEABLE | DNS_ANSWER_SHARED_OWNER); + dns_resource_key_unref(key); + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "unshared.example.com"); + ASSERT_NOT_NULL(key); + answer_add_a(&put_args, key, 0xa87f01c0, 2400, DNS_ANSWER_CACHEABLE); + dns_resource_key_unref(key); + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "shared2.example.com"); + ASSERT_NOT_NULL(key); + answer_add_a(&put_args, key, 0x7f01a8cc, 1800, DNS_ANSWER_CACHEABLE | DNS_ANSWER_SHARED_OWNER); + dns_resource_key_unref(key); + + cache_put(&cache, &put_args); + + dns_packet_new(&packet, DNS_PROTOCOL_MDNS, 0, DNS_PACKET_SIZE_MAX); + ASSERT_NOT_NULL(packet); + ASSERT_OK(dns_cache_export_shared_to_packet(&cache, packet, 0, 1)); + + const uint8_t data1[] = { + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + + /* name */ 0x07, 's', 'h', 'a', 'r', 'e', 'd', '1', + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + /* A */ 0x00, 0x01, + /* IN */ 0x00, 0x01, + /* ttl */ 0x00, 0x00, 0x0e, 0x10, + /* rdata */ 0x00, 0x04, + /* ip */ 0xc0, 0xa8, 0x01, 0x7f + }; + + const uint8_t data2[] = { + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + + /* name */ 0x07, 's', 'h', 'a', 'r', 'e', 'd', '2', + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + /* A */ 0x00, 0x01, + /* IN */ 0x00, 0x01, + /* ttl */ 0x00, 0x00, 0x07, 0x08, + /* rdata */ 0x00, 0x04, + /* ip */ 0x7f, 0x01, 0xa8, 0xcc + }; + + size_t size1 = sizeof(data1), size2 = sizeof(data2); + + /* cache key order is not deterministic; the packets could come out in either order */ + + if (memcmp(DNS_PACKET_DATA(packet), data1, size1) == 0) { + ASSERT_EQ(packet->size, size1); + ASSERT_EQ(memcmp(DNS_PACKET_DATA(packet), data1, size1), 0); + + ASSERT_EQ(packet->more->size, size2); + ASSERT_EQ(memcmp(DNS_PACKET_DATA(packet->more), data2, size2), 0); + } else { + ASSERT_EQ(packet->size, size2); + ASSERT_EQ(memcmp(DNS_PACKET_DATA(packet), data2, size2), 0); + + ASSERT_EQ(packet->more->size, size1); + ASSERT_EQ(memcmp(DNS_PACKET_DATA(packet->more), data1, size1), 0); + } + + ASSERT_NULL(packet->more->more); +} + /* ================================================================ * dns_cache_dump() * ================================================================ */ From 3f6ae4a06271f0a84b1e5bff0197c09281397905 Mon Sep 17 00:00:00 2001 From: James Coglan Date: Wed, 19 Jun 2024 10:05:29 +0100 Subject: [PATCH 16/20] resolved: tests for dns_cache_check_conflicts() --- src/resolve/test-dns-cache.c | 55 ++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/src/resolve/test-dns-cache.c b/src/resolve/test-dns-cache.c index e3dd89b9e8c..0dd69a541d2 100644 --- a/src/resolve/test-dns-cache.c +++ b/src/resolve/test-dns-cache.c @@ -752,6 +752,61 @@ TEST(dns_cache_lookup_mdns_multiple_unshared_responses_are_not_cached) { dns_resource_record_unref(rr); } +/* ================================================================ + * dns_cache_check_conflicts() + * ================================================================ */ + +TEST(dns_cache_check_conflicts_same_key_and_owner) { + _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); + _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + + put_args.key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "ns1.example.com"); + ASSERT_NOT_NULL(put_args.key); + answer_add_a(&put_args, put_args.key, 0xc0a8017f, 3600, DNS_ANSWER_CACHEABLE); + cache_put(&cache, &put_args); + + union in_addr_union owner_addr = { .in.s_addr = htobe32(0x01020304) }; + + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, "ns1.example.com"); + ASSERT_NOT_NULL(rr); + ASSERT_FALSE(dns_cache_check_conflicts(&cache, rr, AF_INET, &owner_addr)); +} + +TEST(dns_cache_check_conflicts_same_key_different_owner) { + _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); + _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + + put_args.key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "ns1.example.com"); + ASSERT_NOT_NULL(put_args.key); + answer_add_a(&put_args, put_args.key, 0xc0a8017f, 3600, DNS_ANSWER_CACHEABLE); + cache_put(&cache, &put_args); + + union in_addr_union owner_addr = { .in.s_addr = htobe32(0x01020305) }; + + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, "ns1.example.com"); + ASSERT_NOT_NULL(rr); + ASSERT_TRUE(dns_cache_check_conflicts(&cache, rr, AF_INET, &owner_addr)); +} + +TEST(dns_cache_check_conflicts_different_key) { + _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); + _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + + put_args.key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "ns2.example.com"); + ASSERT_NOT_NULL(put_args.key); + answer_add_a(&put_args, put_args.key, 0xc0a8017f, 3600, DNS_ANSWER_CACHEABLE); + cache_put(&cache, &put_args); + + union in_addr_union owner_addr = { .in.s_addr = htobe32(0x01020305) }; + + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, "ns1.example.com"); + ASSERT_NOT_NULL(rr); + ASSERT_FALSE(dns_cache_check_conflicts(&cache, rr, AF_INET, &owner_addr)); +} + /* ================================================================ * dns_cache_export_shared_to_packet() * ================================================================ */ From 7137086fa0a42abb2420354e1e7e1b99372c4bdd Mon Sep 17 00:00:00 2001 From: James Coglan Date: Wed, 19 Jun 2024 10:39:01 +0100 Subject: [PATCH 17/20] resolves: tests for dns_cache_prune() --- src/resolve/test-dns-cache.c | 38 ++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/resolve/test-dns-cache.c b/src/resolve/test-dns-cache.c index 0dd69a541d2..a597a112ad6 100644 --- a/src/resolve/test-dns-cache.c +++ b/src/resolve/test-dns-cache.c @@ -752,6 +752,44 @@ TEST(dns_cache_lookup_mdns_multiple_unshared_responses_are_not_cached) { dns_resource_record_unref(rr); } +/* ================================================================ + * dns_cache_prune(), dns_cache_expiry_in_one_second() + * ================================================================ */ + +TEST(dns_cache_prune) { + _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); + _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); + DnsResourceKey *key = NULL; + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "ns1.example.com"); + ASSERT_NOT_NULL(key); + answer_add_a(&put_args, key, 0xc0a8017f, 1, DNS_ANSWER_CACHEABLE); + dns_resource_key_unref(key); + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "ns2.example.com"); + ASSERT_NOT_NULL(key); + answer_add_a(&put_args, key, 0x7f01a8cc, 3, DNS_ANSWER_CACHEABLE); + dns_resource_key_unref(key); + + cache_put(&cache, &put_args); + + dns_cache_prune(&cache); + ASSERT_EQ(dns_cache_size(&cache), 2u); + ASSERT_TRUE(dns_cache_expiry_in_one_second(&cache, now(CLOCK_BOOTTIME))); + + sleep(2); + + dns_cache_prune(&cache); + ASSERT_EQ(dns_cache_size(&cache), 1u); + ASSERT_TRUE(dns_cache_expiry_in_one_second(&cache, now(CLOCK_BOOTTIME))); + + sleep(2); + + dns_cache_prune(&cache); + ASSERT_TRUE(dns_cache_is_empty(&cache)); + ASSERT_FALSE(dns_cache_expiry_in_one_second(&cache, now(CLOCK_BOOTTIME))); +} + /* ================================================================ * dns_cache_check_conflicts() * ================================================================ */ From 6b864ea81373be71ce3ae89fb82890b50e0acb38 Mon Sep 17 00:00:00 2001 From: James Coglan Date: Wed, 19 Jun 2024 11:29:44 +0100 Subject: [PATCH 18/20] resolved: test that pseudo classes and types are not cached --- src/resolve/test-dns-cache.c | 88 ++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/src/resolve/test-dns-cache.c b/src/resolve/test-dns-cache.c index a597a112ad6..e5efd540ace 100644 --- a/src/resolve/test-dns-cache.c +++ b/src/resolve/test-dns-cache.c @@ -113,6 +113,16 @@ static void answer_add_cname(PutArgs *args, DnsResourceKey *key, const char *ali dns_answer_add(args->answer, rr, 1, flags, NULL); } +static void answer_add_opt(PutArgs *args, DnsResourceKey *key, int ttl, DnsAnswerFlags flags) { + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + + rr = dns_resource_record_new(key); + ASSERT_NOT_NULL(rr); + rr->opt.data_size = 0; + rr->ttl = ttl; + dns_answer_add(args->answer, rr, 1, flags, NULL); +} + #define BY_IDX(json, idx) sd_json_variant_by_index(json, idx) #define BY_KEY(json, key) sd_json_variant_by_key(json, key) #define INTVAL(json) sd_json_variant_integer(json) @@ -290,6 +300,45 @@ TEST(dns_a_success_empty_answer_is_not_cached) { ASSERT_TRUE(dns_cache_is_empty(&cache)); } +TEST(dns_a_success_any_class_is_not_cached) { + _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); + _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); + + put_args.key = dns_resource_key_new(DNS_CLASS_ANY, DNS_TYPE_A, "www.example.com"); + ASSERT_NOT_NULL(put_args.key); + put_args.rcode = DNS_RCODE_SUCCESS; + answer_add_a(&put_args, put_args.key, 0xc0a8017f, 3600, DNS_ANSWER_CACHEABLE); + + ASSERT_OK(cache_put(&cache, &put_args)); + ASSERT_TRUE(dns_cache_is_empty(&cache)); +} + +TEST(dns_a_success_any_type_not_cached) { + _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); + _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); + + put_args.key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_ANY, "www.example.com"); + ASSERT_NOT_NULL(put_args.key); + put_args.rcode = DNS_RCODE_SUCCESS; + answer_add_opt(&put_args, put_args.key, 3600, DNS_ANSWER_CACHEABLE); + + ASSERT_OK(cache_put(&cache, &put_args)); + ASSERT_TRUE(dns_cache_is_empty(&cache)); +} + +TEST(dns_a_success_opt_not_cached) { + _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); + _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); + + put_args.key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_OPT, "www.example.com"); + ASSERT_NOT_NULL(put_args.key); + put_args.rcode = DNS_RCODE_SUCCESS; + answer_add_opt(&put_args, put_args.key, 3600, DNS_ANSWER_CACHEABLE); + + ASSERT_OK(cache_put(&cache, &put_args)); + ASSERT_TRUE(dns_cache_is_empty(&cache)); +} + TEST(dns_a_nxdomain_is_cached) { _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); @@ -303,6 +352,45 @@ TEST(dns_a_nxdomain_is_cached) { ASSERT_FALSE(dns_cache_is_empty(&cache)); } +TEST(dns_a_nxdomain_any_class_is_not_cached) { + _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); + _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); + + put_args.key = dns_resource_key_new(DNS_CLASS_ANY, DNS_TYPE_A, "www.example.com"); + ASSERT_NOT_NULL(put_args.key); + put_args.rcode = DNS_RCODE_NXDOMAIN; + dns_answer_add_soa(put_args.answer, "example.com", 3600, 0); + + ASSERT_OK(cache_put(&cache, &put_args)); + ASSERT_TRUE(dns_cache_is_empty(&cache)); +} + +TEST(dns_a_nxdomain_any_type_not_cached) { + _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); + _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); + + put_args.key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_ANY, "www.example.com"); + ASSERT_NOT_NULL(put_args.key); + put_args.rcode = DNS_RCODE_NXDOMAIN; + dns_answer_add_soa(put_args.answer, "example.com", 3600, 0); + + ASSERT_OK(cache_put(&cache, &put_args)); + ASSERT_TRUE(dns_cache_is_empty(&cache)); +} + +TEST(dns_a_nxdomain_opt_not_cached) { + _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); + _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); + + put_args.key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_OPT, "www.example.com"); + ASSERT_NOT_NULL(put_args.key); + put_args.rcode = DNS_RCODE_NXDOMAIN; + dns_answer_add_soa(put_args.answer, "example.com", 3600, 0); + + ASSERT_OK(cache_put(&cache, &put_args)); + ASSERT_TRUE(dns_cache_is_empty(&cache)); +} + TEST(dns_a_servfail_is_cached) { _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); From 1074c9001da609f585adc1cbbf0ee2130b9aec35 Mon Sep 17 00:00:00 2001 From: James Coglan Date: Wed, 19 Jun 2024 12:01:06 +0100 Subject: [PATCH 19/20] resolved: tests for dns_cache_put() for NXDOMAIN with no SOA --- src/resolve/test-dns-cache.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/resolve/test-dns-cache.c b/src/resolve/test-dns-cache.c index e5efd540ace..3411bb55fae 100644 --- a/src/resolve/test-dns-cache.c +++ b/src/resolve/test-dns-cache.c @@ -352,6 +352,18 @@ TEST(dns_a_nxdomain_is_cached) { ASSERT_FALSE(dns_cache_is_empty(&cache)); } +TEST(dns_a_nxdomain_no_soa_not_cached) { + _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); + _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); + + put_args.key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); + ASSERT_NOT_NULL(put_args.key); + put_args.rcode = DNS_RCODE_NXDOMAIN; + + ASSERT_OK(cache_put(&cache, &put_args)); + ASSERT_TRUE(dns_cache_is_empty(&cache)); +} + TEST(dns_a_nxdomain_any_class_is_not_cached) { _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); From f5dd610978daa0970c1ad4665ae90ea54b8fe075 Mon Sep 17 00:00:00 2001 From: James Coglan Date: Wed, 19 Jun 2024 12:22:49 +0100 Subject: [PATCH 20/20] resolved: tests for dns_cache_lookup() clamping the TTL --- src/resolve/test-dns-cache.c | 37 ++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/resolve/test-dns-cache.c b/src/resolve/test-dns-cache.c index 3411bb55fae..dc9152e3f7f 100644 --- a/src/resolve/test-dns-cache.c +++ b/src/resolve/test-dns-cache.c @@ -571,6 +571,43 @@ TEST(dns_cache_lookup_success) { ASSERT_TRUE(dns_answer_contains(ret_answer, rr)); } +TEST(dns_cache_lookup_clamp_ttl) { + _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); + _cleanup_(put_args_unrefp) PutArgs put_args = mk_put_args(); + _cleanup_(dns_answer_unrefp) DnsAnswer *ret_answer = NULL; + _cleanup_(dns_packet_unrefp) DnsPacket *ret_full_packet = NULL; + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; + int query_flags, ret_rcode; + uint64_t ret_query_flags; + + put_args.key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); + ASSERT_NOT_NULL(put_args.key); + put_args.rcode = DNS_RCODE_SUCCESS; + answer_add_a(&put_args, put_args.key, 0xc0a8017f, 3600, DNS_ANSWER_CACHEABLE); + cache_put(&cache, &put_args); + + ASSERT_EQ(dns_cache_size(&cache), 1u); + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); + ASSERT_NOT_NULL(key); + query_flags = SD_RESOLVED_CLAMP_TTL; + ASSERT_TRUE(dns_cache_lookup(&cache, key, query_flags, &ret_rcode, &ret_answer, &ret_full_packet, &ret_query_flags, NULL)); + + ASSERT_EQ(cache.n_hit, 1u); + ASSERT_EQ(cache.n_miss, 0u); + + ASSERT_EQ(ret_rcode, DNS_RCODE_SUCCESS); + ASSERT_EQ(ret_query_flags, SD_RESOLVED_CONFIDENTIAL); + + ASSERT_EQ(dns_answer_size(ret_answer), 1u); + + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com"); + ASSERT_NOT_NULL(rr); + rr->a.in_addr.s_addr = htobe32(0xc0a8017f); + ASSERT_TRUE(dns_answer_contains(ret_answer, rr)); +} + TEST(dns_cache_lookup_returns_most_recent_response) { _cleanup_(dns_cache_unrefp) DnsCache cache = new_cache(); _cleanup_(put_args_unrefp) PutArgs args1 = mk_put_args(), args2 = mk_put_args();