Skip to content

Commit f95464d

Browse files
authored
Merge pull request #1431 from pi-hole/development
v5.18
2 parents f64349b + f7e15ea commit f95464d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+752
-471
lines changed

.github/workflows/build.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -164,4 +164,4 @@ jobs:
164164
uses: softprops/action-gh-release@v1
165165
with:
166166
files: |
167-
*
167+
${{ steps.download.outputs.download-path }}/*

CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@
1111
cmake_minimum_required(VERSION 2.8.12)
1212
project(PIHOLE_FTL C)
1313

14-
set(DNSMASQ_VERSION pi-hole-2.87test8)
14+
set(DNSMASQ_VERSION pi-hole-v2.87rc1)
1515

1616
add_subdirectory(src)

build.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ cmake --build . -- -j $(nproc)
4949
# Otherwise, we simply copy the binary one level up
5050
if [[ -n "${install}" ]]; then
5151
echo "Installing pihole-FTL"
52-
SUDO=$(which sudo)
52+
SUDO=$(command -v sudo)
5353
${SUDO} cmake --install .
5454
else
5555
echo "Copying compiled pihole-FTL binary to repository root"

src/api/api.c

+6-7
Original file line numberDiff line numberDiff line change
@@ -1008,15 +1008,14 @@ void getAllQueries(const char *client_message, const int sock, const bool isteln
10081008
CNAME_domain = getCNAMEDomainString(query);
10091009
}
10101010

1011-
// Get ID of blocking regex, if applicable and permitted by privacy settings
1012-
int regex_idx = -1;
1013-
if ((query->status == QUERY_REGEX || query->status == QUERY_REGEX_CNAME) &&
1014-
config.privacylevel < PRIVACY_HIDE_DOMAINS)
1011+
// Get domainlist table ID, if applicable and permitted by privacy settings
1012+
int domainlist_id = -1;
1013+
if (config.privacylevel < PRIVACY_HIDE_DOMAINS)
10151014
{
1016-
unsigned int cacheID = findCacheID(query->domainID, query->clientID, query->type);
1015+
unsigned int cacheID = findCacheID(query->domainID, query->clientID, query->type, false);
10171016
DNSCacheData *dns_cache = getDNSCache(cacheID, true);
10181017
if(dns_cache != NULL)
1019-
regex_idx = dns_cache->black_regex_idx;
1018+
domainlist_id = dns_cache->domainlist_id;
10201019
}
10211020

10221021
// Get IP of upstream destination, if applicable
@@ -1065,7 +1064,7 @@ void getAllQueries(const char *client_message, const int sock, const bool isteln
10651064
reply,
10661065
delay,
10671066
CNAME_domain,
1068-
regex_idx,
1067+
domainlist_id,
10691068
upstream_name,
10701069
upstream_port,
10711070
query->ede == -1 ? "" : get_edestr(query->ede));

src/database/gravity-db.c

+63-38
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,12 @@
1515
#include "../config.h"
1616
// logg()
1717
#include "../log.h"
18-
// match_regex()
19-
#include "../regex_r.h"
2018
// getstr()
2119
#include "../shmem.h"
2220
// SQLite3 prepared statement vectors
2321
#include "../vector.h"
2422
// log_subnet_warning()
23+
// logg_inaccessible_adlist
2524
#include "message-table.h"
2625
// getMACfromIP()
2726
#include "network-table.h"
@@ -210,11 +209,11 @@ bool gravityDB_reopen(void)
210209
return gravityDB_open();
211210
}
212211

213-
static char* get_client_querystr(const char* table, const char* groups)
212+
static char* get_client_querystr(const char *table, const char *column, const char *groups)
214213
{
215214
// Build query string with group filtering
216215
char *querystr = NULL;
217-
if(asprintf(&querystr, "SELECT EXISTS(SELECT domain from %s WHERE domain = ? AND group_id IN (%s));", table, groups) < 1)
216+
if(asprintf(&querystr, "SELECT %s from %s WHERE domain = ? AND group_id IN (%s);", column, table, groups) < 1)
218217
{
219218
logg("get_client_querystr(%s, %s) - asprintf() error", table, groups);
220219
return NULL;
@@ -858,19 +857,14 @@ bool gravityDB_prepare_client_statements(clientsData *client)
858857
return false;
859858

860859
// Prepare whitelist statement
861-
// We use SELECT EXISTS() as this is known to efficiently use the index
862-
// We are only interested in whether the domain exists or not in the
863-
// list but don't case about duplicates or similar. SELECT EXISTS(...)
864-
// returns true as soon as it sees the first row from the query inside
865-
// of EXISTS().
866860
if(config.debug & DEBUG_DATABASE)
867861
logg("gravityDB_open(): Preparing vw_whitelist statement for client %s", clientip);
868-
querystr = get_client_querystr("vw_whitelist", getstr(client->groupspos));
862+
querystr = get_client_querystr("vw_whitelist", "id", getstr(client->groupspos));
869863
sqlite3_stmt* stmt = NULL;
870864
int rc = sqlite3_prepare_v3(gravity_db, querystr, -1, SQLITE_PREPARE_PERSISTENT, &stmt, NULL);
871865
if( rc != SQLITE_OK )
872866
{
873-
logg("gravityDB_open(\"SELECT EXISTS(... vw_whitelist ...)\") - SQL error prepare: %s", sqlite3_errstr(rc));
867+
logg("gravityDB_open(\"SELECT(... vw_whitelist ...)\") - SQL error prepare: %s", sqlite3_errstr(rc));
874868
gravityDB_close();
875869
return false;
876870
}
@@ -880,11 +874,11 @@ bool gravityDB_prepare_client_statements(clientsData *client)
880874
// Prepare gravity statement
881875
if(config.debug & DEBUG_DATABASE)
882876
logg("gravityDB_open(): Preparing vw_gravity statement for client %s", clientip);
883-
querystr = get_client_querystr("vw_gravity", getstr(client->groupspos));
877+
querystr = get_client_querystr("vw_gravity", "domain", getstr(client->groupspos));
884878
rc = sqlite3_prepare_v3(gravity_db, querystr, -1, SQLITE_PREPARE_PERSISTENT, &stmt, NULL);
885879
if( rc != SQLITE_OK )
886880
{
887-
logg("gravityDB_open(\"SELECT EXISTS(... vw_gravity ...)\") - SQL error prepare: %s", sqlite3_errstr(rc));
881+
logg("gravityDB_open(\"SELECT(... vw_gravity ...)\") - SQL error prepare: %s", sqlite3_errstr(rc));
888882
gravityDB_close();
889883
return false;
890884
}
@@ -894,11 +888,11 @@ bool gravityDB_prepare_client_statements(clientsData *client)
894888
// Prepare blacklist statement
895889
if(config.debug & DEBUG_DATABASE)
896890
logg("gravityDB_open(): Preparing vw_blacklist statement for client %s", clientip);
897-
querystr = get_client_querystr("vw_blacklist", getstr(client->groupspos));
891+
querystr = get_client_querystr("vw_blacklist", "id", getstr(client->groupspos));
898892
rc = sqlite3_prepare_v3(gravity_db, querystr, -1, SQLITE_PREPARE_PERSISTENT, &stmt, NULL);
899893
if( rc != SQLITE_OK )
900894
{
901-
logg("gravityDB_open(\"SELECT EXISTS(... vw_blacklist ...)\") - SQL error prepare: %s", sqlite3_errstr(rc));
895+
logg("gravityDB_open(\"SELECT(... vw_blacklist ...)\") - SQL error prepare: %s", sqlite3_errstr(rc));
902896
gravityDB_close();
903897
return false;
904898
}
@@ -1146,7 +1140,7 @@ int gravityDB_count(const enum gravity_tables list)
11461140
return result;
11471141
}
11481142

1149-
static enum db_result domain_in_list(const char *domain, sqlite3_stmt *stmt, const char *listname)
1143+
static enum db_result domain_in_list(const char *domain, sqlite3_stmt *stmt, const char *listname, int *domain_id)
11501144
{
11511145
// Do not try to bind text to statement when database is not available
11521146
if(!gravityDB_opened && !gravityDB_open())
@@ -1180,19 +1174,21 @@ static enum db_result domain_in_list(const char *domain, sqlite3_stmt *stmt, con
11801174
sqlite3_clear_bindings(stmt);
11811175
return LIST_NOT_AVAILABLE;
11821176
}
1183-
else if(rc != SQLITE_ROW)
1177+
else if(rc != SQLITE_ROW && rc != SQLITE_DONE)
11841178
{
1185-
// Any return code that is neither SQLITE_BUSY not SQLITE_ROW
1186-
// is a real error we should log
1179+
// Any return code that is neither SQLITE_BUSY nor SQLITE_ROW or
1180+
// SQLITE_DONE is an error we should log
11871181
logg("domain_in_list(\"%s\", %p, %s): Failed to perform step: %s",
11881182
domain, stmt, listname, sqlite3_errstr(rc));
11891183
sqlite3_reset(stmt);
11901184
sqlite3_clear_bindings(stmt);
11911185
return LIST_NOT_AVAILABLE;
11921186
}
11931187

1194-
// Get result of query "SELECT EXISTS(...)"
1195-
const int result = sqlite3_column_int(stmt, 0);
1188+
// Get result of query (if available)
1189+
const int result = (rc == SQLITE_ROW) ? sqlite3_column_int(stmt, 0) : -1;
1190+
if(domain_id != NULL)
1191+
*domain_id = result;
11961192

11971193
if(config.debug & DEBUG_DATABASE)
11981194
logg("domain_in_list(\"%s\", %p, %s): %d", domain, stmt, listname, result);
@@ -1209,8 +1205,7 @@ static enum db_result domain_in_list(const char *domain, sqlite3_stmt *stmt, con
12091205
sqlite3_clear_bindings(stmt);
12101206

12111207
// Return if domain was found in current table
1212-
// SELECT EXISTS(...) either returns 0 (false) or 1 (true).
1213-
return (result == 1) ? FOUND : NOT_FOUND;
1208+
return (rc == SQLITE_ROW) ? FOUND : NOT_FOUND;
12141209
}
12151210

12161211
void gravityDB_reload_groups(clientsData* client)
@@ -1268,17 +1263,7 @@ enum db_result in_whitelist(const char *domain, DNSCacheData *dns_cache, clients
12681263
// We have to check both the exact whitelist (using a prepared database statement)
12691264
// as well the compiled regex whitelist filters to check if the current domain is
12701265
// whitelisted.
1271-
enum db_result on_whitelist = domain_in_list(domain, stmt, "whitelist");
1272-
1273-
// For performance reasons, the regex evaluations is executed only if the
1274-
// exact whitelist lookup does not deliver a positive match. This is an
1275-
// optimization as the database lookup will most likely hit (a) more domains
1276-
// and (b) will be faster (given a sufficiently large number of regex
1277-
// whitelisting filters).
1278-
if(on_whitelist == NOT_FOUND)
1279-
on_whitelist = match_regex(domain, dns_cache, client->id, REGEX_WHITELIST, false) != -1;
1280-
1281-
return on_whitelist;
1266+
return domain_in_list(domain, stmt, "whitelist", &dns_cache->domainlist_id);
12821267
}
12831268

12841269
enum db_result in_gravity(const char *domain, clientsData *client)
@@ -1306,10 +1291,10 @@ enum db_result in_gravity(const char *domain, clientsData *client)
13061291
if(stmt == NULL)
13071292
stmt = gravity_stmt->get(gravity_stmt, client->id);
13081293

1309-
return domain_in_list(domain, stmt, "gravity");
1294+
return domain_in_list(domain, stmt, "gravity", NULL);
13101295
}
13111296

1312-
enum db_result in_blacklist(const char *domain, clientsData *client)
1297+
enum db_result in_blacklist(const char *domain, DNSCacheData *dns_cache, clientsData *client)
13131298
{
13141299
// If list statement is not ready and cannot be initialized (e.g. no
13151300
// access to the database), we return false to prevent an FTL crash
@@ -1334,7 +1319,7 @@ enum db_result in_blacklist(const char *domain, clientsData *client)
13341319
if(stmt == NULL)
13351320
stmt = blacklist_stmt->get(blacklist_stmt, client->id);
13361321

1337-
return domain_in_list(domain, stmt, "blacklist");
1322+
return domain_in_list(domain, stmt, "blacklist", &dns_cache->domainlist_id);
13381323
}
13391324

13401325
bool in_auditlist(const char *domain)
@@ -1345,7 +1330,7 @@ bool in_auditlist(const char *domain)
13451330
return false;
13461331

13471332
// We check the domain_audit table for the given domain
1348-
return domain_in_list(domain, auditlist_stmt, "auditlist") == FOUND;
1333+
return domain_in_list(domain, auditlist_stmt, "auditlist", NULL) == FOUND;
13491334
}
13501335

13511336
bool gravityDB_get_regex_client_groups(clientsData* client, const unsigned int numregex, const regexData *regex,
@@ -1407,3 +1392,43 @@ bool gravityDB_get_regex_client_groups(clientsData* client, const unsigned int n
14071392

14081393
return true;
14091394
}
1395+
1396+
void check_inaccessible_adlists(void)
1397+
{
1398+
1399+
// check if any adlist was inaccessible in the last gravity run
1400+
// if so, gravity stored `status` in the adlist table with
1401+
// "3": List unavailable, Pi-hole used a local copy
1402+
// "4": List unavailable, there is no local copy available
1403+
1404+
// Do not proceed when database is not available
1405+
if(!gravityDB_opened && !gravityDB_open())
1406+
{
1407+
logg("check_inaccessible_adlists(): Gravity database not available");
1408+
return;
1409+
}
1410+
1411+
const char *querystr = "SELECT id, address FROM adlist WHERE status IN (3,4) AND enabled=1";
1412+
1413+
// Prepare query
1414+
sqlite3_stmt *query_stmt;
1415+
int rc = sqlite3_prepare_v2(gravity_db, querystr, -1, &query_stmt, NULL);
1416+
if(rc != SQLITE_OK){
1417+
logg("check_inaccessible_adlists(): %s - SQL error prepare: %s", querystr, sqlite3_errstr(rc));
1418+
gravityDB_close();
1419+
return;
1420+
}
1421+
1422+
// Perform query
1423+
while((rc = sqlite3_step(query_stmt)) == SQLITE_ROW)
1424+
{
1425+
int id = sqlite3_column_int(query_stmt, 0);
1426+
const char *address = (const char*)sqlite3_column_text(query_stmt, 1);
1427+
1428+
// log to the message table
1429+
logg_inaccessible_adlist(id, address);
1430+
}
1431+
1432+
// Finalize statement
1433+
sqlite3_finalize(query_stmt);
1434+
}

src/database/gravity-db.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,10 @@ const char* gravityDB_getDomain(int *rowid);
2929
char* get_client_names_from_ids(const char *group_ids) __attribute__ ((malloc));
3030
void gravityDB_finalizeTable(void);
3131
int gravityDB_count(const enum gravity_tables list);
32+
void check_inaccessible_adlists(void);
3233

3334
enum db_result in_gravity(const char *domain, clientsData *client);
34-
enum db_result in_blacklist(const char *domain, clientsData *client);
35+
enum db_result in_blacklist(const char *domain, DNSCacheData *dns_cache, clientsData *client);
3536
enum db_result in_whitelist(const char *domain, DNSCacheData *dns_cache, clientsData *client);
3637
bool in_auditlist(const char *domain);
3738

src/database/message-table.c

+17-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
#include "../gc.h"
2828

2929
static const char *message_types[MAX_MESSAGE] =
30-
{ "REGEX", "SUBNET", "HOSTNAME", "DNSMASQ_CONFIG", "RATE_LIMIT", "DNSMASQ_WARN", "LOAD", "SHMEM", "DISK" };
30+
{ "REGEX", "SUBNET", "HOSTNAME", "DNSMASQ_CONFIG", "RATE_LIMIT", "DNSMASQ_WARN", "LOAD", "SHMEM", "DISK", "ADLIST" };
3131

3232
static unsigned char message_blob_types[MAX_MESSAGE][5] =
3333
{
@@ -94,6 +94,13 @@ static unsigned char message_blob_types[MAX_MESSAGE][5] =
9494
SQLITE_NULL, // Not used
9595
SQLITE_NULL // Not used
9696
},
97+
{ // INACCESSIBLE_ADLIST_MESSAGE: The message column contains the corresponding adlist URL
98+
SQLITE_INTEGER, // database index of the adlist (so the dashboard can show a link)
99+
SQLITE_NULL, // not used
100+
SQLITE_NULL, // not used
101+
SQLITE_NULL, // not used
102+
SQLITE_NULL // not used
103+
},
97104
};
98105
// Create message table in the database
99106
bool create_message_table(sqlite3 *db)
@@ -391,3 +398,12 @@ void log_resource_shortage(const double load, const int nprocs, const int shmem,
391398
add_message(DISK_MESSAGE, true, path, 2, disk, msg);
392399
}
393400
}
401+
402+
void logg_inaccessible_adlist(const int dbindex, const char *address)
403+
{
404+
// Log to FTL.log
405+
logg("Adlist warning: Adlist with ID %d (%s) was inaccessible during last gravity run", dbindex, address);
406+
407+
// Log to database
408+
add_message(INACCESSIBLE_ADLIST_MESSAGE, false, address, 1, dbindex);
409+
}

src/database/message-table.h

+1
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,6 @@ void logg_fatal_dnsmasq_message(const char *message);
2323
void logg_rate_limit_message(const char *clientIP, const unsigned int rate_limit_count);
2424
void logg_warn_dnsmasq_message(char *message);
2525
void log_resource_shortage(const double load, const int nprocs, const int shmem, const int disk, const char *path, const char *msg);
26+
void logg_inaccessible_adlist(const int dbindex, const char *address);
2627

2728
#endif //MESSAGETABLE_H

0 commit comments

Comments
 (0)