Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve insertion of clients, domains and DNS cache records #2095

Merged
merged 4 commits into from
Oct 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/database/query-table.c
Original file line number Diff line number Diff line change
Expand Up @@ -1205,7 +1205,7 @@ void DB_read_queries(void)
query->domainID = domainID;
query->clientID = clientID;
query->upstreamID = upstreamID;
query->cacheID = findCacheID(domainID, clientID, query->type, true);
query->cacheID = -1;
query->id = counters->queries;
query->response = 0;
query->flags.response_calculated = reply_time_avail;
Expand Down Expand Up @@ -1263,6 +1263,7 @@ void DB_read_queries(void)
// Set ID of the domainlist entry that was the reason for permitting/blocking this query
// We assume the value in this field is said ID when it is not a CNAME-related domain
// (checked above) and the value of additional_info is not NULL (0 bytes storage size)
query->cacheID = findCacheID(domainID, clientID, query->type, true);
DNSCacheData *cache = getDNSCache(query->cacheID, true);
// Only load if
// a) we have a cache entry
Expand Down Expand Up @@ -1539,7 +1540,7 @@ bool queries_to_database(void)
}

// Get cache entry for this query
const int cacheID = query->cacheID >= 0 ? query->cacheID : findCacheID(query->domainID, query->clientID, query->type, false);
const unsigned int cacheID = query->cacheID > -1 ? query->cacheID : findCacheID(query->domainID, query->clientID, query->type, false);
DNSCacheData *cache = getDNSCache(cacheID, true);

// ADDITIONAL_INFO
Expand Down
56 changes: 13 additions & 43 deletions src/datastructure.c
Original file line number Diff line number Diff line change
Expand Up @@ -196,20 +196,10 @@ int _findUpstreamID(const char *upstreamString, const in_port_t port, int line,

static int get_next_free_domainID(void)
{
// Compare content of domain against known domain IP addresses
for(unsigned int domainID = 0; domainID < counters->domains; domainID++)
{
// Get domain pointer
const domainsData *domain = getDomain(domainID, false);

// Check if the returned pointer is valid before trying to access it
if(domain == NULL)
continue;

// Check if the magic byte is set
if(domain->magic == 0x00)
return domainID;
}
// First, try to obtain a previously recycled domain ID
unsigned int domainID = 0;
if(get_next_recycled_ID(DOMAINS, &domainID))
return domainID;

// If we did not return until here, then we need to allocate a new domain ID
return counters->domains;
Expand Down Expand Up @@ -289,20 +279,10 @@ int _findDomainID(const char *domainString, const bool count, int line, const ch

static int get_next_free_clientID(void)
{
// Compare content of client against known client IP addresses
for(unsigned int clientID = 0; clientID < counters->clients; clientID++)
{
// Get client pointer
const clientsData *client = getClient(clientID, false);

// Check if the returned pointer is valid before trying to access it
if(client == NULL)
continue;

// Check if the magic byte is unset
if(client->magic == 0x00)
return clientID;
}
// First, try to obtain a previously recycled client ID
unsigned int clientID = 0;
if(get_next_recycled_ID(CLIENTS, &clientID))
return clientID;

// If we did not return until here, then we need to allocate a new client ID
return counters->clients;
Expand Down Expand Up @@ -459,20 +439,10 @@ void change_clientcount(clientsData *client, int total, int blocked, int overTim

static int get_next_free_cacheID(void)
{
// Compare content of cache against known cache IP addresses
for(unsigned int cacheID = 0; cacheID < counters->dns_cache_size; cacheID++)
{
// Get cache pointer
const DNSCacheData *cache = getDNSCache(cacheID, false);

// Check if the returned pointer is valid before trying to access it
if(cache == NULL)
continue;

// Check if the magic byte is set
if(cache->magic == 0x00)
return cacheID;
}
// First, try to obtain a previously recycled cache ID
unsigned int cacheID = 0;
if(get_next_recycled_ID(DNS_CACHE, &cacheID))
return cacheID;

// If we did not return until here, then we need to allocate a new cache ID
return counters->dns_cache_size;
Expand Down Expand Up @@ -1186,7 +1156,7 @@ void _query_set_status(queriesData *query, const enum query_status new_status, c
new_status != QUERY_RETRIED &&
new_status != QUERY_RETRIED_DNSSEC)
{
const int cacheID = findCacheID(query->domainID, query->clientID, query->type, true);
const unsigned int cacheID = query->cacheID > 0 ? query->cacheID : findCacheID(query->domainID, query->clientID, query->type, true);
DNSCacheData *dns_cache = getDNSCache(cacheID, true);
if(dns_cache != NULL && dns_cache->blocking_status != new_status)
{
Expand Down
31 changes: 16 additions & 15 deletions src/dnsmasq_interface.c
Original file line number Diff line number Diff line change
Expand Up @@ -1425,12 +1425,7 @@
}

// Get cache pointer
// When this function is called with a different domain than the one
// already stored in the query, we have to re-lookup the cache ID.
// This can happen when a CNAME chain is followed and analyzed
const int cacheID = query->domainID == domainID && query->clientID == clientID ?
query->cacheID : findCacheID(domainID, clientID, query->type, true);
DNSCacheData *dns_cache = getDNSCache(cacheID, true);
DNSCacheData *dns_cache = getDNSCache(query->cacheID, true);
if(dns_cache == NULL)
{
log_err("No memory available, skipping query analysis");
Expand All @@ -1448,155 +1443,161 @@
dns_cache->list_id = -1;
}

// Check if the cache record we have applies to the current query
// If not, ensure we re-check the domain (happens during CNAME inspection)
enum query_status blocking_status = QUERY_UNKNOWN;
if(query->clientID == clientID && query->domainID == domainID)
blocking_status = dns_cache->blocking_status;

// Memorize blocking status DNS cache for the domain/client combination
cacheStatus = dns_cache->blocking_status;
cacheStatus = blocking_status;
log_debug(DEBUG_QUERIES, "Set global cache status to %d", cacheStatus);

// Skip the entire chain of tests if we already know the answer for this
// particular client
char *domainstr = (char*)getstr(domain->domainpos);
switch(dns_cache->blocking_status)
switch(blocking_status)
{
case QUERY_UNKNOWN:
// New domain/client combination.
// We have to go through all the tests below
log_debug(DEBUG_QUERIES, "%s is not known", domainstr);

break;

case QUERY_DENYLIST:
case QUERY_DENYLIST_CNAME:
// Known as exactly denied, we return this result early, skipping
// all the lengthy tests below
blockingreason = dns_cache->blocking_status == QUERY_DENYLIST ? "exactly denied" : "exactly denied (CNAME)";
blockingreason = blocking_status == QUERY_DENYLIST ? "exactly denied" : "exactly denied (CNAME)";
log_debug(DEBUG_QUERIES, "%s is known as %s", domainstr, blockingreason);

// Do not block if the entire query is to be permitted
// as something along the CNAME path hit the whitelist
if(!query->flags.allowed)
{
force_next_DNS_reply = dns_cache->force_reply;
query_blocked(query, domain, client, QUERY_DENYLIST);
return true;
}
break;

case QUERY_GRAVITY:
case QUERY_GRAVITY_CNAME:
// Known as gravity blocked, we return this result early, skipping
// all the lengthy tests below
blockingreason = dns_cache->blocking_status == QUERY_GRAVITY ? "gravity blocked" : "gravity blocked (CNAME)";
blockingreason = blocking_status == QUERY_GRAVITY ? "gravity blocked" : "gravity blocked (CNAME)";
log_debug(DEBUG_QUERIES, "%s is known as %s", domainstr, blockingreason);

// Do not block if the entire query is to be permitted
// as sometving along the CNAME path hit the whitelist
if(!query->flags.allowed)
{
force_next_DNS_reply = dns_cache->force_reply;
query_blocked(query, domain, client, QUERY_GRAVITY);
return true;
}
break;

case QUERY_REGEX:
case QUERY_REGEX_CNAME:
// Known as regex denied, we return this result early, skipping all
// the lengthy tests below
blockingreason = dns_cache->blocking_status == QUERY_REGEX ? "regex denied" : "regex denied (CNAME)";
blockingreason = blocking_status == QUERY_REGEX ? "regex denied" : "regex denied (CNAME)";
log_debug(DEBUG_QUERIES, "%s is known as %s (cache regex ID: %i)",
domainstr, blockingreason, dns_cache->list_id);

// Do not block if the entire query is to be permitted as something
// along the CNAME path hit the whitelist
if(!query->flags.allowed)
{
force_next_DNS_reply = dns_cache->force_reply;
last_regex_idx = dns_cache->list_id;
query_blocked(query, domain, client, QUERY_REGEX);
return true;
}
break;

case QUERY_SPECIAL_DOMAIN:
// Known as a special domain, we return this result early, skipping
// all the lengthy tests below
blockingreason = "special domain";
log_debug(DEBUG_QUERIES, "%s is known as special domain", domainstr);

force_next_DNS_reply = dns_cache->force_reply;
query_blocked(query, domain, client, QUERY_SPECIAL_DOMAIN);
return true;
break;

case QUERY_EXTERNAL_BLOCKED_IP:
case QUERY_EXTERNAL_BLOCKED_NULL:
case QUERY_EXTERNAL_BLOCKED_NXRA:
case QUERY_EXTERNAL_BLOCKED_EDE15:

switch(dns_cache->blocking_status)
switch(blocking_status)
{
case QUERY_UNKNOWN:
case QUERY_GRAVITY:
case QUERY_DENYLIST:
case QUERY_REGEX:
case QUERY_FORWARDED:
case QUERY_CACHE:
case QUERY_GRAVITY_CNAME:
case QUERY_REGEX_CNAME:
case QUERY_DENYLIST_CNAME:
case QUERY_RETRIED:
case QUERY_RETRIED_DNSSEC:
case QUERY_IN_PROGRESS:
case QUERY_DBBUSY:
case QUERY_SPECIAL_DOMAIN:
case QUERY_CACHE_STALE:
case QUERY_STATUS_MAX:
// Cannot happen
break;
case QUERY_EXTERNAL_BLOCKED_IP:
blockingreason = "blocked upstream with known address";
break;
case QUERY_EXTERNAL_BLOCKED_NULL:
blockingreason = "blocked upstream with NULL address";
break;
case QUERY_EXTERNAL_BLOCKED_EDE15:
blockingreason = "blocked upstream with EDE15";
break;
case QUERY_EXTERNAL_BLOCKED_NXRA:
blockingreason = "blocked upstream with NXRA address";
break;
}

// Known as upstream blocked, we return this result
// early, skipping all the lengthy tests below
log_debug(DEBUG_QUERIES, "%s is known as %s (expires in %lus)",
domainstr, blockingreason, (unsigned long)(dns_cache->expires - time(NULL)));

force_next_DNS_reply = dns_cache->force_reply;
query_blocked(query, domain, client, dns_cache->blocking_status);
query_blocked(query, domain, client, blocking_status);
return true;
break;

case QUERY_CACHE:
case QUERY_FORWARDED:
case QUERY_RETRIED:
case QUERY_RETRIED_DNSSEC:
case QUERY_IN_PROGRESS:
case QUERY_DBBUSY:
case QUERY_CACHE_STALE:
case QUERY_STATUS_MAX:
// Known as not to be blocked, possibly even explicitly
// allowed - we return this result early, skipping all
// the lengthy tests below
log_debug(DEBUG_QUERIES, "%s is known as not to be blocked%s", domainstr,
dns_cache->flags.allowed ? " (allowed)" : "");

if(dns_cache->flags.allowed)
query->flags.allowed = true;

return false;
break;
}

Check notice

Code scanning / CodeQL

Long switch case Note

Switch has at least one case that is too long:
QUERY_EXTERNAL_BLOCKED_EDE15 (55 lines)
.

// Skip all checks and continue if we hit already at least one allowlist in the chain
if(query->flags.allowed)
Expand Down Expand Up @@ -1802,8 +1803,8 @@
else if(query->status == QUERY_REGEX)
{
// Get parent and child DNS cache entries
const int parent_cacheID = query->cacheID;
const int child_cacheID = findCacheID(child_domainID, clientID, query->type, false);
const unsigned int parent_cacheID = query->cacheID > -1 ? query->cacheID : findCacheID(parent_domainID, clientID, query->type, false);
const unsigned int child_cacheID = findCacheID(child_domainID, clientID, query->type, false);

// Get cache pointers
DNSCacheData *parent_cache = getDNSCache(parent_cacheID, true);
Expand Down
12 changes: 11 additions & 1 deletion src/gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@ static void recycle(void)
// Remove client from lookup table
lookup_remove(CLIENTS_LOOKUP, clientID, client->hash);

// Add ID of recycled client to recycle table
set_next_recycled_ID(CLIENTS, clientID);

// Wipe client's memory
memset(client, 0, sizeof(clientsData));

Expand Down Expand Up @@ -152,6 +155,9 @@ static void recycle(void)
// Remove domain from lookup table
lookup_remove(DOMAINS_LOOKUP, domainID, domain->hash);

// Add ID of recycled domain to recycle table
set_next_recycled_ID(DOMAINS, domainID);

// Wipe domain's memory
memset(domain, 0, sizeof(domainsData));

Expand All @@ -174,6 +180,9 @@ static void recycle(void)
// Remove cache entry from lookup table
lookup_remove(DNS_CACHE_LOOKUP, cacheID, cache->hash);

// Add ID of recycled domain to recycle table
set_next_recycled_ID(DNS_CACHE, cacheID);

// Wipe cache entry's memory
memset(cache, 0, sizeof(DNSCacheData));

Expand Down Expand Up @@ -222,7 +231,7 @@ static void recycle(void)
counters->domains_MAX + free_domains - counters->domains, counters->domains_MAX,
counters->dns_cache_MAX + free_cache - counters->dns_cache_size, counters->dns_cache_MAX);

log_debug(DEBUG_GC, "Recycled additional %u clients, %u domains, and %u cache records (scanned %u queries)",
log_debug(DEBUG_GC, "Recycled %u clients, %u domains, and %u cache records (scanned %u queries)",
clients_recycled, domains_recycled, cache_recycled, counters->queries);
}
}
Expand Down Expand Up @@ -619,6 +628,7 @@ void *GC_thread(void *val)
{
lock_shm();
lookup_find_hash_collisions();
print_recycle_list_fullness();
unlock_shm();
}

Expand Down
Loading
Loading