Merge pull request #1409 from pi-hole/tweak/white_regex_id

Store domainlist IDs for blocked/permitted queries
This commit is contained in:
DL6ER
2022-09-11 22:12:33 +02:00
committed by GitHub
13 changed files with 160 additions and 159 deletions
+22 -38
View File
@@ -15,8 +15,6 @@
#include "../config.h"
// logg()
#include "../log.h"
// match_regex()
#include "../regex_r.h"
// getstr()
#include "../shmem.h"
// SQLite3 prepared statement vectors
@@ -211,11 +209,11 @@ bool gravityDB_reopen(void)
return gravityDB_open();
}
static char* get_client_querystr(const char* table, const char* groups)
static char* get_client_querystr(const char *table, const char *column, const char *groups)
{
// Build query string with group filtering
char *querystr = NULL;
if(asprintf(&querystr, "SELECT EXISTS(SELECT domain from %s WHERE domain = ? AND group_id IN (%s));", table, groups) < 1)
if(asprintf(&querystr, "SELECT %s from %s WHERE domain = ? AND group_id IN (%s);", column, table, groups) < 1)
{
logg("get_client_querystr(%s, %s) - asprintf() error", table, groups);
return NULL;
@@ -859,19 +857,14 @@ bool gravityDB_prepare_client_statements(clientsData *client)
return false;
// Prepare whitelist statement
// We use SELECT EXISTS() as this is known to efficiently use the index
// We are only interested in whether the domain exists or not in the
// list but don't case about duplicates or similar. SELECT EXISTS(...)
// returns true as soon as it sees the first row from the query inside
// of EXISTS().
if(config.debug & DEBUG_DATABASE)
logg("gravityDB_open(): Preparing vw_whitelist statement for client %s", clientip);
querystr = get_client_querystr("vw_whitelist", getstr(client->groupspos));
querystr = get_client_querystr("vw_whitelist", "id", getstr(client->groupspos));
sqlite3_stmt* stmt = NULL;
int rc = sqlite3_prepare_v3(gravity_db, querystr, -1, SQLITE_PREPARE_PERSISTENT, &stmt, NULL);
if( rc != SQLITE_OK )
{
logg("gravityDB_open(\"SELECT EXISTS(... vw_whitelist ...)\") - SQL error prepare: %s", sqlite3_errstr(rc));
logg("gravityDB_open(\"SELECT(... vw_whitelist ...)\") - SQL error prepare: %s", sqlite3_errstr(rc));
gravityDB_close();
return false;
}
@@ -881,11 +874,11 @@ bool gravityDB_prepare_client_statements(clientsData *client)
// Prepare gravity statement
if(config.debug & DEBUG_DATABASE)
logg("gravityDB_open(): Preparing vw_gravity statement for client %s", clientip);
querystr = get_client_querystr("vw_gravity", getstr(client->groupspos));
querystr = get_client_querystr("vw_gravity", "domain", getstr(client->groupspos));
rc = sqlite3_prepare_v3(gravity_db, querystr, -1, SQLITE_PREPARE_PERSISTENT, &stmt, NULL);
if( rc != SQLITE_OK )
{
logg("gravityDB_open(\"SELECT EXISTS(... vw_gravity ...)\") - SQL error prepare: %s", sqlite3_errstr(rc));
logg("gravityDB_open(\"SELECT(... vw_gravity ...)\") - SQL error prepare: %s", sqlite3_errstr(rc));
gravityDB_close();
return false;
}
@@ -895,11 +888,11 @@ bool gravityDB_prepare_client_statements(clientsData *client)
// Prepare blacklist statement
if(config.debug & DEBUG_DATABASE)
logg("gravityDB_open(): Preparing vw_blacklist statement for client %s", clientip);
querystr = get_client_querystr("vw_blacklist", getstr(client->groupspos));
querystr = get_client_querystr("vw_blacklist", "id", getstr(client->groupspos));
rc = sqlite3_prepare_v3(gravity_db, querystr, -1, SQLITE_PREPARE_PERSISTENT, &stmt, NULL);
if( rc != SQLITE_OK )
{
logg("gravityDB_open(\"SELECT EXISTS(... vw_blacklist ...)\") - SQL error prepare: %s", sqlite3_errstr(rc));
logg("gravityDB_open(\"SELECT(... vw_blacklist ...)\") - SQL error prepare: %s", sqlite3_errstr(rc));
gravityDB_close();
return false;
}
@@ -1147,7 +1140,7 @@ int gravityDB_count(const enum gravity_tables list)
return result;
}
static enum db_result domain_in_list(const char *domain, sqlite3_stmt *stmt, const char *listname)
static enum db_result domain_in_list(const char *domain, sqlite3_stmt *stmt, const char *listname, int *domain_id)
{
// Do not try to bind text to statement when database is not available
if(!gravityDB_opened && !gravityDB_open())
@@ -1181,10 +1174,10 @@ static enum db_result domain_in_list(const char *domain, sqlite3_stmt *stmt, con
sqlite3_clear_bindings(stmt);
return LIST_NOT_AVAILABLE;
}
else if(rc != SQLITE_ROW)
else if(rc != SQLITE_ROW && rc != SQLITE_DONE)
{
// Any return code that is neither SQLITE_BUSY not SQLITE_ROW
// is a real error we should log
// Any return code that is neither SQLITE_BUSY nor SQLITE_ROW or
// SQLITE_DONE is an error we should log
logg("domain_in_list(\"%s\", %p, %s): Failed to perform step: %s",
domain, stmt, listname, sqlite3_errstr(rc));
sqlite3_reset(stmt);
@@ -1192,8 +1185,10 @@ static enum db_result domain_in_list(const char *domain, sqlite3_stmt *stmt, con
return LIST_NOT_AVAILABLE;
}
// Get result of query "SELECT EXISTS(...)"
const int result = sqlite3_column_int(stmt, 0);
// Get result of query (if available)
const int result = (rc == SQLITE_ROW) ? sqlite3_column_int(stmt, 0) : -1;
if(domain_id != NULL)
*domain_id = result;
if(config.debug & DEBUG_DATABASE)
logg("domain_in_list(\"%s\", %p, %s): %d", domain, stmt, listname, result);
@@ -1210,8 +1205,7 @@ static enum db_result domain_in_list(const char *domain, sqlite3_stmt *stmt, con
sqlite3_clear_bindings(stmt);
// Return if domain was found in current table
// SELECT EXISTS(...) either returns 0 (false) or 1 (true).
return (result == 1) ? FOUND : NOT_FOUND;
return (rc == SQLITE_ROW) ? FOUND : NOT_FOUND;
}
void gravityDB_reload_groups(clientsData* client)
@@ -1269,17 +1263,7 @@ enum db_result in_whitelist(const char *domain, DNSCacheData *dns_cache, clients
// We have to check both the exact whitelist (using a prepared database statement)
// as well the compiled regex whitelist filters to check if the current domain is
// whitelisted.
enum db_result on_whitelist = domain_in_list(domain, stmt, "whitelist");
// For performance reasons, the regex evaluations is executed only if the
// exact whitelist lookup does not deliver a positive match. This is an
// optimization as the database lookup will most likely hit (a) more domains
// and (b) will be faster (given a sufficiently large number of regex
// whitelisting filters).
if(on_whitelist == NOT_FOUND)
on_whitelist = match_regex(domain, dns_cache, client->id, REGEX_WHITELIST, false) != -1;
return on_whitelist;
return domain_in_list(domain, stmt, "whitelist", &dns_cache->domainlist_id);
}
enum db_result in_gravity(const char *domain, clientsData *client)
@@ -1307,10 +1291,10 @@ enum db_result in_gravity(const char *domain, clientsData *client)
if(stmt == NULL)
stmt = gravity_stmt->get(gravity_stmt, client->id);
return domain_in_list(domain, stmt, "gravity");
return domain_in_list(domain, stmt, "gravity", NULL);
}
enum db_result in_blacklist(const char *domain, clientsData *client)
enum db_result in_blacklist(const char *domain, DNSCacheData *dns_cache, clientsData *client)
{
// If list statement is not ready and cannot be initialized (e.g. no
// access to the database), we return false to prevent an FTL crash
@@ -1335,7 +1319,7 @@ enum db_result in_blacklist(const char *domain, clientsData *client)
if(stmt == NULL)
stmt = blacklist_stmt->get(blacklist_stmt, client->id);
return domain_in_list(domain, stmt, "blacklist");
return domain_in_list(domain, stmt, "blacklist", &dns_cache->domainlist_id);
}
bool in_auditlist(const char *domain)
@@ -1346,7 +1330,7 @@ bool in_auditlist(const char *domain)
return false;
// We check the domain_audit table for the given domain
return domain_in_list(domain, auditlist_stmt, "auditlist") == FOUND;
return domain_in_list(domain, auditlist_stmt, "auditlist", NULL) == FOUND;
}
bool gravityDB_get_regex_client_groups(clientsData* client, const unsigned int numregex, const regexData *regex,
+1 -1
View File
@@ -32,7 +32,7 @@ int gravityDB_count(const enum gravity_tables list);
void check_inaccessible_adlists(void);
enum db_result in_gravity(const char *domain, clientsData *client);
enum db_result in_blacklist(const char *domain, clientsData *client);
enum db_result in_blacklist(const char *domain, DNSCacheData *dns_cache, clientsData *client);
enum db_result in_whitelist(const char *domain, DNSCacheData *dns_cache, clientsData *client);
bool in_auditlist(const char *domain);
+25 -29
View File
@@ -373,6 +373,9 @@ int DB_save_queries(sqlite3 *db)
sqlite3_bind_null(query_stmt, 7);
}
const int cacheID = findCacheID(query->domainID, query->clientID, query->type, false);
DNSCacheData *cache = getDNSCache(cacheID, true);
// ADDITIONAL_INFO
if(query->status == QUERY_GRAVITY_CNAME ||
query->status == QUERY_REGEX_CNAME ||
@@ -389,37 +392,29 @@ int DB_save_queries(sqlite3 *db)
sqlite3_bind_text(addinfo_stmt, 2, cname, len, SQLITE_STATIC);
if(sqlite3_step(addinfo_stmt) != SQLITE_DONE)
{
logg("Encountered error while trying to store addinfo in long-term database");
logg("Encountered error while trying to store addinfo in long-term database (CNAME)");
error = true;
break;
}
sqlite3_clear_bindings(addinfo_stmt);
sqlite3_reset(addinfo_stmt);
}
else if(query->status == QUERY_REGEX)
else if(cache != NULL && cache->domainlist_id > -1)
{
// Restore regex ID if applicable
const int cacheID = findCacheID(query->domainID, query->clientID, query->type);
DNSCacheData *cache = getDNSCache(cacheID, true);
if(cache != NULL)
{
sqlite3_bind_int(query_stmt, 8, ADDINFO_REGEX_ID);
sqlite3_bind_int(query_stmt, 9, cache->black_regex_idx);
sqlite3_bind_int(query_stmt, 8, ADDINFO_REGEX_ID);
sqlite3_bind_int(query_stmt, 9, cache->domainlist_id);
// Execute prepared addinfo statement and check if successful
sqlite3_bind_int(addinfo_stmt, 1, ADDINFO_REGEX_ID);
sqlite3_bind_int(addinfo_stmt, 2, cache->black_regex_idx);
if(sqlite3_step(addinfo_stmt) != SQLITE_DONE)
{
logg("Encountered error while trying to store addinfo in long-term database");
error = true;
break;
}
sqlite3_clear_bindings(addinfo_stmt);
sqlite3_reset(addinfo_stmt);
// Execute prepared addinfo statement and check if successful
sqlite3_bind_int(addinfo_stmt, 1, ADDINFO_REGEX_ID);
sqlite3_bind_int(addinfo_stmt, 2, cache->domainlist_id);
if(sqlite3_step(addinfo_stmt) != SQLITE_DONE)
{
logg("Encountered error while trying to store addinfo in long-term database (domainlist_id)");
error = true;
break;
}
else
sqlite3_bind_null(query_stmt, 8);
sqlite3_clear_bindings(addinfo_stmt);
sqlite3_reset(addinfo_stmt);
}
else
{
@@ -933,16 +928,17 @@ void DB_read_queries(void)
query->CNAME_domainID = CNAMEdomainID;
}
}
else if(status == QUERY_REGEX)
else if(sqlite3_column_bytes(stmt, 7) != 0)
{
// QUERY_REGEX: Set ID regex which was the reason for blocking
const int cacheID = findCacheID(query->domainID, query->clientID, query->type);
// 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)
const int cacheID = findCacheID(query->domainID, query->clientID, query->type, true);
DNSCacheData *cache = getDNSCache(cacheID, true);
// Only load if
// a) we have a chace entry
// b) the value of additional_info is not NULL (0 bytes storage size)
if(cache != NULL && sqlite3_column_bytes(stmt, 7) != 0)
cache->black_regex_idx = sqlite3_column_int(stmt, 7);
// a) we have a cache entry
if(cache != NULL)
cache->domainlist_id = sqlite3_column_int(stmt, 7);
}
// Increment status counters, we first have to add one to the count of