Add regex extension ";reply=NXDOMAIN,NODATA,REFUSED,IP,NONE"

Signed-off-by: DL6ER <dl6er@dl6er.de>
This commit is contained in:
DL6ER
2021-09-09 21:50:54 +02:00
parent d7d2178fc3
commit 296f2eb85a
6 changed files with 106 additions and 28 deletions
+1 -1
View File
@@ -1223,7 +1223,7 @@ static void gravityDB_client_check_again(clientsData* client)
}
}
enum db_result in_whitelist(const char *domain, const DNSCacheData *dns_cache, clientsData* client)
enum db_result in_whitelist(const char *domain, DNSCacheData *dns_cache, clientsData* client)
{
// If list statement is not ready and cannot be initialized (e.g. no
// access to the database), we return false to prevent an FTL crash
+1 -1
View File
@@ -32,7 +32,7 @@ int gravityDB_count(const enum gravity_tables list);
enum db_result in_gravity(const char *domain, clientsData *client);
enum db_result in_blacklist(const char *domain, clientsData *client);
enum db_result in_whitelist(const char *domain, const DNSCacheData *dns_cache, clientsData *client);
enum db_result in_whitelist(const char *domain, DNSCacheData *dns_cache, clientsData *client);
bool in_auditlist(const char *domain);
bool gravityDB_get_regex_client_groups(clientsData* client, const unsigned int numregex, const regexData *regex,
+2
View File
@@ -133,6 +133,8 @@ const char *getClientNameString(const queriesData* query);
void change_clientcount(clientsData *client, int total, int blocked, int overTimeIdx, int overTimeMod);
const char *get_query_reply_str(const enum reply_type query) __attribute__ ((const));
// Pointer getter functions
#define getQuery(queryID, checkMagic) _getQuery(queryID, checkMagic, __LINE__, __FUNCTION__, __FILE__)
queriesData* _getQuery(int queryID, bool checkMagic, int line, const char * function, const char * file);
+23 -3
View File
@@ -50,6 +50,7 @@
#include "database/message-table.h"
// Private prototypes
static const char *reply_status_str[QUERY_REPLY_MAX+1];
static void print_flags(const unsigned int flags);
#define query_set_reply(flags, addr, query, response) _query_set_reply(flags, addr, query, response, __FILE__, __LINE__)
static void _query_set_reply(const unsigned int flags, const union all_addr *addr, queriesData* query, const struct timeval response,
@@ -192,6 +193,16 @@ size_t _FTL_make_answer(struct dns_header *header, char *limit, const size_t len
if(config.debug & DEBUG_FLAGS)
logg("Forced DNS reply to NXDOMAIN");
}
else if(force_next_DNS_reply == REPLY_NODATA)
{
flags = F_NOERR;
// Reset DNS reply forcing
force_next_DNS_reply = REPLY_UNKNOWN;
// Debug logging
if(config.debug & DEBUG_FLAGS)
logg("Forced DNS reply to NODATA");
}
else if(force_next_DNS_reply == REPLY_REFUSED)
{
// Empty flags result in REFUSED
@@ -237,7 +248,7 @@ size_t _FTL_make_answer(struct dns_header *header, char *limit, const size_t len
{
// If we block in NXDOMAIN mode, we add the NEGATIVE response
// and the NXDOMAIN flags
flags = F_NEG | F_NXDOMAIN;
flags = F_NXDOMAIN;
if(config.debug & DEBUG_FLAGS)
logg("Configured blocking mode is NXDOMAIN");
}
@@ -953,6 +964,10 @@ static bool check_domain_blocked(const char *domain, const int clientID,
set_dnscache_blockingstatus(dns_cache, client, REGEX_BLOCKED, domain);
dns_cache->black_regex_idx = regex_idx;
// Regex may be overwriting reply type for this domain
if(dns_cache->force_reply != REPLY_UNKNOWN)
force_next_DNS_reply = dns_cache->force_reply;
// We block this domain
return true;
}
@@ -1207,6 +1222,9 @@ static bool _FTL_check_blocking(int queryID, int domainID, int clientID, const c
// Debug output
if(config.debug & DEBUG_QUERIES)
logg("Blocking %s as %s is %s", domainstr, blockedDomain, blockingreason);
if(force_next_DNS_reply != 0)
logg("Forcing next reply to %s", reply_status_str[force_next_DNS_reply]);
}
else if(db_okay)
{
@@ -2276,8 +2294,10 @@ static void _query_set_reply(const unsigned int flags, const union all_addr *add
const char *file, const int line)
{
// Iterate through possible values
if(flags & F_NEG || force_next_DNS_reply == REPLY_NXDOMAIN ||
(flags & F_NOERR && !(flags & (F_IPV4 | F_IPV6)))) // <-- FTL_make_answer() when no A or AAAA is added
if(flags & F_NEG ||
(flags & F_NOERR && !(flags & (F_IPV4 | F_IPV6))) || // <-- FTL_make_answer() when no A or AAAA is added
force_next_DNS_reply == REPLY_NXDOMAIN ||
force_next_DNS_reply == REPLY_NODATA)
{
if(flags & F_NXDOMAIN || force_next_DNS_reply == REPLY_NXDOMAIN)
// NXDOMAIN
+71 -18
View File
@@ -119,7 +119,7 @@ static bool compile_regex(const char *regexin, const enum regex_type regexid, co
if(sscanf(part, "querytype=%16s", extra))
{
// Warn if specified more than one querytype option
if(regex[index].query_type != 0)
if(regex[index].ext.query_type != 0)
logg_regex_warning(regextype[regexid],
"Overwriting previous querytype setting",
dbidx, regexin);
@@ -130,35 +130,38 @@ static bool compile_regex(const char *regexin, const enum regex_type regexid, co
// Check for querytype
if(strcasecmp(extra, querytypes[type]) == 0)
{
regex[index].query_type = type;
regex[index].query_type_inverted = false;
regex[index].ext.query_type = type;
regex[index].ext.query_type_inverted = false;
break;
}
// Check for INVERTED querytype
else if(extra[0] == '!' && strcasecmp(extra + 1u, querytypes[type]) == 0)
{
regex[index].query_type = type;
regex[index].query_type_inverted = true;
regex[index].ext.query_type = type;
regex[index].ext.query_type_inverted = true;
break;
}
}
// Nothing found
if(regex[index].query_type == 0)
logg_regex_warning(regextype[regexid], "Unknown querytype",
dbidx, regexin);
if(regex[index].ext.query_type == 0)
{
char msg[64] = { 0 };
snprintf(msg, sizeof(msg), "Unknown querytype \"%s\"", extra);
logg_regex_warning(regextype[regexid], msg, dbidx, regexin);
}
// Debug output
else if(config.debug & DEBUG_REGEX)
{
logg(" This regex will %s match query type %s",
regex[index].query_type_inverted ? "NOT" : "ONLY",
querytypes[regex[index].query_type]);
regex[index].ext.query_type_inverted ? "NOT" : "ONLY",
querytypes[regex[index].ext.query_type]);
}
}
// option: ";invert"
else if(strcasecmp(part, "invert") == 0)
{
regex[index].inverted = true;
regex[index].ext.inverted = true;
// Debug output
if(config.debug & DEBUG_REGEX)
@@ -166,6 +169,53 @@ static bool compile_regex(const char *regexin, const enum regex_type regexid, co
logg(" This regex will match in inverted mode.");
}
}
// 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)
{
type = "NODATA";
regex[index].ext.reply = REPLY_NODATA;
}
else if(strcasecmp(extra, "NXDOMAIN") == 0)
{
type = "NXDOMAIN";
regex[index].ext.reply = REPLY_NXDOMAIN;
}
else if(strcasecmp(extra, "REFUSED") == 0)
{
type = "REFUSED";
regex[index].ext.reply = REPLY_REFUSED;
}
else if(strcasecmp(extra, "IP") == 0)
{
type = "IP";
regex[index].ext.reply = REPLY_IP;
}
else if(strcasecmp(extra, "NONE") == 0)
{
type = "NONE";
regex[index].ext.reply = REPLY_NONE;
}
else
{
char msg[64] = { 0 };
snprintf(msg, sizeof(msg)-1, "Unknown reply \"%s\"", extra);
logg_regex_warning(regextype[regexid], msg, dbidx, regexin);
}
// Debug output
if(config.debug & DEBUG_REGEX && regex[index].ext.reply != REPLY_UNKNOWN)
logg(" This regex will result in a custom reply: %s", type);
}
else
{
char hint[40 + strlen(part)];
@@ -204,7 +254,7 @@ static bool compile_regex(const char *regexin, const enum regex_type regexid, co
return true;
}
int match_regex(const char *input, const DNSCacheData* dns_cache, const int clientID,
int match_regex(const char *input, DNSCacheData* dns_cache, const int clientID,
const enum regex_type regexid, const bool regextest)
{
int match_idx = -1;
@@ -273,28 +323,31 @@ int match_regex(const char *input, const DNSCacheData* dns_cache, const int clie
int retval = regexec(&regex[index].regex, input, 0, NULL, 0);
#endif
// regexec() returns REG_OK for a successful match or REG_NOMATCH for failure.
if ((retval == REG_OK && !regex[index].inverted) ||
(retval == REG_NOMATCH && regex[index].inverted))
if ((retval == REG_OK && !regex[index].ext.inverted) ||
(retval == REG_NOMATCH && regex[index].ext.inverted))
{
// Check possible additional regex settings
if(dns_cache != NULL)
{
// Check query type filtering
if(regex[index].query_type != 0)
if(regex[index].ext.query_type != 0)
{
if((!regex[index].query_type_inverted && regex[index].query_type != dns_cache->query_type) ||
(regex[index].query_type_inverted && regex[index].query_type == dns_cache->query_type))
if((!regex[index].ext.query_type_inverted && regex[index].ext.query_type != dns_cache->query_type) ||
(regex[index].ext.query_type_inverted && regex[index].ext.query_type == dns_cache->query_type))
{
if(config.debug & DEBUG_REGEX)
{
logg("Regex %s (%u, DB ID %i) NO match: \"%s\" vs. \"%s\""
" (skipped because of query type %smatch)",
regextype[regexid], index, regex[index].database_id,
input, regex[index].string, regex[index].query_type_inverted ? "inversion " : "mis");
input, regex[index].string, regex[index].ext.query_type_inverted ? "inversion " : "mis");
}
continue;
}
}
// Set special reply type if configured for this regex
if(regex[index].ext.reply != REPLY_UNKNOWN)
dns_cache->force_reply = regex[index].ext.reply;
}
// Match, return true
+8 -5
View File
@@ -28,10 +28,13 @@ extern const char *regextype[];
#include "static_assert.h"
typedef struct {
bool available;
bool inverted;
bool query_type_inverted;
enum query_types query_type;
bool available :1;
struct {
bool inverted :1;
bool query_type_inverted :1;
enum query_types query_type;
enum reply_type reply;
} ext;
int database_id;
char *string;
regex_t regex;
@@ -39,7 +42,7 @@ typedef struct {
ASSERT_SIZEOF(regexData, 32, 20, 20);
unsigned int get_num_regex(const enum regex_type regexid) __attribute__((pure));
int match_regex(const char *input, const DNSCacheData* dns_cache, const int clientID,
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);