net: dns: Fix DNS caching bug

The current DNS caching logic is not aware of the IP address version.
If there is a cached address for a query, the caller of
dns_get_addr_info() will receive that address, even if it is not the
same version as the caller requested. For example:

- dns_get_addr_info() is called to resolve an IPv4 address
- the DNS client caches the IPv4 address that was received
- dns_get_addr_info() is called to resolve an IPv6 address
- the DNS client sees that there is a cached IPv4 address for the
  given query
- the DNS client does not check that the cached address is of the
  requested version (IPv6)
- the cached IPv4 address is returned from dns_get_addr_info()

This changes the DNS client to check IP address version when searching
cached addresses.

Signed-off-by: Noah Olson <noah@wavelynx.com>
This commit is contained in:
Noah Olson
2025-02-05 07:58:23 -07:00
committed by Benjamin Cabé
parent 3bbe56060f
commit 37a924be6a
4 changed files with 71 additions and 17 deletions

View File

@@ -5,6 +5,7 @@
*/
#include <zephyr/net/dns_resolve.h>
#include <zephyr/net/net_ip.h>
#include "dns_cache.h"
LOG_MODULE_REGISTER(net_dns_cache, CONFIG_DNS_RESOLVER_LOG_LEVEL);
@@ -97,15 +98,23 @@ int dns_cache_remove(struct dns_cache *cache, char const *query)
return 0;
}
int dns_cache_find(struct dns_cache const *cache, const char *query, struct dns_addrinfo *addrinfo,
size_t addrinfo_array_len)
int dns_cache_find(struct dns_cache const *cache, const char *query, enum dns_query_type type,
struct dns_addrinfo *addrinfo, size_t addrinfo_array_len)
{
size_t found = 0;
sa_family_t family;
NET_DBG("Find \"%s\"", query);
if (cache == NULL || query == NULL || addrinfo == NULL || addrinfo_array_len <= 0) {
return -EINVAL;
}
if (type == DNS_QUERY_TYPE_A) {
family = AF_INET;
} else if (type == DNS_QUERY_TYPE_AAAA) {
family = AF_INET6;
} else {
return -EINVAL;
}
if (strlen(query) >= CONFIG_DNS_RESOLVER_MAX_QUERY_LEN) {
NET_WARN("Query string to big to be processed %u >= "
"CONFIG_DNS_RESOLVER_MAX_QUERY_LEN",
@@ -124,6 +133,9 @@ int dns_cache_find(struct dns_cache const *cache, const char *query, struct dns_
if (strcmp(cache->entries[i].query, query) != 0) {
continue;
}
if (cache->entries[i].data.ai_family != family) {
continue;
}
if (found >= addrinfo_array_len) {
NET_WARN("Found \"%s\" but not enough space in provided buffer.", query);
found++;

View File

@@ -86,6 +86,7 @@ int dns_cache_remove(struct dns_cache *cache, char const *query);
*
* @param cache Cache where the entry should be searched.
* @param query Query which should be searched for.
* @param type Query type which will control the types of addresses that will be found.
* @param addrinfo dns_addrinfo array which will be written if the query was found.
* @param addrinfo_array_len Array size of the dns_addrinfo array
* @retval on success the amount of dns_addrinfo written into the addrinfo array will be returned.
@@ -94,7 +95,7 @@ int dns_cache_remove(struct dns_cache *cache, char const *query);
* -ENOSR means there was not enough space in the addrinfo array to accommodate all cache hits the
* array will however be filled with valid data.
*/
int dns_cache_find(struct dns_cache const *cache, const char *query, struct dns_addrinfo *addrinfo,
size_t addrinfo_array_len);
int dns_cache_find(struct dns_cache const *cache, const char *query, enum dns_query_type type,
struct dns_addrinfo *addrinfo, size_t addrinfo_array_len);
#endif /* ZEPHYR_INCLUDE_NET_DNS_CACHE_H_ */

View File

@@ -1414,7 +1414,7 @@ int dns_resolve_name(struct dns_resolve_context *ctx,
try_resolve:
#ifdef CONFIG_DNS_RESOLVER_CACHE
ret = dns_cache_find(&dns_cache, query, cached_info, ARRAY_SIZE(cached_info));
ret = dns_cache_find(&dns_cache, query, type, cached_info, ARRAY_SIZE(cached_info));
if (ret > 0) {
/* The query was cached, no
* need to continue further.

View File

@@ -24,10 +24,11 @@ ZTEST(net_dns_cache_test, test_simple_cache_entry)
struct dns_addrinfo info_write = {.ai_family = AF_INET};
struct dns_addrinfo info_read = {0};
const char *query = "example.com";
enum dns_query_type query_type = DNS_QUERY_TYPE_A;
zassert_ok(dns_cache_add(&test_dns_cache, query, &info_write, TEST_DNS_CACHE_DEFAULT_TTL),
"Cache entry adding should work.");
zassert_equal(1, dns_cache_find(&test_dns_cache, query, &info_read, 1));
zassert_equal(1, dns_cache_find(&test_dns_cache, query, query_type, &info_read, 1));
zassert_equal(AF_INET, info_read.ai_family);
}
@@ -35,8 +36,9 @@ ZTEST(net_dns_cache_test, test_not_cached)
{
struct dns_addrinfo info_read = {0};
const char *query = "example.com";
enum dns_query_type query_type = DNS_QUERY_TYPE_A;
zassert_equal(0, dns_cache_find(&test_dns_cache, query, &info_read, 1));
zassert_equal(0, dns_cache_find(&test_dns_cache, query, query_type, &info_read, 1));
zassert_equal(0, info_read.ai_family);
}
@@ -45,14 +47,15 @@ ZTEST(net_dns_cache_test, test_fill_cache)
struct dns_addrinfo info_write = {.ai_family = AF_INET};
struct dns_addrinfo info_read[TEST_DNS_CACHE_SIZE] = {0};
const char *query = "example.com";
enum dns_query_type query_type = DNS_QUERY_TYPE_A;
for (size_t i = 0; i < TEST_DNS_CACHE_SIZE; i++) {
zassert_ok(dns_cache_add(&test_dns_cache, query, &info_write,
TEST_DNS_CACHE_DEFAULT_TTL),
"Cache entry adding should work.");
}
zassert_equal(TEST_DNS_CACHE_SIZE,
dns_cache_find(&test_dns_cache, query, info_read, TEST_DNS_CACHE_SIZE));
zassert_equal(TEST_DNS_CACHE_SIZE, dns_cache_find(&test_dns_cache, query, query_type,
info_read, TEST_DNS_CACHE_SIZE));
zassert_equal(AF_INET, info_read[TEST_DNS_CACHE_SIZE - 1].ai_family);
}
@@ -61,6 +64,7 @@ ZTEST(net_dns_cache_test, test_flush)
struct dns_addrinfo info_write = {.ai_family = AF_INET};
struct dns_addrinfo info_read[TEST_DNS_CACHE_SIZE] = {0};
const char *query = "example.com";
enum dns_query_type query_type = DNS_QUERY_TYPE_A;
for (size_t i = 0; i < TEST_DNS_CACHE_SIZE; i++) {
zassert_ok(dns_cache_add(&test_dns_cache, query, &info_write,
@@ -68,7 +72,8 @@ ZTEST(net_dns_cache_test, test_flush)
"Cache entry adding should work.");
}
zassert_ok(dns_cache_flush(&test_dns_cache));
zassert_equal(0, dns_cache_find(&test_dns_cache, query, info_read, TEST_DNS_CACHE_SIZE));
zassert_equal(0, dns_cache_find(&test_dns_cache, query, query_type, info_read,
TEST_DNS_CACHE_SIZE));
zassert_equal(0, info_read[TEST_DNS_CACHE_SIZE - 1].ai_family);
}
@@ -77,14 +82,15 @@ ZTEST(net_dns_cache_test, test_fill_cache_to_small)
struct dns_addrinfo info_write = {.ai_family = AF_INET};
struct dns_addrinfo info_read[TEST_DNS_CACHE_SIZE - 1] = {0};
const char *query = "example.com";
enum dns_query_type query_type = DNS_QUERY_TYPE_A;
for (size_t i = 0; i < TEST_DNS_CACHE_SIZE; i++) {
zassert_ok(dns_cache_add(&test_dns_cache, query, &info_write,
TEST_DNS_CACHE_DEFAULT_TTL),
"Cache entry adding should work.");
}
zassert_equal(-ENOSR,
dns_cache_find(&test_dns_cache, query, info_read, TEST_DNS_CACHE_SIZE - 1));
zassert_equal(-ENOSR, dns_cache_find(&test_dns_cache, query, query_type, info_read,
TEST_DNS_CACHE_SIZE - 1));
zassert_equal(AF_INET, info_read[TEST_DNS_CACHE_SIZE - 2].ai_family);
}
@@ -93,6 +99,7 @@ ZTEST(net_dns_cache_test, test_closest_expiry_removed)
struct dns_addrinfo info_write = {.ai_family = AF_INET};
struct dns_addrinfo info_read = {0};
const char *closest_expiry = "example.com";
enum dns_query_type query_type = DNS_QUERY_TYPE_A;
zassert_ok(dns_cache_add(&test_dns_cache, closest_expiry, &info_write,
TEST_DNS_CACHE_DEFAULT_TTL),
@@ -103,7 +110,8 @@ ZTEST(net_dns_cache_test, test_closest_expiry_removed)
TEST_DNS_CACHE_DEFAULT_TTL),
"Cache entry adding should work.");
}
zassert_equal(0, dns_cache_find(&test_dns_cache, closest_expiry, &info_read, 1));
zassert_equal(0,
dns_cache_find(&test_dns_cache, closest_expiry, query_type, &info_read, 1));
zassert_equal(0, info_read.ai_family);
}
@@ -112,6 +120,7 @@ ZTEST(net_dns_cache_test, test_expired_entries_removed)
struct dns_addrinfo info_write = {.ai_family = AF_INET};
struct dns_addrinfo info_read[3] = {0};
const char *query = "example.com";
enum dns_query_type query_type = DNS_QUERY_TYPE_A;
zassert_ok(dns_cache_add(&test_dns_cache, query, &info_write, TEST_DNS_CACHE_DEFAULT_TTL),
"Cache entry adding should work.");
@@ -121,15 +130,47 @@ ZTEST(net_dns_cache_test, test_expired_entries_removed)
zassert_ok(
dns_cache_add(&test_dns_cache, query, &info_write, TEST_DNS_CACHE_DEFAULT_TTL * 3),
"Cache entry adding should work.");
zassert_equal(3, dns_cache_find(&test_dns_cache, query, info_read, 3));
zassert_equal(3, dns_cache_find(&test_dns_cache, query, query_type, info_read, 3));
zassert_equal(AF_INET, info_read[0].ai_family);
k_sleep(K_MSEC(TEST_DNS_CACHE_DEFAULT_TTL * 1000 + 1));
zassert_equal(2, dns_cache_find(&test_dns_cache, query, info_read, 3));
zassert_equal(2, dns_cache_find(&test_dns_cache, query, query_type, info_read, 3));
zassert_equal(AF_INET, info_read[0].ai_family);
k_sleep(K_MSEC(TEST_DNS_CACHE_DEFAULT_TTL * 1000 + 1));
zassert_equal(1, dns_cache_find(&test_dns_cache, query, info_read, 3));
zassert_equal(1, dns_cache_find(&test_dns_cache, query, query_type, info_read, 3));
zassert_equal(AF_INET, info_read[0].ai_family);
k_sleep(K_MSEC(1));
zassert_equal(1, dns_cache_find(&test_dns_cache, query, info_read, 3));
zassert_equal(1, dns_cache_find(&test_dns_cache, query, query_type, info_read, 3));
zassert_equal(AF_INET, info_read[0].ai_family);
}
ZTEST(net_dns_cache_test, test_different_type_not_returned)
{
struct dns_addrinfo info_write = {.ai_family = AF_INET};
struct dns_addrinfo info_read = {0};
const char *query = "example.com";
enum dns_query_type query_type = DNS_QUERY_TYPE_AAAA;
zassert_ok(dns_cache_add(&test_dns_cache, query, &info_write, TEST_DNS_CACHE_DEFAULT_TTL),
"Cache entry adding should work.");
zassert_equal(0, dns_cache_find(&test_dns_cache, query, query_type, &info_read, 1));
zassert_equal(0, info_read.ai_family);
}
ZTEST(net_dns_cache_test, test_only_expected_type_returned)
{
struct dns_addrinfo info_write_a = {.ai_family = AF_INET};
struct dns_addrinfo info_write_b = {.ai_family = AF_INET6};
struct dns_addrinfo info_read = {0};
const char *query = "example.com";
enum dns_query_type query_type_a = DNS_QUERY_TYPE_A;
enum dns_query_type query_type_b = DNS_QUERY_TYPE_AAAA;
zassert_ok(dns_cache_add(&test_dns_cache, query, &info_write_a, TEST_DNS_CACHE_DEFAULT_TTL),
"Cache entry adding should work.");
zassert_ok(dns_cache_add(&test_dns_cache, query, &info_write_b, TEST_DNS_CACHE_DEFAULT_TTL),
"Cache entry adding should work.");
zassert_equal(1, dns_cache_find(&test_dns_cache, query, query_type_a, &info_read, 1));
zassert_equal(AF_INET, info_read.ai_family);
zassert_equal(1, dns_cache_find(&test_dns_cache, query, query_type_b, &info_read, 1));
zassert_equal(AF_INET6, info_read.ai_family);
}