diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 462ca24ad..e2d6facc8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -143,6 +143,7 @@ set(sources shmem.h signals.c signals.h + static_assert.h timers.c timers.h vector.c diff --git a/src/api/api.c b/src/api/api.c index 3874f4421..a86442aeb 100644 --- a/src/api/api.c +++ b/src/api/api.c @@ -397,7 +397,7 @@ void getTopClients(const char *client_message, const int *sock) // Get client pointer const clientsData* client = getClient(clientID, true); // Skip invalid clients and also those managed by alias clients - if(client == NULL || (!client->aliasclient && client->aliasclient_id >= 0)) + if(client == NULL || (!client->flags.aliasclient && client->aliasclient_id >= 0)) { temparray[clientID][0] = -1; continue; @@ -823,7 +823,7 @@ void getAllQueries(const char *client_message, const int *sock) clientid = i; // Is this a alias-client? - if(client->aliasclient) + if(client->flags.aliasclient) clientid_list = get_aliasclient_list(i); break; @@ -891,19 +891,12 @@ void getAllQueries(const char *client_message, const int *sock) if(query->status == QUERY_UNKNOWN && !(showpermitted && showblocked)) continue; - // 1 = gravity.list, 4 = wildcard, 5 = black.list - if((query->status == QUERY_GRAVITY || - query->status == QUERY_REGEX || - query->status == QUERY_BLACKLIST || - query->status == QUERY_GRAVITY_CNAME || - query->status == QUERY_REGEX_CNAME || - query->status == QUERY_BLACKLIST_CNAME) && !showblocked) + // Skip blocked queries when asked to + if(query->flags.blocked && !showblocked) continue; - // 2 = forwarded, 3 = cached - if((query->status == QUERY_FORWARDED || - query->status == QUERY_CACHE || - query->status == QUERY_RETRIED || - query->status == QUERY_RETRIED_DNSSEC) && !showpermitted) + + // Skip permitted queries when asked to + if(!query->flags.blocked && !showpermitted) continue; // Skip those entries which so not meet the requested timeframe @@ -921,10 +914,7 @@ void getAllQueries(const char *client_message, const int *sock) // If the domain of this query did not match, the CNAME // domain may still match - we have to check it in // addition if this query is of CNAME blocked type - else if((query->status == QUERY_GRAVITY_CNAME || - query->status == QUERY_BLACKLIST_CNAME || - query->status == QUERY_REGEX_CNAME) && - query->CNAME_domainID == domainid) + else if(query->CNAME_domainID > -1) { // Get this query } @@ -959,13 +949,8 @@ void getAllQueries(const char *client_message, const int *sock) if(filterforwarddest) { - // Does the user want to see queries answered from blocking lists? - if(forwarddestid == -2 && query->status != QUERY_GRAVITY - && query->status != QUERY_REGEX - && query->status != QUERY_BLACKLIST - && query->status != QUERY_GRAVITY_CNAME - && query->status != QUERY_REGEX_CNAME - && query->status != QUERY_BLACKLIST_CNAME) + // Skip if not from the virtual blocking "upstream" server + if(forwarddestid == -2 && !query->flags.blocked) continue; // Does the user want to see queries answered from local cache? else if(forwarddestid == -1 && query->status != QUERY_CACHE) @@ -1017,7 +1002,7 @@ void getAllQueries(const char *client_message, const int *sock) // Get IP of upstream destination, if applicable in_port_t upstream_port = 0; const char *upstream_name = "N/A"; - if(query->status == QUERY_FORWARDED) + if(query->upstreamID > -1) { const upstreamsData *upstream = getUpstream(query->upstreamID, true); if(upstream != NULL) @@ -1104,15 +1089,8 @@ void getRecentBlocked(const char *client_message, const int *sock) if(query == NULL) continue; - if(query->status == QUERY_GRAVITY || - query->status == QUERY_REGEX || - query->status == QUERY_BLACKLIST || - query->status == QUERY_GRAVITY_CNAME || - query->status == QUERY_REGEX_CNAME || - query->status == QUERY_BLACKLIST_CNAME) + if(query->flags.blocked) { - found++; - // Ask subroutine for domain. It may return "hidden" depending on // the privacy settings at the time the query was made const char *domain = getDomainString(query); @@ -1123,6 +1101,9 @@ void getRecentBlocked(const char *client_message, const int *sock) ssend(*sock,"%s\n", domain); else if(!pack_str32(*sock, domain)) return; + + // Only count when sent succesfully + found++; } if(found >= num) @@ -1312,7 +1293,7 @@ void getClientsOverTime(const int *sock) // Check if this client should be skipped if(insetupVarsArray(getstr(client->ippos)) || insetupVarsArray(getstr(client->namepos)) || - (!client->aliasclient && client->aliasclient_id > -1)) + (!client->flags.aliasclient && client->aliasclient_id > -1)) skipclient[clientID] = true; } } @@ -1387,7 +1368,7 @@ void getClientNames(const int *sock) // Check if this client should be skipped if(insetupVarsArray(getstr(client->ippos)) || insetupVarsArray(getstr(client->namepos)) || - (!client->aliasclient && client->aliasclient_id > -1)) + (!client->flags.aliasclient && client->aliasclient_id > -1)) skipclient[clientID] = true; } } @@ -1434,7 +1415,7 @@ void getUnknownQueries(const int *sock) const queriesData* query = getQuery(queryID, true); if(query == NULL || - (query->status != QUERY_UNKNOWN && query->complete)) + (query->status != QUERY_UNKNOWN && query->flags.complete)) continue; char type[5]; @@ -1459,7 +1440,7 @@ void getUnknownQueries(const int *sock) const char *clientIP = getstr(client->ippos); if(istelnet[*sock]) - ssend(*sock, "%lli %i %i %s %s %s %i %s\n", (long long)query->timestamp, queryID, query->id, type, getstr(domain->domainpos), clientIP, query->status, query->complete ? "true" : "false"); + ssend(*sock, "%lli %i %i %s %s %s %i %s\n", (long long)query->timestamp, queryID, query->id, type, getstr(domain->domainpos), clientIP, query->status, query->flags.complete ? "true" : "false"); else { pack_int32(*sock, (int32_t)query->timestamp); pack_int32(*sock, query->id); @@ -1473,7 +1454,7 @@ void getUnknownQueries(const int *sock) return; pack_uint8(*sock, query->status); - pack_bool(*sock, query->complete); + pack_bool(*sock, query->flags.complete); } } } diff --git a/src/config.h b/src/config.h index 36c297146..fb767dede 100644 --- a/src/config.h +++ b/src/config.h @@ -17,6 +17,8 @@ #include // typedef uni32_t #include +// assert_sizeof +#include "static_assert.h" void getLogFilePath(void); void read_FTLconf(void); @@ -24,17 +26,11 @@ void get_privacy_level(FILE *fp); void get_blocking_mode(FILE *fp); void read_debuging_settings(FILE *fp); +// We do not use bitfields in here as this struct exists only once in memory. +// Accessing bitfields may produce slightly more inefficient code on some +// architectures (such as ARM) and savng a few bit of RAM but bloating up the +// rest of the application each time these fields are accessed is bad. typedef struct { - int maxDBdays; - int port; - int maxlogage; - int dns_port; - unsigned int delay_startup; - enum debug_flags debug; - unsigned int network_expire; - enum privacy_level privacylevel; - enum blocking_mode blockingmode; - enum refresh_hostnames refresh_hostnames; bool socket_listenlocal; bool analyze_AAAA; bool resolveIPv6; @@ -48,8 +44,19 @@ typedef struct { bool block_esni; bool names_from_netdb; bool edns0_ecs; + enum privacy_level privacylevel; + enum blocking_mode blockingmode; + enum refresh_hostnames refresh_hostnames; + int maxDBdays; + int port; + int maxlogage; + int dns_port; + unsigned int delay_startup; + unsigned int network_expire; + enum debug_flags debug; time_t DBinterval; } ConfigStruct; +ASSERT_SIZEOF(ConfigStruct, 56, 48, 48); typedef struct { const char* conf; diff --git a/src/database/aliasclients.c b/src/database/aliasclients.c index 023c402a6..43b9308b2 100644 --- a/src/database/aliasclients.c +++ b/src/database/aliasclients.c @@ -63,7 +63,7 @@ static void recompute_aliasclient(const int aliasclientID) // Get pointer to client candidate const clientsData *client = getClient(clientID, true); // Skip invalid clients and alias-clients - if(client == NULL || client->aliasclient) + if(client == NULL || client->flags.aliasclient) continue; // Skip clients that are not managed by this aliasclient @@ -131,7 +131,7 @@ bool import_aliasclients(void) const int clientID = findClientID(aliasclient_str, false, true); clientsData *client = getClient(clientID, true); - client->new = false; + client->flags.new = false; // Reset counter client->count = 0; @@ -141,7 +141,7 @@ bool import_aliasclients(void) client->namepos = addstr(name); // This is a aliasclient - client->aliasclient = true; + client->flags.aliasclient = true; client->aliasclient_id = aliasclient_id; // Debug logging @@ -169,7 +169,7 @@ bool import_aliasclients(void) static int get_aliasclient_ID(const clientsData *client) { // Skip alias-clients themselves - if(client->aliasclient) + if(client->flags.aliasclient) return -1; const char *clientIP = getstr(client->ippos); @@ -190,7 +190,7 @@ static int get_aliasclient_ID(const clientsData *client) const clientsData *alias_client = getClient(aliasclientID, true); // Skip clients that are not alias-clients - if(!alias_client->aliasclient) + if(!alias_client->flags.aliasclient) continue; // Compare MAC address of the current client to the @@ -220,7 +220,7 @@ static int get_aliasclient_ID(const clientsData *client) void reset_aliasclient(clientsData *client) { // Skip alias-clients themselves - if(client->aliasclient) + if(client->flags.aliasclient) return; // Find corresponding alias-client (if any) @@ -288,7 +288,7 @@ void reimport_aliasclients(void) // Get pointer to client candidate clientsData *client = getClient(clientID, true); // Skip invalid and non-alias-clients - if(client == NULL || !client->aliasclient) + if(client == NULL || !client->flags.aliasclient) continue; // Reset this alias-client @@ -309,7 +309,7 @@ void reimport_aliasclients(void) // Get pointer to client candidate clientsData *client = getClient(clientID, true); // Skip invalid and alias-clients - if(client == NULL || client->aliasclient) + if(client == NULL || client->flags.aliasclient) continue; reset_aliasclient(client); diff --git a/src/database/gravity-db.c b/src/database/gravity-db.c index 633abc0f9..c067d1dc1 100644 --- a/src/database/gravity-db.c +++ b/src/database/gravity-db.c @@ -30,7 +30,7 @@ // reset_aliasclient() #include "aliasclients.h" -// Definition of struct regex_data +// Definition of struct regexData #include "../regex_r.h" // Prefix of interface names in the client table @@ -219,7 +219,7 @@ static inline const char *show_client_string(const char *hwaddr, const char *hos static bool get_client_groupids(clientsData* client) { const char *ip = getstr(client->ippos); - client->found_group = false; + client->flags.found_group = false; client->groupspos = 0u; // Do not proceed when database is not available @@ -619,7 +619,7 @@ static bool get_client_groupids(clientsData* client) show_client_string(hwaddr, hostname, ip)); client->groupspos = addstr("0"); - client->found_group = true; + client->flags.found_group = true; if(hwaddr != NULL) { @@ -682,7 +682,7 @@ static bool get_client_groupids(clientsData* client) if(result != NULL) { client->groupspos = addstr(result); - client->found_group = true; + client->flags.found_group = true; } } else if(rc == SQLITE_DONE) @@ -690,7 +690,7 @@ static bool get_client_groupids(clientsData* client) // Found no record for this client in the database // -> No associated groups client->groupspos = addstr(""); - client->found_group = true; + client->flags.found_group = true; } else { @@ -805,7 +805,7 @@ bool gravityDB_prepare_client_statements(clientsData *client) // Get associated groups for this client (if defined) char *querystr = NULL; - if(!client->found_group && !get_client_groupids(client)) + if(!client->flags.found_group && !get_client_groupids(client)) return false; // Prepare whitelist statement @@ -888,7 +888,7 @@ static inline void gravityDB_finalize_client_statements(clientsData *client) // client sends a query if(client != NULL) { - client->found_group = false; + client->flags.found_group = false; } } @@ -1304,14 +1304,14 @@ bool in_auditlist(const char *domain) return domain_in_list(domain, auditlist_stmt, "auditlist"); } -bool gravityDB_get_regex_client_groups(clientsData* client, const unsigned int numregex, const regex_data *regex, +bool gravityDB_get_regex_client_groups(clientsData* client, const unsigned int numregex, const regexData *regex, const unsigned char type, const char* table) { if(config.debug & DEBUG_REGEX) logg("Getting regex client groups for client with ID %i", client->id); char *querystr = NULL; - if(!client->found_group && !get_client_groupids(client)) + if(!client->flags.found_group && !get_client_groupids(client)) return false; // Group filtering diff --git a/src/database/gravity-db.h b/src/database/gravity-db.h index 5aa379473..5b8198fca 100644 --- a/src/database/gravity-db.h +++ b/src/database/gravity-db.h @@ -12,7 +12,7 @@ // clientsData #include "../datastructure.h" -// regex_data +// regexData #include "../regex_r.h" // Table indices @@ -35,7 +35,7 @@ bool in_gravity(const char *domain, clientsData* client); bool in_blacklist(const char *domain, clientsData* client); bool in_whitelist(const char *domain, const DNSCacheData *dns_cache, clientsData* client); -bool gravityDB_get_regex_client_groups(clientsData* client, const unsigned int numregex, const regex_data *regex, +bool gravityDB_get_regex_client_groups(clientsData* client, const unsigned int numregex, const regexData *regex, const unsigned char type, const char* table); #endif //GRAVITY_H diff --git a/src/database/network-table.c b/src/database/network-table.c index 40c9f9ab9..458db2b11 100644 --- a/src/database/network-table.c +++ b/src/database/network-table.c @@ -689,7 +689,7 @@ static bool add_FTL_clients_to_network_table(enum arp_status *client_status, tim } // Silently skip alias-clients - they do not really exist - if(client->aliasclient) + if(client->flags.aliasclient) continue; // Get hostname and IP address of this client diff --git a/src/database/query-table.c b/src/database/query-table.c index 169ccdad2..c15cf23c7 100644 --- a/src/database/query-table.c +++ b/src/database/query-table.c @@ -107,7 +107,7 @@ void DB_save_queries(void) continue; } - if(!query->complete && query->timestamp > currenttimestamp-2) + if(!query->flags.complete && query->timestamp > currenttimestamp-2) { // Break if a brand new query (age < 2 seconds) is not yet completed // giving it a chance to be stored next time @@ -148,7 +148,7 @@ void DB_save_queries(void) sqlite3_bind_text(stmt, 5, client, -1, SQLITE_STATIC); // FORWARD - if(query->status == QUERY_FORWARDED && query->upstreamID > -1) + if(query->upstreamID > -1) { // Get forward pointer const upstreamsData* upstream = getUpstream(query->upstreamID, true); @@ -209,15 +209,7 @@ void DB_save_queries(void) // Total counter information (delta computation) total++; - if(query->status == QUERY_GRAVITY || - query->status == QUERY_BLACKLIST || - query->status == QUERY_REGEX || - query->status == QUERY_EXTERNAL_BLOCKED_IP || - query->status == QUERY_EXTERNAL_BLOCKED_NULL || - query->status == QUERY_EXTERNAL_BLOCKED_NXRA || - query->status == QUERY_GRAVITY_CNAME || - query->status == QUERY_REGEX_CNAME || - query->status == QUERY_BLACKLIST_CNAME) + if(query->flags.blocked) blocked++; // Update lasttimestamp variable with timestamp of the latest stored query @@ -395,19 +387,13 @@ void DB_read_queries(void) continue; } - const char *upstream = (const char *)sqlite3_column_text(stmt, 6); - int upstreamID = 0; + const char *upstream = NULL; + int upstreamID = -1; // Default if not forwarded // Determine upstreamID only when status == 2 (forwarded) as the // field need not to be filled for other query status types - if(status == QUERY_FORWARDED) + if(sqlite3_column_bytes(stmt, 6) > 0 && + (upstream = (const char *)sqlite3_column_text(stmt, 6)) != NULL) { - if(upstream == NULL) - { - logg("WARN (during database import): FORWARD should not be NULL with status QUERY_FORWARDED (timestamp: %lli), skipping entry", - (long long)queryTimeStamp); - continue; - } - // Get IP address and port of upstream destination char serv_addr[INET6_ADDRSTRLEN] = { 0 }; unsigned int serv_port = 53; @@ -453,11 +439,14 @@ void DB_read_queries(void) query->timeidx = timeidx; query->db = dbid; query->id = 0; - query->complete = true; // Mark as all information is available query->response = 0; query->dnssec = DNSSEC_UNSPECIFIED; query->reply = REPLY_UNKNOWN; query->CNAME_domainID = -1; + // Initialize flags + query->flags.complete = true; // Mark as all information is available + query->flags.blocked = false; + query->flags.whitelisted = false; // Set lastQuery timer for network table clientsData* client = getClient(clientID, true); @@ -483,7 +472,7 @@ void DB_read_queries(void) status == QUERY_REGEX_CNAME || status == QUERY_BLACKLIST_CNAME) { - // QUERY_*_CNAME: Getdomain causing the blocking + // QUERY_*_CNAME: Get domain causing the blocking const char *CNAMEdomain = (const char *)sqlite3_column_text(stmt, 7); if(CNAMEdomain != NULL && strlen(CNAMEdomain) > 0) { @@ -523,6 +512,7 @@ void DB_read_queries(void) case QUERY_REGEX_CNAME: // Blocked by regex blacklist (inside CNAME path) case QUERY_BLACKLIST_CNAME: // Blocked by exact blacklist (inside CNAME path) counters->blocked++; + query->flags.blocked = true; // Get domain pointer domainsData* domain = getDomain(domainID, true); domain->blockedcount++; diff --git a/src/datastructure.c b/src/datastructure.c index c800fa268..cd0093738 100644 --- a/src/datastructure.c +++ b/src/datastructure.c @@ -245,14 +245,14 @@ int findClientID(const char *clientIP, const bool count, const bool aliasclient) // Due to the nature of us being the resolver, // the actual resolving of the host name has // to be done separately to be non-blocking - client->new = true; + client->flags.new = true; client->namepos = 0; set_event(RESOLVE_NEW_HOSTNAMES); // No query seen so far client->lastQuery = 0; client->numQueriesARP = client->count; // Configured groups are yet unknown - client->found_group = false; + client->flags.found_group = false; client->groupspos = 0u; // Store time this client was added, we re-read group settings // some time after adding a client to ensure we pick up possible @@ -265,7 +265,7 @@ int findClientID(const char *clientIP, const bool count, const bool aliasclient) client->hwlen = -1; memset(client->hwaddr, 0, sizeof(client->hwaddr)); // This may be a alias-client, the ID is set elsewhere - client->aliasclient = aliasclient; + client->flags.aliasclient = aliasclient; client->aliasclient_id = -1; // Initialize client-specific overTime data @@ -303,7 +303,7 @@ void change_clientcount(clientsData *client, int total, int blocked, int overTim client->overTime[overTimeIdx] += overTimeMod; // Also add counts to the conencted alias-client (if any) - if(client->aliasclient) + if(client->flags.aliasclient) { logg("WARN: Should not add to alias-client directly (client \"%s\" (%s))!", getstr(client->namepos), getstr(client->ippos)); diff --git a/src/datastructure.h b/src/datastructure.h index c48a4a2f8..e8e77f68a 100644 --- a/src/datastructure.h +++ b/src/datastructure.h @@ -15,6 +15,8 @@ // enum privacy_level #include "enums.h" +// assert_sizeof +#include "static_assert.h" extern const char *querytypes[TYPE_MAX]; @@ -35,35 +37,50 @@ typedef struct { unsigned long response; // saved in units of 1/10 milliseconds (1 = 0.1ms, 2 = 0.2ms, 2500 = 250.0ms, etc.) time_t timestamp; int64_t db; - bool whitelisted; - bool complete; + // Adjacent bit field members in the struct flags may be packed to share + // and straddle the individual bytes. It is useful to pack the memory as + // tightly as possible as there may be dozens of thousands of these + // objects in memory (one per query). + // C99 guarentees that bit-fields will be packed as tightly as possible, + // provided they don’t cross storageau unit boundaries (6.7.2.1 #10). + struct query_flags { + bool whitelisted :1; + bool complete :1; + bool blocked :1; + } flags; } queriesData; +// ARM needs extra padding at the end +ASSERT_SIZEOF(queriesData, 64, 52, 56); + typedef struct { unsigned char magic; bool new; - in_addr_t port; int count; int failed; + in_addr_t port; size_t ippos; size_t namepos; time_t lastQuery; } upstreamsData; +ASSERT_SIZEOF(upstreamsData, 40, 28, 28); typedef struct { unsigned char magic; unsigned char reread_groups; char hwlen; unsigned char hwaddr[16]; // See DHCP_CHADDR_MAX in dnsmasq/dhcp-protocol.h - bool new; - bool found_group; - bool aliasclient; + struct client_flags { + bool new:1; + bool found_group:1; + bool aliasclient:1; + } flags; int count; int blockedcount; int aliasclient_id; - int overTime[OVERTIME_SLOTS]; unsigned int id; unsigned int numQueriesARP; + int overTime[OVERTIME_SLOTS]; size_t groupspos; size_t ippos; size_t namepos; @@ -71,13 +88,15 @@ typedef struct { time_t lastQuery; time_t firstSeen; } clientsData; +ASSERT_SIZEOF(clientsData, 688, 664, 664); typedef struct { unsigned char magic; - size_t domainpos; int count; int blockedcount; + size_t domainpos; } domainsData; +ASSERT_SIZEOF(domainsData, 24, 16, 16); typedef struct { unsigned char magic; @@ -88,6 +107,7 @@ typedef struct { int clientID; int black_regex_idx; } DNSCacheData; +ASSERT_SIZEOF(DNSCacheData, 16, 16, 16); void strtolower(char *str); int findQueryID(const int id); diff --git a/src/dnsmasq/forward.c b/src/dnsmasq/forward.c index 782963af2..33646911d 100644 --- a/src/dnsmasq/forward.c +++ b/src/dnsmasq/forward.c @@ -1541,7 +1541,7 @@ void receive_query(struct listener *listen, time_t now) #endif //********************** Pi-hole modification **********************// - struct edns_data edns = { 0 }; + ednsData edns = { 0 }; if (find_pseudoheader(header, (size_t)n, NULL, &pheader, NULL, NULL)) FTL_parse_pseudoheaders(header, n, &source_addr, &edns); //******************************************************************// @@ -1945,7 +1945,7 @@ unsigned char *tcp_request(int confd, time_t now, no_cache_dnssec = 1; //********************** Pi-hole modification **********************// - struct edns_data edns = { 0 }; + ednsData edns = { 0 }; if (find_pseudoheader(header, (size_t)size, NULL, &pheader, NULL, NULL)) FTL_parse_pseudoheaders(header, size, &peer_addr, &edns); //******************************************************************// diff --git a/src/dnsmasq_interface.c b/src/dnsmasq_interface.c index d8a62d94e..f4faac65b 100644 --- a/src/dnsmasq_interface.c +++ b/src/dnsmasq_interface.c @@ -104,7 +104,7 @@ static bool check_domain_blocked(const char *domain, const int clientID, // Check domains against gravity domains // Skipped when the domain is whitelisted or blocked by exact blacklist - if(!query->whitelisted && !blockDomain && + if(!query->flags.whitelisted && !blockDomain && in_gravity(domain, client)) { // We block this domain @@ -120,7 +120,7 @@ static bool check_domain_blocked(const char *domain, const int clientID, // Check domain against blacklist regex filters // Skipped when the domain is whitelisted or blocked by exact blacklist or gravity int regex_idx = 0; - if(!query->whitelisted && !blockDomain && + if(!query->flags.whitelisted && !blockDomain && (regex_idx = match_regex(domain, dns_cache, client->id, REGEX_BLACKLIST, false)) > -1) { // We block this domain @@ -188,7 +188,7 @@ static bool _FTL_check_blocking(int queryID, int domainID, int clientID, const c // Do not block if the entire query is to be permitted // as something along the CNAME path hit the whitelist - if(!query->whitelisted) + if(!query->flags.whitelisted) { query_blocked(query, domain, client, QUERY_BLACKLIST); force_next_DNS_reply = dns_cache->force_reply; @@ -208,7 +208,7 @@ static bool _FTL_check_blocking(int queryID, int domainID, int clientID, const c // Do not block if the entire query is to be permitted // as sometving along the CNAME path hit the whitelist - if(!query->whitelisted) + if(!query->flags.whitelisted) { query_blocked(query, domain, client, QUERY_GRAVITY); force_next_DNS_reply = dns_cache->force_reply; @@ -229,7 +229,7 @@ static bool _FTL_check_blocking(int queryID, int domainID, int clientID, const c // Do not block if the entire query is to be permitted // as sometving along the CNAME path hit the whitelist - if(!query->whitelisted) + if(!query->flags.whitelisted) { query_blocked(query, domain, client, QUERY_REGEX); return true; @@ -245,7 +245,7 @@ static bool _FTL_check_blocking(int queryID, int domainID, int clientID, const c logg("%s is known as not to be blocked (whitelisted)", domainstr); } - query->whitelisted = true; + query->flags.whitelisted = true; return false; break; @@ -264,7 +264,7 @@ static bool _FTL_check_blocking(int queryID, int domainID, int clientID, const c } // Skip all checks and continue if we hit already at least one whitelist in the chain - if(query->whitelisted) + if(query->flags.whitelisted) { if(config.debug & DEBUG_QUERIES) { @@ -280,19 +280,19 @@ static bool _FTL_check_blocking(int queryID, int domainID, int clientID, const c const char *blockedDomain = domainstr; // Check whitelist (exact + regex) for match - query->whitelisted = in_whitelist(domainstr, dns_cache, client); + query->flags.whitelisted = in_whitelist(domainstr, dns_cache, client); bool blockDomain = false; unsigned char new_status = QUERY_UNKNOWN; // Check blacklist (exact + regex) and gravity for queried domain - if(!query->whitelisted) + if(!query->flags.whitelisted) { blockDomain = check_domain_blocked(domainstr, clientID, client, query, dns_cache, blockingreason, &new_status); } // Check blacklist (exact + regex) and gravity for _esni.domain if enabled (defaulting to true) - if(config.block_esni && !query->whitelisted && !blockDomain && strncasecmp(domainstr, "_esni.", 6u) == 0) + if(config.block_esni && !query->flags.whitelisted && !blockDomain && strncasecmp(domainstr, "_esni.", 6u) == 0) { blockDomain = check_domain_blocked(domainstr + 6u, clientID, client, query, dns_cache, blockingreason, &new_status); @@ -322,7 +322,7 @@ static bool _FTL_check_blocking(int queryID, int domainID, int clientID, const c // gravity/blacklist chain when the same client asks // for the same domain in the future. Explicitly store // domain as whitelisted if this is the case - dns_cache->blocking_status = query->whitelisted ? WHITELISTED : NOT_BLOCKED; + dns_cache->blocking_status = query->flags.whitelisted ? WHITELISTED : NOT_BLOCKED; } free(domainstr); @@ -458,7 +458,7 @@ 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 struct edns_data *edns, const enum protocol proto, + const ednsData *edns, const enum protocol proto, const char* file, const int line) { // Create new query in data structure @@ -634,13 +634,19 @@ bool _FTL_new_query(const unsigned int flags, const char *name, // Initialize database rowID with zero, will be set when the query is stored in the long-term DB query->db = 0; query->id = id; - query->complete = false; + query->flags.complete = false; query->response = converttimeval(request); // Initialize reply type query->reply = REPLY_UNKNOWN; // Store DNSSEC result for this domain query->dnssec = DNSSEC_UNSPECIFIED; query->CNAME_domainID = -1; + // This query is not yet known ad forwarded or blocked + query->flags.blocked = false; + query->flags.whitelisted = false; + + // Indicator that this query was not forwarded so far + query->upstreamID = -1; // Check and apply possible privacy level rules // The currently set privacy level (at the time the query is @@ -852,7 +858,7 @@ void _FTL_forwarded(const unsigned int flags, const char *name, const struct ser // - the query was formally known as cached but had to be forwarded // (this is a special case further described below) // Use short-circuit evaluation to check if query is NULL - if(query == NULL || (query->complete && query->status != QUERY_CACHE)) + if(query == NULL || (query->flags.complete && query->status != QUERY_CACHE)) { free(upstreamIP); unlock_shm(); @@ -902,7 +908,7 @@ void _FTL_forwarded(const unsigned int flags, const char *name, const struct ser // Query is no longer unknown counters->unknown--; // Hereby, this query is now fully determined - query->complete = true; + query->flags.complete = true; } // Set query status to forwarded only after the @@ -1055,7 +1061,7 @@ void _FTL_reply(const unsigned int flags, const char *name, const union all_addr // Check if this domain matches exactly const bool isExactMatch = strcmp_escaped(name, getstr(domain->domainpos)); - if((flags & F_CONFIG) && isExactMatch && !query->complete) + if((flags & F_CONFIG) && isExactMatch && !query->flags.complete) { // Answered from local configuration, might be a wildcard or user-provided // This query is no longer unknown @@ -1075,7 +1081,7 @@ void _FTL_reply(const unsigned int flags, const char *name, const union all_addr save_reply_type(flags, addr, query, response); // Hereby, this query is now fully determined - query->complete = true; + query->flags.complete = true; } else if((flags & F_FORWARD) && isExactMatch) { @@ -1106,7 +1112,7 @@ void _FTL_reply(const unsigned int flags, const char *name, const union all_addr // Save reply type and update individual reply counters save_reply_type(flags, addr, query, response); } - else if(isExactMatch && !query->complete) + else if(isExactMatch && !query->flags.complete) { logg("*************************** unknown REPLY ***************************"); print_flags(flags); @@ -1357,7 +1363,7 @@ void _FTL_cache(const unsigned int flags, const char *name, const union all_addr // Skip this query if already marked as complete // Use short-circuit evaluation to check query if query is NULL - if(query == NULL || query->complete) + if(query == NULL || query->flags.complete) { unlock_shm(); return; @@ -1396,7 +1402,7 @@ void _FTL_cache(const unsigned int flags, const char *name, const union all_addr save_reply_type(flags, addr, query, response); // Hereby, this query is now fully determined - query->complete = true; + query->flags.complete = true; } else { @@ -1442,6 +1448,7 @@ static void query_blocked(queriesData* query, domainsData* domain, clientsData* // Update status query->status = new_status; + query->flags.blocked = true; } void _FTL_dnssec(const int status, const int id, const char* file, const int line) diff --git a/src/dnsmasq_interface.h b/src/dnsmasq_interface.h index e80de13c9..7f7d34bfc 100644 --- a/src/dnsmasq_interface.h +++ b/src/dnsmasq_interface.h @@ -22,7 +22,7 @@ enum protocol { TCP, UDP }; void FTL_next_iface(const char *newiface); #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); +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 ednsData *edns, enum protocol proto, const char* file, const int line); #define FTL_forwarded(flags, name, serv, id) _FTL_forwarded(flags, name, serv, id, __FILE__, __LINE__) void _FTL_forwarded(const unsigned int flags, const char *name, const struct server *serv, const int id, const char* file, const int line); diff --git a/src/edns0.c b/src/edns0.c index 32c4033b0..7e4170cab 100644 --- a/src/edns0.c +++ b/src/edns0.c @@ -41,7 +41,7 @@ // dnsmasq option: --add-cpe-id=... #define EDNS0_CPE_ID EDNS0_OPTION_NOMCPEID -void FTL_parse_pseudoheaders(struct dns_header *header, size_t n, union mysockaddr *peer, struct edns_data *edns) +void FTL_parse_pseudoheaders(struct dns_header *header, size_t n, union mysockaddr *peer, ednsData *edns) { int is_sign; size_t plen; diff --git a/src/edns0.h b/src/edns0.h index 3f1cc74fc..e5eafd299 100644 --- a/src/edns0.h +++ b/src/edns0.h @@ -10,16 +10,18 @@ #ifndef EDNS0_HEADER #define EDNS0_HEADER -#include "edns0.h" +// assert_sizeof +#include "static_assert.h" -struct edns_data { +typedef struct { bool client_set; - char client[ADDRSTRLEN]; bool mac_set; + char client[ADDRSTRLEN]; char mac_byte[6]; char mac_text[18]; -}; +} ednsData; +ASSERT_SIZEOF(ednsData, 72, 72, 72); -void FTL_parse_pseudoheaders(struct dns_header *header, size_t n, union mysockaddr *peer, struct edns_data *edns); +void FTL_parse_pseudoheaders(struct dns_header *header, size_t n, union mysockaddr *peer, ednsData *edns); #endif // EDNS0_HEADER diff --git a/src/overTime.h b/src/overTime.h index e02480605..709419219 100644 --- a/src/overTime.h +++ b/src/overTime.h @@ -14,6 +14,9 @@ // TYPE_MAX #include "datastructure.h" +// assert_sizeof +#include "static_assert.h" + void initOverTime(void); unsigned int getOverTimeID(const time_t timestamp); @@ -27,13 +30,14 @@ void moveOverTimeMemory(const time_t mintime); typedef struct { unsigned char magic; - time_t timestamp; int total; int blocked; int cached; int forwarded; + time_t timestamp; int querytypedata[TYPE_MAX-1]; } overTimeData; +ASSERT_SIZEOF(overTimeData, 96, 88, 88); extern overTimeData *overTime; diff --git a/src/regex.c b/src/regex.c index d4f8622b9..1ec309c5e 100644 --- a/src/regex.c +++ b/src/regex.c @@ -28,13 +28,13 @@ const char *regextype[REGEX_MAX] = { "blacklist", "whitelist", "CLI" }; -static regex_data *white_regex = NULL; -static regex_data *black_regex = NULL; -static regex_data *cli_regex = NULL; +static regexData *white_regex = NULL; +static regexData *black_regex = NULL; +static regexData *cli_regex = NULL; static unsigned int num_regex[REGEX_MAX] = { 0 }; unsigned int regex_change = 0; -static inline regex_data *get_regex_ptr(const enum regex_type regexid) +static inline regexData *get_regex_ptr(const enum regex_type regexid) { switch (regexid) { @@ -52,7 +52,7 @@ static inline regex_data *get_regex_ptr(const enum regex_type regexid) static inline void free_regex_ptr(const enum regex_type regexid) { - regex_data **regex; + regexData **regex; switch (regexid) { case REGEX_BLACKLIST: @@ -97,7 +97,7 @@ unsigned int __attribute__((pure)) get_num_regex(const enum regex_type regexid) regexec() to match against a string */ static bool compile_regex(const char *regexin, const enum regex_type regexid) { - regex_data *regex = get_regex_ptr(regexid); + regexData *regex = get_regex_ptr(regexid); int index = num_regex[regexid]++; // Extract possible Pi-hole extensions @@ -205,7 +205,7 @@ int match_regex(const char *input, const DNSCacheData* dns_cache, const int clie const enum regex_type regexid, const bool regextest) { int match_idx = -1; - regex_data *regex = get_regex_ptr(regexid); + regexData *regex = get_regex_ptr(regexid); #ifdef USE_TRE_REGEX regmatch_t match = { 0 }; // This also disables any sub-matching #endif @@ -370,7 +370,7 @@ static void free_regex(void) // Loop over regex types for(enum regex_type regexid = REGEX_BLACKLIST; regexid < REGEX_MAX; regexid++) { - regex_data *regex = get_regex_ptr(regexid); + regexData *regex = get_regex_ptr(regexid); // Reset counter for number of regex const unsigned int oldcount = num_regex[regexid]; @@ -460,15 +460,15 @@ static void read_regex_table(const enum regex_type regexid) } // Allocate memory for regex - regex_data *regex = NULL; + regexData *regex = NULL; if(regexid == REGEX_BLACKLIST) { - black_regex = calloc(count, sizeof(regex_data)); + black_regex = calloc(count, sizeof(regexData)); regex = black_regex; } else { - white_regex = calloc(count, sizeof(regex_data)); + white_regex = calloc(count, sizeof(regexData)); regex = white_regex; } @@ -554,7 +554,7 @@ void read_regex_from_database(void) // Get client pointer clientsData *client = getClient(clientID, true); // Skip invalid and alias-clients - if(client == NULL || client->aliasclient) + if(client == NULL || client->flags.aliasclient) continue; reload_per_client_regex(client); @@ -614,7 +614,7 @@ int regex_test(const bool debug_mode, const bool quiet, const char *domainin, co { // Compile CLI regex logg("%s Compiling regex filter...", cli_info()); - cli_regex = calloc(1, sizeof(regex_data)); + cli_regex = calloc(1, sizeof(regexData)); // Compile CLI regex timer_start(REGEX_TIMER); diff --git a/src/regex_r.h b/src/regex_r.h index 3627b5983..45b56303c 100644 --- a/src/regex_r.h +++ b/src/regex_r.h @@ -24,19 +24,19 @@ extern const char *regextype[]; #include #endif -typedef struct regex_data { +// assert_sizeof +#include "static_assert.h" + +typedef struct { bool available; bool inverted; bool query_type_inverted; - char *string; - int database_id; enum query_types query_type; + int database_id; + char *string; regex_t regex; -} regex_data; - -struct query_details { - enum query_types query_type; -}; +} regexData; +ASSERT_SIZEOF(regexData, 32, 20, 20); unsigned int get_num_regex(const enum regex_type regexid) __attribute__((pure)); int match_regex(const char *input, const DNSCacheData* dns_cache, const int clientID, diff --git a/src/resolve.c b/src/resolve.c index d06b0108c..0365c5931 100644 --- a/src/resolve.c +++ b/src/resolve.c @@ -373,13 +373,13 @@ static void resolveClients(const bool onlynew, const bool force_refreshing) } // Skip alias-clients - if(client->aliasclient) + if(client->flags.aliasclient) { unlock_shm(); continue; } - bool newflag = client->new; + bool newflag = client->flags.new; size_t ippos = client->ippos; size_t oldnamepos = client->namepos; @@ -470,7 +470,7 @@ static void resolveClients(const bool onlynew, const bool force_refreshing) // Store obtained host name (may be unchanged) client->namepos = newnamepos; // Mark entry as not new - client->new = false; + client->flags.new = false; if(config.debug & DEBUG_RESOLVER) logg("Client %s -> \"%s\" is new", getstr(ippos), getstr(newnamepos)); diff --git a/src/shmem.h b/src/shmem.h index 7d7eb9fe0..601cb9cea 100644 --- a/src/shmem.h +++ b/src/shmem.h @@ -18,17 +18,22 @@ // TYPE_MAX #include "datastructure.h" +// assert_sizeof +#include "static_assert.h" + typedef struct { const char *name; size_t size; void *ptr; } SharedMemory; +ASSERT_SIZEOF(SharedMemory, 24, 12, 12); typedef struct { int version; unsigned int global_shm_counter; unsigned int next_str_pos; } ShmSettings; +ASSERT_SIZEOF(ShmSettings, 12, 12, 12); typedef struct { int queries; diff --git a/src/static_assert.h b/src/static_assert.h new file mode 100644 index 000000000..4df9ecb20 --- /dev/null +++ b/src/static_assert.h @@ -0,0 +1,27 @@ +/* Pi-hole: A black hole for Internet advertisements +* (c) 2021 Pi-hole, LLC (https://pi-hole.net) +* Network-wide ad blocking via your own hardware. +* +* FTL Engine +* Struct size assertion tool +* +* This file is copyright under the latest version of the EUPL. +* Please see LICENSE file for your rights under this license. */ + +#include + + +#define STATIC_ASSERT(OBJECT, EXPECTED) \ + static_assert(sizeof(OBJECT) == EXPECTED , "Expected size of " #OBJECT " is " #EXPECTED " on this architecture."); + +// Check based on detected architecture +#if defined(__x86_64__) || defined(__aarch64__) +#define ASSERT_SIZEOF(OBJECT, SIZE64, SIZE32, SIZEARM) \ + STATIC_ASSERT(OBJECT, SIZE64) +#elif defined(__i386__) +#define ASSERT_SIZEOF(OBJECT, SIZE64, SIZE32, SIZEARM) \ + STATIC_ASSERT(OBJECT, SIZE32) +#elif defined(__arm__) +#define ASSERT_SIZEOF(OBJECT, SIZE64, SIZE32, SIZEARM) \ + STATIC_ASSERT(OBJECT, SIZEARM) +#endif \ No newline at end of file diff --git a/src/vector.h b/src/vector.h index f39aa9875..48a159cec 100644 --- a/src/vector.h +++ b/src/vector.h @@ -18,6 +18,8 @@ #include // type sqlite3_stmt #include "database/sqlite3.h" +// assert_sizeof +#include "static_assert.h" #define VEC_ALLOC_STEP 10u @@ -28,6 +30,7 @@ typedef struct sqlite3_stmt_vec { void (*set)(struct sqlite3_stmt_vec *, unsigned int, sqlite3_stmt*); void (*free)(struct sqlite3_stmt_vec *); } sqlite3_stmt_vec; +ASSERT_SIZEOF(sqlite3_stmt_vec, 40, 20, 20); sqlite3_stmt_vec *new_sqlite3_stmt_vec(unsigned int initial_size); void set_sqlite3_stmt_vec(sqlite3_stmt_vec *v, unsigned int index, sqlite3_stmt* item);