diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4d68700e..13fd854d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -114,6 +114,7 @@ set(sources datastructure.h dnsmasq_interface.c dnsmasq_interface.h + enums.h files.c files.h FTL.h diff --git a/src/FTL.h b/src/FTL.h index 2dfefb55..c6630f26 100644 --- a/src/FTL.h +++ b/src/FTL.h @@ -103,17 +103,6 @@ // Default: 1000 (one second) #define DATABASE_BUSY_TIMEOUT 1000 -// FTLDNS enums -enum { QUERIES, UPSTREAMS, CLIENTS, DOMAINS, OVERTIME, WILDCARD, DNS_CACHE }; -enum { DNSSEC_UNSPECIFIED, DNSSEC_SECURE, DNSSEC_INSECURE, DNSSEC_BOGUS, DNSSEC_ABANDONED }; -enum { QUERY_UNKNOWN, QUERY_GRAVITY, QUERY_FORWARDED, QUERY_CACHE, QUERY_REGEX, QUERY_BLACKLIST, \ - QUERY_EXTERNAL_BLOCKED_IP, QUERY_EXTERNAL_BLOCKED_NULL, QUERY_EXTERNAL_BLOCKED_NXRA, \ - QUERY_GRAVITY_CNAME, QUERY_REGEX_CNAME, QUERY_BLACKLIST_CNAME, QUERY_STATUS_MAX }; -enum { REPLY_UNKNOWN, REPLY_NODATA, REPLY_NXDOMAIN, REPLY_CNAME, REPLY_IP, REPLY_DOMAIN, REPLY_RRNAME, REPLY_SERVFAIL, REPLY_REFUSED, REPLY_NOTIMP, REPLY_OTHER }; -enum { PRIVACY_SHOW_ALL = 0, PRIVACY_HIDE_DOMAINS, PRIVACY_HIDE_DOMAINS_CLIENTS, PRIVACY_MAXIMUM, PRIVACY_NOSTATS }; -enum { MODE_IP, MODE_NX, MODE_NULL, MODE_IP_NODATA_AAAA, MODE_NODATA }; -enum { REGEX_BLACKLIST, REGEX_WHITELIST }; - // Use out own memory handling functions that will detect possible errors // and report accordingly in the log. This will make debugging FTL crashs // caused by insufficient memory or by code bugs (not properly dealing diff --git a/src/api/api.c b/src/api/api.c index f85fd5d2..a6bf4e02 100644 --- a/src/api/api.c +++ b/src/api/api.c @@ -9,6 +9,7 @@ * Please see LICENSE file for your rights under this license. */ #include "FTL.h" +#include "enums.h" #include "memory.h" #include "shmem.h" #include "datastructure.h" @@ -1368,15 +1369,18 @@ void getDomainDetails(const char *client_message, const int *sock) case UNKNOWN_BLOCKED: str = "unknown"; break; - case BLACKLIST_BLOCKED: - str = "blacklisted"; - break; case GRAVITY_BLOCKED: str = "gravity"; break; + case BLACKLIST_BLOCKED: + str = "blacklisted"; + break; case REGEX_BLOCKED: str = "regex"; break; + case WHITELISTED: + str = "whitelisted"; + break; case NOT_BLOCKED: str = "not blocked"; break; diff --git a/src/api/socket.c b/src/api/socket.c index 34c45237..d034db7d 100644 --- a/src/api/socket.c +++ b/src/api/socket.c @@ -29,27 +29,11 @@ #define BACKLOG 5 // File descriptors -int socketfd, telnetfd4 = 0, telnetfd6 = 0; +int socketfd = 0, telnetfd4 = 0, telnetfd6 = 0; bool dualstack = false; -bool ipv4telnet = false, ipv6telnet = false; -bool sock_avail = false; +bool ipv4telnet = false, ipv6telnet = false, sock_avail = false; bool istelnet[MAXCONNS]; -static void saveport(void) -{ - FILE *f; - if((f = fopen(FTLfiles.port, "w+")) == NULL) - { - logg("WARNING: Unable to write used port to file."); - logg(" Continuing anyway (API might not find the port)."); - } - else - { - fprintf(f, "%i", config.port); - fclose(f); - } -} - static bool bind_to_telnet_port_IPv4(int *socketdescriptor) { // IPv4 socket @@ -161,15 +145,14 @@ static bool bind_to_telnet_port_IPv6(int *socketdescriptor) return true; } -static void bind_to_unix_socket(int *socketdescriptor) +static bool bind_to_unix_socket(int *socketdescriptor) { *socketdescriptor = socket(AF_LOCAL, SOCK_STREAM, 0); if(*socketdescriptor < 0) { logg("WARNING: Error opening Unix socket."); - logg(" Continuing anyway."); - return; + return false; } // Make sure unix socket file handle does not exist, if it exists, remove it @@ -190,32 +173,18 @@ static void bind_to_unix_socket(int *socketdescriptor) if(bind(*socketdescriptor, (struct sockaddr *) &address, sizeof (address)) != 0) { logg("WARNING: Cannot bind on Unix socket %s: %s (%i)", FTLfiles.socketfile, strerror(errno), errno); - logg(" Continuing anyway."); - return; + return false; } // The listen system call allows the process to listen on the Unix socket for connections if(listen(*socketdescriptor, BACKLOG) == -1) { logg("WARNING: Cannot listen on Unix socket: %s (%i)", strerror(errno), errno); - logg(" Continuing anyway."); - return; + return false; } logg("Listening on Unix socket"); - sock_avail = true; -} - -// Called from main() at graceful shutdown -static void removeport(void) -{ - FILE *f; - if((f = fopen(FTLfiles.port, "w+")) == NULL) - { - logg("WARNING: Unable to empty port file"); - return; - } - fclose(f); + return true; } void seom(const int sock) @@ -295,7 +264,6 @@ static int listener(const int sockfd, const char type) void close_telnet_socket(void) { - removeport(); // Using global variable here if(telnetfd4) close(telnetfd4); @@ -303,12 +271,17 @@ void close_telnet_socket(void) close(telnetfd6); } -void close_unix_socket(void) +void close_unix_socket(bool unlink_file) { - // The process has to take care of unlinking the socket file description on exit - unlink(FTLfiles.socketfile); + if(unlink_file) + { + // The process has to take care of unlinking the socket file description on exit + unlink(FTLfiles.socketfile); + } + // Using global variable here - close(socketfd); + if(sock_avail) + close(socketfd); } static void *telnet_connection_handler_thread(void *socket_desc) @@ -413,24 +386,6 @@ static void *socket_connection_handler_thread(void *socket_desc) return false; } -void bind_sockets(void) -{ - // Initialize IPv4 telnet socket - if(bind_to_telnet_port_IPv4(&telnetfd4)) - ipv4telnet = true; - - // Initialize IPv6 telnet socket - // only if IPv6 interfaces are available - if(ipv6_available()) - if(bind_to_telnet_port_IPv6(&telnetfd6)) - ipv6telnet = true; - - saveport(); - - // Initialize Unix socket - bind_to_unix_socket(&socketfd); -} - void *telnet_listening_thread_IPv4(void *args) { // We will use the attributes object later to start all threads in detached mode @@ -444,6 +399,11 @@ void *telnet_listening_thread_IPv4(void *args) // Set thread name prctl(PR_SET_NAME,"telnet-IPv4",0,0,0); + // Initialize IPv4 telnet socket + ipv4telnet = bind_to_telnet_port_IPv4(&telnetfd4); + if(!ipv4telnet) + return NULL; + // Listen as long as FTL is not killed while(!killed) { @@ -466,7 +426,7 @@ void *telnet_listening_thread_IPv4(void *args) if(pthread_create( &telnet_connection_thread, &attr, telnet_connection_handler_thread, (void*) newsock ) != 0) { // Log the error code description - logg("WARNING: Unable to open telnet processing thread, error: %s", strerror(errno)); + logg("WARNING: Unable to open telnet processing thread: %s", strerror(errno)); } } return false; @@ -485,6 +445,14 @@ void *telnet_listening_thread_IPv6(void *args) // Set thread name prctl(PR_SET_NAME,"telnet-IPv6",0,0,0); + // Initialize IPv6 telnet socket but only if IPv6 interfaces are available + if(!ipv6_available()) + return NULL; + + ipv6telnet = bind_to_telnet_port_IPv6(&telnetfd6); + if(!ipv6telnet) + return NULL; + // Listen as long as FTL is not killed while(!killed) { @@ -507,7 +475,7 @@ void *telnet_listening_thread_IPv6(void *args) if(pthread_create( &telnet_connection_thread, &attr, telnet_connection_handler_thread, (void*) newsock ) != 0) { // Log the error code description - logg("WARNING: Unable to open telnet processing thread, error: %s", strerror(errno)); + logg("WARNING: Unable to open telnet processing thread: %s", strerror(errno)); } } return false; @@ -527,7 +495,8 @@ void *socket_listening_thread(void *args) prctl(PR_SET_NAME,"socket listener",0,0,0); // Return early to avoid CPU spinning if Unix socket is not available - if(!sock_avail) + sock_avail = bind_to_unix_socket(&socketfd); + if(sock_avail) return NULL; // Listen as long as FTL is not killed @@ -535,7 +504,8 @@ void *socket_listening_thread(void *args) { // Look for new clients that want to connect const int csck = listener(socketfd, 0); - if(csck < 0) continue; + if(csck < 0) + continue; // Allocate memory used to transport client socket ID to client listening thread int *newsock; @@ -548,7 +518,7 @@ void *socket_listening_thread(void *args) if(pthread_create( &socket_connection_thread, &attr, socket_connection_handler_thread, (void*) newsock ) != 0) { // Log the error code description - logg("WARNING: Unable to open socket processing thread, error: %s", strerror(errno)); + logg("WARNING: Unable to open socket processing thread: %s", strerror(errno)); } } return false; @@ -557,6 +527,7 @@ void *socket_listening_thread(void *args) bool ipv6_available(void) { struct ifaddrs *allInterfaces; + enum { IPv4, IPv6 }; int iface[2] = { 0 }; // Get all interfaces @@ -572,7 +543,7 @@ bool ipv6_available(void) // Check only for up and running IPv4, IPv6 interfaces if ((flags & (IFF_UP|IFF_RUNNING)) && addr != NULL) { - iface[addr->sa_family == AF_INET6 ? 1 : 0]++; + iface[addr->sa_family == AF_INET6 ? IPv6 : IPv4]++; if(config.debug & DEBUG_NETWORKING) logg("Interface %s is %s", interface->ifa_name, addr->sa_family == AF_INET6 ? "IPv6" : "IPv4"); @@ -583,8 +554,8 @@ bool ipv6_available(void) if(config.debug & DEBUG_NETWORKING) { - logg("Found %i IPv4 and %i IPv6 capable interfaces", iface[0], iface[1]); + logg("Found %i IPv4 and %i IPv6 capable interfaces", iface[IPv4], iface[IPv6]); } - return (iface[1] > 0); + return (iface[IPv6] > 0); } diff --git a/src/api/socket.h b/src/api/socket.h index 62ce923c..15d54231 100644 --- a/src/api/socket.h +++ b/src/api/socket.h @@ -11,7 +11,7 @@ #define SOCKET_H void close_telnet_socket(void); -void close_unix_socket(void); +void close_unix_socket(bool unlink_file); void seom(const int sock); void ssend(const int sock, const char *format, ...) __attribute__ ((format (gnu_printf, 2, 3))); void swrite(const int sock, const void* value, const size_t size); diff --git a/src/config.c b/src/config.c index 06a20f63..f0355d3c 100644 --- a/src/config.c +++ b/src/config.c @@ -29,7 +29,6 @@ FTLFileNamesStruct FTLfiles = { NULL, NULL, NULL, - NULL, NULL }; @@ -301,9 +300,6 @@ void read_FTLconf(void) // PIDFILE getpath(fp, "PIDFILE", "/run/pihole-FTL.pid", &FTLfiles.pid); - // PORTFILE - getpath(fp, "PORTFILE", "/run/pihole-FTL.port", &FTLfiles.port); - // SOCKETFILE getpath(fp, "SOCKETFILE", "/run/pihole/FTL.sock", &FTLfiles.socketfile); @@ -512,7 +508,7 @@ void get_privacy_level(FILE *fp) { // Check for change and validity of privacy level (set in FTL.h) if(value >= PRIVACY_SHOW_ALL && - value <= PRIVACY_NOSTATS && + value <= PRIVACY_MAXIMUM && value > config.privacylevel) { logg("Notice: Increasing privacy level from %i to %i", config.privacylevel, value); diff --git a/src/config.h b/src/config.h index 8b978a72..503850f2 100644 --- a/src/config.h +++ b/src/config.h @@ -10,6 +10,9 @@ #ifndef CONFIG_H #define CONFIG_H +// enum privacy_level +#include "enums.h" + // typedef int16_t #include @@ -27,8 +30,8 @@ typedef struct { int dns_port; unsigned int delay_startup; int16_t debug; - unsigned char privacylevel; - unsigned char blockingmode; + enum privacy_level privacylevel; + enum blocking_mode blockingmode; bool socket_listenlocal; bool analyze_AAAA; bool resolveIPv6; @@ -47,7 +50,6 @@ typedef struct { const char* snapConf; char* log; char* pid; - char* port; char* socketfile; char* FTL_db; char* gravity_db; @@ -59,23 +61,4 @@ typedef struct { extern ConfigStruct config; extern FTLFileNamesStruct FTLfiles; -enum { - DEBUG_DATABASE = (1 << 0), /* 00000000 00000001 */ - DEBUG_NETWORKING = (1 << 1), /* 00000000 00000010 */ - DEBUG_LOCKS = (1 << 2), /* 00000000 00000100 */ - DEBUG_QUERIES = (1 << 3), /* 00000000 00001000 */ - DEBUG_FLAGS = (1 << 4), /* 00000000 00010000 */ - DEBUG_SHMEM = (1 << 5), /* 00000000 00100000 */ - DEBUG_GC = (1 << 6), /* 00000000 01000000 */ - DEBUG_ARP = (1 << 7), /* 00000000 10000000 */ - DEBUG_REGEX = (1 << 8), /* 00000001 00000000 */ - DEBUG_API = (1 << 9), /* 00000010 00000000 */ - DEBUG_OVERTIME = (1 << 10), /* 00000100 00000000 */ - DEBUG_EXTBLOCKED = (1 << 11), /* 00001000 00000000 */ - DEBUG_CAPS = (1 << 12), /* 00010000 00000000 */ - DEBUG_DNSMASQ_LINES = (1 << 13), /* 00100000 00000000 */ - DEBUG_VECTORS = (1 << 14), /* 01000000 00000000 */ - DEBUG_RESOLVER = (1 << 15), /* 10000000 00000000 */ -}; - #endif //CONFIG_H diff --git a/src/daemon.c b/src/daemon.c index a8e1bd21..1992ba07 100644 --- a/src/daemon.c +++ b/src/daemon.c @@ -141,3 +141,19 @@ void delay_startup(void) sleep(config.delay_startup); logg("Done sleeping, continuing startup of resolver...\n"); } + +// Is this a fork? +bool __attribute__ ((const)) is_fork(const pid_t mpid, const pid_t pid) +{ + return mpid > -1 && mpid != pid; +} + +pid_t FTL_gettid(void) +{ +#ifdef SYS_gettid + return (pid_t)syscall(SYS_gettid); +#else +#warning SYS_gettid is not available on this system + return -1; +#endif // SYS_gettid +} diff --git a/src/daemon.h b/src/daemon.h index d94f4cf5..cbbe68d0 100644 --- a/src/daemon.h +++ b/src/daemon.h @@ -15,5 +15,22 @@ void savepid(void); char * getUserName(void); void removepid(void); void delay_startup(void); +bool is_fork(const pid_t mpid, const pid_t pid) __attribute__ ((const)); + + +#include +#include +// Get ID of current thread (incorrectly shown as "PID" in, e.g., htop) +// We define this wrapper ourselves as the GNU C Library only added it +// in 2019 meaning that, while we're writing this, it will not be widely +// available. It was only added even later (end of 2019) to musl libc. +// https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=1d0fc213824eaa2a8f8c4385daaa698ee8fb7c92 +// https://www.openwall.com/lists/musl/2019/08/01/11 +// To avoid any conflicts, also in the future, we use our own macro for this +#if !defined(SYS_gettid) && defined(__NR_gettid) +#define SYS_gettid __NR_gettid +#endif // !SYS_gettid && __NR_gettid +pid_t FTL_gettid(void); +#define gettid FTL_gettid #endif //DAEMON_H diff --git a/src/database/common.c b/src/database/common.c index ddffa2e5..d87b7b2a 100644 --- a/src/database/common.c +++ b/src/database/common.c @@ -377,7 +377,7 @@ void db_init(void) logg("Database successfully initialized"); } -int db_get_FTL_property(const unsigned int ID) +int db_get_FTL_property(const enum ftl_table_props ID) { if(!database || FTL_db == NULL) { @@ -400,7 +400,7 @@ int db_get_FTL_property(const unsigned int ID) return value; } -bool db_set_FTL_property(const unsigned int ID, const int value) +bool db_set_FTL_property(const enum ftl_table_props ID, const int value) { if(!database || FTL_db == NULL) { @@ -410,7 +410,7 @@ bool db_set_FTL_property(const unsigned int ID, const int value) return dbquery("INSERT OR REPLACE INTO ftl (id, value) VALUES ( %u, %i );", ID, value) == SQLITE_OK; } -bool db_set_counter(const unsigned int ID, const int value) +bool db_set_counter(const enum counters_table_props ID, const int value) { if(!database || FTL_db == NULL) { diff --git a/src/database/common.h b/src/database/common.h index 64d9d967..22286a10 100644 --- a/src/database/common.h +++ b/src/database/common.h @@ -12,9 +12,22 @@ #include "sqlite3.h" +// Database table "ftl" +enum ftl_table_props { + DB_VERSION, + DB_LASTTIMESTAMP, + DB_FIRSTCOUNTERTIMESTAMP +} __attribute__ ((packed)); + +// Database table "counters" +enum counters_table_props { + DB_TOTALQUERIES, + DB_BLOCKEDQUERIES +} __attribute__ ((packed)); + void db_init(void); -int db_get_FTL_property(const unsigned int ID); -bool db_set_FTL_property(const unsigned int ID, const int value); +int db_get_FTL_property(const enum ftl_table_props ID); +bool db_set_FTL_property(const enum ftl_table_props ID, const int value); /// Execute a formatted SQL query and get the return code int dbquery(const char *format, ...); @@ -26,7 +39,7 @@ int db_query_int(const char*); long get_lastID(void); void SQLite3LogCallback(void *pArg, int iErrCode, const char *zMsg); long int get_max_query_ID(void); -bool db_set_counter(const unsigned int ID, const int value); +bool db_set_counter(const enum counters_table_props ID, const int value); bool db_update_counters(const int total, const int blocked); const char *get_sqlite3_version(void); bool use_database(void) __attribute__ ((pure)); @@ -59,9 +72,4 @@ extern bool DBdeleteoldqueries; }\ } -// Database table "ftl" -enum { DB_VERSION, DB_LASTTIMESTAMP, DB_FIRSTCOUNTERTIMESTAMP }; -// Database table "counters" -enum { DB_TOTALQUERIES, DB_BLOCKEDQUERIES }; - #endif //DATABASE_COMMON_H diff --git a/src/database/gravity-db.c b/src/database/gravity-db.c index e95a40f4..17dcc13d 100644 --- a/src/database/gravity-db.c +++ b/src/database/gravity-db.c @@ -34,39 +34,16 @@ static sqlite3 *gravity_db = NULL; static sqlite3_stmt* table_stmt = NULL; static sqlite3_stmt* auditlist_stmt = NULL; bool gravityDB_opened = false; -static pid_t main_process = 0, this_process = 0; // Table names corresponding to the enum defined in gravity-db.h -static const char* tablename[] = { "vw_gravity", "vw_blacklist", "vw_whitelist", "vw_regex_blacklist", "vw_regex_whitelist" , ""}; +static const char* tablename[] = { "vw_gravity", "vw_blacklist", "vw_whitelist", "vw_regex_blacklist", "vw_regex_whitelist" , "" }; // Prototypes from functions in dnsmasq's source void rehash(int size); // Initialize gravity subroutines -static void gravityDB_check_fork(void) +void gravityDB_forked(void) { - // Memorize main process PID on first call of this funtion (guaranteed to be - // the main dnsmasq thread) - if(main_process == 0) - { - main_process = getpid(); - this_process = main_process; - } - - if(this_process == getpid()) - return; - - // If we reach this point, FTL forked to handle TCP connections with - // dedicated (forked) workers SQLite3's mentions that carrying an open - // database connection across a fork() can lead to all kinds of locking - // problems as SQLite3 was not intended to work under such circumstances. - // Doing so may easily lead to ending up with a corrupted database. - logg("Note: FTL forked to handle TCP requests"); - - // Memorize PID of this thread to avoid re-opening the gravity database - // connection multiple times for the same fork - this_process = getpid(); - // Pretend that we did not open the database so far so it needs to be // re-opened, also pretend we have not yet prepared the list statements gravityDB_opened = false; @@ -546,9 +523,6 @@ void gravityDB_close(void) // blocking domains from a table which is specified when calling this function bool gravityDB_getTable(const unsigned char list) { - // First check if FTL forked to handle TCP connections - gravityDB_check_fork(); - if(!gravityDB_opened && !gravityDB_open()) { logg("gravityDB_getTable(%u): Gravity database not available", list); @@ -637,7 +611,7 @@ void gravityDB_finalizeTable(void) // Get number of domains in a specified table of the gravity database // We return the constant DB_FAILED and log to pihole-FTL.log if we // encounter any error -int gravityDB_count(const unsigned char list) +int gravityDB_count(const enum gravity_tables list) { if(!gravityDB_opened && !gravityDB_open()) { @@ -645,31 +619,37 @@ int gravityDB_count(const unsigned char list) return DB_FAILED; } - // Checking for smaller than GRAVITY_LIST is omitted due to list being unsigned - if(list >= UNKNOWN_TABLE) + const char *querystr = NULL; + // Build query string to be used depending on list to be read + switch (list) { - logg("gravityDB_getTable(%u): Requested list is not known!", list); - return false; - } - - char *querystr = NULL; - // Build correct query string to be used depending on list to be read - if(list != GRAVITY_TABLE && asprintf(&querystr, "SELECT COUNT(DISTINCT domain) FROM %s", tablename[list]) < 18) - { - logg("readGravity(%u) - asprintf() error", list); - return false; - } - // We get the number of unique gravity domains as counted and stored by gravity. Counting the number - // of distinct domains in vw_gravity may take up to several minutes for very large blocking lists on - // very low-end devices such as the Raspierry Pi Zero - else if(list == GRAVITY_TABLE && asprintf(&querystr, "SELECT value FROM info WHERE property = 'gravity_count';") < 18) - { - logg("readGravity(%u) - asprintf() error", list); - return false; + case GRAVITY_TABLE: + // We get the number of unique gravity domains as counted and stored by gravity. Counting the number + // of distinct domains in vw_gravity may take up to several minutes for very large blocking lists on + // very low-end devices such as the Raspierry Pi Zero + querystr = "SELECT value FROM info WHERE property = 'gravity_count';"; + break; + case EXACT_BLACKLIST_TABLE: + querystr = "SELECT COUNT(DISTINCT domain) FROM vw_blacklist"; + break; + case EXACT_WHITELIST_TABLE: + querystr = "SELECT COUNT(DISTINCT domain) FROM vw_whitelist"; + break; + case REGEX_BLACKLIST_TABLE: + querystr = "SELECT COUNT(DISTINCT domain) FROM vw_regex_blacklist"; + break; + case REGEX_WHITELIST_TABLE: + querystr = "SELECT COUNT(DISTINCT domain) FROM vw_regex_whitelist"; + break; + case UNKNOWN_TABLE: + logg("Error: List type %u unknown!", list); + gravityDB_close(); + return DB_FAILED; } if(config.debug & DEBUG_DATABASE) - logg("Querying count of distinct domains in gravity database table %s", tablename[list]); + logg("Querying count of distinct domains in gravity database table %s: %s", + tablename[list], querystr); // Prepare query int rc = sqlite3_prepare_v2(gravity_db, querystr, -1, &table_stmt, NULL); @@ -677,7 +657,6 @@ int gravityDB_count(const unsigned char list) logg("gravityDB_count(%s) - SQL error prepare %s", querystr, sqlite3_errstr(rc)); gravityDB_finalizeTable(); gravityDB_close(); - free(querystr); return DB_FAILED; } @@ -691,7 +670,6 @@ int gravityDB_count(const unsigned char list) } gravityDB_finalizeTable(); gravityDB_close(); - free(querystr); return DB_FAILED; } @@ -701,8 +679,7 @@ int gravityDB_count(const unsigned char list) // Finalize statement gravityDB_finalizeTable(); - // Free allocated memory and return result - free(querystr); + // Return result return result; } @@ -777,9 +754,6 @@ static bool domain_in_list(const char *domain, sqlite3_stmt* stmt, const char* l bool in_whitelist(const char *domain, const int clientID, clientsData* client) { - // First check if FTL forked to handle TCP connections - gravityDB_check_fork(); - // 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 if(whitelist_stmt == NULL) @@ -814,9 +788,6 @@ bool in_whitelist(const char *domain, const int clientID, clientsData* client) bool in_gravity(const char *domain, const int clientID, clientsData* client) { - // First check if FTL forked to handle TCP connections - gravityDB_check_fork(); - // 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 if(gravity_stmt == NULL) @@ -844,9 +815,6 @@ bool in_gravity(const char *domain, const int clientID, clientsData* client) inline bool in_blacklist(const char *domain, const int clientID, clientsData* client) { - // First check if FTL forked to handle TCP connections - gravityDB_check_fork(); - // 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 if(blacklist_stmt == NULL) @@ -874,9 +842,6 @@ inline bool in_blacklist(const char *domain, const int clientID, clientsData* cl bool in_auditlist(const char *domain) { - // First check if FTL forked to handle TCP connections - gravityDB_check_fork(); - // If audit list statement is not ready and cannot be initialized (e.g. no access // to the database), we return false (not in audit list) to prevent an FTL crash if(auditlist_stmt == NULL) @@ -889,9 +854,6 @@ bool in_auditlist(const char *domain) bool gravityDB_get_regex_client_groups(clientsData* client, const int numregex, const int *regexid, const unsigned char type, const char* table, const int clientID) { - // First check if FTL forked to handle TCP connections - gravityDB_check_fork(); - char *querystr = NULL; if(!client->found_group && !get_client_groupids(client)) return false; diff --git a/src/database/gravity-db.h b/src/database/gravity-db.h index 022fc49e..50a078e5 100644 --- a/src/database/gravity-db.h +++ b/src/database/gravity-db.h @@ -16,8 +16,9 @@ #include "datastructure.h" // Table indices -enum { GRAVITY_TABLE, EXACT_BLACKLIST_TABLE, EXACT_WHITELIST_TABLE, REGEX_BLACKLIST_TABLE, REGEX_WHITELIST_TABLE, UNKNOWN_TABLE }; +enum gravity_tables { GRAVITY_TABLE, EXACT_BLACKLIST_TABLE, EXACT_WHITELIST_TABLE, REGEX_BLACKLIST_TABLE, REGEX_WHITELIST_TABLE, UNKNOWN_TABLE } __attribute__ ((packed)); +void gravityDB_forked(void); bool gravityDB_open(void); bool gravityDB_prepare_client_statements(const int clientID, clientsData* client); void gravityDB_close(void); @@ -25,7 +26,7 @@ bool gravityDB_getTable(unsigned char list); const char* gravityDB_getDomain(int *rowid); char* get_group_names(const char *group_ids) __attribute__ ((malloc)); void gravityDB_finalizeTable(void); -int gravityDB_count(unsigned char list); +int gravityDB_count(const enum gravity_tables list); bool in_auditlist(const char *domain); bool in_gravity(const char *domain, const int clientID, clientsData* client); diff --git a/src/database/query-table.c b/src/database/query-table.c index e27c8e50..82e09dee 100644 --- a/src/database/query-table.c +++ b/src/database/query-table.c @@ -46,10 +46,6 @@ int get_number_of_queries_in_DB(void) void DB_save_queries(void) { - // Don't save anything to the database if in PRIVACY_NOSTATS mode - if(config.privacylevel >= PRIVACY_NOSTATS) - return; - // Start database timer if(config.debug & DEBUG_DATABASE) timer_start(DATABASE_WRITE_TIMER); @@ -296,10 +292,6 @@ void delete_old_queries_in_DB(void) // Get most recent 24 hours data from long-term database void DB_read_queries(void) { - // Don't try to load anything to the database if in PRIVACY_NOSTATS mode - if(config.privacylevel >= PRIVACY_NOSTATS) - return; - // Open database file if(!dbopen()) { diff --git a/src/datastructure.h b/src/datastructure.h index ec4a81bb..5493d6e5 100644 --- a/src/datastructure.h +++ b/src/datastructure.h @@ -13,6 +13,9 @@ // Definition of sqlite3_stmt #include "database/sqlite3.h" +// enum privacy_level +#include "enums.h" + void strtolower(char *str); int findUpstreamID(const char * upstream, const bool count); int findDomainID(const char *domain, const bool count); @@ -24,16 +27,13 @@ bool isValidIPv6(const char *addr); void FTL_reload_all_domainlists(void); void FTL_reset_per_client_domain_data(void); -enum { TYPE_A = 1, TYPE_AAAA, TYPE_ANY, TYPE_SRV, TYPE_SOA, TYPE_PTR, TYPE_TXT, TYPE_NAPTR, - TYPE_MX, TYPE_DS, TYPE_RRSIG, TYPE_DNSKEY, TYPE_OTHER, TYPE_MAX }; - typedef struct { unsigned char magic; - unsigned char status; - unsigned char type; - unsigned char privacylevel; - unsigned char reply; - unsigned char dnssec; + enum query_status status; + enum query_types type; + enum privacy_level privacylevel; + enum reply_type reply; + enum dnssec_status dnssec; time_t timestamp; int domainID; int clientID; @@ -79,7 +79,7 @@ typedef struct { typedef struct { unsigned char magic; - unsigned char blocking_status; + enum domain_client_status blocking_status; unsigned char force_reply; int domainID; int clientID; diff --git a/src/dnsmasq/dnsmasq.c b/src/dnsmasq/dnsmasq.c index 179927ed..7379758f 100644 --- a/src/dnsmasq/dnsmasq.c +++ b/src/dnsmasq/dnsmasq.c @@ -1249,7 +1249,7 @@ static void sig_handler(int sig) { /*** Pi-hole modification ***/ // TCP workers ignore all signals except SIGALRM - FTL_TCP_worker_terminating(); + FTL_TCP_worker_terminating(false); /*** Pi-hole modification ***/ _exit(0); } @@ -1957,8 +1957,16 @@ static void check_dns_listeners(time_t now) if ((flags = fcntl(confd, F_GETFL, 0)) != -1) fcntl(confd, F_SETFL, flags & ~O_NONBLOCK); + /******* Pi-hole modification *******/ + FTL_TCP_worker_created(); + /************************************/ + buff = tcp_request(confd, now, &tcp_addr, netmask, auth_dns); + /******* Pi-hole modification *******/ + FTL_TCP_worker_terminating(true); + /************************************/ + shutdown(confd, SHUT_RDWR); close(confd); @@ -1974,10 +1982,6 @@ static void check_dns_listeners(time_t now) if (!option_bool(OPT_DEBUG)) { - /*** Pi-hole modification ***/ - // TCP workers ignore all signals except SIGALRM - FTL_TCP_worker_terminating(); - /****************************/ close(daemon->pipe_to_parent); flush_log(); _exit(0); diff --git a/src/dnsmasq_interface.c b/src/dnsmasq_interface.c index 00b50be6..8779860a 100644 --- a/src/dnsmasq_interface.c +++ b/src/dnsmasq_interface.c @@ -12,6 +12,7 @@ #include "dnsmasq/dnsmasq.h" #undef __USE_XOPEN #include "FTL.h" +#include "enums.h" #include "dnsmasq_interface.h" #include "shmem.h" #include "overTime.h" @@ -302,10 +303,6 @@ static bool _FTL_check_blocking(int queryID, int domainID, int clientID, const c bool _FTL_CNAME(const char *domain, const struct crec *cpp, const int id, const char* file, const int line) { - // Don't analyze anything if in PRIVACY_NOSTATS mode - if(config.privacylevel >= PRIVACY_NOSTATS) - return false; - // Does the user want to skip deep CNAME inspection? if(!config.cname_inspection) { @@ -423,10 +420,6 @@ bool _FTL_new_query(const unsigned int flags, const char *name, { // Create new query in data structure - // Don't analyze anything if in PRIVACY_NOSTATS mode - if(config.privacylevel >= PRIVACY_NOSTATS) - return false; - // Get timestamp const time_t querytimestamp = time(NULL); @@ -692,10 +685,6 @@ void _FTL_forwarded(const unsigned int flags, const char *name, const union all_ { // Save that this query got forwarded to an upstream server - // Don't analyze anything if in PRIVACY_NOSTATS mode - if(config.privacylevel >= PRIVACY_NOSTATS) - return; - // Lock shared memory lock_shm(); @@ -810,7 +799,6 @@ void FTL_dnsmasq_reload(void) { // This function is called by the dnsmasq code on receive of SIGHUP // *before* clearing the cache and rereading the lists - // This is the only hook that is not skipped in PRIVACY_NOSTATS mode logg("Reloading DNS cache"); @@ -844,10 +832,6 @@ void FTL_dnsmasq_reload(void) void _FTL_reply(const unsigned short flags, const char *name, const union all_addr *addr, const int id, const char* file, const int line) { - // Don't analyze anything if in PRIVACY_NOSTATS mode - if(config.privacylevel >= PRIVACY_NOSTATS) - return; - // Lock shared memory lock_shm(); @@ -968,9 +952,9 @@ void _FTL_reply(const unsigned short flags, const char *name, const union all_ad { // Only proceed if query is not already known // to have been blocked by Quad9 - if(query->reply != QUERY_EXTERNAL_BLOCKED_IP && - query->reply != QUERY_EXTERNAL_BLOCKED_NULL && - query->reply != QUERY_EXTERNAL_BLOCKED_NXRA) + if(query->status != QUERY_EXTERNAL_BLOCKED_IP && + query->status != QUERY_EXTERNAL_BLOCKED_NULL && + query->status != QUERY_EXTERNAL_BLOCKED_NXRA) { // Save reply type and update individual reply counters save_reply_type(flags, addr, query, response); @@ -1129,7 +1113,7 @@ static void detect_blocked_IP(const unsigned short flags, const union all_addr * } } -static void query_externally_blocked(const int queryID, const unsigned char status) +static void query_externally_blocked(const int queryID, const enum query_status status) { // Get query pointer queriesData* query = getQuery(queryID, true); @@ -1172,10 +1156,6 @@ void _FTL_cache(const unsigned int flags, const char *name, const union all_addr { // Save that this query got answered from cache - // Don't analyze anything if in PRIVACY_NOSTATS mode - if(config.privacylevel >= PRIVACY_NOSTATS) - return; - // If domain is "pi.hole", we skip this query // We compare case-insensitive here if(strcasecmp(name, "pi.hole") == 0) @@ -1339,10 +1319,6 @@ void _FTL_dnssec(const int status, const int id, const char* file, const int lin { // Process DNSSEC result for a domain - // Don't analyze anything if in PRIVACY_NOSTATS mode - if(config.privacylevel >= PRIVACY_NOSTATS) - return; - // Lock shared memory lock_shm(); @@ -1393,10 +1369,6 @@ void _FTL_upstream_error(const unsigned int rcode, const int id, const char* fil // Queries with error are those where the RCODE // in the DNS header is neither NOERROR nor NXDOMAIN. - // Don't analyze anything if in PRIVACY_NOSTATS mode - if(config.privacylevel >= PRIVACY_NOSTATS) - return; - // Lock shared memory lock_shm(); @@ -1469,10 +1441,6 @@ void _FTL_header_analysis(const unsigned char header4, const unsigned int rcode, { // Analyze DNS header bits - // Don't analyze anything if in PRIVACY_NOSTATS mode - if(config.privacylevel >= PRIVACY_NOSTATS) - return; - // Check if RA bit is unset in DNS header and rcode is NXDOMAIN // If the response code (rcode) is NXDOMAIN, we may be seeing a response from // an externally blocked query. As they are not always accompany a necessary @@ -1649,18 +1617,15 @@ void FTL_fork_and_bind_sockets(struct passwd *ent_pw) // join with the terminated thread pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - // Bind to sockets - bind_sockets(); - // Start TELNET IPv4 thread - if(ipv4telnet && pthread_create( &telnet_listenthreadv4, &attr, telnet_listening_thread_IPv4, NULL ) != 0) + if(pthread_create( &telnet_listenthreadv4, &attr, telnet_listening_thread_IPv4, NULL ) != 0) { logg("Unable to open IPv4 telnet listening thread. Exiting..."); exit(EXIT_FAILURE); } // Start TELNET IPv6 thread - if(ipv6telnet && pthread_create( &telnet_listenthreadv6, &attr, telnet_listening_thread_IPv6, NULL ) != 0) + if(pthread_create( &telnet_listenthreadv6, &attr, telnet_listening_thread_IPv6, NULL ) != 0) { logg("Unable to open IPv6 telnet listening thread. Exiting..."); exit(EXIT_FAILURE); @@ -1735,10 +1700,6 @@ void _FTL_forwarding_failed(const struct server *server, const char* file, const { // Forwarding to upstream server failed - // Don't analyze anything if in PRIVACY_NOSTATS mode - if(config.privacylevel >= PRIVACY_NOSTATS) - return; - // Lock shared memory lock_shm(); @@ -1824,14 +1785,52 @@ static void prepare_blocking_metadata(void) // Called when a (forked) TCP worker is terminated by receiving SIGALRM // We close the dedicated database connection this client had opened // to avoid dangling database locks -void FTL_TCP_worker_terminating(void) +void FTL_TCP_worker_terminating(bool finished) { - if(config.debug & DEBUG_DATABASE) + if(config.debug != 0) { - logg("TCP worker terminating, " - "closing gravity database connection"); + const char *reason = finished ? "client disconnected" : "timeout"; + logg("TCP worker terminating (%s)", reason); + } + + if(main_pid() == getpid()) + { + // If this is not really a fork (e.g. in debug mode), we don't + // actually close gravity here + return; } // Close dedicated database connection of this fork gravityDB_close(); } + +// Called when a (forked) TCP worker is created +// FTL forked to handle TCP connections with dedicated (forked) workers +// SQLite3's mentions that carrying an open database connection across a +// fork() can lead to all kinds of locking problems as SQLite3 was not +// intended to work under such circumstances. Doing so may easily lead +// to ending up with a corrupted database. +void FTL_TCP_worker_created(void) +{ + if(config.debug != 0) + { + // Print this if any debug setting is enabled + logg("TCP worker forked"); + } + + if(main_pid() == getpid()) + { + // If this is not really a fork (e.g. in debug mode), we don't + // actually re-open gravity or close sockets here + return; + } + + // Reopen gravity database handle in this fork as the main process's + // handle isn't valid here + gravityDB_forked(); + + // Children inherit file descriptors from their parents + // We don't need them in the forks, so we clean them up + close_telnet_socket(); + close_unix_socket(false); +} diff --git a/src/dnsmasq_interface.h b/src/dnsmasq_interface.h index ba754e33..be56f209 100644 --- a/src/dnsmasq_interface.h +++ b/src/dnsmasq_interface.h @@ -51,7 +51,8 @@ bool _FTL_CNAME(const char *domain, const struct crec *cpp, const int id, const void FTL_dnsmasq_reload(void); void FTL_fork_and_bind_sockets(struct passwd *ent_pw); -void FTL_TCP_worker_terminating(void); +void FTL_TCP_worker_created(void); +void FTL_TCP_worker_terminating(bool finished); void set_debug_dnsmasq_lines(char enabled); extern char debug_dnsmasq_lines; diff --git a/src/enums.h b/src/enums.h new file mode 100644 index 00000000..854d6d3f --- /dev/null +++ b/src/enums.h @@ -0,0 +1,132 @@ +/* Pi-hole: A black hole for Internet advertisements +* (c) 2020 Pi-hole, LLC (https://pi-hole.net) +* Network-wide ad blocking via your own hardware. +* +* FTL Engine +* Global enums +* +* This file is copyright under the latest version of the EUPL. +* Please see LICENSE file for your rights under this license. */ +#ifndef ENUMS_H +#define ENUMS_H + +enum memory_type { + QUERIES, + UPSTREAMS, + CLIENTS, + DOMAINS, + OVERTIME, + DNS_CACHE +} __attribute__ ((packed)); + +enum dnssec_status { + DNSSEC_UNSPECIFIED, + DNSSEC_SECURE, + DNSSEC_INSECURE, + DNSSEC_BOGUS, + DNSSEC_ABANDONED +} __attribute__ ((packed)); + +enum query_status { + QUERY_UNKNOWN, + QUERY_GRAVITY, + QUERY_FORWARDED, + QUERY_CACHE, + QUERY_REGEX, + QUERY_BLACKLIST, + QUERY_EXTERNAL_BLOCKED_IP, + QUERY_EXTERNAL_BLOCKED_NULL, + QUERY_EXTERNAL_BLOCKED_NXRA, + QUERY_GRAVITY_CNAME, + QUERY_REGEX_CNAME, + QUERY_BLACKLIST_CNAME, + QUERY_STATUS_MAX +} __attribute__ ((packed)); + +enum reply_type { + REPLY_UNKNOWN, + REPLY_NODATA, + REPLY_NXDOMAIN, + REPLY_CNAME, + REPLY_IP, + REPLY_DOMAIN, + REPLY_RRNAME, + REPLY_SERVFAIL, + REPLY_REFUSED, + REPLY_NOTIMP, + REPLY_OTHER + } __attribute__ ((packed)); + +enum privacy_level { + PRIVACY_SHOW_ALL = 0, + PRIVACY_HIDE_DOMAINS, + PRIVACY_HIDE_DOMAINS_CLIENTS, + PRIVACY_MAXIMUM +} __attribute__ ((packed)); + +enum blocking_mode { + MODE_IP, + MODE_NX, + MODE_NULL, + MODE_IP_NODATA_AAAA, + MODE_NODATA +} __attribute__ ((packed)); + +enum regex_id { + REGEX_BLACKLIST, + REGEX_WHITELIST +} __attribute__ ((packed)); + +enum query_types { + TYPE_A = 1, + TYPE_AAAA, + TYPE_ANY, + TYPE_SRV, + TYPE_SOA, + TYPE_PTR, + TYPE_TXT, + TYPE_NAPTR, + TYPE_MX, + TYPE_DS, + TYPE_RRSIG, + TYPE_DNSKEY, + TYPE_OTHER, + TYPE_MAX +} __attribute__ ((packed)); + +enum blocking_status { + BLOCKING_DISABLED, + BLOCKING_ENABLED, + BLOCKING_UNKNOWN +} __attribute__ ((packed)); + +// Blocking status constants used by the dns_cache->blocking_status vector +enum domain_client_status { + UNKNOWN_BLOCKED = 0, + GRAVITY_BLOCKED, + BLACKLIST_BLOCKED, + REGEX_BLOCKED, + WHITELISTED, + NOT_BLOCKED +} __attribute__ ((packed)); + +enum debug_mode { + DEBUG_DATABASE = (1 << 0), /* 00000000 00000001 */ + DEBUG_NETWORKING = (1 << 1), /* 00000000 00000010 */ + DEBUG_LOCKS = (1 << 2), /* 00000000 00000100 */ + DEBUG_QUERIES = (1 << 3), /* 00000000 00001000 */ + DEBUG_FLAGS = (1 << 4), /* 00000000 00010000 */ + DEBUG_SHMEM = (1 << 5), /* 00000000 00100000 */ + DEBUG_GC = (1 << 6), /* 00000000 01000000 */ + DEBUG_ARP = (1 << 7), /* 00000000 10000000 */ + DEBUG_REGEX = (1 << 8), /* 00000001 00000000 */ + DEBUG_API = (1 << 9), /* 00000010 00000000 */ + DEBUG_OVERTIME = (1 << 10), /* 00000100 00000000 */ + DEBUG_EXTBLOCKED = (1 << 11), /* 00001000 00000000 */ + DEBUG_CAPS = (1 << 12), /* 00010000 00000000 */ + DEBUG_DNSMASQ_LINES = (1 << 13), /* 00100000 00000000 */ + DEBUG_VECTORS = (1 << 14), /* 01000000 00000000 */ + DEBUG_RESOLVER = (1 << 15), /* 10000000 00000000 */ +} __attribute__ ((packed)); + +#endif // ENUMS_H \ No newline at end of file diff --git a/src/gc.c b/src/gc.c index d8435ade..59b4c457 100644 --- a/src/gc.c +++ b/src/gc.c @@ -129,6 +129,7 @@ void *GC_thread(void *val) if(client != NULL) client->blockedcount--; break; + case QUERY_STATUS_MAX: // fall through default: /* That cannot happen */ break; @@ -138,27 +139,33 @@ void *GC_thread(void *val) switch(query->reply) { case REPLY_NODATA: // NODATA(-IPv6) - counters->reply_NODATA--; - break; + counters->reply_NODATA--; + break; case REPLY_NXDOMAIN: // NXDOMAIN - counters->reply_NXDOMAIN--; - break; + counters->reply_NXDOMAIN--; + break; case REPLY_CNAME: // - counters->reply_CNAME--; - break; + counters->reply_CNAME--; + break; case REPLY_IP: // valid IP - counters->reply_IP--; - break; + counters->reply_IP--; + break; case REPLY_DOMAIN: // reverse lookup - counters->reply_domain--; - break; + counters->reply_domain--; + break; - default: // Incomplete query or TXT, do nothing - break; + case REPLY_RRNAME: // fall through + case REPLY_SERVFAIL: // fall through + case REPLY_REFUSED: // fall through + case REPLY_NOTIMP: // fall through + case REPLY_OTHER: // fall through + case REPLY_UNKNOWN: // fall through + default: + break; } // Update type counters diff --git a/src/log.c b/src/log.c index 93a8771d..6e79c23c 100644 --- a/src/log.c +++ b/src/log.c @@ -11,6 +11,7 @@ #include "FTL.h" #include "version.h" #include "memory.h" +// is_fork() #include "daemon.h" #include "config.h" #include "log.h" @@ -20,6 +21,8 @@ #include "args.h" // global counters variable #include "shmem.h" +// main_pid() +#include "signals.h" static pthread_mutex_t lock; static FILE *logfile = NULL; @@ -83,12 +86,31 @@ void __attribute__ ((format (gnu_printf, 1, 2))) logg(const char *format, ...) // Get and log PID of current process to avoid ambiguities when more than one // pihole-FTL instance is logging into the same file - const long pid = (long)getpid(); + char idstr[42]; + const int pid = getpid(); // Get the process ID of the calling process + const int mpid = main_pid(); // Get the process ID of the main FTL process + const int tid = gettid(); // Get the thread ID of the callig process + + // There are four cases we have to differentiate here: + if(pid == tid) + if(is_fork(mpid, pid)) + // Fork of the main process + snprintf(idstr, sizeof(idstr)-1, "%i/F%i", pid, mpid); + else + // Main process + snprintf(idstr, sizeof(idstr)-1, "%iM", pid); + else + if(is_fork(mpid, pid)) + // Thread of a fork of the main process + snprintf(idstr, sizeof(idstr)-1, "%i/F%i/T%i", pid, mpid, tid); + else + // Thread of the main process + snprintf(idstr, sizeof(idstr)-1, "%i/T%i", pid, tid); // Print to stdout before writing to file if(!daemonmode) { - printf("[%s %ld] ", timestring, pid); + printf("[%s %s] ", timestring, idstr); va_start(args, format); vprintf(format, args); va_end(args); @@ -101,7 +123,7 @@ void __attribute__ ((format (gnu_printf, 1, 2))) logg(const char *format, ...) // Write to log file if(logfile != NULL) { - fprintf(logfile, "[%s %ld] ", timestring, pid); + fprintf(logfile, "[%s %s] ", timestring, idstr); va_start(args, format); vfprintf(logfile, format, args); va_end(args); diff --git a/src/main.c b/src/main.c index f1430b72..2fa4ea95 100644 --- a/src/main.c +++ b/src/main.c @@ -108,9 +108,9 @@ int main (int argc, char* argv[]) logg("Finished final database update"); } - // Close sockets + // Close sockets and delete Unix socket file handle close_telnet_socket(); - close_unix_socket(); + close_unix_socket(true); // Close gravity database connection gravityDB_close(); diff --git a/src/memory.h b/src/memory.h index b5648f8b..2b2b2e7d 100644 --- a/src/memory.h +++ b/src/memory.h @@ -10,7 +10,8 @@ #ifndef MEMORY_H #define MEMORY_H -void memory_check(const int which); +#include "enums.h" + char *FTLstrdup(const char *src, const char *file, const char *function, const int line) __attribute__((malloc)); void *FTLcalloc(size_t nmemb, size_t size, const char *file, const char *function, const int line) __attribute__((malloc)) __attribute__((alloc_size(1,2))); void *FTLrealloc(void *ptr_in, size_t size, const char *file, const char *function, const int line) __attribute__((alloc_size(2))); diff --git a/src/regex.c b/src/regex.c index cf4b390e..edbe4607 100644 --- a/src/regex.c +++ b/src/regex.c @@ -194,10 +194,10 @@ void allocate_regex_client_enabled(clientsData *client, const int clientID) } } -static void read_regex_table(const unsigned char regexid) +static void read_regex_table(const enum regex_id regexid) { // Get table ID - unsigned char tableID = (regexid == REGEX_BLACKLIST) ? REGEX_BLACKLIST_TABLE : REGEX_WHITELIST_TABLE; + const enum gravity_tables tableID = (regexid == REGEX_BLACKLIST) ? REGEX_BLACKLIST_TABLE : REGEX_WHITELIST_TABLE; // Get number of lines in the regex table counters->num_regex[regexid] = gravityDB_count(tableID); @@ -293,7 +293,7 @@ void read_regex_from_database(void) } // Print message to FTL's log after reloading regex filters - logg("Compiled %i whitelist and %i blacklist regex filters in %.1f msec", + logg("Compiled %i whitelist and %i blacklist regex filters for %i clients in %.1f msec", counters->num_regex[REGEX_WHITELIST], counters->num_regex[REGEX_BLACKLIST], - timer_elapsed_msec(REGEX_TIMER)); + counters->clients, timer_elapsed_msec(REGEX_TIMER)); } diff --git a/src/regex_r.h b/src/regex_r.h index aca18f97..027a9232 100644 --- a/src/regex_r.h +++ b/src/regex_r.h @@ -19,9 +19,4 @@ int match_regex(const char *input, const int clientID, const unsigned char regex void allocate_regex_client_enabled(clientsData *client, const int clientID); void read_regex_from_database(void); -// Blocking status constants used by the domain->clientstatus vector -// We explicitly force UNKNOWN_BLOCKED to zero on all platforms as this is the -// default value set initially with calloc -enum { UNKNOWN_BLOCKED = 0, GRAVITY_BLOCKED, BLACKLIST_BLOCKED, REGEX_BLOCKED, WHITELISTED, NOT_BLOCKED }; - #endif //REGEX_H diff --git a/src/setupVars.c b/src/setupVars.c index 80924ecd..d018a797 100644 --- a/src/setupVars.c +++ b/src/setupVars.c @@ -232,7 +232,7 @@ bool __attribute__((pure)) getSetupVarsBool(const char * input) } // Global variable showing current blocking status -unsigned char blockingstatus = BLOCKING_UNKNOWN; +enum blocking_status blockingstatus = BLOCKING_UNKNOWN; void check_blocking_status(void) { diff --git a/src/setupVars.h b/src/setupVars.h index af3279f6..7588be64 100644 --- a/src/setupVars.h +++ b/src/setupVars.h @@ -22,6 +22,4 @@ void check_blocking_status(void); extern unsigned char blockingstatus; -enum { BLOCKING_DISABLED, BLOCKING_ENABLED, BLOCKING_UNKNOWN }; - #endif //SETUPVARS_H diff --git a/src/shmem.c b/src/shmem.c index 39f8e86d..a1401c8a 100644 --- a/src/shmem.c +++ b/src/shmem.c @@ -608,7 +608,7 @@ static size_t get_optimal_object_size(const size_t objsize, const size_t minsize } } -void memory_check(int which) +void memory_check(const enum memory_type which) { switch(which) { @@ -623,7 +623,7 @@ void memory_check(int which) exit(EXIT_FAILURE); } } - break; + break; case UPSTREAMS: if(counters->upstreams >= counters->upstreams_MAX-1) { @@ -635,7 +635,7 @@ void memory_check(int which) exit(EXIT_FAILURE); } } - break; + break; case CLIENTS: if(counters->clients >= counters->clients_MAX-1) { @@ -647,7 +647,7 @@ void memory_check(int which) exit(EXIT_FAILURE); } } - break; + break; case DOMAINS: if(counters->domains >= counters->domains_MAX-1) { @@ -659,7 +659,7 @@ void memory_check(int which) exit(EXIT_FAILURE); } } - break; + break; case DNS_CACHE: if(counters->dns_cache_size >= counters->dns_cache_MAX-1) { @@ -671,12 +671,13 @@ void memory_check(int which) exit(EXIT_FAILURE); } } - break; + break; + case OVERTIME: // fall through default: /* That cannot happen */ logg("Fatal error in memory_check(%i)", which); exit(EXIT_FAILURE); - break; + break; } } diff --git a/src/shmem.h b/src/shmem.h index e0490212..10554ea6 100644 --- a/src/shmem.h +++ b/src/shmem.h @@ -114,4 +114,6 @@ void reset_per_client_regex(const int clientID); bool get_per_client_regex(const int clientID, const int regexID); void set_per_client_regex(const int clientID, const int regexID, const bool value); +void memory_check(const enum memory_type which); + #endif //SHARED_MEMORY_SERVER_H diff --git a/src/signals.c b/src/signals.c index 6f088d2d..b98718e5 100644 --- a/src/signals.c +++ b/src/signals.c @@ -20,14 +20,24 @@ // FTL_reload_all_domainlists() #include "datastructure.h" #include "config.h" +// gettid() +#include "daemon.h" #define BINARY_NAME "pihole-FTL" volatile sig_atomic_t killed = 0; -static volatile pid_t pid = 0; +static volatile pid_t mpid = -1; static time_t FTLstarttime = 0; extern volatile int exit_code; +// Return the (null-terminated) name of the calling thread +// The name is stored in the buffer as well as returned for convenience +static char * __attribute__ ((nonnull (1))) getthread_name(char buffer[16]) +{ + prctl(PR_GET_NAME, buffer, 0, 0, 0); + return buffer; +} + #if defined(__GLIBC__) static void print_addr2line(const char *symbol, const void *address, const int j, const void *offset) { @@ -57,8 +67,15 @@ static void print_addr2line(const char *symbol, const void *address, const int j // Strip possible newline at the end of the addr2line output if ((pos=strchr(linebuffer, '\n')) != NULL) *pos = '\0'; - logg("L[%04i]: %s", j, linebuffer); } + else + { + snprintf(linebuffer, sizeof(linebuffer), "N/A (%p)", addr); + } + // Log result + logg("L[%04i]: %s", j, linebuffer); + + // Close pipe pclose(addr2line); } #endif @@ -76,17 +93,22 @@ static void __attribute__((noreturn)) SIGSEGV_handler(int sig, siginfo_t *si, vo logg("FTL has been running for %li seconds", time(NULL)-FTLstarttime); } log_FTL_version(true); + char namebuf[16]; + logg("Process details: MID: %i",mpid); + logg(" PID: %i", getpid()); + logg(" TID: %i", gettid()); + logg(" Name: %s", getthread_name(namebuf)); logg("Received signal: %s", strsignal(sig)); logg(" at address: %p", si->si_addr); switch (si->si_code) { - case SEGV_MAPERR: logg(" with code: SEGV_MAPERR (Address not mapped to object)"); break; - case SEGV_ACCERR: logg(" with code: SEGV_ACCERR (Invalid permissions for mapped object)"); break; + case SEGV_MAPERR: logg(" with code: SEGV_MAPERR (Address not mapped to object)"); break; + case SEGV_ACCERR: logg(" with code: SEGV_ACCERR (Invalid permissions for mapped object)"); break; #if defined(SEGV_BNDERR) - case SEGV_BNDERR: logg(" with code: SEGV_BNDERR (Failed address bound checks)"); break; + case SEGV_BNDERR: logg(" with code: SEGV_BNDERR (Failed address bound checks)"); break; #endif - default: logg(" with code: Unknown (%i)", si->si_code); break; + default: logg(" with code: Unknown (%i)", si->si_code); break; } // Check GLIBC availability as MUSL does not support live backtrace generation @@ -114,7 +136,7 @@ static void __attribute__((noreturn)) SIGSEGV_handler(int sig, siginfo_t *si, vo for(int j = 0; j < calls; j++) { - logg("B[%04i]: %p, %s", j, buffer[j], + logg("B[%04i]: %s", j, bcktrace != NULL ? bcktrace[j] : "---"); if(bcktrace != NULL) @@ -132,11 +154,11 @@ static void __attribute__((noreturn)) SIGSEGV_handler(int sig, siginfo_t *si, vo logg("Thank you for helping us to improve our FTL engine!"); // Terminate main process if crash happened in a TCP worker - if(pid != getpid()) + if(mpid != getpid()) { // This is a forked process - logg("Asking parent pihole-FTL (PID %i) to shut down", (int)pid); - kill(pid, SIGRTMIN+2); + logg("Asking parent pihole-FTL (PID %i) to shut down", (int)mpid); + kill(mpid, SIGRTMIN+2); logg("FTL fork terminated!"); } else @@ -152,7 +174,7 @@ static void __attribute__((noreturn)) SIGSEGV_handler(int sig, siginfo_t *si, vo static void SIGRT_handler(int signum, siginfo_t *si, void *unused) { // Ignore real-time signals outside of the main process (TCP forks) - if(pid != getpid()) + if(mpid != getpid()) return; int rtsig = signum - SIGRTMIN; @@ -207,7 +229,7 @@ void handle_realtime_signals(void) { // This function is only called once (after forking), store the PID of // the main process - pid = getpid(); + mpid = getpid(); // Catch first five real-time signals for(unsigned int i = 0; i < 5; i++) @@ -220,3 +242,9 @@ void handle_realtime_signals(void) sigaction(SIGRTMIN + i, &SIGACTION, NULL); } } + +// Return PID of the main FTL process +pid_t main_pid(void) +{ + return mpid; +} \ No newline at end of file diff --git a/src/signals.h b/src/signals.h index 7509b446..cebfd26a 100644 --- a/src/signals.h +++ b/src/signals.h @@ -12,6 +12,7 @@ void handle_SIGSEGV(void); void handle_realtime_signals(void); +pid_t main_pid(void); extern volatile sig_atomic_t killed; diff --git a/src/timers.c b/src/timers.c index 58ce51d1..72c1b7ab 100644 --- a/src/timers.c +++ b/src/timers.c @@ -15,7 +15,7 @@ struct timeval t0[NUMTIMERS]; -void timer_start(const int i) +void timer_start(enum timers i) { if(i >= NUMTIMERS) { @@ -25,7 +25,7 @@ void timer_start(const int i) gettimeofday(&t0[i], 0); } -double timer_elapsed_msec(const int i) +double timer_elapsed_msec(enum timers i) { if(i >= NUMTIMERS) { diff --git a/src/timers.h b/src/timers.h index e30f9878..390d490c 100644 --- a/src/timers.h +++ b/src/timers.h @@ -11,12 +11,20 @@ #define TIMERS_H // Timer enumeration -enum { DATABASE_WRITE_TIMER, EXIT_TIMER, GC_TIMER, LISTS_TIMER, REGEX_TIMER, ARP_TIMER, LAST_TIMER }; +enum timers { + DATABASE_WRITE_TIMER, + EXIT_TIMER, + GC_TIMER, + LISTS_TIMER, + REGEX_TIMER, + ARP_TIMER, + LAST_TIMER + } __attribute__ ((packed)); #define NUMTIMERS LAST_TIMER -void timer_start(const int i); -double timer_elapsed_msec(const int i); +void timer_start(const enum timers i); +double timer_elapsed_msec(const enum timers i); void sleepms(const int milliseconds); #endif //TIMERS_H