mirror of
https://github.com/pi-hole/FTL.git
synced 2024-10-26 16:52:18 +02:00
Instead of populating the DNS cache with the domains from the blacklist and gravity on startup/reload, we move the evaluation of whether or not a certain query is to be blocked out of the (generic) cache into FTL's main code. As this gives us full control over what is blocked and what not, it is also the first step into an often requested feature (per-client blocking with Pi-hole). Note that this commit should work without any changes to the gravity database. The performance impact is entirely untested as are the various blocking modes (I tested our standard NULL blocking over UDP and TCP and both seem to work). Consider this code as highly experimental but hopefully stable (given what "stable" means on our development branches)
Signed-off-by: DL6ER <dl6er@dl6er.de>
This commit is contained in:
@@ -23,6 +23,8 @@ static sqlite3 *gravity_db = NULL;
|
||||
static sqlite3_stmt* table_stmt = NULL;
|
||||
static sqlite3_stmt* whitelist_stmt = NULL;
|
||||
static sqlite3_stmt* auditlist_stmt = NULL;
|
||||
static sqlite3_stmt* gravity_stmt = NULL;
|
||||
static sqlite3_stmt* blacklist_stmt = NULL;
|
||||
bool gravity_database_avail = false;
|
||||
|
||||
// Table names corresponding to the enum defined in gravity-db.h
|
||||
@@ -92,6 +94,24 @@ bool gravityDB_open(void)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prepare gravity statement
|
||||
rc = sqlite3_prepare_v2(gravity_db, "SELECT EXISTS(SELECT domain from vw_gravity WHERE domain = ?);", -1, &gravity_stmt, NULL);
|
||||
if( rc != SQLITE_OK )
|
||||
{
|
||||
logg("gravityDB_open(\"SELECT EXISTS(... vw_gravity ...)\") - SQL error prepare (%i): %s", rc, sqlite3_errmsg(gravity_db));
|
||||
gravityDB_close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prepare blacklist statement
|
||||
rc = sqlite3_prepare_v2(gravity_db, "SELECT EXISTS(SELECT domain from vw_blacklist WHERE domain = ?);", -1, &blacklist_stmt, NULL);
|
||||
if( rc != SQLITE_OK )
|
||||
{
|
||||
logg("gravityDB_open(\"SELECT EXISTS(... vw_blacklist ...)\") - SQL error prepare (%i): %s", rc, sqlite3_errmsg(gravity_db));
|
||||
gravityDB_close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Database connection is now open
|
||||
gravity_database_avail = true;
|
||||
if(config.debug & DEBUG_DATABASE)
|
||||
@@ -332,6 +352,16 @@ bool in_whitelist(const char *domain)
|
||||
return domain_in_list(domain, whitelist_stmt) || match_regex(domain, REGEX_WHITELIST);
|
||||
}
|
||||
|
||||
inline bool in_gravity(const char *domain)
|
||||
{
|
||||
return domain_in_list(domain, gravity_stmt);
|
||||
}
|
||||
|
||||
inline bool in_blacklist(const char *domain)
|
||||
{
|
||||
return domain_in_list(domain, blacklist_stmt);
|
||||
}
|
||||
|
||||
bool in_auditlist(const char *domain)
|
||||
{
|
||||
if(config.debug & DEBUG_DATABASE)
|
||||
|
||||
@@ -22,4 +22,7 @@ int gravityDB_count(unsigned char list);
|
||||
bool in_whitelist(const char *domain);
|
||||
bool in_auditlist(const char *domain);
|
||||
|
||||
bool in_gravity(const char *domain);
|
||||
bool in_blacklist(const char *domain);
|
||||
|
||||
#endif //GRAVITY_H
|
||||
|
||||
@@ -1182,10 +1182,6 @@ void cache_reload(void)
|
||||
}
|
||||
else
|
||||
{
|
||||
/*------------------------------- Pi-hole modification -------------------------------*/
|
||||
total_size = FTL_database_import(total_size, (struct crec **)daemon->packet, revhashsz);
|
||||
/*------------------------------------------------------------------------------------*/
|
||||
|
||||
if (!option_bool(OPT_NO_HOSTS))
|
||||
total_size = read_hostsfile(HOSTSFILE, SRC_HOSTS, total_size, (struct crec **)daemon->packet, revhashsz);
|
||||
|
||||
|
||||
+37
-4
@@ -1333,6 +1333,9 @@ void receive_query(struct listener *listen, time_t now)
|
||||
#else
|
||||
int check_dst = !option_bool(OPT_NOWILD);
|
||||
#endif
|
||||
/************ Pi-hole modification ************/
|
||||
char piholeblocked = 0;
|
||||
/**********************************************/
|
||||
|
||||
/* packet buffer overwritten */
|
||||
daemon->srv_save = NULL;
|
||||
@@ -1556,7 +1559,7 @@ void receive_query(struct listener *listen, time_t now)
|
||||
{
|
||||
log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
|
||||
(struct all_addr *)&source_addr.in.sin_addr, types);
|
||||
FTL_new_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
|
||||
piholeblocked = FTL_new_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
|
||||
(struct all_addr *)&source_addr.in.sin_addr, types, daemon->log_display_id, UDP);
|
||||
}
|
||||
#ifdef HAVE_IPV6
|
||||
@@ -1564,7 +1567,7 @@ void receive_query(struct listener *listen, time_t now)
|
||||
{
|
||||
log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff,
|
||||
(struct all_addr *)&source_addr.in6.sin6_addr, types);
|
||||
FTL_new_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff,
|
||||
piholeblocked = FTL_new_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff,
|
||||
(struct all_addr *)&source_addr.in6.sin6_addr, types, daemon->log_display_id, UDP);
|
||||
}
|
||||
#endif
|
||||
@@ -1632,6 +1635,20 @@ void receive_query(struct listener *listen, time_t now)
|
||||
m = answer_request(header, ((char *) header) + udp_size, (size_t)n,
|
||||
dst_addr_4, netmask, now, ad_reqd, do_bit, have_pseudoheader);
|
||||
|
||||
/************ Pi-hole modification ************/
|
||||
if(piholeblocked)
|
||||
{
|
||||
size_t plen = n;
|
||||
struct all_addr *addrp = NULL;
|
||||
unsigned int flags = (listen->family == AF_INET) ? F_IPV4 : F_IPV6;
|
||||
FTL_get_blocking_metadata(&addrp, &flags);
|
||||
plen = setup_reply(header, n, addrp, flags, daemon->local_ttl);
|
||||
// if (find_pseudoheader(header, plen, NULL, NULL, NULL, NULL))
|
||||
// plen = add_pseudoheader(header, plen, ((unsigned char *) header) + PACKETSZ, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0);
|
||||
send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND), (char *)header, plen, (union mysockaddr*)&source_addr, &dst_addr, if_index);
|
||||
}
|
||||
else
|
||||
/**********************************************/
|
||||
if (m >= 1)
|
||||
{
|
||||
send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND),
|
||||
@@ -1837,6 +1854,10 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
(void)mark;
|
||||
(void)have_mark;
|
||||
|
||||
/************ Pi-hole modification ************/
|
||||
char piholeblocked = 0;
|
||||
/**********************************************/
|
||||
|
||||
if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) == -1)
|
||||
return packet;
|
||||
|
||||
@@ -1925,7 +1946,7 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
{
|
||||
log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
|
||||
(struct all_addr *)&peer_addr.in.sin_addr, types);
|
||||
FTL_new_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
|
||||
piholeblocked = FTL_new_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
|
||||
(struct all_addr *)&peer_addr.in.sin_addr, types, daemon->log_display_id, TCP);
|
||||
}
|
||||
#ifdef HAVE_IPV6
|
||||
@@ -1933,7 +1954,7 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
{
|
||||
log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff,
|
||||
(struct all_addr *)&peer_addr.in6.sin6_addr, types);
|
||||
FTL_new_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff,
|
||||
piholeblocked = FTL_new_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff,
|
||||
(struct all_addr *)&peer_addr.in6.sin6_addr, types, daemon->log_display_id, TCP);
|
||||
}
|
||||
#endif
|
||||
@@ -1989,6 +2010,18 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
/* Do this by steam now we're not in the select() loop */
|
||||
check_log_writer(1);
|
||||
|
||||
/************ Pi-hole modification ************/
|
||||
if(piholeblocked)
|
||||
{
|
||||
struct all_addr *addrp = NULL;
|
||||
unsigned int flags = (peer_addr.sa.sa_family == AF_INET) ? F_IPV4 : F_IPV6;
|
||||
FTL_get_blocking_metadata(&addrp, &flags);
|
||||
m = setup_reply(header, size, addrp, flags, daemon->local_ttl);
|
||||
if (have_pseudoheader)
|
||||
m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0);
|
||||
}
|
||||
else
|
||||
/**********************************************/
|
||||
if (m == 0)
|
||||
{
|
||||
unsigned int flags = 0;
|
||||
|
||||
+91
-227
@@ -39,15 +39,20 @@
|
||||
static void print_flags(const unsigned int flags);
|
||||
static void save_reply_type(const unsigned int flags, const int queryID, const struct timeval response);
|
||||
static unsigned long converttimeval(const struct timeval time) __attribute__((const));
|
||||
static void block_single_domain_regex(const char *domain);
|
||||
static void detect_blocked_IP(const unsigned short flags, const char* answer, const int queryID);
|
||||
static void query_externally_blocked(const int queryID, const unsigned char status);
|
||||
static int findQueryID(const int id);
|
||||
static void prepare_blocking_metadata(void);
|
||||
|
||||
// Static blocking metadata (stored precomputed as time-critical)
|
||||
static unsigned int blocking_flags = 0;
|
||||
static struct all_addr blocking_addrp_v4 = {{{ 0 }}};
|
||||
static struct all_addr blocking_addrp_v6 = {{{ 0 }}};
|
||||
|
||||
unsigned char* pihole_privacylevel = &config.privacylevel;
|
||||
const char flagnames[28][12] = {"F_IMMORTAL ", "F_NAMEP ", "F_REVERSE ", "F_FORWARD ", "F_DHCP ", "F_NEG ", "F_HOSTS ", "F_IPV4 ", "F_IPV6 ", "F_BIGNAME ", "F_NXDOMAIN ", "F_CNAME ", "F_DNSKEY ", "F_CONFIG ", "F_DS ", "F_DNSSECOK ", "F_UPSTREAM ", "F_RRNAME ", "F_SERVER ", "F_QUERY ", "F_NOERR ", "F_AUTH ", "F_DNSSEC ", "F_KEYTAG ", "F_SECSTAT ", "F_NO_RR ", "F_IPSET ", "F_NOEXTRA "};
|
||||
|
||||
void _FTL_new_query(const unsigned int flags, const char *name, const struct all_addr *addr,
|
||||
char _FTL_new_query(const unsigned int flags, const char *name, const struct all_addr *addr,
|
||||
const char *types, const int id, const char type,
|
||||
const char* file, const int line)
|
||||
{
|
||||
@@ -55,7 +60,7 @@ void _FTL_new_query(const unsigned int flags, const char *name, const struct all
|
||||
|
||||
// Don't analyze anything if in PRIVACY_NOSTATS mode
|
||||
if(config.privacylevel >= PRIVACY_NOSTATS)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
// Lock shared memory
|
||||
lock_shm();
|
||||
@@ -89,7 +94,7 @@ void _FTL_new_query(const unsigned int flags, const char *name, const struct all
|
||||
if(config.debug & DEBUG_QUERIES)
|
||||
logg("Notice: Skipping unknown query type: %s (%i)", types, id);
|
||||
unlock_shm();
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Skip AAAA queries if user doesn't want to have them analyzed
|
||||
@@ -98,7 +103,7 @@ void _FTL_new_query(const unsigned int flags, const char *name, const struct all
|
||||
if(config.debug & DEBUG_QUERIES)
|
||||
logg("Not analyzing AAAA query");
|
||||
unlock_shm();
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Ensure we have enough space in the queries struct
|
||||
@@ -109,7 +114,7 @@ void _FTL_new_query(const unsigned int flags, const char *name, const struct all
|
||||
if(strcasecmp(name, "pi.hole") == 0)
|
||||
{
|
||||
unlock_shm();
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Convert domain to lower case
|
||||
@@ -129,7 +134,7 @@ void _FTL_new_query(const unsigned int flags, const char *name, const struct all
|
||||
free(domainString);
|
||||
free(clientIP);
|
||||
unlock_shm();
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Log new query if in debug mode
|
||||
@@ -156,7 +161,7 @@ void _FTL_new_query(const unsigned int flags, const char *name, const struct all
|
||||
free(domainString);
|
||||
free(clientIP);
|
||||
unlock_shm();
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Go through already knows domains and see if it is one of them
|
||||
@@ -212,26 +217,36 @@ void _FTL_new_query(const unsigned int flags, const char *name, const struct all
|
||||
// Get domain pointer
|
||||
domainsData* domain = getDomain(domainID, true);
|
||||
|
||||
// Try blocking regex if configured
|
||||
if(domain->regexmatch == REGEX_UNKNOWN && blockingstatus != BLOCKING_DISABLED)
|
||||
// Only check domains for blocking conditions when global blocking is enabled
|
||||
char blockDomain = 0;
|
||||
if(blockingstatus != BLOCKING_DISABLED)
|
||||
{
|
||||
// For minimal performance impact, we test the regex only when
|
||||
// - regex checking is enabled, and
|
||||
// - this domain has not already been validated against the regex.
|
||||
// This effectively prevents multiple evaluations of the same domain
|
||||
//
|
||||
// We check the user blacklist first as it is typically smaller than gravity
|
||||
if(in_blacklist(domainString) || in_gravity(domainString))
|
||||
{
|
||||
blockDomain = 1;
|
||||
}
|
||||
|
||||
// If a regex filter matched, we additionally compare the domain
|
||||
// against all known whitelisted domains to possibly prevent blocking
|
||||
// of a specific domain. The logic herein is:
|
||||
// If matched, then compare against whitelist
|
||||
// If in whitelist, negate matched so this function returns: not-to-be-blocked
|
||||
if(match_regex(domainString, REGEX_BLACKLIST) && !in_whitelist(domainString))
|
||||
// - Walk regex only if not already exactly matched above
|
||||
// - If matched, then compare against whitelist
|
||||
// - If in whitelist, negate matched so this function returns: not-to-be-blocked
|
||||
if(domain->regexmatch == REGEX_UNKNOWN &&
|
||||
!blockDomain &&
|
||||
match_regex(domainString, REGEX_BLACKLIST) &&
|
||||
!in_whitelist(domainString))
|
||||
{
|
||||
// We have to block this domain
|
||||
block_single_domain_regex(domainString);
|
||||
// Mark domain as regex match
|
||||
domain->regexmatch = REGEX_BLOCKED;
|
||||
}
|
||||
else
|
||||
if(domain->regexmatch == REGEX_BLOCKED)
|
||||
{
|
||||
// We have to block this domain
|
||||
blockDomain = 1;
|
||||
}
|
||||
else if(domain->regexmatch == REGEX_UNKNOWN && !blockDomain)
|
||||
{
|
||||
// Explicitly mark as not blocked to skip regex test
|
||||
// next time we see this domain
|
||||
@@ -245,6 +260,33 @@ void _FTL_new_query(const unsigned int flags, const char *name, const struct all
|
||||
|
||||
// Release thread lock
|
||||
unlock_shm();
|
||||
|
||||
return blockDomain;
|
||||
}
|
||||
|
||||
void _FTL_get_blocking_metadata(struct all_addr **addrp, unsigned int *flags, const char* file, const int line)
|
||||
{
|
||||
// Add flags according to current blocking mode
|
||||
// We bit-add here as flags already contains either F_IPV4 or F_IPV6
|
||||
*flags |= blocking_flags;
|
||||
|
||||
if(*flags & F_IPV6)
|
||||
{
|
||||
if(config.blockingmode == MODE_IP_NODATA_AAAA)
|
||||
{
|
||||
// Overwrite flags in this mode as the response
|
||||
// for IPv4 and IPv6 is different
|
||||
*flags = F_NEG;
|
||||
}
|
||||
|
||||
// Pass blocking IPv6 address (will be :: in most cases)
|
||||
*addrp = &blocking_addrp_v6;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Pass blocking IPv4 address (will be 0.0.0.0 in most cases)
|
||||
*addrp = &blocking_addrp_v4;
|
||||
}
|
||||
}
|
||||
|
||||
static int findQueryID(const int id)
|
||||
@@ -411,6 +453,9 @@ void FTL_dnsmasq_reload(void)
|
||||
// Passing NULL to this function means it has to open the config file on
|
||||
// its own behalf (on initial reading, the config file is already opened)
|
||||
get_blocking_mode(NULL);
|
||||
// Update blocking metadata (target IP addresses and DNS header flags)
|
||||
// as the blocking mode might have changed
|
||||
prepare_blocking_metadata();
|
||||
|
||||
// Reread pihole-FTL.conf to see which debugging flags are set
|
||||
read_debuging_settings(NULL);
|
||||
@@ -1267,9 +1312,14 @@ static unsigned long __attribute__((const)) converttimeval(const struct timeval
|
||||
}
|
||||
|
||||
// This subroutine prepares IPv4 and IPv6 addresses for blocking queries depending on the configured blocking mode
|
||||
static void prepare_blocking_mode(struct all_addr *addr4, struct all_addr *addr6, bool *has_IPv4, bool *has_IPv6)
|
||||
static void prepare_blocking_metadata(void)
|
||||
{
|
||||
// Read IPv4 address for host entries from setupVars.conf
|
||||
// Reset all blocking metadata
|
||||
blocking_flags = 0;
|
||||
memset(&blocking_addrp_v4, 0, sizeof(blocking_addrp_v4));
|
||||
memset(&blocking_addrp_v6, 0, sizeof(blocking_addrp_v6));
|
||||
|
||||
// Use the blocking IPv4 address from setupVars.conf only if needed for selected blocking mode
|
||||
char* const IPv4addr = read_setupVarsconf("IPV4_ADDRESS");
|
||||
if((config.blockingmode == MODE_IP || config.blockingmode == MODE_IP_NODATA_AAAA) &&
|
||||
IPv4addr != NULL && strlen(IPv4addr) > 0)
|
||||
@@ -1277,18 +1327,26 @@ static void prepare_blocking_mode(struct all_addr *addr4, struct all_addr *addr6
|
||||
// Strip off everything at the end of the IP (CIDR might be there)
|
||||
char* a=IPv4addr; for(;*a;a++) if(*a == '/') *a = 0;
|
||||
// Prepare IPv4 address for records
|
||||
if(inet_pton(AF_INET, IPv4addr, addr4) > 0)
|
||||
*has_IPv4 = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Blocking mode will use zero-initialized all_addr struct
|
||||
*has_IPv4 = true;
|
||||
if(inet_pton(AF_INET, IPv4addr, &blocking_addrp_v4) != 1)
|
||||
logg("ERROR: Found invalid IPv4 address in setupVars.conf: %s", IPv4addr);
|
||||
}
|
||||
// Free IPv4addr
|
||||
clearSetupVarsArray();
|
||||
|
||||
// Read IPv6 address for host entries from setupVars.conf
|
||||
if(config.blockingmode == MODE_NX)
|
||||
{
|
||||
// If we block in NXDOMAIN mode, we add the NXDOMAIN flag and make this host record
|
||||
// also valid for AAAA requests
|
||||
blocking_flags |= F_NEG | F_NXDOMAIN;
|
||||
}
|
||||
else if(config.blockingmode == MODE_NODATA)
|
||||
{
|
||||
// If we block in NODATA mode, we make this host record also valid for AAAA requests
|
||||
// and apply the NEG response flag (but not the NXDOMAIN flag)
|
||||
blocking_flags |= F_NEG;
|
||||
}
|
||||
|
||||
// Use the blocking IPv6 address from setupVars.conf only if needed for selected blocking mode
|
||||
char* const IPv6addr = read_setupVarsconf("IPV6_ADDRESS");
|
||||
if(config.blockingmode == MODE_IP &&
|
||||
IPv6addr != NULL && strlen(IPv6addr) > 0)
|
||||
@@ -1296,203 +1354,9 @@ static void prepare_blocking_mode(struct all_addr *addr4, struct all_addr *addr6
|
||||
// Strip off everything at the end of the IP (CIDR might be there)
|
||||
char* a=IPv6addr; for(;*a;a++) if(*a == '/') *a = 0;
|
||||
// Prepare IPv6 address for records
|
||||
if(inet_pton(AF_INET6, IPv6addr, addr6) > 0)
|
||||
*has_IPv6 = true;
|
||||
}
|
||||
else if(config.blockingmode == MODE_IP_NODATA_AAAA)
|
||||
{
|
||||
// Blocking mode will use zero-initialized all_addr struct
|
||||
// This is irrelevant, however, as this blocking mode will
|
||||
// reply with NODATA to AAAA queries. Still, we need to
|
||||
// generate separate IPv4 (IP) and AAAA (NODATA) records
|
||||
*has_IPv6 = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Don't create IPv6 cache entries when we don't need them
|
||||
// Also, don't create them if we are in IP blocking mode and
|
||||
// strlen(IPv6addr) == 0
|
||||
*has_IPv6 = false;
|
||||
if(inet_pton(AF_INET6, IPv6addr, &blocking_addrp_v6) != 1)
|
||||
logg("ERROR: Found invalid IPv6 address in setupVars.conf: %s", IPv4addr);
|
||||
}
|
||||
// Free IPv6addr
|
||||
clearSetupVarsArray();
|
||||
}
|
||||
|
||||
// Prototypes from functions in dnsmasq's source
|
||||
void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrlen, unsigned int index, struct crec **rhash, int hashsz);
|
||||
void rehash(int size);
|
||||
|
||||
// This routine adds one domain to the resolver's cache. Depending on the configured blocking mode it may create
|
||||
// a single entry valid for IPv4 & IPv6 or two entries one for IPv4 and one for IPv6.
|
||||
// When IPv6 is not available on the machine, we do not add IPv6 cache entries (likewise for IPv4)
|
||||
static int add_blocked_domain(struct all_addr *addr4, struct all_addr *addr6, const bool has_IPv4, const bool has_IPv6,
|
||||
const char *domain, const int len, struct crec **rhash, int hashsz, const unsigned int index)
|
||||
{
|
||||
int name_count = 0;
|
||||
struct crec *cache4,*cache6;
|
||||
// Add IPv4 record, allocate enough space for cache entry including arbitrary domain name length
|
||||
// (the domain name is stored at the end of struct crec)
|
||||
if(has_IPv4 &&
|
||||
(cache4 = malloc(sizeof(struct crec) + len+1-SMALLDNAME)))
|
||||
{
|
||||
strcpy(cache4->name.sname, domain);
|
||||
cache4->flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_IPV4;
|
||||
int memorysize = INADDRSZ;
|
||||
if(config.blockingmode == MODE_NX)
|
||||
{
|
||||
// If we block in NXDOMAIN mode, we add the NXDOMAIN flag and make this host record
|
||||
// also valid for AAAA requests
|
||||
cache4->flags |= F_IPV6 | F_NEG | F_NXDOMAIN;
|
||||
}
|
||||
else if(config.blockingmode == MODE_NULL)
|
||||
{
|
||||
// If we block in NULL mode, we make this host record also valid for AAAA requests
|
||||
// This is okay as the addr structs have been statically zero-initialized
|
||||
cache4->flags |= F_IPV6;
|
||||
memorysize = IN6ADDRSZ;
|
||||
}
|
||||
else if(config.blockingmode == MODE_NODATA)
|
||||
{
|
||||
// If we block in NODATA mode, we make this host record also valid for AAAA requests
|
||||
// and apply the NEG response flag (but not the NXDOMAIN flag)
|
||||
cache4->flags |= F_IPV6 | F_NEG;
|
||||
}
|
||||
cache4->ttd = daemon->local_ttl;
|
||||
add_hosts_entry(cache4, addr4, memorysize, index, rhash, hashsz);
|
||||
name_count++;
|
||||
}
|
||||
// Add IPv6 record only if we respond with a non-NULL IP address to blocked domains
|
||||
if(has_IPv6 && (config.blockingmode == MODE_IP || config.blockingmode == MODE_IP_NODATA_AAAA) &&
|
||||
(cache6 = malloc(sizeof(struct crec) + len+1-SMALLDNAME)))
|
||||
{
|
||||
strcpy(cache6->name.sname, domain);
|
||||
cache6->flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_IPV6;
|
||||
if(config.blockingmode == MODE_IP_NODATA_AAAA) cache6->flags |= F_NEG;
|
||||
cache6->ttd = daemon->local_ttl;
|
||||
add_hosts_entry(cache6, addr6, IN6ADDRSZ, index, rhash, hashsz);
|
||||
name_count++;
|
||||
}
|
||||
|
||||
// Return 1 if only one cache slot was allocated (IPv4) or 2 if two slots were allocated (IPv4 + IPv6)
|
||||
return name_count;
|
||||
}
|
||||
|
||||
// Add a single domain to resolver's cache. This respects the configured blocking mode
|
||||
// Note: This routine is meant for adding a single domain at a time. It should not be
|
||||
// invoked for batch processing
|
||||
static void block_single_domain_regex(const char *domain)
|
||||
{
|
||||
struct all_addr addr4 = {{{ 0 }}}, addr6 = {{{ 0 }}};
|
||||
bool has_IPv4 = false, has_IPv6 = false;
|
||||
|
||||
// Get IPv4/v6 addresses for blocking depending on user configures blocking mode
|
||||
prepare_blocking_mode(&addr4, &addr6, &has_IPv4, &has_IPv6);
|
||||
add_blocked_domain(&addr4, &addr6, has_IPv4, has_IPv6, domain, strlen(domain), NULL, 0, SRC_REGEX);
|
||||
|
||||
if(config.debug & DEBUG_QUERIES)
|
||||
logg("Added %s to cache", domain);
|
||||
}
|
||||
|
||||
// Import a specified table from the gravity database and
|
||||
// add the read domains to the cache using the currently
|
||||
// selected blocking mode. This function is used to import
|
||||
// both the blacklist and the gravity blocking domains
|
||||
static int FTL_table_import(const char *tablename, const unsigned char list, const unsigned int index,
|
||||
struct all_addr addr4, struct all_addr addr6, bool has_IPv4, bool has_IPv6,
|
||||
int cache_size, struct crec **rhash, int hashsz)
|
||||
{
|
||||
// Variables
|
||||
int name_count = cache_size, added = 0;
|
||||
|
||||
// Start timer for list analysis
|
||||
timer_start(LISTS_TIMER);
|
||||
|
||||
// Get database table handle
|
||||
if(!gravityDB_getTable(list))
|
||||
{
|
||||
logg("FTL_listsfile(): Error getting %s table from database", tablename);
|
||||
return name_count;
|
||||
}
|
||||
|
||||
// Walk database table
|
||||
const char *domain = NULL;
|
||||
while((domain = gravityDB_getDomain()) != NULL)
|
||||
{
|
||||
int len = strlen(domain);
|
||||
// Skip empty database rows
|
||||
if(len == 0)
|
||||
continue;
|
||||
|
||||
// Do not add gravity or blacklist domains that match
|
||||
// a regex-based whitelist filter
|
||||
if(match_regex(domain, REGEX_WHITELIST))
|
||||
continue;
|
||||
|
||||
// As of here we assume the entry to be valid
|
||||
// Rehash every 1000 valid names
|
||||
if(rhash && ((name_count - cache_size) > 1000))
|
||||
{
|
||||
rehash(name_count);
|
||||
cache_size = name_count;
|
||||
}
|
||||
|
||||
// Add domain
|
||||
name_count += add_blocked_domain(&addr4, &addr6, has_IPv4, has_IPv6, domain, len, rhash, hashsz, index);
|
||||
|
||||
// Count added domain
|
||||
added++;
|
||||
}
|
||||
|
||||
// Rehash after having read all entries
|
||||
if(rhash)
|
||||
rehash(name_count);
|
||||
|
||||
// Finalize statement and close gravity database handle
|
||||
gravityDB_finalizeTable();
|
||||
|
||||
// Final logging
|
||||
logg("Database (%s): imported %i domains (took %.1f ms)", tablename, added, timer_elapsed_msec(LISTS_TIMER));
|
||||
|
||||
// Return number of domains added to the cache
|
||||
return added;
|
||||
}
|
||||
|
||||
// Import blocking domains from the gravity database
|
||||
// This function is run whenever dnsmasq reads in HOSTS
|
||||
// files (on startup as well as on receipt of SIGHUP)
|
||||
int FTL_database_import(int cache_size, struct crec **rhash, int hashsz)
|
||||
{
|
||||
struct all_addr addr4 = {{{ 0 }}}, addr6 = {{{ 0 }}};
|
||||
bool has_IPv4 = false, has_IPv6 = false;
|
||||
|
||||
if(blockingstatus == BLOCKING_DISABLED)
|
||||
{
|
||||
logg("Skipping import of database tables because blocking is disabled");
|
||||
return cache_size;
|
||||
}
|
||||
|
||||
// Get IPv4/v6 addresses for blocking depending on user configured blocking mode
|
||||
prepare_blocking_mode(&addr4, &addr6, &has_IPv4, &has_IPv6);
|
||||
|
||||
// If we have neither a valid IPv4 nor a valid IPv6 but the user asked for
|
||||
// blocking modes MODE_IP or MODE_IP_NODATA_AAAA then we cannot add any entries here
|
||||
if(!has_IPv4 && !has_IPv6)
|
||||
{
|
||||
logg("ERROR: Cannot add domains from gravity because pihole-FTL found\n" \
|
||||
" neither a valid IPV4_ADDRESS nor IPV6_ADDRESS in setupVars.conf" \
|
||||
" This is an impossible configuration. Please contact the Pi-hole" \
|
||||
" support if you need assistance.");
|
||||
return cache_size;
|
||||
}
|
||||
|
||||
// Import gravity and exact blacklisted domains
|
||||
int added;
|
||||
added = FTL_table_import("gravity", GRAVITY_TABLE, SRC_GRAVITY, addr4, addr6, has_IPv4, has_IPv6, cache_size, rhash, hashsz);
|
||||
added += FTL_table_import("blacklist", EXACT_BLACKLIST_TABLE, SRC_BLACK, addr4, addr6, has_IPv4, has_IPv6, cache_size, rhash, hashsz);
|
||||
|
||||
// Update counter of blocked domains
|
||||
counters->gravity = added;
|
||||
|
||||
// Return new cache size which now includes more domains than before
|
||||
return cache_size + added;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ extern unsigned char* pihole_privacylevel;
|
||||
enum { TCP, UDP };
|
||||
|
||||
#define FTL_new_query(flags, name, addr, types, id, type) _FTL_new_query(flags, name, addr, types, id, type, __FILE__, __LINE__)
|
||||
void _FTL_new_query(const unsigned int flags, const char *name, const struct all_addr *addr, const char *types, const int id, const char type, const char* file, const int line);
|
||||
char _FTL_new_query(const unsigned int flags, const char *name, const struct all_addr *addr, const char *types, const int id, const char type, const char* file, const int line);
|
||||
|
||||
#define FTL_forwarded(flags, name, addr, id) _FTL_forwarded(flags, name, addr, id, __FILE__, __LINE__)
|
||||
void _FTL_forwarded(const unsigned int flags, const char *name, const struct all_addr *addr, const int id, const char* file, const int line);
|
||||
@@ -38,8 +38,10 @@ void _FTL_forwarding_failed(const struct server *server, const char* file, const
|
||||
#define FTL_upstream_error(rcode, id) _FTL_upstream_error(rcode, id, __FILE__, __LINE__)
|
||||
void _FTL_upstream_error(const unsigned int rcode, const int id, const char* file, const int line);
|
||||
|
||||
#define FTL_get_blocking_metadata(addrp, flags) _FTL_get_blocking_metadata(addrp, flags, __FILE__, __LINE__)
|
||||
void _FTL_get_blocking_metadata(struct all_addr **addrp, unsigned int *flags, const char* file, const int line);
|
||||
|
||||
void FTL_dnsmasq_reload(void);
|
||||
void FTL_fork_and_bind_sockets(struct passwd *ent_pw);
|
||||
int FTL_database_import(int cache_size, struct crec **rhash, int hashsz);
|
||||
|
||||
#endif // DNSMASQ_INTERFACE_H
|
||||
|
||||
Reference in New Issue
Block a user