diff --git a/src/dnsmasq_interface.c b/src/dnsmasq_interface.c index ea514159..24ab9f42 100644 --- a/src/dnsmasq_interface.c +++ b/src/dnsmasq_interface.c @@ -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; } diff --git a/src/regex.c b/src/regex.c index f6e4bcc8..470533b6 100644 --- a/src/regex.c +++ b/src/regex.c @@ -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; -} \ No newline at end of file +} + +// 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; +} diff --git a/src/regex_r.h b/src/regex_r.h index e2785c22..6ae8a6fc 100644 --- a/src/regex_r.h +++ b/src/regex_r.h @@ -27,19 +27,26 @@ extern const char *regextype[]; // assert_sizeof #include "static_assert.h" +#include + 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);