From 55285056741dd3c2b350a89a7fec49671d5576e0 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 9 Aug 2018 11:46:15 +0200 Subject: [PATCH 01/13] Determine query type earlier on Signed-off-by: DL6ER --- dnsmasq_interface.c | 53 +++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/dnsmasq_interface.c b/dnsmasq_interface.c index 0dd655d08..58c106a4f 100644 --- a/dnsmasq_interface.c +++ b/dnsmasq_interface.c @@ -32,8 +32,32 @@ void FTL_new_query(unsigned int flags, char *name, struct all_addr *addr, char * struct timeval request; gettimeofday(&request, 0); + // Determine query type + unsigned char querytype = 0; + if(strcmp(types,"query[A]") == 0) + querytype = TYPE_A; + else if(strcmp(types,"query[AAAA]") == 0) + querytype = TYPE_AAAA; + else if(strcmp(types,"query[ANY]") == 0) + querytype = TYPE_ANY; + else if(strcmp(types,"query[SRV]") == 0) + querytype = TYPE_SRV; + else if(strcmp(types,"query[SOA]") == 0) + querytype = TYPE_SOA; + else if(strcmp(types,"query[PTR]") == 0) + querytype = TYPE_PTR; + else if(strcmp(types,"query[TXT]") == 0) + querytype = TYPE_TXT; + else + { + // Return early to avoid accessing querytypedata out of bounds + if(debug) logg("Notice: Skipping unknown query type: %s (%i)", types, id); + disable_thread_lock(); + return; + } + // Skip AAAA queries if user doesn't want to have them analyzed - if(!config.analyze_AAAA && strcmp(types,"query[AAAA]") == 0) + if(!config.analyze_AAAA && querytype == TYPE_AAAA) { if(debug) logg("Not analyzing AAAA query"); disable_thread_lock(); @@ -98,33 +122,6 @@ void FTL_new_query(unsigned int flags, char *name, struct all_addr *addr, char * char *proto = (type == UDP) ? "UDP" : "TCP"; if(debug) logg("**** new %s %s \"%s\" from %s (ID %i)", proto, types, domain, client, id); - // Determine query type - unsigned char querytype = 0; - if(strcmp(types,"query[A]") == 0) - querytype = TYPE_A; - else if(strcmp(types,"query[AAAA]") == 0) - querytype = TYPE_AAAA; - else if(strcmp(types,"query[ANY]") == 0) - querytype = TYPE_ANY; - else if(strcmp(types,"query[SRV]") == 0) - querytype = TYPE_SRV; - else if(strcmp(types,"query[SOA]") == 0) - querytype = TYPE_SOA; - else if(strcmp(types,"query[PTR]") == 0) - querytype = TYPE_PTR; - else if(strcmp(types,"query[TXT]") == 0) - querytype = TYPE_TXT; - else - { - // Return early to avoid accessing querytypedata out of bounds - if(debug) logg("Notice: Skipping unknown query type: %s (%i)", types, id); - free(domain); - free(domainbuffer); - free(client); - disable_thread_lock(); - return; - } - // Update counters int timeidx = findOverTimeID(overTimetimestamp); validate_access("overTime", timeidx, true, __LINE__, __FUNCTION__, __FILE__); From 64a895d7207a2414d21d081064639794b103b111 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 9 Aug 2018 11:57:41 +0200 Subject: [PATCH 02/13] Allow all query types (not only A and AAAA) during database import Signed-off-by: DL6ER --- database.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/database.c b/database.c index 6700c94d1..65c869acc 100644 --- a/database.c +++ b/database.c @@ -628,9 +628,9 @@ void read_data_from_DB(void) } int type = sqlite3_column_int(stmt, 2); - if(type != TYPE_A && type != TYPE_AAAA) + if(type < TYPE_A || type >= TYPE_MAX) { - logg("DB warn: TYPE should be either 1 or 2 but not %i", type); + logg("DB warn: TYPE should not be %i", type); continue; } // Don't import AAAA queries from database if the user set From a3ac3bc569845c46b8e0cc69bbe5853ec075be3b Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 9 Aug 2018 11:58:08 +0200 Subject: [PATCH 03/13] Be able to return any query type through API Signed-off-by: DL6ER --- api.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/api.c b/api.c index 8a9f946d2..7435f9824 100644 --- a/api.c +++ b/api.c @@ -585,6 +585,7 @@ void getQueryTypes(int *sock) } } +char *querytypes[8] = {"A","AAAA","ANY","SRV","SOA","PTR","TXT","UNKN"}; void getAllQueries(char *client_message, int *sock) { @@ -661,7 +662,7 @@ void getAllQueries(char *client_message, int *sock) validate_access("domains", queries[i].domainID, true, __LINE__, __FUNCTION__, __FILE__); validate_access("clients", queries[i].clientID, true, __LINE__, __FUNCTION__, __FILE__); - char *qtype = (queries[i].type == TYPE_A)? "A" : "AAAA"; + char *qtype = querytypes[queries[i].type - TYPE_A]; // 1 = gravity.list, 4 = wildcard, 5 = black.list if((queries[i].status == QUERY_GRAVITY || From 124f81ee9caea0b956450ea1acb89c8f0cd8d60b Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 9 Aug 2018 11:58:34 +0200 Subject: [PATCH 04/13] Remove limitation to only analyze A and AAAA queries Signed-off-by: DL6ER --- dnsmasq_interface.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/dnsmasq_interface.c b/dnsmasq_interface.c index 58c106a4f..8196e6ae3 100644 --- a/dnsmasq_interface.c +++ b/dnsmasq_interface.c @@ -128,18 +128,6 @@ void FTL_new_query(unsigned int flags, char *name, struct all_addr *addr, char * overTime[timeidx].querytypedata[querytype-1]++; counters.querytype[querytype-1]++; - // Skip rest of the analysis if this query is not of type A or AAAA - if(querytype != TYPE_A && querytype != TYPE_AAAA) - { - // Don't process this query further here, we already counted it - if(debug) logg("Notice: Skipping new query: %s (%i)", types, id); - free(domain); - free(domainbuffer); - free(client); - disable_thread_lock(); - return; - } - // Go through already knows domains and see if it is one of them int domainID = findDomainID(domain); From 835ae441fdfd0e880ec828d1b2f7a85ec1e94e89 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 9 Aug 2018 12:01:20 +0200 Subject: [PATCH 05/13] Correct comment Signed-off-by: DL6ER --- dnsmasq_interface.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/dnsmasq_interface.c b/dnsmasq_interface.c index 8196e6ae3..11d7f8626 100644 --- a/dnsmasq_interface.c +++ b/dnsmasq_interface.c @@ -227,8 +227,7 @@ void FTL_forwarded(unsigned int flags, char *name, struct all_addr *addr, int id // find the correct query with zero iterations, but it may happen that queries are processed // asynchronously, e.g. for slow upstream relies to a huge amount of requests. // We iterate from the most recent query down to at most MAXITER queries in the past to avoid - // iterating through the entire array of queries when queries that have not been recorded - // (like PTR queries, etc.) are processed. + // iterating through the entire array of queries // MAX(0, a) is used to return 0 in case a is negative (negative array indices are harmful) // Validate access only once for the maximum index (all lower will work) @@ -629,7 +628,7 @@ void FTL_dnssec(int status, int id) { // Process DNSSEC result for a domain enable_thread_lock(); - // Search for corresponding query indentified by ID + // Search for corresponding query identified by ID bool found = false; int i; // Search match in known queries From 1edbd994b5552e94bee0a59f4d9009bbf71b8794 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 9 Aug 2018 17:05:57 +0200 Subject: [PATCH 06/13] Also call FTL_cache(...) for the other query types (so far, we only called the routine for A and AAAA queries) Signed-off-by: DL6ER --- dnsmasq/rfc1035.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/dnsmasq/rfc1035.c b/dnsmasq/rfc1035.c index ab34f3514..ee61d411e 100644 --- a/dnsmasq/rfc1035.c +++ b/dnsmasq/rfc1035.c @@ -1341,6 +1341,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, unsigned long ttl = daemon->local_ttl; int ok = 1; log_query(F_CONFIG | F_RRNAME, name, NULL, ""); + FTL_cache(F_CONFIG | F_RRNAME, name, NULL, "", daemon->log_display_id); #ifndef NO_ID /* Dynamically generate stat record */ if (t->stat != 0) @@ -1372,6 +1373,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, if (!dryrun) { log_query(F_CONFIG | F_RRNAME, name, NULL, ""); + FTL_cache(F_CONFIG | F_RRNAME, name, NULL, "", daemon->log_display_id); if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, NULL, t->class, C_IN, "t", t->len, t->txt)) @@ -1430,6 +1432,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, if (!dryrun) { log_query(is_arpa | F_REVERSE | F_CONFIG, intr->name, &addr, NULL); + FTL_cache(is_arpa | F_REVERSE | F_CONFIG, intr->name, &addr, NULL, daemon->log_display_id); if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, NULL, T_PTR, C_IN, "d", intr->name)) @@ -1443,6 +1446,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, if (!dryrun) { log_query(F_CONFIG | F_RRNAME, name, NULL, ""); + FTL_cache(F_CONFIG | F_RRNAME, name, NULL, "", daemon->log_display_id); for (ptr = daemon->ptr; ptr; ptr = ptr->next) if (hostname_isequal(name, ptr->name) && add_resource_record(header, limit, &trunc, nameoffset, &ansp, @@ -1479,6 +1483,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, nxdomain = 1; if (!dryrun) log_query(crecp->flags & ~F_FORWARD, name, &addr, NULL); + FTL_cache(crecp->flags & ~F_FORWARD, name, &addr, NULL, daemon->log_display_id); } else { @@ -1488,6 +1493,8 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, { log_query(crecp->flags & ~F_FORWARD, cache_get_name(crecp), &addr, record_source(crecp->uid)); + FTL_cache(crecp->flags & ~F_FORWARD, cache_get_name(crecp), &addr, + record_source(crecp->uid), daemon->log_display_id); if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, crec_ttl(crecp, now), NULL, @@ -1505,6 +1512,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, if (!dryrun) { log_query(F_CONFIG | F_REVERSE | is_arpa, name, &addr, NULL); + FTL_cache(F_CONFIG | F_REVERSE | is_arpa, name, &addr, NULL, daemon->log_display_id); if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, NULL, @@ -1548,8 +1556,12 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, sec_data = 0; nxdomain = 1; if (!dryrun) + { log_query(F_CONFIG | F_REVERSE | is_arpa | F_NEG | F_NXDOMAIN, name, &addr, NULL); + FTL_cache(F_CONFIG | F_REVERSE | is_arpa | F_NEG | F_NXDOMAIN, + name, &addr, NULL, daemon->log_display_id); + } } } } @@ -1619,6 +1631,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, { gotit = 1; log_query(F_FORWARD | F_CONFIG | flag, name, &addrlist->addr, NULL); + FTL_cache(F_FORWARD | F_CONFIG | flag, name, &addrlist->addr, NULL, daemon->log_display_id); if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, NULL, type, C_IN, type == T_A ? "4" : "6", &addrlist->addr)) @@ -1628,7 +1641,10 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, } if (!dryrun && !gotit) + { log_query(F_FORWARD | F_CONFIG | flag | F_NEG, name, NULL, NULL); + FTL_cache(F_FORWARD | F_CONFIG | flag | F_NEG, name, NULL, NULL, daemon->log_display_id); + } continue; } @@ -1673,6 +1689,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, if (!dryrun) { log_query(crecp->flags, name, NULL, record_source(crecp->uid)); + FTL_cache(crecp->flags, name, NULL, record_source(crecp->uid), daemon->log_display_id); if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, crec_ttl(crecp, now), &nameoffset, T_CNAME, C_IN, "d", cname_target)) From 2f4dd87c435c762993d59796379f7dde9891eb42 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 9 Aug 2018 17:06:38 +0200 Subject: [PATCH 07/13] Remove more checks that skipped PTR replies Signed-off-by: DL6ER --- dnsmasq_interface.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/dnsmasq_interface.c b/dnsmasq_interface.c index 11d7f8626..ac4c70bcc 100644 --- a/dnsmasq_interface.c +++ b/dnsmasq_interface.c @@ -434,7 +434,7 @@ void FTL_reply(unsigned short flags, char *name, struct all_addr *addr, int id) disable_thread_lock(); return; } - else if(flags & F_FORWARD) + else if((flags & F_FORWARD) || (flags & F_REVERSE)) { // Search for corresponding query identified by dnsmasq's ID bool found = false; @@ -470,10 +470,6 @@ void FTL_reply(unsigned short flags, char *name, struct all_addr *addr, int id) save_reply_type(flags, i, response); } } - else if(flags & F_REVERSE) - { - if(debug) logg("Skipping result of PTR query"); - } else { logg("*************************** unknown REPLY ***************************"); @@ -515,7 +511,10 @@ void FTL_cache(unsigned int flags, char *name, struct all_addr *addr, char *arg, struct timeval response; gettimeofday(&response, 0); - if(((flags & F_HOSTS) && (flags & F_IMMORTAL)) || ((flags & F_NAMEP) && (flags & F_DHCP)) || (flags & F_FORWARD)) + if(((flags & F_HOSTS) && (flags & F_IMMORTAL)) || + ((flags & F_NAMEP) && (flags & F_DHCP)) || + (flags & F_FORWARD) || + (flags & F_REVERSE)) { // List data: /etc/pihole/gravity.list, /etc/pihole/black.list, /etc/pihole/local.list, etc. // or @@ -540,6 +539,8 @@ void FTL_cache(unsigned int flags, char *name, struct all_addr *addr, char *arg, requesttype = QUERY_CACHE; else if(flags & F_FORWARD) // cached answer to previously forwarded request requesttype = QUERY_CACHE; + else if(flags & F_REVERSE) // cached answer to reverse request (PTR) + requesttype = QUERY_CACHE; else { logg("*************************** unknown CACHE reply (1) ***************************"); From 111f1602c3ac3a45becac927164ab351b4692ee7 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 9 Aug 2018 17:13:53 +0200 Subject: [PATCH 08/13] Add new reply type: REPLY_DOMAIN Signed-off-by: DL6ER --- FTL.h | 3 ++- dnsmasq_interface.c | 6 ++++++ gc.c | 4 ++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/FTL.h b/FTL.h index c5267aa14..cd8414404 100644 --- a/FTL.h +++ b/FTL.h @@ -84,7 +84,7 @@ enum { QUERIES, FORWARDED, CLIENTS, DOMAINS, OVERTIME, WILDCARD }; enum { DNSSEC_UNSPECIFIED, DNSSEC_SECURE, DNSSEC_INSECURE, DNSSEC_BOGUS, DNSSEC_ABANDONED, DNSSEC_UNKNOWN }; enum { QUERY_UNKNOWN, QUERY_GRAVITY, QUERY_FORWARDED, QUERY_CACHE, QUERY_WILDCARD, QUERY_BLACKLIST }; enum { TYPE_A = 1, TYPE_AAAA, TYPE_ANY, TYPE_SRV, TYPE_SOA, TYPE_PTR, TYPE_TXT, TYPE_MAX }; -enum { REPLY_UNKNOWN, REPLY_NODATA, REPLY_NXDOMAIN, REPLY_CNAME, REPLY_IP }; +enum { REPLY_UNKNOWN, REPLY_NODATA, REPLY_NXDOMAIN, REPLY_CNAME, REPLY_IP, REPLY_DOMAIN }; enum { PRIVACY_SHOW_ALL = 0, PRIVACY_HIDE_DOMAINS, PRIVACY_HIDE_DOMAINS_CLIENTS, PRIVACY_MAXIMUM }; enum { MODE_IP, MODE_NX, MODE_NULL, MODE_IP_NODATA_AAAA }; enum { REGEX_UNKNOWN, REGEX_BLOCKED, REGEX_NOTBLOCKED }; @@ -134,6 +134,7 @@ typedef struct { int reply_NXDOMAIN; int reply_CNAME; int reply_IP; + int reply_domain; } countersStruct; typedef struct { diff --git a/dnsmasq_interface.c b/dnsmasq_interface.c index ac4c70bcc..427c276c3 100644 --- a/dnsmasq_interface.c +++ b/dnsmasq_interface.c @@ -710,6 +710,12 @@ void save_reply_type(unsigned int flags, int queryID, struct timeval response) queries[queryID].reply = REPLY_CNAME; counters.reply_CNAME++; } + else if(flags & F_REVERSE) + { + // reserve lookup + queries[queryID].reply = REPLY_DOMAIN; + counters.reply_domain++; + } else { // Valid IP diff --git a/gc.c b/gc.c index 662253121..bdf13d9d1 100644 --- a/gc.c +++ b/gc.c @@ -128,6 +128,10 @@ void *GC_thread(void *val) counters.reply_IP--; break; + case REPLY_DOMAIN: // reverse lookup + counters.reply_domain--; + break; + default: // Incomplete query, do nothing break; } From 1f360dc3f92e3789338bb59f68b6583aed96be61 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 9 Aug 2018 17:14:13 +0200 Subject: [PATCH 09/13] Don't forget to GC wildcard blocked entries Signed-off-by: DL6ER --- gc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gc.c b/gc.c index bdf13d9d1..c0d748034 100644 --- a/gc.c +++ b/gc.c @@ -97,8 +97,8 @@ void *GC_thread(void *val) counters.cached--; overTime[timeidx].cached--; break; - case QUERY_BLACKLIST: - // Blocked by user's black list + case QUERY_BLACKLIST: // exact blocked + case QUERY_WILDCARD: // regex blocked (fall through) counters.blocked--; overTime[timeidx].blocked--; domains[domainID].blockedcount--; From baeceeb7d87ba5ca2babb17563eb06d0358db7a3 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 9 Aug 2018 17:31:10 +0200 Subject: [PATCH 10/13] Extend FTL_reply(...). Meanwhile, I removed some code duplication and fixed the behavior to avoid multiple counting of query types if there are multiple replies (for instance, a PTR query can return multiple host names for a given IP address) Signed-off-by: DL6ER --- dnsmasq_interface.c | 124 +++++++++++++++++++------------------------- 1 file changed, 52 insertions(+), 72 deletions(-) diff --git a/dnsmasq_interface.c b/dnsmasq_interface.c index 427c276c3..8e0513803 100644 --- a/dnsmasq_interface.c +++ b/dnsmasq_interface.c @@ -378,90 +378,65 @@ void FTL_reply(unsigned short flags, char *name, struct all_addr *addr, int id) struct timeval response; gettimeofday(&response, 0); - if(flags & F_CONFIG) - { - // Answered from local configuration, might be a wildcard or user-provided - // Save status in corresponding query identified by dnsmasq's ID - bool found = false; - int i; - - // Search match in known queries - // See comments in FTL_forwarded() for further details about this loop - validate_access("queries", counters.queries-1, false, __LINE__, __FUNCTION__, __FILE__); - int until = MAX(0, counters.queries-MAXITER); - for(i = counters.queries-1; i >= until; i--) - { - // Check UUID of this query - if(queries[i].id == id) - { - found = true; - break; - } - } + // Save status in corresponding query identified by dnsmasq's ID + bool found = false; + int i; - if(!found) + // Search match in known queries + // See comments in FTL_forwarded() for further details about this loop + validate_access("queries", counters.queries-1, false, __LINE__, __FUNCTION__, __FILE__); + int until = MAX(0, counters.queries-MAXITER); + for(i = counters.queries-1; i >= until; i--) + { + // Check UUID of this query + if(queries[i].id == id) { - // This may happen e.g. if the original query was a PTR query or "pi.hole" - // as we ignore them altogether - disable_thread_lock(); - return; + found = true; + break; } + } - if(!queries[i].complete) - { - // This query is no longer unknown - counters.unknown--; - // Answered from a custom (user provided) cache file - counters.cached++; - queries[i].status = QUERY_CACHE; - - // Get time index - int querytimestamp, overTimetimestamp; - gettimestamp(&querytimestamp, &overTimetimestamp); - int timeidx = findOverTimeID(overTimetimestamp); - validate_access("overTime", timeidx, true, __LINE__, __FUNCTION__, __FILE__); - - overTime[timeidx].cached++; - - // Save reply type and update individual reply counters - save_reply_type(flags, i, response); - - // Hereby, this query is now fully determined - queries[i].complete = true; - } + if(!found) + { + // This may happen e.g. if the original query was a PTR query or "pi.hole" + // as we ignore them altogether + if(debug) logg("FTL_reply(): Query %i has not been found", id); + disable_thread_lock(); + return; + } - // We are done here + if(queries[i].reply != REPLY_UNKNOWN) + { + // Nothing to be done here disable_thread_lock(); return; } - else if((flags & F_FORWARD) || (flags & F_REVERSE)) + + if(flags & F_CONFIG) { - // Search for corresponding query identified by dnsmasq's ID - bool found = false; - int i; + // Answered from local configuration, might be a wildcard or user-provided + // This query is no longer unknown + counters.unknown--; + // Answered from a custom (user provided) cache file + counters.cached++; + queries[i].status = QUERY_CACHE; - // Search match in known queries - // See comments in FTL_forwarded() for further details about this loop - validate_access("queries", counters.queries-1, false, __LINE__, __FUNCTION__, __FILE__); - int until = MAX(0, counters.queries-MAXITER); - for(i = counters.queries-1; i >= until; i--) - { - // Check UUID of this query - if(queries[i].id == id) - { - found = true; - break; - } - } + // Get time index + int querytimestamp, overTimetimestamp; + gettimestamp(&querytimestamp, &overTimetimestamp); + int timeidx = findOverTimeID(overTimetimestamp); + validate_access("overTime", timeidx, true, __LINE__, __FUNCTION__, __FILE__); - if(!found) - { - // This may happen e.g. if the original query was a PTR query or "pi.hole" - // as we ignore them altogether - disable_thread_lock(); - return; - } + overTime[timeidx].cached++; + + // Save reply type and update individual reply counters + save_reply_type(flags, i, response); + // Hereby, this query is now fully determined + queries[i].complete = true; + } + else if(flags & F_FORWARD) + { int domainID = queries[i].domainID; validate_access("domains", domainID, true, __LINE__, __FUNCTION__, __FILE__); if(strcmp(domains[domainID].domain, name) == 0) @@ -470,6 +445,11 @@ void FTL_reply(unsigned short flags, char *name, struct all_addr *addr, int id) save_reply_type(flags, i, response); } } + else if(flags & F_REVERSE) + { + // Save reply type and update individual reply counters + save_reply_type(flags, i, response); + } else { logg("*************************** unknown REPLY ***************************"); From 877c81be7ca3698906d635abd3ce0a129ab58a8e Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 9 Aug 2018 18:08:23 +0200 Subject: [PATCH 11/13] Add RRNAME reply type (needed for TXT queries) Signed-off-by: DL6ER --- FTL.h | 2 +- dnsmasq_interface.c | 10 +++++++++- gc.c | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/FTL.h b/FTL.h index cd8414404..448175535 100644 --- a/FTL.h +++ b/FTL.h @@ -84,7 +84,7 @@ enum { QUERIES, FORWARDED, CLIENTS, DOMAINS, OVERTIME, WILDCARD }; enum { DNSSEC_UNSPECIFIED, DNSSEC_SECURE, DNSSEC_INSECURE, DNSSEC_BOGUS, DNSSEC_ABANDONED, DNSSEC_UNKNOWN }; enum { QUERY_UNKNOWN, QUERY_GRAVITY, QUERY_FORWARDED, QUERY_CACHE, QUERY_WILDCARD, QUERY_BLACKLIST }; enum { TYPE_A = 1, TYPE_AAAA, TYPE_ANY, TYPE_SRV, TYPE_SOA, TYPE_PTR, TYPE_TXT, TYPE_MAX }; -enum { REPLY_UNKNOWN, REPLY_NODATA, REPLY_NXDOMAIN, REPLY_CNAME, REPLY_IP, REPLY_DOMAIN }; +enum { REPLY_UNKNOWN, REPLY_NODATA, REPLY_NXDOMAIN, REPLY_CNAME, REPLY_IP, REPLY_DOMAIN, REPLY_RRNAME }; enum { PRIVACY_SHOW_ALL = 0, PRIVACY_HIDE_DOMAINS, PRIVACY_HIDE_DOMAINS_CLIENTS, PRIVACY_MAXIMUM }; enum { MODE_IP, MODE_NX, MODE_NULL, MODE_IP_NODATA_AAAA }; enum { REGEX_UNKNOWN, REGEX_BLOCKED, REGEX_NOTBLOCKED }; diff --git a/dnsmasq_interface.c b/dnsmasq_interface.c index 8e0513803..b3752ca7e 100644 --- a/dnsmasq_interface.c +++ b/dnsmasq_interface.c @@ -494,7 +494,8 @@ void FTL_cache(unsigned int flags, char *name, struct all_addr *addr, char *arg, if(((flags & F_HOSTS) && (flags & F_IMMORTAL)) || ((flags & F_NAMEP) && (flags & F_DHCP)) || (flags & F_FORWARD) || - (flags & F_REVERSE)) + (flags & F_REVERSE) || + (flags & F_RRNAME)) { // List data: /etc/pihole/gravity.list, /etc/pihole/black.list, /etc/pihole/local.list, etc. // or @@ -521,6 +522,8 @@ void FTL_cache(unsigned int flags, char *name, struct all_addr *addr, char *arg, requesttype = QUERY_CACHE; else if(flags & F_REVERSE) // cached answer to reverse request (PTR) requesttype = QUERY_CACHE; + else if(flags & F_RRNAME) // cached answer to TXT query + requesttype = QUERY_CACHE; else { logg("*************************** unknown CACHE reply (1) ***************************"); @@ -696,6 +699,11 @@ void save_reply_type(unsigned int flags, int queryID, struct timeval response) queries[queryID].reply = REPLY_DOMAIN; counters.reply_domain++; } + else if(flags & F_RRNAME) + { + // TXT query + queries[queryID].reply = REPLY_RRNAME; + } else { // Valid IP diff --git a/gc.c b/gc.c index c0d748034..878ec6f73 100644 --- a/gc.c +++ b/gc.c @@ -132,7 +132,7 @@ void *GC_thread(void *val) counters.reply_domain--; break; - default: // Incomplete query, do nothing + default: // Incomplete query or TXT, do nothing break; } From 1a242be979bd0f11677276d23b85fc68d303e812 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 9 Aug 2018 20:28:00 +0200 Subject: [PATCH 12/13] Add "ANALYZE_ONLY_A_AND_AAAA" config option to obtain pre-v4.1 behavior (show and record only A and AAAA queries, ignore all others) Signed-off-by: DL6ER --- FTL.h | 1 + config.c | 13 +++++++++++++ dnsmasq_interface.c | 13 +++++++++++++ 3 files changed, 27 insertions(+) diff --git a/FTL.h b/FTL.h index 448175535..6c52d11f5 100644 --- a/FTL.h +++ b/FTL.h @@ -150,6 +150,7 @@ typedef struct { bool ignore_localhost; unsigned char blockingmode; bool regex_debugmode; + bool analyze_only_A_AAAA; } ConfigStruct; // Dynamic structs diff --git a/config.c b/config.c index 4f98c4594..fb20da46b 100644 --- a/config.c +++ b/config.c @@ -219,6 +219,19 @@ void read_FTLconf(void) else logg(" REGEX_DEBUGMODE: Inactive"); + // ANALYZE_ONLY_A_AND_AAAA + // defaults to: No + config.analyze_only_A_AAAA = false; + buffer = parse_FTLconf(fp, "ANALYZE_ONLY_A_AND_AAAA"); + + if(buffer != NULL && strcasecmp(buffer, "true") == 0) + config.analyze_only_A_AAAA = true; + + if(config.analyze_only_A_AAAA) + logg(" ANALYZE_ONLY_A_AND_AAAA: Enabled. Analyzing only A and AAAA queries"); + else + logg(" ANALYZE_ONLY_A_AND_AAAA: Disabled. Analyzing all queries"); + logg("Finished config file parsing"); // Release memory diff --git a/dnsmasq_interface.c b/dnsmasq_interface.c index 42010c059..f7d5f69e5 100644 --- a/dnsmasq_interface.c +++ b/dnsmasq_interface.c @@ -128,6 +128,19 @@ void FTL_new_query(unsigned int flags, char *name, struct all_addr *addr, char * overTime[timeidx].querytypedata[querytype-1]++; counters.querytype[querytype-1]++; + // Skip rest of the analysis if this query is not of type A or AAAA + // but user wants to see only A and AAAA queried (pre-v4.1 behavior) + if(config.analyze_only_A_AAAA && querytype != TYPE_A && querytype != TYPE_AAAA) + { + // Don't process this query further here, we already counted it + if(debug) logg("Notice: Skipping new query: %s (%i)", types, id); + free(domain); + free(domainbuffer); + free(client); + disable_thread_lock(); + return; + } + // Go through already knows domains and see if it is one of them int domainID = findDomainID(domain); From 25042de8f8c232fa1628c6540fb7d8330d65ae65 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 13 Aug 2018 00:51:10 +0200 Subject: [PATCH 13/13] Review comments Signed-off-by: DL6ER --- dnsmasq_interface.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/dnsmasq_interface.c b/dnsmasq_interface.c index 213ba6032..4da60aed3 100644 --- a/dnsmasq_interface.c +++ b/dnsmasq_interface.c @@ -129,7 +129,7 @@ void FTL_new_query(unsigned int flags, char *name, struct all_addr *addr, char * counters.querytype[querytype-1]++; // Skip rest of the analysis if this query is not of type A or AAAA - // but user wants to see only A and AAAA queried (pre-v4.1 behavior) + // but user wants to see only A and AAAA queries (pre-v4.1 behavior) if(config.analyze_only_A_AAAA && querytype != TYPE_A && querytype != TYPE_AAAA) { // Don't process this query further here, we already counted it @@ -413,8 +413,7 @@ void FTL_reply(unsigned short flags, char *name, struct all_addr *addr, int id) if(!found) { - // This may happen e.g. if the original query was a PTR query or "pi.hole" - // as we ignore them altogether + // This may happen e.g. if the original query was "pi.hole" if(debug) logg("FTL_reply(): Query %i has not been found", id); disable_thread_lock(); return; @@ -435,7 +434,7 @@ void FTL_reply(unsigned short flags, char *name, struct all_addr *addr, int id) // Answered from a custom (user provided) cache file counters.cached++; - // Detect user-defined blocking rules in .conf files + // Detect user-defined blocking rules if(strcmp(answer, "(NXDOMAIN)") == 0 || strcmp(answer, "0.0.0.0") == 0 || strcmp(answer, "::") == 0)