diff --git a/src/database/gravity-db.c b/src/database/gravity-db.c index 3b91eb7a..c3e372b6 100644 --- a/src/database/gravity-db.c +++ b/src/database/gravity-db.c @@ -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 diff --git a/src/database/gravity-db.h b/src/database/gravity-db.h index ed97fdbc..6c4b9f00 100644 --- a/src/database/gravity-db.h +++ b/src/database/gravity-db.h @@ -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, diff --git a/src/datastructure.h b/src/datastructure.h index 5fdb7931..52dc0345 100644 --- a/src/datastructure.h +++ b/src/datastructure.h @@ -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); diff --git a/src/dnsmasq_interface.c b/src/dnsmasq_interface.c index 2bfe70af..ea514159 100644 --- a/src/dnsmasq_interface.c +++ b/src/dnsmasq_interface.c @@ -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 diff --git a/src/regex.c b/src/regex.c index d013bc36..f6e4bcc8 100644 --- a/src/regex.c +++ b/src/regex.c @@ -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(®ex[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 diff --git a/src/regex_r.h b/src/regex_r.h index 45b56303..e2785c22 100644 --- a/src/regex_r.h +++ b/src/regex_r.h @@ -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);