Skip to content

Commit

Permalink
Implement support for custom redirection targets in regex extension, …
Browse files Browse the repository at this point in the history
…e.g., "someregex;reply=1.2.3.4;reply=fe80::1234"

Signed-off-by: DL6ER <dl6er@dl6er.de>
  • Loading branch information
DL6ER committed Sep 11, 2021
1 parent 8bc00f2 commit 42a7948
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 22 deletions.
47 changes: 35 additions & 12 deletions src/dnsmasq_interface.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand All @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}
Expand Down
127 changes: 118 additions & 9 deletions src/regex.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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, &regex[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, &regex[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";
Expand All @@ -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);
}

Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
}
10 changes: 9 additions & 1 deletion src/regex_r.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,26 +27,34 @@ 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,
const enum regex_type regexid, const bool regextest);
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);

Expand Down

0 comments on commit 42a7948

Please sign in to comment.