From e870f7a36a309b519a65bddb96e96b794d6a9910 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Fri, 16 Apr 2021 11:19:45 +0200 Subject: [PATCH] FTL automatically replies with the appropriate IP address to both p.hole and the machine's hostname. The replied with IP address can be overwritten using the REPLY_ADDR4/6 setting. Signed-off-by: DL6ER --- src/daemon.c | 26 ++++++++++ src/daemon.h | 1 + src/datastructure.h | 2 +- src/dnsmasq_interface.c | 108 +++++++++++++++++++++++++++++----------- src/main.c | 2 +- src/main.h | 2 +- 6 files changed, 110 insertions(+), 31 deletions(-) diff --git a/src/daemon.c b/src/daemon.c index bfb79507..87416b05 100644 --- a/src/daemon.c +++ b/src/daemon.c @@ -22,6 +22,8 @@ #include "database/common.h" // destroy_shmem() #include "shmem.h" +// uname() +#include pthread_t threads[THREADS_MAX] = { 0 }; bool resolver_ready = false; @@ -139,6 +141,30 @@ char *getUserName(void) return name; } +// "man 7 hostname" says: +// +// Each element of the hostname must be from 1 to 63 characters long and the +// entire hostname, including the dots, can be at most 253 characters long. +// +// Valid characters for hostnames are ASCII(7) letters from a to z, the +// digits from 0 to 9, and the hyphen (-). A hostname may not start with a +// hyphen. +#define HOSTNAMESIZE 256 +static char nodename[HOSTNAMESIZE] = { 0 }; +const char *hostname(void) +{ + // Ask kernel for node name if not known + // This is equivalent to "uname -n" + if(nodename[0] == '\0') + { + struct utsname buf; + if(uname(&buf) == 0) + strncpy(nodename, buf.nodename, HOSTNAMESIZE); + nodename[HOSTNAMESIZE-1] = '\0'; + } + return nodename; +} + void delay_startup(void) { // Exit early if not sleeping diff --git a/src/daemon.h b/src/daemon.h index 6282a20d..488bd5b1 100644 --- a/src/daemon.h +++ b/src/daemon.h @@ -16,6 +16,7 @@ extern pthread_t threads[THREADS_MAX]; void go_daemon(void); void savepid(void); char *getUserName(void); +const char *hostname(void); void delay_startup(void); bool is_fork(const pid_t mpid, const pid_t pid) __attribute__ ((const)); void cleanup(const int ret); diff --git a/src/datastructure.h b/src/datastructure.h index ffcd0c20..a2a9c0a0 100644 --- a/src/datastructure.h +++ b/src/datastructure.h @@ -102,7 +102,7 @@ ASSERT_SIZEOF(domainsData, 24, 16, 16); typedef struct { unsigned char magic; enum domain_client_status blocking_status; - unsigned char force_reply; + enum reply_type force_reply; enum query_types query_type; int domainID; int clientID; diff --git a/src/dnsmasq_interface.c b/src/dnsmasq_interface.c index d14b3e18..95de0208 100644 --- a/src/dnsmasq_interface.c +++ b/src/dnsmasq_interface.c @@ -58,7 +58,7 @@ static void query_blocked(queriesData* query, // Static blocking metadata static union all_addr null_addrp = {{ 0 }}; -static unsigned char force_next_DNS_reply = 0u; +static enum reply_type force_next_DNS_reply = REPLY_UNKNOWN; // Adds debug information to the regular pihole.log file char debug_dnsmasq_lines = 0; @@ -82,10 +82,28 @@ void FTL_iface(const int ifidx, const struct irec *ifaces) // Copy overwrite addresses if configured via REPLY_ADDR4 and/or REPLY_ADDR6 settings if(config.reply_addr.overwrite_v4) + { memcpy(&next_iface.addr4, &config.reply_addr.v4, sizeof(config.reply_addr.v4)); + + if(config.debug & DEBUG_NETWORKING) + { + char buffer[ADDRSTRLEN+1] = { 0 }; + inet_ntop(AF_INET, &next_iface.addr4, buffer, ADDRSTRLEN); + logg("Interface (%d) %s OVERWRITES IPv4 address %s", ifidx, next_iface.name, buffer); + } + } if(config.reply_addr.overwrite_v6) + { memcpy(&next_iface.addr6, &config.reply_addr.v6, sizeof(config.reply_addr.v6)); + if(config.debug & DEBUG_NETWORKING) + { + char buffer[ADDRSTRLEN+1] = { 0 }; + inet_ntop(AF_INET6, &next_iface.addr6, buffer, ADDRSTRLEN); + logg("Interface (%d) %s OVERWRITES IPv6 address %s", ifidx, next_iface.name, buffer); + } + } + // Use dummy when interface record is not available next_iface.name[0] = '-'; next_iface.name[1] = '\0'; @@ -390,8 +408,8 @@ static bool _FTL_check_blocking(int queryID, int domainID, int clientID, const c // Truncate "_esni." from queried domain if the parenting domain was the reason for blocking this query blockedDomain = domainstr + 6u; // Force next DNS reply to be NXDOMAIN for _esni.* queries - force_next_DNS_reply = NXDOMAIN; - dns_cache->force_reply = NXDOMAIN; + force_next_DNS_reply = REPLY_NXDOMAIN; + dns_cache->force_reply = REPLY_NXDOMAIN; } } @@ -621,9 +639,24 @@ bool _FTL_new_query(const unsigned int flags, const char *name, return false; } - // If domain is "pi.hole" we skip this query - if(strcasecmp(name, "pi.hole") == 0) - return false; + // If domain is "pi.hole" or the local hostname we skip analyzing this query + // and, instead, immediately reply with the IP address + if(strcasecmp(name, "pi.hole") == 0 || strcasecmp(name, hostname()) == 0) + { + if(querytype == TYPE_A || querytype == TYPE_AAAA || querytype == TYPE_ANY) + { + // "Block" this query by sending the interface IP address + force_next_DNS_reply = REPLY_IP; + if(config.debug & DEBUG_QUERIES) + logg("Replying to %s with interface-local IP address", name); + return true; + } + else + { + // Don't block this query + return false; + } + } // Convert domain to lower case char *domainString = strdup(name); @@ -648,7 +681,7 @@ bool _FTL_new_query(const unsigned int flags, const char *name, inet_ntop(family, family == AF_INET ? (union mysockaddr*)&addr->in.sin_addr : - (union mysockaddr*)&addr->in6.sin6_addr, + (union mysockaddr*)&addr->in6.sin6_addr, clientIP, ADDRSTRLEN); } @@ -691,7 +724,7 @@ bool _FTL_new_query(const unsigned int flags, const char *name, } // Block this query - force_next_DNS_reply = REFUSED; + force_next_DNS_reply = REPLY_REFUSED; // Do not further process this query, Pi-hole has never seen it unlock_shm(); @@ -856,19 +889,21 @@ void _FTL_get_blocking_metadata(union all_addr **addrp, unsigned int *flags, con // Check first if we need to force our reply to something different than the // default/configured blocking mode. For instance, we need to force NXDOMAIN // for intercepted _esni.* queries. - if(force_next_DNS_reply == NXDOMAIN) + if(force_next_DNS_reply == REPLY_NXDOMAIN) { *flags = F_NXDOMAIN; + // Reset DNS reply forcing - force_next_DNS_reply = 0u; + force_next_DNS_reply = REPLY_UNKNOWN; return; } - else if(force_next_DNS_reply == REFUSED) + else if(force_next_DNS_reply == REPLY_REFUSED) { // Empty flags result in REFUSED *flags = 0; + // Reset DNS reply forcing - force_next_DNS_reply = 0u; + force_next_DNS_reply = REPLY_UNKNOWN; return; } @@ -882,18 +917,35 @@ void _FTL_get_blocking_metadata(union all_addr **addrp, unsigned int *flags, con if(*flags & F_IPV6) { // Pass blocking IPv6 address - if(config.blockingmode == MODE_IP) + if(config.blockingmode == MODE_IP || + force_next_DNS_reply == REPLY_IP) + { *addrp = &next_iface.addr6; + } else + { *addrp = &null_addrp; + } + + // Reset reply forcing + force_next_DNS_reply = REPLY_UNKNOWN; } else { // Pass blocking IPv4 address - if(config.blockingmode == MODE_IP || config.blockingmode == MODE_IP_NODATA_AAAA) + if(config.blockingmode == MODE_IP || + config.blockingmode == MODE_IP_NODATA_AAAA || + force_next_DNS_reply == REPLY_IP) + { *addrp = &next_iface.addr4; + } else + { *addrp = &null_addrp; + } + + // Reset reply forcing + force_next_DNS_reply = REPLY_UNKNOWN; } if(config.blockingmode == MODE_NX) @@ -1074,6 +1126,16 @@ void _FTL_reply(const unsigned int flags, const char *name, const union all_addr // Lock shared memory lock_shm(); + // Save status in corresponding query identified by dnsmasq's ID + const int queryID = findQueryID(id); + if(queryID < 0) + { + // This may happen e.g. if the original query was "pi.hole" + if(config.debug & DEBUG_QUERIES) logg("FTL_reply(): Query %i has not been found", id); + unlock_shm(); + return; + } + // Determine returned result if available char dest[ADDRSTRLEN]; dest[0] = '\0'; if(addr) @@ -1116,18 +1178,8 @@ void _FTL_reply(const unsigned int flags, const char *name, const union all_addr struct timeval response; gettimeofday(&response, 0); - // Save status in corresponding query identified by dnsmasq's ID - const int i = findQueryID(id); - if(i < 0) - { - // This may happen e.g. if the original query was "pi.hole" - if(config.debug & DEBUG_QUERIES) logg("FTL_reply(): Query %i has not been found", id); - unlock_shm(); - return; - } - // Get query pointer - queriesData* query = getQuery(i, true); + queriesData* query = getQuery(queryID, true); // Check if reply time is still unknown // We only process the first reply in here @@ -1696,7 +1748,7 @@ static void query_set_reply(const unsigned int flags, const union all_addr *addr queriesData* query, const struct timeval response) { // Iterate through possible values - if(flags & F_NEG || force_next_DNS_reply == NXDOMAIN) + if(flags & F_NEG || force_next_DNS_reply == REPLY_NXDOMAIN) { if(flags & F_NXDOMAIN) // NXDOMAIN @@ -1714,10 +1766,10 @@ static void query_set_reply(const unsigned int flags, const union all_addr *addr else if(flags & F_RRNAME) // TXT query query->reply = REPLY_RRNAME; - else if((flags & F_RCODE && addr != NULL) || force_next_DNS_reply == REFUSED) + else if((flags & F_RCODE && addr != NULL) || force_next_DNS_reply == REPLY_REFUSED) { if((addr != NULL && addr->log.rcode == REFUSED) - || force_next_DNS_reply == REFUSED ) + || force_next_DNS_reply == REPLY_REFUSED ) { // REFUSED query query->reply = REPLY_REFUSED; diff --git a/src/main.c b/src/main.c index 6743f242..dba32166 100644 --- a/src/main.c +++ b/src/main.c @@ -47,7 +47,7 @@ int main (int argc, char* argv[]) // Try to open FTL log init_FTL_log(); timer_start(EXIT_TIMER); - logg("########## FTL started! ##########"); + logg("########## FTL started on %s! ##########", hostname()); log_FTL_version(false); // Catch signals not handled by dnsmasq diff --git a/src/main.h b/src/main.h index 53b95e3f..0c3a5007 100644 --- a/src/main.h +++ b/src/main.h @@ -12,7 +12,7 @@ int main_dnsmasq(int argc, const char ** argv); -extern char * username; +extern char *username; extern bool startup; #endif //MAIN_H