Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Analyze EDNS(0) records #851

Merged
merged 18 commits into from
Aug 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
c74b6c5
Add support for ECS subnet parsing in FTL.
DL6ER Jul 20, 2020
fd79c1f
Log previously seen client when interpreting EDNS0 client subnet info…
DL6ER Jul 20, 2020
ddb79c3
Evaluate possible EDNS data before analyzing a new query
DL6ER Jul 20, 2020
238a1e4
Analyse ECS information only if EDNS0_ECS is enabled (enabled by defa…
DL6ER Jul 20, 2020
1746224
Add support for EDNS(0) CPE-ID (Common Platform Enumeration Identifier)
DL6ER Jul 21, 2020
cd981bb
Protect against possible buffer overflow due to a malicious/malformed…
DL6ER Jul 21, 2020
88ce374
Add support for EDNS(0) MAC in BYTE format (dnsmasq option add-mac)
DL6ER Jul 21, 2020
109f32b
Add support for EDNS(0) MAC in TEXT format (dnsmasq option add-mac=text)
DL6ER Jul 21, 2020
8c8a9b0
Add partial support for EDNS(0) MAC in BASE64 format (dnsmasq option …
DL6ER Jul 21, 2020
cc2de86
Correct name is EDNS(0) not EDNS0.
DL6ER Jul 21, 2020
b1c2cfa
EDNS(0) debug message fine-tuning
DL6ER Jul 21, 2020
261a278
Use preprocessor constants for OPTCODES to improve readability of the…
DL6ER Jul 21, 2020
e267459
Add partial support for EDNS(0) COOKIES
DL6ER Jul 21, 2020
cab4664
Tests: EDNS(0) analysis
DL6ER Jul 21, 2020
fd7ae44
Make EDNS MAC available for FTL_new_query()
DL6ER Jul 21, 2020
e20e378
Tests: Simplify EDNS(0) tests
DL6ER Jul 21, 2020
1cd1b39
Use %z to print size_t for both 32 and 64 bit compatibility
DL6ER Jul 21, 2020
56aeab8
The version of dig in the CI containers is too old for the option +co…
DL6ER Jul 21, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ set(sources
datastructure.h
dnsmasq_interface.c
dnsmasq_interface.h
edns0.c
edns0.h
enums.h
files.c
files.h
Expand Down
24 changes: 19 additions & 5 deletions src/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -382,17 +382,26 @@ void read_FTLconf(void)
// we use the host name associated to the other address as this is the same
// device. This behavior can be disabled using NAMES_FROM_NETDB=false
// defaults to: true
config.names_from_netdb = true;
buffer = parse_FTLconf(fp, "NAMES_FROM_NETDB");

if(buffer != NULL && strcasecmp(buffer, "false") == 0)
config.names_from_netdb = false;
config.names_from_netdb = read_bool(buffer, true);

if(config.names_from_netdb)
logg(" NAMES_FROM_NETDB: Enabled, trying to get names from network database");
else
logg(" NAMES_FROM_NETDB: Disabled");

// EDNS0_ECS
// Should we overwrite the query source when client information is
// provided through EDNS0 client subnet (ECS) information?
// defaults to: true
buffer = parse_FTLconf(fp, "EDNS0_ECS");
config.edns0_ecs = read_bool(buffer, true);

if(config.edns0_ecs)
logg(" EDNS0_ECS: Overwrite client from ECS information");
else
logg(" EDNS0_ECS: Don't use ECS information");

// Read DEBUG_... setting from pihole-FTL.conf
read_debuging_settings(fp);

Expand Down Expand Up @@ -574,7 +583,7 @@ void get_blocking_mode(FILE *fp)
}

// Routine for setting the debug flags in the config struct
static void setDebugOption(FILE* fp, const char* option, int16_t bitmask)
static void setDebugOption(FILE* fp, const char* option, enum debug_mode bitmask)
{
const char* buffer = parse_FTLconf(fp, option);

Expand Down Expand Up @@ -676,6 +685,10 @@ void read_debuging_settings(FILE *fp)
// defaults to: false
setDebugOption(fp, "DEBUG_RESOLVER", DEBUG_RESOLVER);

// DEBUG_EDNS0
// defaults to: false
setDebugOption(fp, "DEBUG_EDNS0", DEBUG_EDNS0);

if(config.debug)
{
logg("*****************************");
Expand All @@ -696,6 +709,7 @@ void read_debuging_settings(FILE *fp)
logg("* DEBUG_DNSMASQ_LINES %s *", (config.debug & DEBUG_DNSMASQ_LINES)? "YES":"NO ");
logg("* DEBUG_VECTORS %s *", (config.debug & DEBUG_VECTORS)? "YES":"NO ");
logg("* DEBUG_RESOLVER %s *", (config.debug & DEBUG_RESOLVER)? "YES":"NO ");
logg("* DEBUG_EDNS0 %s *", (config.debug & DEBUG_EDNS0)? "YES":"NO ");
logg("*****************************");
}

Expand Down
3 changes: 2 additions & 1 deletion src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ typedef struct {
int maxlogage;
int dns_port;
unsigned int delay_startup;
int16_t debug;
enum debug_mode debug;
enum privacy_level privacylevel;
enum blocking_mode blockingmode;
bool socket_listenlocal;
Expand All @@ -43,6 +43,7 @@ typedef struct {
bool cname_inspection;
bool block_esni;
bool names_from_netdb;
bool edns0_ecs;
} ConfigStruct;

typedef struct {
Expand Down
29 changes: 29 additions & 0 deletions src/datastructure.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,35 @@ void strtolower(char *str)
while(str[i]){ str[i] = tolower(str[i]); i++; }
}

int findQueryID(const int id)
{
// Loop over all queries - we loop in reverse order (start from the most recent query and
// continuously walk older queries while trying to find a match. Ideally, we should always
// 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
// MAX(0, a) is used to return 0 in case a is negative (negative array indices are harmful)
const int until = MAX(0, counters->queries-MAXITER);
const int start = MAX(0, counters->queries-1);

// Check UUIDs of queries
for(int i = start; i >= until; i--)
{
const queriesData* query = getQuery(i, true);

// Check if the returned pointer is valid before trying to access it
if(query == NULL)
continue;

if(query->id == id)
return i;
}

// If not found
return -1;
}

int findUpstreamID(const char * upstreamString, const bool count)
{
// Go through already knows upstream servers and see if we used one of those
Expand Down
1 change: 1 addition & 0 deletions src/datastructure.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "enums.h"

void strtolower(char *str);
int findQueryID(const int id);
int findUpstreamID(const char * upstream, const bool count);
int findDomainID(const char *domain, const bool count);
int findClientID(const char *client, const bool count);
Expand Down
22 changes: 17 additions & 5 deletions src/dnsmasq/forward.c
Original file line number Diff line number Diff line change
Expand Up @@ -1530,7 +1530,13 @@ void receive_query(struct listener *listen, time_t now)
#ifdef HAVE_DUMPFILE
dump_packet(DUMP_QUERY, daemon->packet, (size_t)n, &source_addr, NULL);
#endif


//********************** Pi-hole modification **********************//
struct edns_data edns = { 0 };
if (find_pseudoheader(header, (size_t)n, NULL, &pheader, NULL, NULL))
FTL_parse_pseudoheaders(header, n, &source_addr, &edns);
//******************************************************************//

if (extract_request(header, (size_t)n, daemon->namebuff, &type))
{
#ifdef HAVE_AUTH
Expand All @@ -1543,14 +1549,14 @@ void receive_query(struct listener *listen, time_t now)
log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
(union all_addr *)&source_addr.in.sin_addr, types);
piholeblocked = FTL_new_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff, &blockingreason,
(union all_addr *)&source_addr.in.sin_addr, types, type, daemon->log_display_id, UDP);
(union all_addr *)&source_addr.in.sin_addr, types, type, daemon->log_display_id, &edns, UDP);
}
else
{
log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff,
(union all_addr *)&source_addr.in6.sin6_addr, types);
piholeblocked = FTL_new_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff, &blockingreason,
(union all_addr *)&source_addr.in6.sin6_addr, types, type, daemon->log_display_id, UDP);
(union all_addr *)&source_addr.in6.sin6_addr, types, type, daemon->log_display_id, &edns, UDP);
}

#ifdef HAVE_AUTH
Expand Down Expand Up @@ -1931,6 +1937,12 @@ unsigned char *tcp_request(int confd, time_t now,
/* save state of "cd" flag in query */
if ((checking_disabled = header->hb4 & HB4_CD))
no_cache_dnssec = 1;

//********************** Pi-hole modification **********************//
struct edns_data edns = { 0 };
if (find_pseudoheader(header, (size_t)size, NULL, &pheader, NULL, NULL))
FTL_parse_pseudoheaders(header, size, &peer_addr, &edns);
//******************************************************************//

if ((gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype)))
{
Expand All @@ -1944,14 +1956,14 @@ unsigned char *tcp_request(int confd, time_t now,
log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
(union all_addr *)&peer_addr.in.sin_addr, types);
piholeblocked = FTL_new_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff, &blockingreason,
(union all_addr *)&peer_addr.in.sin_addr, types, qtype, daemon->log_display_id, TCP);
(union all_addr *)&peer_addr.in.sin_addr, types, qtype, daemon->log_display_id, &edns, TCP);
}
else
{
log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff,
(union all_addr *)&peer_addr.in6.sin6_addr, types);
piholeblocked = FTL_new_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff, &blockingreason,
(union all_addr *)&peer_addr.in6.sin6_addr, types, qtype, daemon->log_display_id, TCP);
(union all_addr *)&peer_addr.in6.sin6_addr, types, qtype, daemon->log_display_id, &edns, TCP);
}

#ifdef HAVE_AUTH
Expand Down
58 changes: 16 additions & 42 deletions src/dnsmasq_interface.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ static void save_reply_type(const unsigned int flags, const union all_addr *addr
static unsigned long converttimeval(const struct timeval time) __attribute__((const));
static void detect_blocked_IP(const unsigned short flags, const union all_addr *addr, const int queryID);
static void query_externally_blocked(const int queryID, const unsigned char status);
static int findQueryID(const int id);
static void prepare_blocking_metadata(void);
static void query_blocked(queriesData* query, domainsData* domain, clientsData* client, const unsigned char new_status);

Expand Down Expand Up @@ -437,7 +436,8 @@ bool _FTL_CNAME(const char *domain, const struct crec *cpp, const int id, const
bool _FTL_new_query(const unsigned int flags, const char *name,
const char **blockingreason, const union all_addr *addr,
const char *types, const unsigned short qtype, const int id,
const enum protocol proto, const char* file, const int line)
const struct edns_data *edns, const enum protocol proto,
const char* file, const int line)
{
// Create new query in data structure

Expand Down Expand Up @@ -503,18 +503,25 @@ bool _FTL_new_query(const unsigned int flags, const char *name,
char *domainString = strdup(name);
strtolower(domainString);

// Get client IP address
char dest[ADDRSTRLEN];
inet_ntop((flags & F_IPV4) ? AF_INET : AF_INET6, addr, dest, ADDRSTRLEN);
char *clientIP = strdup(dest);
strtolower(clientIP);
// Get client IP address (can be overwritten by EDNS(0) client subnet (ECS) data)
char clientIP[ADDRSTRLEN] = { 0 };
if(config.edns0_ecs && edns->client_set)
{
// Use ECS provided client
strncpy(clientIP, edns->client, ADDRSTRLEN);
clientIP[ADDRSTRLEN-1] = '\0';
}
else
{
// Use original requestor
inet_ntop((flags & F_IPV4) ? AF_INET : AF_INET6, addr, clientIP, ADDRSTRLEN);
}

// Check if user wants to skip queries coming from localhost
if(config.ignore_localhost &&
(strcmp(clientIP, "127.0.0.1") == 0 || strcmp(clientIP, "::1") == 0))
{
free(domainString);
free(clientIP);
unlock_shm();
return false;
}
Expand All @@ -541,7 +548,6 @@ bool _FTL_new_query(const unsigned int flags, const char *name,
// Don't process this query further here, we already counted it
if(config.debug & DEBUG_QUERIES) logg("Notice: Skipping new query: %s (%i)", types, id);
free(domainString);
free(clientIP);
unlock_shm();
return false;
}
Expand All @@ -559,7 +565,6 @@ bool _FTL_new_query(const unsigned int flags, const char *name,
// Encountered memory error, skip query
// Free allocated memory
free(domainString);
free(clientIP);
// Release thread lock
unlock_shm();
return false;
Expand Down Expand Up @@ -604,7 +609,6 @@ bool _FTL_new_query(const unsigned int flags, const char *name,
// Encountered memory error, skip query
// Free allocated memory
free(domainString);
free(clientIP);
// Release thread lock
unlock_shm();
return false;
Expand All @@ -621,7 +625,6 @@ bool _FTL_new_query(const unsigned int flags, const char *name,

// Free allocated memory
free(domainString);
free(clientIP);

// Release thread lock
unlock_shm();
Expand Down Expand Up @@ -664,43 +667,14 @@ void _FTL_get_blocking_metadata(union all_addr **addrp, unsigned int *flags, con
*flags = F_NXDOMAIN;
}
else if(config.blockingmode == MODE_NODATA ||
(config.blockingmode == MODE_IP_NODATA_AAAA && (*flags & F_IPV6)))
(config.blockingmode == MODE_IP_NODATA_AAAA && (*flags & F_IPV6)))
{
// If we block in NODATA mode or NODATA for AAAA queries, we apply
// the NOERROR response flag. This ensures we're sending an empty response
*flags = F_NOERR;
}
}

static int findQueryID(const int id)
{
// Loop over all queries - we loop in reverse order (start from the most recent query and
// continuously walk older queries while trying to find a match. Ideally, we should always
// 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
// MAX(0, a) is used to return 0 in case a is negative (negative array indices are harmful)
const int until = MAX(0, counters->queries-MAXITER);
const int start = MAX(0, counters->queries-1);

// Check UUIDs of queries
for(int i = start; i >= until; i--)
{
const queriesData* query = getQuery(i, true);

// Check if the returned pointer is valid before trying to access it
if(query == NULL)
continue;

if(query->id == id)
return i;
}

// If not found
return -1;
}

void _FTL_forwarded(const unsigned int flags, const char *name, const union all_addr *addr, const int id,
const char* file, const int line)
{
Expand Down
6 changes: 4 additions & 2 deletions src/dnsmasq_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@
// Including stdbool.h here as it is required for defining the boolean prototype of FTL_new_query
#include <stdbool.h>

#include "edns0.h"

extern int socketfd, telnetfd4, telnetfd6;
extern unsigned char* pihole_privacylevel;
enum protocol { TCP, UDP };

#define FTL_new_query(flags, name, blockingreason, addr, types, qtype, id, proto) _FTL_new_query(flags, name, blockingreason, addr, types, qtype, id, proto, __FILE__, __LINE__)
bool _FTL_new_query(const unsigned int flags, const char *name, const char** blockingreason, const union all_addr *addr, const char *types, const unsigned short qtype, const int id, enum protocol proto, const char* file, const int line);
#define FTL_new_query(flags, name, blockingreason, addr, types, qtype, id, edns, proto) _FTL_new_query(flags, name, blockingreason, addr, types, qtype, id, edns, proto, __FILE__, __LINE__)
bool _FTL_new_query(const unsigned int flags, const char *name, const char** blockingreason, const union all_addr *addr, const char *types, const unsigned short qtype, const int id, const struct edns_data *edns, enum protocol proto, const char* file, const int line);

#define FTL_forwarded(flags, name, addr, id) _FTL_forwarded(flags, name, addr, id, __FILE__, __LINE__)
void _FTL_forwarded(const unsigned int flags, const char *name, const union all_addr *addr, const int id, const char* file, const int line);
Expand Down
Loading