1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-01-18 12:52:44 +01:00
RIOT/sys/net/application_layer/dns/cache.c

191 lines
4.6 KiB
C
Raw Normal View History

2022-02-19 14:20:40 +01:00
/*
* Copyright (C) 2022 ML!PA Consulting GmbH
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup net_dns_cache
2022-02-19 14:20:40 +01:00
* @{
* @file
* @brief DNS cache implementation
* @author Benjamin Valentin <benjamin.valentin@ml-pa.com>
* @}
*/
#include "bitfield.h"
#include "checksum/fletcher32.h"
#include "time_units.h"
#include "net/af.h"
#include "net/dns/cache.h"
#include "net/ipv4/addr.h"
#include "net/ipv6/addr.h"
2022-02-19 14:20:40 +01:00
#include "ztimer.h"
#define ENABLE_DEBUG 0
#include "debug.h"
static struct dns_cache_entry {
uint32_t hash;
uint32_t expires;
union {
#if IS_ACTIVE(CONFIG_DNS_CACHE_A)
2022-02-19 14:20:40 +01:00
ipv4_addr_t v4;
#endif
#if IS_ACTIVE(CONFIG_DNS_CACHE_AAAA)
2022-02-19 14:20:40 +01:00
ipv6_addr_t v6;
#endif
} addr;
} cache[CONFIG_DNS_CACHE_SIZE];
#if IS_ACTIVE(CONFIG_DNS_CACHE_A) && IS_ACTIVE(CONFIG_DNS_CACHE_AAAA)
2022-02-19 14:20:40 +01:00
BITFIELD(cache_is_v6, CONFIG_DNS_CACHE_SIZE);
static inline uint8_t _get_len(unsigned idx)
{
return bf_isset(cache_is_v6, idx) ? 16 : 4;
}
#elif IS_ACTIVE(CONFIG_DNS_CACHE_A)
2022-02-19 14:20:40 +01:00
static inline uint8_t _get_len(unsigned idx)
{
(void)idx;
return 4;
}
#elif IS_ACTIVE(CONFIG_DNS_CACHE_AAAA)
2022-02-19 14:20:40 +01:00
static inline uint8_t _get_len(unsigned idx)
{
(void)idx;
return 16;
}
#endif
static void _set_len(unsigned idx, uint8_t len)
{
#if IS_ACTIVE(CONFIG_DNS_CACHE_A) && IS_ACTIVE(CONFIG_DNS_CACHE_AAAA)
2022-02-19 14:20:40 +01:00
if (len == 16) {
bf_set(cache_is_v6, idx);
} else {
bf_unset(cache_is_v6, idx);
}
#else
(void)idx;
(void)len;
#endif
}
static bool _is_empty(unsigned idx)
{
const uint8_t len = _get_len(idx);
const uint8_t *addr = (void *)&cache[idx].addr;
for (unsigned i = 0; i < len; ++i) {
if (addr[i]) {
return false;
}
}
return true;
}
static void _set_empty(unsigned idx)
{
memset(&cache[idx].addr, 0, _get_len(idx));
}
static uint8_t _addr_len(int family)
{
switch (family) {
#if IS_ACTIVE(CONFIG_DNS_CACHE_A)
2022-02-19 14:20:40 +01:00
case AF_INET:
return sizeof(ipv4_addr_t);
#endif
#if IS_ACTIVE(CONFIG_DNS_CACHE_AAAA)
2022-02-19 14:20:40 +01:00
case AF_INET6:
return sizeof(ipv6_addr_t);
#endif
case AF_UNSPEC:
return 0;
default:
return 255;
}
}
static uint32_t _hash(const void *data, size_t len)
{
return fletcher32(data, (len + 1) / 2);
}
int dns_cache_query(const char *domain_name, void *addr_out, int family)
2022-02-19 14:20:40 +01:00
{
uint32_t now = ztimer_now(ZTIMER_MSEC) / MS_PER_SEC;
uint32_t hash = _hash(domain_name, strlen(domain_name));
uint8_t addr_len = _addr_len(family);
for (unsigned i = 0; i < CONFIG_DNS_CACHE_SIZE; ++i) {
/* empty slot */
if (_is_empty(i)) {
continue;
}
/* TTL expired - invalidate slot */
if (now > cache[i].expires) {
DEBUG("dns_cache[%u] expired\n", i);
_set_empty(i);
continue;
}
/* check if hash and length match */
if (cache[i].hash == hash && (!addr_len || addr_len == _get_len(i))) {
DEBUG("dns_cache[%u] hit\n", i);
memcpy(addr_out, &cache[i].addr, _get_len(i));
return _get_len(i);
}
}
DEBUG("dns_cache miss\n");
return 0;
}
static void _add_entry(uint8_t i, uint32_t hash, const void *addr_out,
int addr_len, uint32_t expires)
{
DEBUG("dns_cache[%u] add cache entry\n", i);
cache[i].hash = hash;
cache[i].expires = expires;
memcpy(&cache[i].addr, addr_out, addr_len);
_set_len(i, addr_len);
}
void dns_cache_add(const char *domain_name, const void *addr_out,
2022-02-19 14:20:40 +01:00
int addr_len, uint32_t ttl)
{
uint32_t now = ztimer_now(ZTIMER_MSEC) / MS_PER_SEC;
uint32_t hash = _hash(domain_name, strlen(domain_name));
uint32_t oldest = ttl;
int idx = -1;
assert(addr_len == 4 || addr_len == 16);
DEBUG("dns_cache: lifetime of %s is %"PRIu32" s\n", domain_name, ttl);
for (unsigned i = 0; i < CONFIG_DNS_CACHE_SIZE; ++i) {
if (now > cache[i].expires || _is_empty(i)) {
_add_entry(i, hash, addr_out, addr_len, now + ttl);
return;
}
if (cache[i].hash == hash && _get_len(i) == addr_len) {
DEBUG("dns_cache[%u] update ttl\n", i);
cache[i].expires = now + ttl;
return;
}
uint32_t _ttl = cache[i].expires - now;
if (_ttl < oldest) {
oldest = _ttl;
idx = i;
}
}
if (idx >= 0) {
DEBUG("dns_cache: evict first entry to expire\n");
_add_entry(idx, hash, addr_out, addr_len, now + ttl);
}
}