mirror of
https://github.com/pi-hole/FTL.git
synced 2024-10-26 16:52:18 +02:00
Merge pull request #1409 from pi-hole/tweak/white_regex_id
Store domainlist IDs for blocked/permitted queries
This commit is contained in:
+22
-38
@@ -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,
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user