mirror of
https://github.com/pi-hole/FTL.git
synced 2024-10-26 16:52:18 +02:00
Implement support for custom redirection targets in regex extension, e.g., "someregex;reply=1.2.3.4;reply=fe80::1234"
Signed-off-by: DL6ER <dl6er@dl6er.de>
This commit is contained in:
+35
-12
@@ -75,9 +75,10 @@ static void _query_set_dnssec(queriesData *query, const enum dnssec_status dnsse
|
||||
static const char *blockingreason = NULL;
|
||||
static union all_addr null_addrp = {{ 0 }};
|
||||
static enum reply_type force_next_DNS_reply = REPLY_UNKNOWN;
|
||||
static int last_regex_idx = -1;
|
||||
static struct ptr_record *pihole_ptr = NULL;
|
||||
|
||||
// Fork-private copy of the interface name the most recent query came from
|
||||
// Fork-private copy of the interface data the most recent query came from
|
||||
static struct {
|
||||
char name[IFNAMSIZ];
|
||||
union all_addr addr4;
|
||||
@@ -264,6 +265,20 @@ size_t _FTL_make_answer(struct dns_header *header, char *limit, const size_t len
|
||||
}
|
||||
}
|
||||
|
||||
// Check for regex redirecting
|
||||
bool redirecting = false;
|
||||
union all_addr redirect_addr4 = {{ 0 }}, redirect_addr6 = {{ 0 }};
|
||||
if(last_regex_idx > -1)
|
||||
{
|
||||
redirecting = regex_get_redirect(last_regex_idx, &redirect_addr4.addr4, &redirect_addr6.addr6);
|
||||
// Reset regex redirection forcing
|
||||
last_regex_idx = -1;
|
||||
|
||||
// Debug logging
|
||||
if(config.debug & DEBUG_FLAGS)
|
||||
logg("Regex match is %sredirected", redirecting ? "" : "NOT ");
|
||||
}
|
||||
|
||||
// Debug logging
|
||||
if(config.debug & DEBUG_FLAGS)
|
||||
print_flags(flags);
|
||||
@@ -285,13 +300,15 @@ size_t _FTL_make_answer(struct dns_header *header, char *limit, const size_t len
|
||||
// Add A answer record if requested
|
||||
if(flags & F_IPV4)
|
||||
{
|
||||
union all_addr *addr;
|
||||
if(config.blockingmode == MODE_IP ||
|
||||
config.blockingmode == MODE_IP_NODATA_AAAA ||
|
||||
forced_ip)
|
||||
union all_addr *addr = &null_addrp;
|
||||
|
||||
// Overwrite with IP address if requested
|
||||
if(redirecting)
|
||||
addr = &redirect_addr4;
|
||||
else if(config.blockingmode == MODE_IP ||
|
||||
config.blockingmode == MODE_IP_NODATA_AAAA ||
|
||||
forced_ip)
|
||||
addr = &next_iface.addr4;
|
||||
else
|
||||
addr = &null_addrp;
|
||||
|
||||
// Debug logging
|
||||
if(config.debug & DEBUG_QUERIES)
|
||||
@@ -312,12 +329,14 @@ size_t _FTL_make_answer(struct dns_header *header, char *limit, const size_t len
|
||||
// Add AAAA answer record if requested
|
||||
if(flags & F_IPV6)
|
||||
{
|
||||
union all_addr *addr;
|
||||
if(config.blockingmode == MODE_IP ||
|
||||
forced_ip)
|
||||
union all_addr *addr = &null_addrp;
|
||||
|
||||
// Overwrite with IP address if requested
|
||||
if(redirecting)
|
||||
addr = &redirect_addr6;
|
||||
else if(config.blockingmode == MODE_IP ||
|
||||
forced_ip)
|
||||
addr = &next_iface.addr6;
|
||||
else
|
||||
addr = &null_addrp;
|
||||
|
||||
// Debug logging
|
||||
if(config.debug & DEBUG_QUERIES)
|
||||
@@ -968,6 +987,9 @@ static bool check_domain_blocked(const char *domain, const int clientID,
|
||||
if(dns_cache->force_reply != REPLY_UNKNOWN)
|
||||
force_next_DNS_reply = dns_cache->force_reply;
|
||||
|
||||
// Store ID of this regex (fork-private)
|
||||
last_regex_idx = regex_idx;
|
||||
|
||||
// We block this domain
|
||||
return true;
|
||||
}
|
||||
@@ -1104,6 +1126,7 @@ static bool _FTL_check_blocking(int queryID, int domainID, int clientID, const c
|
||||
if(!query->flags.whitelisted)
|
||||
{
|
||||
force_next_DNS_reply = dns_cache->force_reply;
|
||||
last_regex_idx = dns_cache->black_regex_idx;
|
||||
query_blocked(query, domain, client, QUERY_REGEX);
|
||||
return true;
|
||||
}
|
||||
|
||||
+118
-9
@@ -77,6 +77,46 @@ static inline void free_regex_ptr(const enum regex_type regexid)
|
||||
}
|
||||
}
|
||||
|
||||
static __attribute__ ((pure)) regexData *get_regex_ptr_from_id(unsigned int regexID)
|
||||
{
|
||||
unsigned int maxi;
|
||||
enum regex_type regex_type;
|
||||
if(regexID < num_regex[REGEX_BLACKLIST])
|
||||
{
|
||||
// Regex blacklist
|
||||
regex_type = REGEX_BLACKLIST;
|
||||
maxi = num_regex[REGEX_BLACKLIST];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Subtract regex blacklist
|
||||
regexID -= num_regex[REGEX_BLACKLIST];
|
||||
|
||||
// Check for regex whitelist
|
||||
if(regexID < num_regex[REGEX_WHITELIST])
|
||||
{
|
||||
// Regex whitelist
|
||||
regex_type = REGEX_WHITELIST;
|
||||
maxi = num_regex[REGEX_WHITELIST];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Subtract regex whitelist
|
||||
regexID -= num_regex[REGEX_WHITELIST];
|
||||
|
||||
// CLI regex
|
||||
regex_type = REGEX_CLI;
|
||||
maxi = num_regex[REGEX_CLI];
|
||||
}
|
||||
}
|
||||
|
||||
regexData *regex = get_regex_ptr(regex_type);
|
||||
if(regex != NULL && regexID < maxi)
|
||||
return &(regex[regexID]);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned int __attribute__((pure)) get_num_regex(const enum regex_type regexid)
|
||||
{
|
||||
// count number of all available reges
|
||||
@@ -172,12 +212,6 @@ static bool compile_regex(const char *regexin, const enum regex_type regexid, co
|
||||
// options ";reply=NXDOMAIN", etc.
|
||||
else if(sscanf(part, "reply=%16s", extra))
|
||||
{
|
||||
// Warn if specified more than one repl option
|
||||
if(regex[index].ext.reply != 0)
|
||||
logg_regex_warning(regextype[regexid],
|
||||
"Overwriting previous replytype setting",
|
||||
dbidx, regexin);
|
||||
|
||||
// Test input string against all implemented reply types
|
||||
const char *type = "";
|
||||
if(strcasecmp(extra, "NODATA") == 0)
|
||||
@@ -200,6 +234,20 @@ static bool compile_regex(const char *regexin, const enum regex_type regexid, co
|
||||
type = "IP";
|
||||
regex[index].ext.reply = REPLY_IP;
|
||||
}
|
||||
else if(inet_pton(AF_INET, extra, ®ex[index].ext.addr4) == 1)
|
||||
{
|
||||
// Custom IPv4 target
|
||||
type = extra;
|
||||
regex[index].ext.reply = REPLY_IP;
|
||||
regex[index].ext.custom_ip4 = true;
|
||||
}
|
||||
else if(inet_pton(AF_INET6, extra, ®ex[index].ext.addr6) == 1)
|
||||
{
|
||||
// Custom IPv6 target
|
||||
type = extra;
|
||||
regex[index].ext.reply = REPLY_IP;
|
||||
regex[index].ext.custom_ip6 = true;
|
||||
}
|
||||
else if(strcasecmp(extra, "NONE") == 0)
|
||||
{
|
||||
type = "NONE";
|
||||
@@ -208,7 +256,7 @@ static bool compile_regex(const char *regexin, const enum regex_type regexid, co
|
||||
else
|
||||
{
|
||||
char msg[64] = { 0 };
|
||||
snprintf(msg, sizeof(msg)-1, "Unknown reply \"%s\"", extra);
|
||||
snprintf(msg, sizeof(msg)-1, "Unknown reply type \"%s\"", extra);
|
||||
logg_regex_warning(regextype[regexid], msg, dbidx, regexin);
|
||||
}
|
||||
|
||||
@@ -398,7 +446,7 @@ int match_regex(const char *input, DNSCacheData* dns_cache, const int clientID,
|
||||
}
|
||||
}
|
||||
|
||||
// No match, no error, return false
|
||||
// Return match_idx (-1 if there was no match)
|
||||
return match_idx;
|
||||
}
|
||||
|
||||
@@ -691,4 +739,65 @@ int regex_test(const bool debug_mode, const bool quiet, const char *domainin, co
|
||||
|
||||
// Return status 0 = MATCH, 1 = ERROR, 2 = NO MATCH
|
||||
return matchidx > -1 ? EXIT_SUCCESS : 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Get internal ID of regex with this database ID
|
||||
static int __attribute__ ((pure)) regex_id_from_database_id(const int dbID)
|
||||
{
|
||||
// Get number of defined regular expressions
|
||||
unsigned int sum_regex = 0;
|
||||
for(unsigned int i = 0; i < REGEX_MAX; i++)
|
||||
sum_regex += num_regex[i];
|
||||
|
||||
// Find internal ID of regular expression with this database ID
|
||||
for(unsigned int i = 0; i < sum_regex; i++)
|
||||
{
|
||||
regexData *regex = get_regex_ptr_from_id(i);
|
||||
if(regex == NULL)
|
||||
continue;
|
||||
if(regex->database_id == dbID)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Return redirection addresses for a given blacklist regex (if specified)
|
||||
bool regex_get_redirect(const int dbID, struct in_addr *addr4, struct in6_addr *addr6)
|
||||
{
|
||||
// Check dbID for validity, return early if negative
|
||||
if(dbID < 0)
|
||||
return false;
|
||||
|
||||
// Get internal regex ID from database regex ID
|
||||
const int regexID = regex_id_from_database_id(dbID);
|
||||
|
||||
if(config.debug & DEBUG_REGEX)
|
||||
logg("Regex: %d (database) -> %d (internal)", dbID, regexID);
|
||||
|
||||
// Check internal regex ID for validity, return early if negative
|
||||
if(regexID < 0)
|
||||
return false;
|
||||
|
||||
// Get regex from regexID
|
||||
regexData *regex = get_regex_ptr_from_id(regexID);
|
||||
if(regex == NULL)
|
||||
return false;
|
||||
|
||||
bool custom_addr = false;
|
||||
// Check for IPv4 redirect
|
||||
if(regex->ext.custom_ip4 && addr4 != NULL)
|
||||
{
|
||||
memcpy(addr4, &(regex->ext.addr4), sizeof(*addr4));
|
||||
custom_addr = true;
|
||||
}
|
||||
|
||||
// Check for IPv6 redirect
|
||||
if(regex->ext.custom_ip6 && addr6 != NULL)
|
||||
{
|
||||
memcpy(addr6, &(regex->ext.addr6), sizeof(*addr6));
|
||||
custom_addr = true;
|
||||
}
|
||||
|
||||
return custom_addr;
|
||||
}
|
||||
|
||||
+9
-1
@@ -27,19 +27,26 @@ extern const char *regextype[];
|
||||
// assert_sizeof
|
||||
#include "static_assert.h"
|
||||
|
||||
#include <netinet/in.h>
|
||||
|
||||
typedef struct {
|
||||
bool available :1;
|
||||
struct {
|
||||
bool inverted :1;
|
||||
bool query_type_inverted :1;
|
||||
bool custom_ip4 :1;
|
||||
bool custom_ip6 :1;
|
||||
enum query_types query_type;
|
||||
enum reply_type reply;
|
||||
struct in_addr addr4;
|
||||
struct in6_addr addr6;
|
||||
} ext;
|
||||
int database_id;
|
||||
char *string;
|
||||
regex_t regex;
|
||||
} regexData;
|
||||
ASSERT_SIZEOF(regexData, 32, 20, 20);
|
||||
|
||||
ASSERT_SIZEOF(regexData, 56, 44, 44);
|
||||
|
||||
unsigned int get_num_regex(const enum regex_type regexid) __attribute__((pure));
|
||||
int match_regex(const char *input, DNSCacheData* dns_cache, const int clientID,
|
||||
@@ -47,6 +54,7 @@ int match_regex(const char *input, DNSCacheData* dns_cache, const int clientID,
|
||||
void allocate_regex_client_enabled(clientsData *client, const int clientID);
|
||||
void reload_per_client_regex(clientsData *client);
|
||||
void read_regex_from_database(void);
|
||||
bool regex_get_redirect(const int regexID, struct in_addr *addr4, struct in6_addr *addr6);
|
||||
|
||||
int regex_test(const bool debug_mode, const bool quiet, const char *domainin, const char *regexin);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user