Merge branch 'development' into new/gravityDB

Signed-off-by: DL6ER <dl6er@dl6er.de>
This commit is contained in:
DL6ER
2019-04-24 17:20:58 +02:00
19 changed files with 5830 additions and 4556 deletions

30
FTL.h
View File

@@ -7,6 +7,8 @@
*
* This file is copyright under the latest version of the EUPL.
* Please see LICENSE file for your rights under this license. */
#ifndef FTL_H
#define FTL_H
#define __USE_XOPEN
#define _GNU_SOURCE
@@ -206,7 +208,7 @@ typedef struct {
int64_t db;
unsigned int timeidx;
bool complete;
} queriesDataStruct;
} queriesData;
typedef struct {
unsigned char magic;
@@ -215,7 +217,7 @@ typedef struct {
int count;
int failed;
bool new;
} forwardedDataStruct;
} forwardedData;
typedef struct {
unsigned char magic;
@@ -227,7 +229,7 @@ typedef struct {
int overTime[OVERTIME_SLOTS];
unsigned int numQueriesARP;
bool new;
} clientsDataStruct;
} clientsData;
typedef struct {
unsigned char magic;
@@ -235,7 +237,7 @@ typedef struct {
size_t domainpos;
int count;
int blockedcount;
} domainsDataStruct;
} domainsData;
typedef struct {
unsigned char magic;
@@ -245,7 +247,7 @@ typedef struct {
int cached;
int forwarded;
int querytypedata[TYPE_MAX-1];
} overTimeDataStruct;
} overTimeData;
typedef struct {
char **domains;
@@ -273,11 +275,7 @@ extern FTLFileNamesStruct FTLfiles;
extern countersStruct *counters;
extern ConfigStruct config;
extern queriesDataStruct *queries;
extern forwardedDataStruct *forwarded;
extern clientsDataStruct *clients;
extern domainsDataStruct *domains;
extern overTimeDataStruct *overTime;
extern overTimeData *overTime;
// Used in gc.c, memory.c, resolve.c, signals.c, and socket.c
extern volatile sig_atomic_t killed;
@@ -318,3 +316,15 @@ extern pthread_t socket_listenthread;
extern pthread_t DBthread;
extern pthread_t GCthread;
extern pthread_t DNSclientthread;
// Pointer getter functions
#define getQuery(queryID, checkMagic) _getQuery(queryID, checkMagic, __LINE__, __FUNCTION__, __FILE__)
queriesData* _getQuery(int queryID, bool checkMagic, int line, const char * function, const char * file);
#define getClient(clientID, checkMagic) _getClient(clientID, checkMagic, __LINE__, __FUNCTION__, __FILE__)
clientsData* _getClient(int clientID, bool checkMagic, int line, const char * function, const char * file);
#define getDomain(domainID, checkMagic) _getDomain(domainID, checkMagic, __LINE__, __FUNCTION__, __FILE__)
domainsData* _getDomain(int domainID, bool checkMagic, int line, const char * function, const char * file);
#define getForward(forwardID, checkMagic) _getForward(forwardID, checkMagic, __LINE__, __FUNCTION__, __FILE__)
forwardedData* _getForward(int forwardID, bool checkMagic, int line, const char * function, const char * file);
#endif // FTL_H

View File

@@ -141,11 +141,14 @@ clean:
version~: force
@echo '$(GIT_BRANCH) $(GIT_VERSION) $(GIT_DATE) $(GIT_TAG)' | cmp -s - $@ || echo '$(GIT_BRANCH) $(GIT_VERSION) $(GIT_DATE) $(GIT_TAG)' > $@
version.h: version~
@echo '#define GIT_VERSION "$(GIT_VERSION)"' > "$@"
@echo '#ifndef VERSION_H' > "$@"
@echo '#define VERSION_H' >> "$@"
@echo '#define GIT_VERSION "$(GIT_VERSION)"' >> "$@"
@echo '#define GIT_DATE "$(GIT_DATE)"' >> "$@"
@echo '#define GIT_BRANCH "$(GIT_BRANCH)"' >> "$@"
@echo '#define GIT_TAG "$(GIT_TAG)"' >> "$@"
@echo '#define GIT_HASH "$(GIT_HASH)"' >> "$@"
@echo '#endif // VERSION_H' >> "$@"
@echo "Making FTL version on branch $(GIT_BRANCH) - $(GIT_VERSION) ($(GIT_DATE))"
prefix=/usr

422
api.c
View File

@@ -62,11 +62,12 @@ void getStats(const int *sock)
pack_int32(*sock, counters->gravity);
// unique_clients: count only clients that have been active within the most recent 24 hours
int i, activeclients = 0;
for(i=0; i < counters->clients; i++)
int activeclients = 0;
for(int clientID=0; clientID < counters->clients; clientID++)
{
validate_access("clients", i, true, __LINE__, __FUNCTION__, __FILE__);
if(clients[i].count > 0)
// Get client pointer
const clientsData* client = getClient(clientID, true);
if(client->count > 0)
activeclients++;
}
@@ -80,9 +81,9 @@ void getStats(const int *sock)
// Sum up all query types (A, AAAA, ANY, SRV, SOA, ...)
int sumalltypes = 0;
for(i=0; i < TYPE_MAX-1; i++)
for(int queryType=0; queryType < TYPE_MAX-1; queryType++)
{
sumalltypes += counters->querytype[i];
sumalltypes += counters->querytype[queryType];
}
ssend(*sock, "dns_queries_all_types %i\n", sumalltypes);
@@ -113,28 +114,28 @@ void getStats(const int *sock)
void getOverTime(const int *sock)
{
int i, from = 0, until = OVERTIME_SLOTS;
int from = 0, until = OVERTIME_SLOTS;
bool found = false;
time_t mintime = overTime[0].timestamp;
// Start with the first non-empty overTime slot
for(i=0; i < OVERTIME_SLOTS; i++)
for(int slot = 0; slot < OVERTIME_SLOTS; slot++)
{
if((overTime[i].total > 0 || overTime[i].blocked > 0) &&
overTime[i].timestamp >= mintime)
if((overTime[slot].total > 0 || overTime[slot].blocked > 0) &&
overTime[slot].timestamp >= mintime)
{
from = i;
from = slot;
found = true;
break;
}
}
// End with last non-empty overTime slot
for(i = 0; i < OVERTIME_SLOTS; i++)
for(int slot = 0; slot < OVERTIME_SLOTS; slot++)
{
if(overTime[i].timestamp >= time(NULL))
if(overTime[slot].timestamp >= time(NULL))
{
until = i;
until = slot;
break;
}
}
@@ -145,9 +146,12 @@ void getOverTime(const int *sock)
if(istelnet[*sock])
{
for(i = from; i < until; i++)
for(int slot = from; slot < until; slot++)
{
ssend(*sock,"%li %i %i\n",overTime[i].timestamp,overTime[i].total,overTime[i].blocked);
ssend(*sock,"%li %i %i\n",
overTime[slot].timestamp,
overTime[slot].total,
overTime[slot].blocked);
}
}
else
@@ -157,23 +161,23 @@ void getOverTime(const int *sock)
// Send domains over time
pack_map16_start(*sock, (uint16_t) (until - from));
for(i = from; i < until; i++) {
pack_int32(*sock, overTime[i].timestamp);
pack_int32(*sock, overTime[i].total);
for(int slot = from; slot < until; slot++) {
pack_int32(*sock, overTime[slot].timestamp);
pack_int32(*sock, overTime[slot].total);
}
// Send ads over time
pack_map16_start(*sock, (uint16_t) (until - from));
for(i = from; i < until; i++) {
pack_int32(*sock, overTime[i].timestamp);
pack_int32(*sock, overTime[i].blocked);
for(int slot = from; slot < until; slot++) {
pack_int32(*sock, overTime[slot].timestamp);
pack_int32(*sock, overTime[slot].blocked);
}
}
}
void getTopDomains(const char *client_message, const int *sock)
{
int i, temparray[counters->domains][2], count=10, num;
int temparray[counters->domains][2], count=10, num;
bool audit = false, asc = false;
const bool blocked = command(client_message, ">top-ads");
@@ -205,15 +209,17 @@ void getTopDomains(const char *client_message, const int *sock)
if(command(client_message, " asc"))
asc = true;
for(i=0; i < counters->domains; i++)
for(int domainID=0; domainID < counters->domains; domainID++)
{
validate_access("domains", i, true, __LINE__, __FUNCTION__, __FILE__);
temparray[i][0] = i;
// Get domain pointer
const domainsData* domain = getDomain(domainID, true);
temparray[domainID][0] = domainID;
if(blocked)
temparray[i][1] = domains[i].blockedcount;
temparray[domainID][1] = domain->blockedcount;
else
// Count only permitted queries
temparray[i][1] = (domains[i].count - domains[i].blockedcount);
temparray[domainID][1] = (domain->count - domain->blockedcount);
}
// Sort temporary array
@@ -261,65 +267,66 @@ void getTopDomains(const char *client_message, const int *sock)
}
int n = 0;
for(i=0; i < counters->domains; i++)
for(int i=0; i < counters->domains; i++)
{
// Get sorted indices
const int j = temparray[i][0];
validate_access("domains", j, true, __LINE__, __FUNCTION__, __FILE__);
// Get sorted index
const int domainID = temparray[i][0];
// Get domain pointer
const domainsData* domain = getDomain(domainID, true);
// Skip this domain if there is a filter on it
if(excludedomains != NULL && insetupVarsArray(getstr(domains[j].domainpos)))
if(excludedomains != NULL && insetupVarsArray(getstr(domain->domainpos)))
continue;
// Skip this domain if already included in audit
if(audit && countlineswith(getstr(domains[j].domainpos), files.auditlist) > 0)
if(audit && countlineswith(getstr(domain->domainpos), files.auditlist) > 0)
continue;
// Hidden domain, probably due to privacy level. Skip this in the top lists
if(strcmp(getstr(domains[j].domainpos), HIDDEN_DOMAIN) == 0)
if(strcmp(getstr(domain->domainpos), HIDDEN_DOMAIN) == 0)
continue;
if(blocked && showblocked && domains[j].blockedcount > 0)
if(blocked && showblocked && domain->blockedcount > 0)
{
if(audit && domains[j].regexmatch == REGEX_BLOCKED)
if(audit && domain->regexmatch == REGEX_BLOCKED)
{
if(istelnet[*sock])
ssend(*sock, "%i %i %s wildcard\n", n, domains[j].blockedcount, getstr(domains[j].domainpos));
ssend(*sock, "%i %i %s wildcard\n", n, domain->blockedcount, getstr(domain->domainpos));
else {
char *fancyWildcard = calloc(3 + strlen(getstr(domains[j].domainpos)), sizeof(char));
char *fancyWildcard = calloc(3 + strlen(getstr(domain->domainpos)), sizeof(char));
if(fancyWildcard == NULL) return;
sprintf(fancyWildcard, "*.%s", getstr(domains[j].domainpos));
sprintf(fancyWildcard, "*.%s", getstr(domain->domainpos));
if(!pack_str32(*sock, fancyWildcard))
return;
pack_int32(*sock, domains[j].blockedcount);
pack_int32(*sock, domain->blockedcount);
free(fancyWildcard);
}
}
else
{
if(istelnet[*sock])
ssend(*sock, "%i %i %s\n", n, domains[j].blockedcount, getstr(domains[j].domainpos));
ssend(*sock, "%i %i %s\n", n, domain->blockedcount, getstr(domain->domainpos));
else {
if(!pack_str32(*sock, getstr(domains[j].domainpos)))
if(!pack_str32(*sock, getstr(domain->domainpos)))
return;
pack_int32(*sock, domains[j].blockedcount);
pack_int32(*sock, domain->blockedcount);
}
}
n++;
}
else if(!blocked && showpermitted && (domains[j].count - domains[j].blockedcount) > 0)
else if(!blocked && showpermitted && (domain->count - domain->blockedcount) > 0)
{
if(istelnet[*sock])
ssend(*sock,"%i %i %s\n",n,(domains[j].count - domains[j].blockedcount),getstr(domains[j].domainpos));
ssend(*sock,"%i %i %s\n",n,(domain->count - domain->blockedcount),getstr(domain->domainpos));
else
{
if(!pack_str32(*sock, getstr(domains[j].domainpos)))
if(!pack_str32(*sock, getstr(domain->domainpos)))
return;
pack_int32(*sock, domains[j].count - domains[j].blockedcount);
pack_int32(*sock, domain->count - domain->blockedcount);
}
n++;
}
@@ -335,7 +342,7 @@ void getTopDomains(const char *client_message, const int *sock)
void getTopClients(const char *client_message, const int *sock)
{
int i, temparray[counters->clients][2], count=10, num;
int temparray[counters->clients][2], count=10, num;
// Exit before processing any data if requested via config setting
get_privacy_level(NULL);
@@ -368,12 +375,13 @@ void getTopClients(const char *client_message, const int *sock)
if(command(client_message, " blocked"))
blockedonly = true;
for(i=0; i < counters->clients; i++)
for(int clientID = 0; clientID < counters->clients; clientID++)
{
validate_access("clients", i, true, __LINE__, __FUNCTION__, __FILE__);
temparray[i][0] = i;
// Get client pointer
const clientsData* client = getClient(clientID, true);
temparray[clientID][0] = clientID;
// Use either blocked or total count based on request string
temparray[i][1] = blockedonly ? clients[i].blockedcount : clients[i].count;
temparray[clientID][1] = blockedonly ? client->blockedcount : client->count;
}
// Sort in ascending order?
@@ -402,24 +410,26 @@ void getTopClients(const char *client_message, const int *sock)
}
int n = 0;
for(i=0; i < counters->clients; i++)
for(int i=0; i < counters->clients; i++)
{
// Get sorted indices and counter values (may be either total or blocked count)
const int j = temparray[i][0];
const int clientID = temparray[i][0];
const int ccount = temparray[i][1];
validate_access("clients", j, true, __LINE__, __FUNCTION__, __FILE__);
// Get client pointer
const clientsData* client = getClient(clientID, true);
// Skip this client if there is a filter on it
if(excludeclients != NULL &&
(insetupVarsArray(getstr(clients[j].ippos)) || insetupVarsArray(getstr(clients[j].namepos))))
(insetupVarsArray(getstr(client->ippos)) || insetupVarsArray(getstr(client->namepos))))
continue;
// Hidden client, probably due to privacy level. Skip this in the top lists
if(strcmp(getstr(clients[j].ippos), HIDDEN_CLIENT) == 0)
if(strcmp(getstr(client->ippos), HIDDEN_CLIENT) == 0)
continue;
const char *client_ip = getstr(clients[j].ippos);
const char *client_name = getstr(clients[j].namepos);
// Get client IP and name
const char *client_ip = getstr(client->ippos);
const char *client_name = getstr(client->namepos);
// Return this client if either
// - "withzero" option is set, and/or
@@ -455,13 +465,16 @@ void getForwardDestinations(const char *client_message, const int *sock)
if(command(client_message, "unsorted"))
sort = false;
for(int i = 0; i < counters->forwarded; i++) {
validate_access("forwarded", i, true, __LINE__, __FUNCTION__, __FILE__);
for(int forwardID = 0; forwardID < counters->forwarded; forwardID++)
{
// If we want to print a sorted output, we fill the temporary array with
// the values we will use for sorting afterwards
if(sort) {
temparray[i][0] = i;
temparray[i][1] = forwarded[i].count;
// Get forward pointer
const forwardedData* forward = getForward(forwardID, true);
temparray[forwardID][0] = forwardID;
temparray[forwardID][1] = forward->count;
}
}
@@ -503,20 +516,22 @@ void getForwardDestinations(const char *client_message, const int *sock)
{
// Regular forward destionation
// Get sorted indices
int j;
int forwardID;
if(sort)
j = temparray[i][0];
forwardID = temparray[i][0];
else
j = i;
validate_access("forwarded", j, true, __LINE__, __FUNCTION__, __FILE__);
forwardID = i;
// Get forward pointer
const forwardedData* forward = getForward(forwardID, true);
// Get IP and host name of forward destination if available
ip = getstr(forwarded[j].ippos);
name = getstr(forwarded[j].namepos);
ip = getstr(forward->ippos);
name = getstr(forward->namepos);
// Get percentage
if(totalqueries > 0)
percentage = 1e2f * forwarded[j].count / totalqueries;
percentage = 1e2f * forward->count / totalqueries;
}
// Send data:
@@ -542,14 +557,20 @@ void getQueryTypes(const int *sock)
{
int total = 0;
for(int i=0; i < TYPE_MAX-1; i++)
{
total += counters->querytype[i];
}
float percentage[TYPE_MAX-1] = { 0.0 };
// Prevent floating point exceptions by checking if the divisor is != 0
if(total > 0)
{
for(int i=0; i < TYPE_MAX-1; i++)
{
percentage[i] = 1e2f*counters->querytype[i]/total;
}
}
if(istelnet[*sock]) {
ssend(*sock, "A (IPv4): %.2f\nAAAA (IPv6): %.2f\nANY: %.2f\nSRV: %.2f\nSOA: %.2f\nPTR: %.2f\nTXT: %.2f\n",
@@ -631,15 +652,16 @@ void getAllQueries(const char *client_message, const int *sock)
else
{
// Iterate through all known forward destinations
validate_access("forwards", MAX(0,counters->forwarded-1), true, __LINE__, __FUNCTION__, __FILE__);
forwarddestid = -3;
for(int i = 0; i < counters->forwarded; i++)
{
// Get forward pointer
const forwardedData* forward = getForward(i, true);
// Try to match the requested string against their IP addresses and
// (if available) their host names
if(strcmp(getstr(forwarded[i].ippos), forwarddest) == 0 ||
(forwarded[i].namepos != 0 &&
strcmp(getstr(forwarded[i].namepos), forwarddest) == 0))
if(strcmp(getstr(forward->ippos), forwarddest) == 0 ||
(forward->namepos != 0 &&
strcmp(getstr(forward->namepos), forwarddest) == 0))
{
forwarddestid = i;
break;
@@ -663,13 +685,15 @@ void getAllQueries(const char *client_message, const int *sock)
sscanf(client_message, ">getallqueries-domain %255s", domainname);
filterdomainname = true;
// Iterate through all known domains
validate_access("domains", MAX(0,counters->domains-1), true, __LINE__, __FUNCTION__, __FILE__);
for(int i = 0; i < counters->domains; i++)
for(int domainID = 0; domainID < counters->domains; domainID++)
{
// Get domain pointer
const domainsData* domain = getDomain(domainID, true);
// Try to match the requested string
if(strcmp(getstr(domains[i].domainpos), domainname) == 0)
if(strcmp(getstr(domain->domainpos), domainname) == 0)
{
domainid = i;
domainid = domainID;
break;
}
}
@@ -689,14 +713,16 @@ void getAllQueries(const char *client_message, const int *sock)
if(clientname == NULL) return;
sscanf(client_message, ">getallqueries-client %255s", clientname);
filterclientname = true;
// Iterate through all known clients
validate_access("clients", MAX(0,counters->clients-1), true, __LINE__, __FUNCTION__, __FILE__);
for(int i = 0; i < counters->clients; i++)
{
// Get client pointer
const clientsData* client = getClient(i, true);
// Try to match the requested string
if(strcmp(getstr(clients[i].ippos), clientname) == 0 ||
(clients[i].namepos != 0 &&
strcmp(getstr(clients[i].namepos), clientname) == 0))
if(strcmp(getstr(client->ippos), clientname) == 0 ||
(client->namepos != 0 &&
strcmp(getstr(client->namepos), clientname) == 0))
{
clientid = i;
break;
@@ -739,95 +765,98 @@ void getAllQueries(const char *client_message, const int *sock)
}
clearSetupVarsArray();
// Main loop
for(int i=ibeg; i < counters->queries; i++)
for(int queryID = ibeg; queryID < counters->queries; queryID++)
{
validate_access("queries", i, true, __LINE__, __FUNCTION__, __FILE__);
const queriesData* query = getQuery(queryID, true);
// Check if this query has been create while in maximum privacy mode
if(queries[i].privacylevel >= PRIVACY_MAXIMUM) continue;
if(query->privacylevel >= PRIVACY_MAXIMUM) continue;
validate_access("domains", queries[i].domainID, true, __LINE__, __FUNCTION__, __FILE__);
validate_access("clients", queries[i].clientID, true, __LINE__, __FUNCTION__, __FILE__);
const char *qtype = querytypes[queries[i].type - TYPE_A];
// Verify query type
if(query->type > TYPE_MAX-1)
continue;
// Get query type
const char *qtype = querytypes[query->type - TYPE_A];
// 1 = gravity.list, 4 = wildcard, 5 = black.list
if((queries[i].status == QUERY_GRAVITY ||
queries[i].status == QUERY_WILDCARD ||
queries[i].status == QUERY_BLACKLIST) && !showblocked)
if((query->status == QUERY_GRAVITY ||
query->status == QUERY_WILDCARD ||
query->status == QUERY_BLACKLIST) && !showblocked)
continue;
// 2 = forwarded, 3 = cached
if((queries[i].status == QUERY_FORWARDED ||
queries[i].status == QUERY_CACHE) && !showpermitted)
if((query->status == QUERY_FORWARDED ||
query->status == QUERY_CACHE) && !showpermitted)
continue;
// Skip those entries which so not meet the requested timeframe
if((from > queries[i].timestamp && from != 0) || (queries[i].timestamp > until && until != 0))
if((from > query->timestamp && from != 0) || (query->timestamp > until && until != 0))
continue;
// Skip if domain is not identical with what the user wants to see
if(filterdomainname && queries[i].domainID != domainid)
if(filterdomainname && query->domainID != domainid)
continue;
// Skip if client name and IP are not identical with what the user wants to see
if(filterclientname && queries[i].clientID != clientid)
if(filterclientname && query->clientID != clientid)
continue;
// Skip if query type is not identical with what the user wants to see
if(querytype != 0 && querytype != queries[i].type)
if(querytype != 0 && querytype != query->type)
continue;
if(filterforwarddest)
{
// Does the user want to see queries answered from blocking lists?
if(forwarddestid == -2 && queries[i].status != QUERY_GRAVITY
&& queries[i].status != QUERY_WILDCARD
&& queries[i].status != QUERY_BLACKLIST)
if(forwarddestid == -2 && query->status != QUERY_GRAVITY
&& query->status != QUERY_WILDCARD
&& query->status != QUERY_BLACKLIST)
continue;
// Does the user want to see queries answered from local cache?
else if(forwarddestid == -1 && queries[i].status != QUERY_CACHE)
else if(forwarddestid == -1 && query->status != QUERY_CACHE)
continue;
// Does the user want to see queries answered by an upstream server?
else if(forwarddestid >= 0 && forwarddestid != queries[i].forwardID)
else if(forwarddestid >= 0 && forwarddestid != query->forwardID)
continue;
}
// Ask subroutine for domain. It may return "hidden" depending on
// the privacy settings at the time the query was made
const char *domain = getDomainString(i);
// Similarly for the client
const char *client;
if(strlen(getstr(clients[queries[i].clientID].namepos)) > 0)
client = getClientNameString(i);
else
client = getClientIPString(i);
const char *domain = getDomainString(queryID);
unsigned long delay = queries[i].response;
// Similarly for the client
const char *clientIPName = NULL;
// Get client pointer
const clientsData* client = getClient(query->clientID, true);
if(strlen(getstr(client->namepos)) > 0)
clientIPName = getClientNameString(queryID);
else
clientIPName = getClientIPString(queryID);
unsigned long delay = query->response;
// Check if received (delay should be smaller than 30min)
if(delay > 1.8e7)
delay = 0;
if(istelnet[*sock])
{
ssend(*sock,"%li %s %s %s %i %i %i %lu",queries[i].timestamp,qtype,domain,client,queries[i].status,queries[i].dnssec,queries[i].reply,delay);
ssend(*sock,"%li %s %s %s %i %i %i %lu",query->timestamp,qtype,domain,clientIPName,query->status,query->dnssec,query->reply,delay);
if(config.debug & DEBUG_API)
ssend(*sock, " %i", i);
ssend(*sock, " %i", queryID);
ssend(*sock, "\n");
}
else
{
pack_int32(*sock, queries[i].timestamp);
pack_int32(*sock, query->timestamp);
// Use a fixstr because the length of qtype is always 4 (max is 31 for fixstr)
if(!pack_fixstr(*sock, qtype))
return;
// Use str32 for domain and client because we have no idea how long they will be (max is 4294967295 for str32)
if(!pack_str32(*sock, domain) || !pack_str32(*sock, client))
if(!pack_str32(*sock, domain) || !pack_str32(*sock, clientIPName))
return;
pack_uint8(*sock, queries[i].status);
pack_uint8(*sock, queries[i].dnssec);
pack_uint8(*sock, query->status);
pack_uint8(*sock, query->dnssec);
}
}
@@ -855,19 +884,19 @@ void getRecentBlocked(const char *client_message, const int *sock)
// Find most recently blocked query
int found = 0;
for(int i = counters->queries - 1; i > 0 ; i--)
for(int queryID = counters->queries - 1; queryID > 0 ; queryID--)
{
validate_access("queries", i, true, __LINE__, __FUNCTION__, __FILE__);
const queriesData* query = getQuery(queryID, true);
if(queries[i].status == QUERY_GRAVITY ||
queries[i].status == QUERY_WILDCARD ||
queries[i].status == QUERY_BLACKLIST)
if(query->status == QUERY_GRAVITY ||
query->status == QUERY_WILDCARD ||
query->status == QUERY_BLACKLIST)
{
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(i);
const char *domain = getDomainString(queryID);
if(istelnet[*sock])
ssend(*sock,"%s\n", domain);
@@ -891,22 +920,23 @@ void getClientID(const int *sock)
void getQueryTypesOverTime(const int *sock)
{
int from = -1, until = OVERTIME_SLOTS;
time_t mintime = overTime[0].timestamp;
for(int i = 0; i < OVERTIME_SLOTS; i++)
const time_t mintime = overTime[0].timestamp;
for(int slot = 0; slot < OVERTIME_SLOTS; slot++)
{
if((overTime[i].total > 0 || overTime[i].blocked > 0) && overTime[i].timestamp >= mintime)
if((overTime[slot].total > 0 || overTime[slot].blocked > 0) && overTime[slot].timestamp >= mintime)
{
from = i;
from = slot;
break;
}
}
// End with last non-empty overTime slot
for(int i = 0; i < OVERTIME_SLOTS; i++)
for(int slot = 0; slot < OVERTIME_SLOTS; slot++)
{
if(overTime[i].timestamp >= time(NULL))
if(overTime[slot].timestamp >= time(NULL))
{
until = i;
until = slot;
break;
}
}
@@ -915,20 +945,20 @@ void getQueryTypesOverTime(const int *sock)
if(from < 0)
return;
for(int i = from; i < until; i++)
for(int slot = from; slot < until; slot++)
{
float percentageIPv4 = 0.0, percentageIPv6 = 0.0;
int sum = overTime[i].querytypedata[0] + overTime[i].querytypedata[1];
int sum = overTime[slot].querytypedata[0] + overTime[slot].querytypedata[1];
if(sum > 0) {
percentageIPv4 = (float) (1e2 * overTime[i].querytypedata[0] / sum);
percentageIPv6 = (float) (1e2 * overTime[i].querytypedata[1] / sum);
percentageIPv4 = (float) (1e2 * overTime[slot].querytypedata[0] / sum);
percentageIPv6 = (float) (1e2 * overTime[slot].querytypedata[1] / sum);
}
if(istelnet[*sock])
ssend(*sock, "%li %.2f %.2f\n", overTime[i].timestamp, percentageIPv4, percentageIPv6);
ssend(*sock, "%li %.2f %.2f\n", overTime[slot].timestamp, percentageIPv4, percentageIPv6);
else {
pack_int32(*sock, overTime[i].timestamp);
pack_int32(*sock, overTime[slot].timestamp);
pack_float(*sock, percentageIPv4);
pack_float(*sock, percentageIPv6);
}
@@ -1021,12 +1051,12 @@ void getClientsOverTime(const int *sock)
return;
// Find minimum ID to send
for(int i = 0; i < OVERTIME_SLOTS; i++)
for(int slot = 0; slot < OVERTIME_SLOTS; slot++)
{
if((overTime[i].total > 0 || overTime[i].blocked > 0) &&
overTime[i].timestamp >= overTime[0].timestamp)
if((overTime[slot].total > 0 || overTime[slot].blocked > 0) &&
overTime[slot].timestamp >= overTime[0].timestamp)
{
sendit = i;
sendit = slot;
break;
}
}
@@ -1034,11 +1064,11 @@ void getClientsOverTime(const int *sock)
return;
// Find minimum ID to send
for(int i = 0; i < OVERTIME_SLOTS; i++)
for(int slot = 0; slot < OVERTIME_SLOTS; slot++)
{
if(overTime[i].timestamp >= time(NULL))
if(overTime[slot].timestamp >= time(NULL))
{
until = i;
until = slot;
break;
}
}
@@ -1055,31 +1085,34 @@ void getClientsOverTime(const int *sock)
{
getSetupVarsArray(excludeclients);
for(int i=0; i < counters->clients; i++)
for(int clientID=0; clientID < counters->clients; clientID++)
{
validate_access("clients", i, true, __LINE__, __FUNCTION__, __FILE__);
// Get client pointer
const clientsData* client = getClient(clientID, true);
// Check if this client should be skipped
if(insetupVarsArray(getstr(clients[i].ippos)) ||
insetupVarsArray(getstr(clients[i].namepos)))
skipclient[i] = true;
if(insetupVarsArray(getstr(client->ippos)) ||
insetupVarsArray(getstr(client->namepos)))
skipclient[clientID] = true;
}
}
// Main return loop
for(int i = sendit; i < until; i++)
for(int slot = sendit; slot < until; slot++)
{
if(istelnet[*sock])
ssend(*sock, "%li", overTime[i].timestamp);
ssend(*sock, "%li", overTime[slot].timestamp);
else
pack_int32(*sock, overTime[i].timestamp);
pack_int32(*sock, overTime[slot].timestamp);
// Loop over forward destinations to generate output to be sent to the client
for(int j = 0; j < counters->clients; j++)
for(int clientID = 0; clientID < counters->clients; clientID++)
{
if(skipclient[j])
if(skipclient[clientID])
continue;
const int thisclient = clients[j].overTime[i];
// Get client pointer
const clientsData* client = getClient(clientID, true);
const int thisclient = client->overTime[slot];
if(istelnet[*sock])
ssend(*sock, " %i", thisclient);
@@ -1116,25 +1149,27 @@ void getClientNames(const int *sock)
{
getSetupVarsArray(excludeclients);
for(int i=0; i < counters->clients; i++)
for(int clientID=0; clientID < counters->clients; clientID++)
{
validate_access("clients", i, true, __LINE__, __FUNCTION__, __FILE__);
// Get client pointer
const clientsData* client = getClient(clientID, true);
// Check if this client should be skipped
if(insetupVarsArray(getstr(clients[i].ippos)) ||
insetupVarsArray(getstr(clients[i].namepos)))
skipclient[i] = true;
if(insetupVarsArray(getstr(client->ippos)) ||
insetupVarsArray(getstr(client->namepos)))
skipclient[clientID] = true;
}
}
// Loop over clients to generate output to be sent to the client
for(int i = 0; i < counters->clients; i++)
for(int clientID = 0; clientID < counters->clients; clientID++)
{
validate_access("clients", i, true, __LINE__, __FUNCTION__, __FILE__);
if(skipclient[i])
if(skipclient[clientID])
continue;
const char *client_ip = getstr(clients[i].ippos);
const char *client_name = getstr(clients[i].namepos);
// Get client pointer
const clientsData* client = getClient(clientID, true);
const char *client_ip = getstr(client->ippos);
const char *client_name = getstr(client->namepos);
if(istelnet[*sock])
ssend(*sock, "%s %s\n", client_name, client_ip);
@@ -1155,13 +1190,14 @@ void getUnknownQueries(const int *sock)
if(config.privacylevel >= PRIVACY_HIDE_DOMAINS)
return;
for(int i=0; i < counters->queries; i++)
for(int queryID = 0; queryID < counters->queries; queryID++)
{
validate_access("queries", i, true, __LINE__, __FUNCTION__, __FILE__);
if(queries[i].status != QUERY_UNKNOWN && queries[i].complete) continue;
const queriesData* query = getQuery(queryID, true);
if(query->status != QUERY_UNKNOWN && query->complete) continue;
char type[5];
if(queries[i].type == TYPE_A)
if(query->type == TYPE_A)
{
strcpy(type,"IPv4");
}
@@ -1170,28 +1206,30 @@ void getUnknownQueries(const int *sock)
strcpy(type,"IPv6");
}
validate_access("domains", queries[i].domainID, true, __LINE__, __FUNCTION__, __FILE__);
validate_access("clients", queries[i].clientID, true, __LINE__, __FUNCTION__, __FILE__);
// Get domain pointer
const domainsData* domain = getDomain(query->domainID, true);
// Get client pointer
const clientsData* client = getClient(query->clientID, true);
const char *client = getstr(clients[queries[i].clientID].ippos);
// Get client IP string
const char *clientIP = getstr(client->ippos);
if(istelnet[*sock])
ssend(*sock, "%li %i %i %s %s %s %i %s\n", queries[i].timestamp, i, queries[i].id, type, getstr(domains[queries[i].domainID].domainpos), client, queries[i].status, queries[i].complete ? "true" : "false");
ssend(*sock, "%li %i %i %s %s %s %i %s\n", query->timestamp, queryID, query->id, type, getstr(domain->domainpos), clientIP, query->status, query->complete ? "true" : "false");
else {
pack_int32(*sock, queries[i].timestamp);
pack_int32(*sock, queries[i].id);
pack_int32(*sock, query->timestamp);
pack_int32(*sock, query->id);
// Use a fixstr because the length of qtype is always 4 (max is 31 for fixstr)
if(!pack_fixstr(*sock, type))
return;
// Use str32 for domain and client because we have no idea how long they will be (max is 4294967295 for str32)
if(!pack_str32(*sock, getstr(domains[queries[i].domainID].domainpos)) || !pack_str32(*sock, client))
if(!pack_str32(*sock, getstr(domain->domainpos)) || !pack_str32(*sock, clientIP))
return;
pack_uint8(*sock, queries[i].status);
pack_bool(*sock, queries[i].complete);
pack_uint8(*sock, query->status);
pack_bool(*sock, query->complete);
}
}
}
@@ -1199,25 +1237,27 @@ void getUnknownQueries(const int *sock)
void getDomainDetails(const char *client_message, const int *sock)
{
// Get domain name
char domain[128];
if(sscanf(client_message, "%*[^ ] %127s", domain) < 1)
char domainString[128];
if(sscanf(client_message, "%*[^ ] %127s", domainString) < 1)
{
ssend(*sock, "Need domain for this request\n");
return;
}
for(int i = 0; i < counters->domains; i++)
for(int domainID = 0; domainID < counters->domains; domainID++)
{
validate_access("domains", i, true, __LINE__, __FUNCTION__, __FILE__);
if(strcmp(getstr(domains[i].domainpos), domain) == 0)
// Get domain pointer
const domainsData* domain = getDomain(domainID, true);
if(strcmp(getstr(domain->domainpos), domainString) == 0)
{
ssend(*sock,"Domain \"%s\", ID: %i\n", domain, i);
ssend(*sock,"Total: %i\n", domains[i].count);
ssend(*sock,"Blocked: %i\n", domains[i].blockedcount);
ssend(*sock,"Domain \"%s\", ID: %i\n", domainString, domainID);
ssend(*sock,"Total: %i\n", domain->count);
ssend(*sock,"Blocked: %i\n", domain->blockedcount);
const char *regexstatus;
if(domains[i].regexmatch == REGEX_BLOCKED)
if(domain->regexmatch == REGEX_BLOCKED)
regexstatus = "blocked";
else if(domains[i].regexmatch == REGEX_NOTBLOCKED)
else if(domain->regexmatch == REGEX_NOTBLOCKED)
regexstatus = "not blocked";
else
regexstatus = "unknown";
@@ -1227,5 +1267,5 @@ void getDomainDetails(const char *client_message, const int *sock)
}
// for loop finished without an exact match
ssend(*sock,"Domain \"%s\" is unknown\n", domain);
ssend(*sock,"Domain \"%s\" is unknown\n", domainString);
}

4
api.h
View File

@@ -7,6 +7,8 @@
*
* This file is copyright under the latest version of the EUPL.
* Please see LICENSE file for your rights under this license. */
#ifndef API_H
#define API_H
// Statistic methods
void getStats(const int *sock);
@@ -42,3 +44,5 @@ void pack_float(const int sock, const float value);
bool pack_fixstr(const int sock, const char *string);
bool pack_str32(const int sock, const char *string);
void pack_map16_start(const int sock, const uint16_t length);
#endif // API_H

View File

@@ -428,7 +428,6 @@ void save_to_DB(void)
}
unsigned int saved = 0, saved_error = 0;
long int i;
sqlite3_stmt* stmt = NULL;
// Get last ID stored in the database
@@ -454,26 +453,24 @@ void save_to_DB(void)
int total = 0, blocked = 0;
time_t currenttimestamp = time(NULL);
time_t newlasttimestamp = 0;
for(i = MAX(0, lastdbindex); i < counters->queries; i++)
long int queryID;
for(queryID = MAX(0, lastdbindex); queryID < counters->queries; queryID++)
{
validate_access("queries", i, true, __LINE__, __FUNCTION__, __FILE__);
if(queries[i].db != 0)
queriesData* query = getQuery(queryID, true);
if(query->db != 0)
{
// Skip, already saved in database
continue;
}
if(!queries[i].complete && queries[i].timestamp > currenttimestamp-2)
if(!query->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
break;
}
// Memory checks
validate_access("queries", i, true, __LINE__, __FUNCTION__, __FILE__);
if(queries[i].privacylevel >= PRIVACY_MAXIMUM)
if(query->privacylevel >= PRIVACY_MAXIMUM)
{
// Skip, we never store nor count queries recorded
// while have been in maximum privacy mode in the database
@@ -481,27 +478,28 @@ void save_to_DB(void)
}
// TIMESTAMP
sqlite3_bind_int(stmt, 1, queries[i].timestamp);
sqlite3_bind_int(stmt, 1, query->timestamp);
// TYPE
sqlite3_bind_int(stmt, 2, queries[i].type);
sqlite3_bind_int(stmt, 2, query->type);
// STATUS
sqlite3_bind_int(stmt, 3, queries[i].status);
sqlite3_bind_int(stmt, 3, query->status);
// DOMAIN
const char *domain = getDomainString(i);
const char *domain = getDomainString(queryID);
sqlite3_bind_text(stmt, 4, domain, -1, SQLITE_TRANSIENT);
// CLIENT
const char *client = getClientIPString(i);
const char *client = getClientIPString(queryID);
sqlite3_bind_text(stmt, 5, client, -1, SQLITE_TRANSIENT);
// FORWARD
if(queries[i].status == QUERY_FORWARDED && queries[i].forwardID > -1)
if(query->status == QUERY_FORWARDED && query->forwardID > -1)
{
validate_access("forwarded", queries[i].forwardID, true, __LINE__, __FUNCTION__, __FILE__);
sqlite3_bind_text(stmt, 6, getstr(forwarded[queries[i].forwardID].ippos), -1, SQLITE_TRANSIENT);
// Get forward pointer
const forwardedData* forward = getForward(query->forwardID, true);
sqlite3_bind_text(stmt, 6, getstr(forward->ippos), -1, SQLITE_TRANSIENT);
}
else
{
@@ -531,21 +529,21 @@ void save_to_DB(void)
saved++;
// Mark this query as saved in the database by setting the corresponding ID
queries[i].db = ++lastID;
query->db = ++lastID;
// Total counter information (delta computation)
total++;
if(queries[i].status == QUERY_GRAVITY ||
queries[i].status == QUERY_BLACKLIST ||
queries[i].status == QUERY_WILDCARD ||
queries[i].status == QUERY_EXTERNAL_BLOCKED_IP ||
queries[i].status == QUERY_EXTERNAL_BLOCKED_NULL ||
queries[i].status == QUERY_EXTERNAL_BLOCKED_NXRA)
if(query->status == QUERY_GRAVITY ||
query->status == QUERY_BLACKLIST ||
query->status == QUERY_WILDCARD ||
query->status == QUERY_EXTERNAL_BLOCKED_IP ||
query->status == QUERY_EXTERNAL_BLOCKED_NULL ||
query->status == QUERY_EXTERNAL_BLOCKED_NXRA)
blocked++;
// Update lasttimestamp variable with timestamp of the latest stored query
if(queries[i].timestamp > newlasttimestamp)
newlasttimestamp = queries[i].timestamp;
if(query->timestamp > newlasttimestamp)
newlasttimestamp = query->timestamp;
}
// Finish prepared statement
@@ -557,7 +555,7 @@ void save_to_DB(void)
// in the database only if all queries have been saved successfully
if(saved > 0 && saved_error == 0)
{
lastdbindex = i;
lastdbindex = queryID;
db_set_FTL_property(DB_LASTTIMESTAMP, newlasttimestamp);
}
@@ -732,15 +730,15 @@ void read_data_from_DB(void)
continue;
}
const char * domain = (const char *)sqlite3_column_text(stmt, 4);
if(domain == NULL)
const char * domainname = (const char *)sqlite3_column_text(stmt, 4);
if(domainname == NULL)
{
logg("DB warn: DOMAIN should never be NULL, %li", queryTimeStamp);
continue;
}
const char * client = (const char *)sqlite3_column_text(stmt, 5);
if(client == NULL)
const char * clientIP = (const char *)sqlite3_column_text(stmt, 5);
if(clientIP == NULL)
{
logg("DB warn: CLIENT should never be NULL, %li", queryTimeStamp);
continue;
@@ -748,7 +746,7 @@ void read_data_from_DB(void)
// Check if user wants to skip queries coming from localhost
if(config.ignore_localhost &&
(strcmp(client, "127.0.0.1") == 0 || strcmp(client, "::1") == 0))
(strcmp(clientIP, "127.0.0.1") == 0 || strcmp(clientIP, "::1") == 0))
{
continue;
}
@@ -769,8 +767,8 @@ void read_data_from_DB(void)
// Obtain IDs only after filtering which queries we want to keep
const int timeidx = getOverTimeID(queryTimeStamp);
const int domainID = findDomainID(domain);
const int clientID = findClientID(client, true);
const int domainID = findDomainID(domainname);
const int clientID = findClientID(clientIP, true);
// Ensure we have enough space in the queries struct
memory_check(QUERIES);
@@ -779,26 +777,26 @@ void read_data_from_DB(void)
const int queryIndex = counters->queries;
// Store this query in memory
validate_access("queries", queryIndex, false, __LINE__, __FUNCTION__, __FILE__);
validate_access("clients", clientID, true, __LINE__, __FUNCTION__, __FILE__);
queries[queryIndex].magic = MAGICBYTE;
queries[queryIndex].timestamp = queryTimeStamp;
queries[queryIndex].type = type;
queries[queryIndex].status = status;
queries[queryIndex].domainID = domainID;
queries[queryIndex].clientID = clientID;
queries[queryIndex].forwardID = forwardID;
queries[queryIndex].timeidx = timeidx;
queries[queryIndex].db = dbid;
queries[queryIndex].id = 0;
queries[queryIndex].complete = true; // Mark as all information is available
queries[queryIndex].response = 0;
queries[queryIndex].dnssec = DNSSEC_UNKNOWN;
queries[queryIndex].reply = REPLY_UNKNOWN;
queriesData* query = getQuery(queryIndex, false);
query->magic = MAGICBYTE;
query->timestamp = queryTimeStamp;
query->type = type;
query->status = status;
query->domainID = domainID;
query->clientID = clientID;
query->forwardID = forwardID;
query->timeidx = timeidx;
query->db = dbid;
query->id = 0;
query->complete = true; // Mark as all information is available
query->response = 0;
query->dnssec = DNSSEC_UNKNOWN;
query->reply = REPLY_UNKNOWN;
// Set lastQuery timer and add one query for network table
clients[clientID].lastQuery = queryTimeStamp;
clients[clientID].numQueriesARP++;
clientsData* client = getClient(clientID, true);
client->lastQuery = queryTimeStamp;
client->numQueriesARP++;
// Handle type counters
if(type >= TYPE_A && type < TYPE_MAX)
@@ -810,7 +808,7 @@ void read_data_from_DB(void)
// Update overTime data
overTime[timeidx].total++;
// Update overTime data structure with the new client
clients[clientID].overTime[timeidx]++;
client->overTime[timeidx]++;
// Increase DNS queries counter
counters->queries++;
@@ -829,8 +827,10 @@ void read_data_from_DB(void)
case QUERY_EXTERNAL_BLOCKED_NULL: // Blocked by external provider
case QUERY_EXTERNAL_BLOCKED_NXRA: // Blocked by external provider
counters->blocked++;
domains[domainID].blockedcount++;
clients[clientID].blockedcount++;
// Get domain pointer
domainsData* domain = getDomain(domainID, true);
domain->blockedcount++;
client->blockedcount++;
// Update overTime data structure
overTime[timeidx].blocked++;
break;

View File

@@ -17,112 +17,116 @@ void strtolower(char *str)
while(str[i]){ str[i] = tolower(str[i]); i++; }
}
int findForwardID(const char * forward, const bool count)
int findForwardID(const char * forwardString, const bool count)
{
int i, forwardID = -1;
if(counters->forwarded > 0)
validate_access("forwarded", counters->forwarded-1, true, __LINE__, __FUNCTION__, __FILE__);
// Go through already knows forward servers and see if we used one of those
for(i=0; i < counters->forwarded; i++)
for(int forwardID=0; forwardID < counters->forwarded; forwardID++)
{
if(strcmp(getstr(forwarded[i].ippos), forward) == 0)
// Get forward pointer
forwardedData* forward = getForward(forwardID, true);
if(strcmp(getstr(forward->ippos), forwardString) == 0)
{
forwardID = i;
if(count) forwarded[forwardID].count++;
if(count) forward->count++;
return forwardID;
}
}
// This forward server is not known
// Store ID
forwardID = counters->forwarded;
logg("New forward server: %s (%i/%u)", forward, forwardID, counters->forwarded_MAX);
const int forwardID = counters->forwarded;
logg("New forward server: %s (%i/%u)", forwardString, forwardID, counters->forwarded_MAX);
// Check struct size
memory_check(FORWARDED);
validate_access("forwarded", forwardID, false, __LINE__, __FUNCTION__, __FILE__);
// Get forward pointer
forwardedData* forward = getForward(forwardID, false);
// Set magic byte
forwarded[forwardID].magic = MAGICBYTE;
forward->magic = MAGICBYTE;
// Initialize its counter
if(count)
forwarded[forwardID].count = 1;
forward->count = 1;
else
forwarded[forwardID].count = 0;
forward->count = 0;
// Save forward destination IP address
forwarded[forwardID].ippos = addstr(forward);
forwarded[forwardID].failed = 0;
forward->ippos = addstr(forwardString);
forward->failed = 0;
// Initialize forward hostname
// 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
forwarded[forwardID].new = true;
forwarded[forwardID].namepos = 0; // 0 -> string with length zero
forward->new = true;
forward->namepos = 0; // 0 -> string with length zero
// Increase counter by one
counters->forwarded++;
return forwardID;
}
int findDomainID(const char *domain)
int findDomainID(const char *domainString)
{
int i;
if(counters->domains > 0)
validate_access("domains", counters->domains-1, true, __LINE__, __FUNCTION__, __FILE__);
for(i=0; i < counters->domains; i++)
for(int domainID = 0; domainID < counters->domains; domainID++)
{
// Get domain pointer
domainsData* domain = getDomain(domainID, true);
// Quick test: Does the domain start with the same character?
if(getstr(domains[i].domainpos)[0] != domain[0])
if(getstr(domain->domainpos)[0] != domainString[0])
continue;
// If so, compare the full domain using strcmp
if(strcmp(getstr(domains[i].domainpos), domain) == 0)
if(strcmp(getstr(domain->domainpos), domainString) == 0)
{
domains[i].count++;
return i;
domain->count++;
return domainID;
}
}
// If we did not return until here, then this domain is not known
// Store ID
int domainID = counters->domains;
const int domainID = counters->domains;
// Check struct size
memory_check(DOMAINS);
validate_access("domains", domainID, false, __LINE__, __FUNCTION__, __FILE__);
// Get domain pointer
domainsData* domain = getDomain(domainID, false);
// Set magic byte
domains[domainID].magic = MAGICBYTE;
domain->magic = MAGICBYTE;
// Set its counter to 1
domains[domainID].count = 1;
domain->count = 1;
// Set blocked counter to zero
domains[domainID].blockedcount = 0;
domain->blockedcount = 0;
// Store domain name - no need to check for NULL here as it doesn't harm
domains[domainID].domainpos = addstr(domain);
domain->domainpos = addstr(domainString);
// RegEx needs to be evaluated for this new domain
domains[domainID].regexmatch = REGEX_UNKNOWN;
domain->regexmatch = REGEX_UNKNOWN;
// Increase counter by one
counters->domains++;
return domainID;
}
int findClientID(const char *client, const bool count)
int findClientID(const char *clientIP, const bool count)
{
// Compare content of client against known client IP addresses
if(counters->clients > 0)
validate_access("clients", counters->clients-1, true, __LINE__, __FUNCTION__, __FILE__);
for(int i=0; i < counters->clients; i++)
for(int clientID=0; clientID < counters->clients; clientID++)
{
// Get client pointer
clientsData* client = getClient(clientID, true);
// Quick test: Does the clients IP start with the same character?
if(getstr(clients[i].ippos)[0] != client[0])
if(getstr(client->ippos)[0] != clientIP[0])
continue;
// If so, compare the full IP using strcmp
if(strcmp(getstr(clients[i].ippos), client) == 0)
if(strcmp(getstr(client->ippos), clientIP) == 0)
{
// Add one if count == true (do not add one, e.g., during ARP table processing)
if(count) clients[i].count++;
return i;
if(count) client->count++;
return clientID;
}
}
@@ -133,33 +137,35 @@ int findClientID(const char *client, const bool count)
// If we did not return until here, then this client is definitely new
// Store ID
int clientID = counters->clients;
const int clientID = counters->clients;
// Check struct size
memory_check(CLIENTS);
validate_access("clients", clientID, false, __LINE__, __FUNCTION__, __FILE__);
// Get client pointer
clientsData* client = getClient(clientID, false);
// Set magic byte
clients[clientID].magic = MAGICBYTE;
client->magic = MAGICBYTE;
// Set its counter to 1
clients[clientID].count = 1;
client->count = 1;
// Initialize blocked count to zero
clients[clientID].blockedcount = 0;
client->blockedcount = 0;
// Store client IP - no need to check for NULL here as it doesn't harm
clients[clientID].ippos = addstr(client);
client->ippos = addstr(clientIP);
// Initialize client hostname
// 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
clients[clientID].new = true;
clients[clientID].namepos = 0;
client->new = true;
client->namepos = 0;
// No query seen so far
clients[clientID].lastQuery = 0;
clients[clientID].numQueriesARP = 0;
client->lastQuery = 0;
client->numQueriesARP = 0;
// Initialize client-specific overTime data
for(int i = 0; i < OVERTIME_SLOTS; i++)
clients[clientID].overTime[i] = 0;
client->overTime[i] = 0;
// Increase counter by one
counters->clients++;
@@ -183,10 +189,14 @@ bool isValidIPv6(const char *addr)
// only when appropriate for the requested query
const char *getDomainString(const int queryID)
{
if(queries[queryID].privacylevel < PRIVACY_HIDE_DOMAINS)
const queriesData* query = getQuery(queryID, true);
if(query->privacylevel < PRIVACY_HIDE_DOMAINS)
{
validate_access("domains", queries[queryID].domainID, true, __LINE__, __FUNCTION__, __FILE__);
return getstr(domains[queries[queryID].domainID].domainpos);
// Get domain pointer
const domainsData* domain = getDomain(query->domainID, true);
// Return string
return getstr(domain->domainpos);
}
else
return HIDDEN_DOMAIN;
@@ -196,10 +206,14 @@ const char *getDomainString(const int queryID)
// only when appropriate for the requested query
const char *getClientIPString(const int queryID)
{
if(queries[queryID].privacylevel < PRIVACY_HIDE_DOMAINS_CLIENTS)
const queriesData* query = getQuery(queryID, false);
if(query->privacylevel < PRIVACY_HIDE_DOMAINS_CLIENTS)
{
validate_access("clients", queries[queryID].clientID, true, __LINE__, __FUNCTION__, __FILE__);
return getstr(clients[queries[queryID].clientID].ippos);
// Get client pointer
const clientsData* client = getClient(query->clientID, false);
// Return string
return getstr(client->ippos);
}
else
return HIDDEN_CLIENT;
@@ -209,10 +223,14 @@ const char *getClientIPString(const int queryID)
// only when appropriate for the requested query
const char *getClientNameString(const int queryID)
{
if(queries[queryID].privacylevel < PRIVACY_HIDE_DOMAINS_CLIENTS)
const queriesData* query = getQuery(queryID, true);
if(query->privacylevel < PRIVACY_HIDE_DOMAINS_CLIENTS)
{
validate_access("clients", queries[queryID].clientID, true, __LINE__, __FUNCTION__, __FILE__);
return getstr(clients[queries[queryID].clientID].namepos);
// Get client pointer
const clientsData* client = getClient(query->clientID, true);
// Return string
return getstr(client->namepos);
}
else
return HIDDEN_CLIENT;

View File

@@ -82,34 +82,29 @@ void _FTL_new_query(const unsigned int flags, const char *name, const struct all
memory_check(QUERIES);
const int queryID = counters->queries;
// Convert domain to lower case
char *domain = strdup(name);
strtolower(domain);
// If domain is "pi.hole" we skip this query
if(strcmp(domain, "pi.hole") == 0)
if(strcasecmp(name, "pi.hole") == 0)
{
// free memory already allocated here
free(domain);
unlock_shm();
return;
}
// Store plain text domain in buffer for regex validation
char *domainbuffer = strdup(domain);
// Convert domain to lower case
char *domainString = strdup(name);
strtolower(domainString);
// Get client IP address
char dest[ADDRSTRLEN];
inet_ntop((flags & F_IPV4) ? AF_INET : AF_INET6, addr, dest, ADDRSTRLEN);
char *client = strdup(dest);
strtolower(client);
char *clientIP = strdup(dest);
strtolower(clientIP);
// Check if user wants to skip queries coming from localhost
if(config.ignore_localhost &&
(strcmp(client, "127.0.0.1") == 0 || strcmp(client, "::1") == 0))
(strcmp(clientIP, "127.0.0.1") == 0 || strcmp(clientIP, "::1") == 0))
{
free(domain);
free(client);
free(domainString);
free(clientIP);
unlock_shm();
return;
}
@@ -119,7 +114,7 @@ void _FTL_new_query(const unsigned int flags, const char *name, const struct all
if(config.debug & DEBUG_QUERIES)
{
logg("**** new %s %s \"%s\" from %s (ID %i, FTL %i, %s:%i)",
proto, types, domain, client, id, queryID, file, line);
proto, types, domainString, clientIP, id, queryID, file, line);
}
// Update counters
@@ -135,43 +130,42 @@ void _FTL_new_query(const unsigned int flags, const char *name, const struct all
{
// Don't process this query further here, we already counted it
if(config.debug & DEBUG_QUERIES) logg("Notice: Skipping new query: %s (%i)", types, id);
free(domain);
free(domainbuffer);
free(client);
free(domainString);
free(clientIP);
unlock_shm();
return;
}
// Go through already knows domains and see if it is one of them
const int domainID = findDomainID(domain);
const int domainID = findDomainID(domainString);
// Go through already knows clients and see if it is one of them
const int clientID = findClientID(client, true);
const int clientID = findClientID(clientIP, true);
// Save everything
validate_access("queries", queryID, false, __LINE__, __FUNCTION__, __FILE__);
queries[queryID].magic = MAGICBYTE;
queries[queryID].timestamp = querytimestamp;
queries[queryID].type = querytype;
queries[queryID].status = QUERY_UNKNOWN;
queries[queryID].domainID = domainID;
queries[queryID].clientID = clientID;
queries[queryID].timeidx = timeidx;
queriesData* query = getQuery(queryID, false);
query->magic = MAGICBYTE;
query->timestamp = querytimestamp;
query->type = querytype;
query->status = QUERY_UNKNOWN;
query->domainID = domainID;
query->clientID = clientID;
query->timeidx = timeidx;
// Initialize database rowID with zero, will be set when the query is stored in the long-term DB
queries[queryID].db = 0;
queries[queryID].id = id;
queries[queryID].complete = false;
queries[queryID].response = converttimeval(request);
query->db = 0;
query->id = id;
query->complete = false;
query->response = converttimeval(request);
// Initialize reply type
queries[queryID].reply = REPLY_UNKNOWN;
query->reply = REPLY_UNKNOWN;
// Store DNSSEC result for this domain
queries[queryID].dnssec = DNSSEC_UNSPECIFIED;
query->dnssec = DNSSEC_UNSPECIFIED;
// Check and apply possible privacy level rules
// The currently set privacy level (at the time the query is
// generated) is stored in the queries structure
get_privacy_level(NULL);
queries[queryID].privacylevel = config.privacylevel;
query->privacylevel = config.privacylevel;
// Increase DNS queries counter
counters->queries++;
@@ -181,16 +175,21 @@ void _FTL_new_query(const unsigned int flags, const char *name, const struct all
// Update overTime data
overTime[timeidx].total++;
// Get client pointer
clientsData* client = getClient(clientID, true);
// Update overTime data structure with the new client
clients[clientID].overTime[timeidx]++;
client->overTime[timeidx]++;
// Set lastQuery timer and add one query for network table
clients[clientID].lastQuery = querytimestamp;
clients[clientID].numQueriesARP++;
client->lastQuery = querytimestamp;
client->numQueriesARP++;
// Get domain pointer
domainsData* domain = getDomain(domainID, true);
// Try blocking regex if configured
validate_access("domains", domainID, false, __LINE__, __FUNCTION__, __FILE__);
if(domains[domainID].regexmatch == REGEX_UNKNOWN && blockingstatus != BLOCKING_DISABLED)
if(domain->regexmatch == REGEX_UNKNOWN && blockingstatus != BLOCKING_DISABLED)
{
// For minimal performance impact, we test the regex only when
// - regex checking is enabled, and
@@ -202,24 +201,23 @@ void _FTL_new_query(const unsigned int flags, const char *name, const struct all
// of a specific domain. The logic herein is:
// If matched, then compare against whitelist
// If in whitelist, negate matched so this function returns: not-to-be-blocked
if(match_regex(domainbuffer) && !in_whitelist(domainbuffer))
if(match_regex(domainString) && !in_whitelist(domainString))
{
// We have to block this domain
block_single_domain_regex(domainbuffer);
domains[domainID].regexmatch = REGEX_BLOCKED;
block_single_domain_regex(domainString);
domain->regexmatch = REGEX_BLOCKED;
}
else
{
// Explicitly mark as not blocked to skip regex test
// next time we see this domain
domains[domainID].regexmatch = REGEX_NOTBLOCKED;
domain->regexmatch = REGEX_NOTBLOCKED;
}
}
// Free allocated memory
free(client);
free(domain);
free(domainbuffer);
free(domainString);
free(clientIP);
// Release thread lock
unlock_shm();
@@ -234,16 +232,16 @@ static int findQueryID(const int id)
// We iterate from the most recent query down to at most MAXITER queries in the past to avoid
// iterating through the entire array of queries
// MAX(0, a) is used to return 0 in case a is negative (negative array indices are harmful)
// Validate access only once for the maximum index (all lower will work)
const int until = MAX(0, counters->queries-MAXITER);
const int start = MAX(0, counters->queries-1);
validate_access("queries", until, false, __LINE__, __FUNCTION__, __FILE__);
// Check UUIDs of queries
for(int i = start; i >= until; i--)
if(queries[i].id == id)
{
const queriesData* query = getQuery(i, true);
if(query->id == id)
return i;
}
// If not found
return -1;
@@ -273,8 +271,8 @@ void _FTL_forwarded(const unsigned int flags, const char *name, const struct all
if(config.debug & DEBUG_QUERIES) logg("**** forwarded %s to %s (ID %i, %s:%i)", name, forward, id, file, line);
// Save status and forwardID in corresponding query identified by dnsmasq's ID
const int i = findQueryID(id);
if(i < 0)
const int queryID = findQueryID(id);
if(queryID < 0)
{
// This may happen e.g. if the original query was a PTR query or "pi.hole"
// as we ignore them altogether
@@ -283,13 +281,16 @@ void _FTL_forwarded(const unsigned int flags, const char *name, const struct all
return;
}
// Get query pointer
queriesData* query = getQuery(queryID, true);
// Proceed only if
// - current query has not been marked as replied to so far
// (it could be that answers from multiple forward
// destinations are coming in for the same query)
// - the query was formally known as cached but had to be forwarded
// (this is a special case further described below)
if(queries[i].complete && queries[i].status != QUERY_CACHE)
if(query->complete && query->status != QUERY_CACHE)
{
free(forward);
unlock_shm();
@@ -299,11 +300,11 @@ void _FTL_forwarded(const unsigned int flags, const char *name, const struct all
// Get ID of forward destination, create new forward destination record
// if not found in current data structure
const int forwardID = findForwardID(forward, true);
queries[i].forwardID = forwardID;
query->forwardID = forwardID;
const unsigned int timeidx = queries[i].timeidx;
const unsigned int timeidx = query->timeidx;
if(queries[i].status == QUERY_CACHE)
if(query->status == QUERY_CACHE)
{
// Detect if we cached the <CNAME> but need to ask the upstream
// servers for the actual IPs now, we remove this query from the
@@ -330,7 +331,7 @@ void _FTL_forwarded(const unsigned int flags, const char *name, const struct all
gettimeofday(&response, 0);
// Reset timer, shift slightly into the past to acknowledge the time
// FTLDNS needed to look up the CNAME in its cache
queries[i].response = converttimeval(response) - queries[i].response;
query->response = converttimeval(response) - query->response;
}
else
{
@@ -338,14 +339,14 @@ void _FTL_forwarded(const unsigned int flags, const char *name, const struct all
// Query is no longer unknown
counters->unknown--;
// Hereby, this query is now fully determined
queries[i].complete = true;
query->complete = true;
}
// Set query status to forwarded only after the
// if(queries[i].status == QUERY_CACHE) { ... }
// if(query->status == QUERY_CACHE) { ... }
// from above as otherwise this check will always
// be negative
queries[i].status = QUERY_FORWARDED;
query->status = QUERY_FORWARDED;
// Update overTime data
overTime[timeidx].forwarded++;
@@ -449,7 +450,10 @@ void _FTL_reply(const unsigned short flags, const char *name, const struct all_a
return;
}
if(queries[i].reply != REPLY_UNKNOWN)
// Get query pointer
queriesData* query = getQuery(i, true);
if(query->reply != REPLY_UNKNOWN)
{
// Nothing to be done here
unlock_shm();
@@ -457,18 +461,22 @@ void _FTL_reply(const unsigned short flags, const char *name, const struct all_a
}
// Determine if this reply is an exact match for the queried domain
const int domainID = queries[i].domainID;
validate_access("domains", domainID, true, __LINE__, __FUNCTION__, __FILE__);
const bool isExactMatch = (name != NULL && strcmp(getstr(domains[domainID].domainpos), name) == 0);
const int domainID = query->domainID;
if((flags & F_CONFIG) && isExactMatch && !queries[i].complete)
// Get domain pointer
domainsData* domain = getDomain(domainID, true);
// Check if this domain matches exactly
const bool isExactMatch = (name != NULL && strcmp(getstr(domain->domainpos), name) == 0);
if((flags & F_CONFIG) && isExactMatch && !query->complete)
{
// Answered from local configuration, might be a wildcard or user-provided
// This query is no longer unknown
counters->unknown--;
// Get time index
const unsigned int timeidx = queries[i].timeidx;
const unsigned int timeidx = query->timeidx;
if(strcmp(answer, "(NXDOMAIN)") == 0 ||
strcmp(answer, "0.0.0.0") == 0 ||
@@ -478,13 +486,17 @@ void _FTL_reply(const unsigned short flags, const char *name, const struct all_a
counters->blocked++;
overTime[timeidx].blocked++;
validate_access("domains", queries[i].domainID, true, __LINE__, __FUNCTION__, __FILE__);
domains[queries[i].domainID].blockedcount++;
// Update domain blocked counter
domain->blockedcount++;
validate_access("clients", queries[i].clientID, true, __LINE__, __FUNCTION__, __FILE__);
clients[queries[i].clientID].blockedcount++;
// Get client pointer
clientsData* client = getClient(query->clientID, true);
queries[i].status = QUERY_WILDCARD;
// Update client blocked counter
client->blockedcount++;
// Set query status to wildcard
query->status = QUERY_WILDCARD;
}
else
{
@@ -492,22 +504,22 @@ void _FTL_reply(const unsigned short flags, const char *name, const struct all_a
counters->cached++;
overTime[timeidx].cached++;
queries[i].status = QUERY_CACHE;
query->status = QUERY_CACHE;
}
// Save reply type and update individual reply counters
save_reply_type(flags, i, response);
// Hereby, this query is now fully determined
queries[i].complete = true;
query->complete = true;
}
else if((flags & F_FORWARD) && isExactMatch)
{
// Only proceed if query is not already known
// to have been blocked by Quad9
if(queries[i].reply != QUERY_EXTERNAL_BLOCKED_IP &&
queries[i].reply != QUERY_EXTERNAL_BLOCKED_NULL &&
queries[i].reply != QUERY_EXTERNAL_BLOCKED_NXRA)
if(query->reply != QUERY_EXTERNAL_BLOCKED_IP &&
query->reply != QUERY_EXTERNAL_BLOCKED_NULL &&
query->reply != QUERY_EXTERNAL_BLOCKED_NXRA)
{
// Save reply type and update individual reply counters
save_reply_type(flags, i, response);
@@ -522,7 +534,7 @@ void _FTL_reply(const unsigned short flags, const char *name, const struct all_a
// Example:
// Question: PTR 8.8.8.8
// will lead to:
// domains[domainID].domain = 8.8.8.8.in-addr.arpa
// domain->domain = 8.8.8.8.in-addr.arpa
// and will return
// name = google-public-dns-a.google.com
// Hence, isExactMatch is always false
@@ -530,7 +542,7 @@ void _FTL_reply(const unsigned short flags, const char *name, const struct all_a
// Save reply type and update individual reply counters
save_reply_type(flags, i, response);
}
else if(isExactMatch && !queries[i].complete)
else if(isExactMatch && !query->complete)
{
logg("*************************** unknown REPLY ***************************");
print_flags(flags);
@@ -576,8 +588,10 @@ static void detect_blocked_IP(const unsigned short flags, const char* answer, co
{
if(config.debug & DEBUG_EXTBLOCKED)
{
const queriesData* query = getQuery(queryID, true);
const domainsData* domain = getDomain(query->domainID, true);
logg("Upstream responded with known blocking page (IPv4), ID %i:\n\t\"%s\" -> \"%s\"",
queryID, getstr(domains[queryID].domainpos), answer);
queryID, getstr(domain->domainpos), answer);
}
// Update status
@@ -595,8 +609,10 @@ static void detect_blocked_IP(const unsigned short flags, const char* answer, co
{
if(config.debug & DEBUG_EXTBLOCKED)
{
const queriesData* query = getQuery(queryID, true);
const domainsData* domain = getDomain(query->domainID, true);
logg("Upstream responded with known blocking page (IPv6), ID %i:\n\t\"%s\" -> \"%s\"",
queryID, getstr(domains[queryID].domainpos), answer);
queryID, getstr(domain->domainpos), answer);
}
// Update status
@@ -611,8 +627,10 @@ static void detect_blocked_IP(const unsigned short flags, const char* answer, co
{
if(config.debug & DEBUG_EXTBLOCKED)
{
const queriesData* query = getQuery(queryID, true);
const domainsData* domain = getDomain(query->domainID, true);
logg("Upstream responded with 0.0.0.0, ID %i:\n\t\"%s\" -> \"%s\"",
queryID, getstr(domains[queryID].domainpos), answer);
queryID, getstr(domain->domainpos), answer);
}
// Update status
@@ -624,8 +642,10 @@ static void detect_blocked_IP(const unsigned short flags, const char* answer, co
{
if(config.debug & DEBUG_EXTBLOCKED)
{
const queriesData* query = getQuery(queryID, true);
const domainsData* domain = getDomain(query->domainID, true);
logg("Upstream responded with ::, ID %i:\n\t\"%s\" -> \"%s\"",
queryID, getstr(domains[queryID].domainpos), answer);
queryID, getstr(domain->domainpos), answer);
}
// Update status
@@ -635,34 +655,43 @@ static void detect_blocked_IP(const unsigned short flags, const char* answer, co
static void query_externally_blocked(const int queryID, const unsigned char status)
{
// Get query pointer
queriesData* query = getQuery(queryID, true);
// Get time index
const unsigned int timeidx = query->timeidx;
// If query is already known to be externally blocked,
// then we have nothing to do here
if(queries[queryID].status == QUERY_EXTERNAL_BLOCKED_IP ||
queries[queryID].status == QUERY_EXTERNAL_BLOCKED_NULL ||
queries[queryID].status == QUERY_EXTERNAL_BLOCKED_NXRA)
if(query->status == QUERY_EXTERNAL_BLOCKED_IP ||
query->status == QUERY_EXTERNAL_BLOCKED_NULL ||
query->status == QUERY_EXTERNAL_BLOCKED_NXRA)
return;
// Get time index of this query
const unsigned int timeidx = queries[queryID].timeidx;
// Correct counters if necessary ...
if(queries[queryID].status == QUERY_FORWARDED)
if(query->status == QUERY_FORWARDED)
{
counters->forwardedqueries--;
overTime[timeidx].forwarded--;
validate_access("forwarded", queries[queryID].forwardID, true, __LINE__, __FUNCTION__, __FILE__);
forwarded[queries[queryID].forwardID].count--;
// Get forward pointer
forwardedData* forward = getForward(query->forwardID, true);
forward->count--;
}
// ... but as blocked
// Mark query as blocked
counters->blocked++;
overTime[timeidx].blocked++;
validate_access("domains", queries[queryID].domainID, true, __LINE__, __FUNCTION__, __FILE__);
domains[queries[queryID].domainID].blockedcount++;
validate_access("clients", queries[queryID].clientID, true, __LINE__, __FUNCTION__, __FILE__);
clients[queries[queryID].clientID].blockedcount++;
// Update status
queries[queryID].status = status;
// Get domain pointer
domainsData* domain = getDomain(query->domainID, true);
domain->blockedcount++;
// Get client pointer
clientsData* client = getClient(query->clientID, true);
client->blockedcount++;
// Set query status
query->status = status;
}
void _FTL_cache(const unsigned int flags, const char *name, const struct all_addr *addr,
@@ -680,19 +709,13 @@ void _FTL_cache(const unsigned int flags, const char *name, const struct all_add
inet_ntop((flags & F_IPV4) ? AF_INET : AF_INET6, addr, dest, ADDRSTRLEN);
}
// Convert domain to lower case
char *domain = strdup(name);
strtolower(domain);
// If domain is "pi.hole", we skip this query
if(strcmp(domain, "pi.hole") == 0)
// We compare case-insensitive here
if(strcasecmp(name, "pi.hole") == 0)
{
// free memory already allocated here
free(domain);
unlock_shm();
return;
}
free(domain);
// Debug logging
if(config.debug & DEBUG_QUERIES)
@@ -745,10 +768,20 @@ void _FTL_cache(const unsigned int flags, const char *name, const struct all_add
}
const int queryID = findQueryID(id);
if(queryID < 0 || queries[queryID].complete)
if(queryID < 0)
{
// This may happen e.g. if the original query was a PTR query or "pi.hole"
// as we ignore them altogether or if the query is already complete
// as we ignore them altogether
unlock_shm();
return;
}
// Get query pointer
queriesData* query = getQuery(queryID, true);
if(query->complete)
{
// Skip query if already complete
unlock_shm();
return;
}
@@ -757,25 +790,25 @@ void _FTL_cache(const unsigned int flags, const char *name, const struct all_add
counters->unknown--;
// Get time index
const unsigned int timeidx = queries[queryID].timeidx;
const unsigned int timeidx = query->timeidx;
const int domainID = queries[queryID].domainID;
validate_access("domains", domainID, true, __LINE__, __FUNCTION__, __FILE__);
// Get domain pointer
domainsData* domain = getDomain(query->domainID, true);
const int clientID = queries[queryID].clientID;
validate_access("clients", clientID, true, __LINE__, __FUNCTION__, __FILE__);
// Get client pointer
clientsData* client = getClient(query->clientID, true);
// Mark this query as blocked if domain was matched by a regex
if(domains[domainID].regexmatch == REGEX_BLOCKED)
if(domain->regexmatch == REGEX_BLOCKED)
requesttype = QUERY_WILDCARD;
queries[queryID].status = requesttype;
query->status = requesttype;
// Detect if returned IP indicates that this query was blocked
detect_blocked_IP(flags, dest, queryID);
// Re-read requesttype as detect_blocked_IP() might have changed it
requesttype = queries[queryID].status;
requesttype = query->status;
// Handle counters accordingly
switch(requesttype)
@@ -785,8 +818,8 @@ void _FTL_cache(const unsigned int flags, const char *name, const struct all_add
case QUERY_WILDCARD: // regex blocked
counters->blocked++;
overTime[timeidx].blocked++;
domains[domainID].blockedcount++;
clients[clientID].blockedcount++;
domain->blockedcount++;
client->blockedcount++;
break;
case QUERY_CACHE: // cached from one of the lists
counters->cached++;
@@ -804,7 +837,7 @@ void _FTL_cache(const unsigned int flags, const char *name, const struct all_add
save_reply_type(flags, queryID, response);
// Hereby, this query is now fully determined
queries[queryID].complete = true;
query->complete = true;
}
else
{
@@ -831,21 +864,25 @@ void _FTL_dnssec(const int status, const int id, const char* file, const int lin
return;
}
// Get query pointer
queriesData* query = getQuery(queryID, true);
// Debug logging
if(config.debug & DEBUG_QUERIES)
{
const int domainID = queries[queryID].domainID;
validate_access("domains", domainID, true, __LINE__, __FUNCTION__, __FILE__);
logg("**** got DNSSEC details for %s: %i (ID %i, %s:%i)", getstr(domains[domainID].domainpos), status, id, file, line);
// Get domain pointer
const domainsData* domain = getDomain(query->domainID, true);
logg("**** got DNSSEC details for %s: %i (ID %i, %s:%i)", getstr(domain->domainpos), status, id, file, line);
}
// Iterate through possible values
if(status == STAT_SECURE)
queries[queryID].dnssec = DNSSEC_SECURE;
query->dnssec = DNSSEC_SECURE;
else if(status == STAT_INSECURE)
queries[queryID].dnssec = DNSSEC_INSECURE;
query->dnssec = DNSSEC_INSECURE;
else
queries[queryID].dnssec = DNSSEC_BOGUS;
query->dnssec = DNSSEC_BOGUS;
unlock_shm();
}
@@ -870,35 +907,41 @@ void _FTL_upstream_error(const unsigned int rcode, const int id, const char* fil
unlock_shm();
return;
}
// Get query pointer
queriesData* query = getQuery(queryID, true);
// Translate dnsmasq's rcode into something we can use
const char *rcodestr = NULL;
switch(rcode)
{
case SERVFAIL:
rcodestr = "SERVFAIL";
queries[queryID].reply = REPLY_SERVFAIL;
query->reply = REPLY_SERVFAIL;
break;
case REFUSED:
rcodestr = "REFUSED";
queries[queryID].reply = REPLY_REFUSED;
query->reply = REPLY_REFUSED;
break;
case NOTIMP:
rcodestr = "NOT IMPLEMENTED";
queries[queryID].reply = REPLY_NOTIMP;
query->reply = REPLY_NOTIMP;
break;
default:
rcodestr = "UNKNOWN";
queries[queryID].reply = REPLY_OTHER;
query->reply = REPLY_OTHER;
break;
}
// Debug logging
if(config.debug & DEBUG_QUERIES)
{
const int domainID = queries[queryID].domainID;
validate_access("domains", domainID, true, __LINE__, __FUNCTION__, __FILE__);
logg("**** got error report for %s: %s (ID %i, %s:%i)", getstr(domains[domainID].domainpos), rcodestr, id, file, line);
if(queries[queryID].reply == REPLY_OTHER)
// Get domain pointer
const domainsData* domain = getDomain(query->domainID, true);
logg("**** got error report for %s: %s (ID %i, %s:%i)", getstr(domain->domainpos), rcodestr, id, file, line);
if(query->reply == REPLY_OTHER)
{
logg("Unknown rcode = %i", rcode);
}
@@ -937,14 +980,16 @@ void _FTL_header_analysis(const unsigned char header4, const unsigned int rcode,
return;
}
// Get query pointer
queriesData* query = getQuery(queryID, true);
if(config.debug & DEBUG_QUERIES)
{
const int domainID = queries[queryID].domainID;
validate_access("domains", domainID, true, __LINE__, __FUNCTION__, __FILE__);
logg("**** %s externally blocked (ID %i, FTL %i, %s:%i)", getstr(domains[domainID].domainpos), id, queryID, file, line);
// Get domain pointer
const domainsData* domain = getDomain(query->domainID, true);
logg("**** %s externally blocked (ID %i, FTL %i, %s:%i)", getstr(domain->domainpos), id, queryID, file, line);
}
// Get response time
struct timeval response;
gettimeofday(&response, 0);
@@ -977,50 +1022,52 @@ void print_flags(const unsigned int flags)
void save_reply_type(const unsigned int flags, const int queryID, const struct timeval response)
{
// Get query pointer
queriesData* query = getQuery(queryID, true);
// Iterate through possible values
validate_access("queries", queryID, false, __LINE__, __FUNCTION__, __FILE__);
if(flags & F_NEG)
{
if(flags & F_NXDOMAIN)
{
// NXDOMAIN
queries[queryID].reply = REPLY_NXDOMAIN;
query->reply = REPLY_NXDOMAIN;
counters->reply_NXDOMAIN++;
}
else
{
// NODATA(-IPv6)
queries[queryID].reply = REPLY_NODATA;
query->reply = REPLY_NODATA;
counters->reply_NODATA++;
}
}
else if(flags & F_CNAME)
{
// <CNAME>
queries[queryID].reply = REPLY_CNAME;
query->reply = REPLY_CNAME;
counters->reply_CNAME++;
}
else if(flags & F_REVERSE)
{
// reserve lookup
queries[queryID].reply = REPLY_DOMAIN;
query->reply = REPLY_DOMAIN;
counters->reply_domain++;
}
else if(flags & F_RRNAME)
{
// TXT query
queries[queryID].reply = REPLY_RRNAME;
query->reply = REPLY_RRNAME;
}
else
{
// Valid IP
queries[queryID].reply = REPLY_IP;
query->reply = REPLY_IP;
counters->reply_IP++;
}
// Save response time (relative time)
queries[queryID].response = converttimeval(response) -
queries[queryID].response;
query->response = converttimeval(response) -
query->response;
}
pthread_t telnet_listenthreadv4;
@@ -1133,15 +1180,19 @@ void _FTL_forwarding_failed(const struct server *server, const char* file, const
inet_ntop(AF_INET6, &server->addr.in6.sin6_addr, dest, ADDRSTRLEN);
// Convert forward to lower case
char *forward = strdup(dest);
strtolower(forward);
const int forwardID = findForwardID(forward, false);
char *forwarddest = strdup(dest);
strtolower(forwarddest);
const int forwardID = findForwardID(forwarddest, false);
if(config.debug & DEBUG_QUERIES) logg("**** forwarding to %s (ID %i, %s:%i) failed", dest, forwardID, file, line);
forwarded[forwardID].failed++;
// Get forward pointer
forwardedData* forward = getForward(forwardID, true);
free(forward);
// Update counter
forward->failed++;
free(forwarddest);
unlock_shm();
return;
}

View File

@@ -7,6 +7,9 @@
*
* This file is copyright under the latest version of the EUPL.
* Please see LICENSE file for your rights under this license. */
#ifndef DNSMASQ_INTERFACE_H
#define DNSMASQ_INTERFACE_H
extern int socketfd, telnetfd4, telnetfd6;
extern unsigned char* pihole_privacylevel;
enum { TCP, UDP };
@@ -38,3 +41,5 @@ void _FTL_upstream_error(const unsigned int rcode, const int id, const char* fil
void FTL_dnsmasq_reload(void);
void FTL_fork_and_bind_sockets(struct passwd *ent_pw);
int FTL_listsfile(const char* filename, unsigned int index, FILE *f, int cache_size, struct crec **rhash, int hashsz);
#endif // DNSMASQ_INTERFACE_H

79
gc.c
View File

@@ -42,37 +42,42 @@ void *GC_thread(void *val)
mintime -= mintime % 3600;
mintime += 3600;
if(config.debug & DEBUG_GC) timer_start(GC_TIMER);
int removed = 0;
if(config.debug & DEBUG_GC) logg("GC starting, mintime: %lu %s", mintime, ctime(&mintime));
if(config.debug & DEBUG_GC)
{
timer_start(GC_TIMER);
char timestring[84] = "";
get_timestr(timestring, mintime);
logg("GC starting, mintime: %s (%lu)", timestring, mintime);
}
// Process all queries
int removed = 0;
for(long int i=0; i < counters->queries; i++)
{
validate_access("queries", i, true, __LINE__, __FUNCTION__, __FILE__);
queriesData* query = getQuery(i, true);
// Test if this query is too new
if(queries[i].timestamp > mintime)
if(query->timestamp > mintime)
break;
// Adjust client counter
const int clientID = queries[i].clientID;
validate_access("clients", clientID, true, __LINE__, __FUNCTION__, __FILE__);
clients[clientID].count--;
clientsData* client = getClient(query->clientID, true);
client->count--;
// Adjust total counters and total over time data
const int timeidx = queries[i].timeidx;
const int timeidx = query->timeidx;
overTime[timeidx].total--;
// Adjust corresponding overTime counters
clients[clientID].overTime[timeidx]--;
client->overTime[timeidx]--;
// Adjust domain counter (no overTime information)
const int domainID = queries[i].domainID;
validate_access("domains", domainID, true, __LINE__, __FUNCTION__, __FILE__);
domains[domainID].count--;
domainsData* domain = getDomain(query->domainID, true);
domain->count--;
// Get forward pointer
forwardedData* forward = getForward(query->forwardID, true);
// Change other counters according to status of this query
switch(queries[i].status)
switch(query->status)
{
case QUERY_UNKNOWN:
// Unknown (?)
@@ -80,9 +85,9 @@ void *GC_thread(void *val)
break;
case QUERY_FORWARDED:
// Forwarded to an upstream DNS server
// Adjust counters
counters->forwardedqueries--;
validate_access("forwarded", queries[i].forwardID, true, __LINE__, __FUNCTION__, __FILE__);
forwarded[queries[i].forwardID].count--;
forward->count--;
overTime[timeidx].forwarded--;
break;
case QUERY_CACHE:
@@ -98,8 +103,8 @@ void *GC_thread(void *val)
case QUERY_EXTERNAL_BLOCKED_NULL: // Blocked by upstream provider (fall through)
counters->blocked--;
overTime[timeidx].blocked--;
domains[domainID].blockedcount--;
clients[clientID].blockedcount--;
domain->blockedcount--;
client->blockedcount--;
break;
default:
/* That cannot happen */
@@ -107,7 +112,7 @@ void *GC_thread(void *val)
}
// Update reply counters
switch(queries[i].reply)
switch(query->reply)
{
case REPLY_NODATA: // NODATA(-IPv6)
counters->reply_NODATA--;
@@ -134,10 +139,10 @@ void *GC_thread(void *val)
}
// Update type counters
if(queries[i].type >= TYPE_A && queries[i].type < TYPE_MAX)
if(query->type >= TYPE_A && query->type < TYPE_MAX)
{
counters->querytype[queries[i].type-1]--;
overTime[timeidx].querytypedata[queries[i].type-1]--;
counters->querytype[query->type-1]--;
overTime[timeidx].querytypedata[query->type-1]--;
}
// Count removed queries
@@ -145,20 +150,24 @@ void *GC_thread(void *val)
}
// Move memory forward to keep only what we want
// Note: for overlapping memory blocks, memmove() is a safer approach than memcpy()
// Example: (I = now invalid, X = still valid queries, F = free space)
// Before: IIIIIIXXXXFF
// After: XXXXFFFFFFFF
memmove(&queries[0], &queries[removed], (counters->queries - removed)*sizeof(*queries));
// Only perform memory operations when we actually removed queries
if(removed > 0)
{
// Move memory forward to keep only what we want
// Note: for overlapping memory blocks, memmove() is a safer approach than memcpy()
// Example: (I = now invalid, X = still valid queries, F = free space)
// Before: IIIIIIXXXXFF
// After: XXXXFFFFFFFF
memmove(getQuery(0, true), getQuery(removed, true), (counters->queries - removed)*sizeof(queriesData));
// Update queries counter
counters->queries -= removed;
// Update DB index as total number of queries reduced
lastdbindex -= removed;
// Update queries counter
counters->queries -= removed;
// Update DB index as total number of queries reduced
lastdbindex -= removed;
// Zero out remaining memory (marked as "F" in the above example)
memset(&queries[counters->queries], 0, (counters->queries_MAX - counters->queries)*sizeof(*queries));
// ensure remaining memory is zeroed out (marked as "F" in the above example)
memset(getQuery(counters->queries, true), 0, (counters->queries_MAX - counters->queries)*sizeof(queriesData));
}
// Determine if overTime memory needs to get moved
moveOverTimeMemory(mintime);

9
log.c
View File

@@ -50,10 +50,11 @@ void open_FTL_log(const bool test)
}
}
static void get_timestr(char *timestring)
void get_timestr(char *timestring, const time_t timein)
{
const time_t t = time(NULL);
const struct tm tm = *localtime(&t);
struct tm tm;
localtime_r(&timein, &tm);
struct timeval tv;
gettimeofday(&tv, NULL);
const int millisec = tv.tv_usec/1000;
@@ -68,7 +69,7 @@ void __attribute__ ((format (gnu_printf, 1, 2))) logg(const char *format, ...)
pthread_mutex_lock(&lock);
get_timestr(timestring);
get_timestr(timestring, time(NULL));
// Get and log PID of current process to avoid ambiguities when more than one
// pihole-FTL instance is logging into the same file

100
memory.c
View File

@@ -36,107 +36,9 @@ logFileNamesStruct files = {
// Fixed size structs
countersStruct *counters = NULL;
overTimeData *overTime = NULL;
ConfigStruct config;
// Variable size array structs
queriesDataStruct *queries = NULL;
forwardedDataStruct *forwarded = NULL;
clientsDataStruct *clients = NULL;
domainsDataStruct *domains = NULL;
overTimeDataStruct *overTime = NULL;
void memory_check(const int which)
{
switch(which)
{
case QUERIES:
if(counters->queries >= counters->queries_MAX-1)
{
// Have to reallocate shared memory
queries = enlarge_shmem_struct(QUERIES);
if(queries == NULL)
{
logg("FATAL: Memory allocation failed! Exiting");
exit(EXIT_FAILURE);
}
}
break;
case FORWARDED:
if(counters->forwarded >= counters->forwarded_MAX-1)
{
// Have to reallocate shared memory
forwarded = enlarge_shmem_struct(FORWARDED);
if(forwarded == NULL)
{
logg("FATAL: Memory allocation failed! Exiting");
exit(EXIT_FAILURE);
}
}
break;
case CLIENTS:
if(counters->clients >= counters->clients_MAX-1)
{
// Have to reallocate shared memory
clients = enlarge_shmem_struct(CLIENTS);
if(clients == NULL)
{
logg("FATAL: Memory allocation failed! Exiting");
exit(EXIT_FAILURE);
}
}
break;
case DOMAINS:
if(counters->domains >= counters->domains_MAX-1)
{
// Have to reallocate shared memory
domains = enlarge_shmem_struct(DOMAINS);
if(domains == NULL)
{
logg("FATAL: Memory allocation failed! Exiting");
exit(EXIT_FAILURE);
}
}
break;
default:
/* That cannot happen */
logg("Fatal error in memory_check(%i)", which);
exit(EXIT_FAILURE);
break;
}
}
void validate_access(const char * name, const int pos, const bool testmagic,
const int line, const char * function, const char * file)
{
int limit = 0;
if(name[0] == 'c') limit = counters->clients_MAX;
else if(name[0] == 'd') limit = counters->domains_MAX;
else if(name[0] == 'q') limit = counters->queries_MAX;
else if(name[0] == 'f') limit = counters->forwarded_MAX;
else { logg("Validator error (range)"); killed = 1; }
if(pos >= limit || pos < 0)
{
logg("FATAL ERROR: Trying to access %s[%i], but maximum is %i", name, pos, limit);
logg(" found in %s() (%s:%i)", function, file, line);
}
// Don't test magic byte if detected potential out-of-bounds error
else if(testmagic)
{
unsigned char magic = 0x00;
if(name[0] == 'c') magic = clients[pos].magic;
else if(name[0] == 'd') magic = domains[pos].magic;
else if(name[0] == 'q') magic = queries[pos].magic;
else if(name[0] == 'f') magic = forwarded[pos].magic;
else { logg("Validator error (magic byte)"); killed = 1; }
if(magic != MAGICBYTE)
{
logg("FATAL ERROR: Trying to access %s[%i], but magic byte is %x", name, pos, magic);
logg(" found in %s() (%s:%i)", function, file, line);
}
}
}
// The special memory handling routines have to be the last ones in this source file
// as we restore the original definition of the strdup, free, calloc, and realloc
// functions in here, i.e. if anything extra would come below these lines, it would

View File

@@ -69,6 +69,7 @@ void parse_arp_cache(void)
time_t now = time(NULL);
// Start collecting database commands
lock_shm();
dbquery("BEGIN TRANSACTION");
// Read ARP cache line by line
@@ -114,20 +115,19 @@ void parse_arp_cache(void)
// is known to pihole-FTL
// false = do not create a new record if the client is
// unknown (only DNS requesting clients do this)
lock_shm();
int clientID = findClientID(ip, false);
unlock_shm();
// This client is known (by its IP address) to pihole-FTL if
// findClientID() returned a non-negative index
const bool clientKnown = clientID >= 0;
// Get hostname of this client if the client is known
const char *hostname = "";
if(clientKnown)
// Get client pointer
clientsData* client = NULL;
// This client is known (by its IP address) to pihole-FTL if
// findClientID() returned a non-negative index
if(clientID >= 0)
{
validate_access("clients", clientID, true, __LINE__, __FUNCTION__, __FILE__);
hostname = getstr(clients[clientID].namepos);
client = getClient(clientID, true);
hostname = getstr(client->namepos);
}
// Device not in database, add new entry
@@ -138,31 +138,31 @@ void parse_arp_cache(void)
"(ip,hwaddr,interface,firstSeen,lastQuery,numQueries,name,macVendor) "\
"VALUES (\'%s\',\'%s\',\'%s\',%lu, %ld, %u, \'%s\', \'%s\');",\
ip, hwaddr, iface, now,
clientKnown ? clients[clientID].lastQuery : 0L,
clientKnown ? clients[clientID].numQueriesARP : 0u,
client != NULL ? client->lastQuery : 0L,
client != NULL ? client->numQueriesARP : 0u,
hostname,
macVendor);
free(macVendor);
}
// Device in database AND client known to Pi-hole
else if(clientKnown)
else if(client != NULL)
{
// Update lastQuery. Only use new value if larger
// clients[clientID].lastQuery may be zero if this
// client->lastQuery may be zero if this
// client is only known from a database entry but has
// not been seen since then
dbquery("UPDATE network "\
"SET lastQuery = MAX(lastQuery, %ld) "\
"WHERE id = %i;",\
clients[clientID].lastQuery, dbID);
client->lastQuery, dbID);
// Update numQueries. Add queries seen since last update
// and reset counter afterwards
dbquery("UPDATE network "\
"SET numQueries = numQueries + %u "\
"WHERE id = %i;",\
clients[clientID].numQueriesARP, dbID);
clients[clientID].numQueriesARP = 0;
client->numQueriesARP, dbID);
client->numQueriesARP = 0;
// Store hostname if available
if(strlen(hostname) > 0)
@@ -183,9 +183,11 @@ void parse_arp_cache(void)
// Actually update the database
dbquery("COMMIT");
unlock_shm();
// Debug logging
if(config.debug & DEBUG_ARP) logg("ARP table processing (%i entries) took %.1f ms", entries, timer_elapsed_msec(ARP_TIMER));
if(config.debug & DEBUG_ARP)
logg("ARP table processing (%i entries) took %.1f ms", entries, timer_elapsed_msec(ARP_TIMER));
// Close file handle
fclose(arpfp);
@@ -200,13 +202,15 @@ static char* getMACVendor(const char* hwaddr)
if(stat(FTLfiles.macvendordb, &st) != 0)
{
// File does not exist
if(config.debug & DEBUG_ARP) logg("getMACVenor(%s): %s does not exist", hwaddr, FTLfiles.macvendordb);
if(config.debug & DEBUG_ARP)
logg("getMACVenor(%s): %s does not exist", hwaddr, FTLfiles.macvendordb);
return strdup("");
}
else if(strlen(hwaddr) != 17)
{
// MAC address is incomplete
if(config.debug & DEBUG_ARP) logg("getMACVenor(%s): MAC invalid (length %zu)", hwaddr, strlen(hwaddr));
if(config.debug & DEBUG_ARP)
logg("getMACVenor(%s): MAC invalid (length %zu)", hwaddr, strlen(hwaddr));
return strdup("");
}
@@ -270,7 +274,8 @@ void updateMACVendorRecords()
if(stat(FTLfiles.macvendordb, &st) != 0)
{
// File does not exist
if(config.debug & DEBUG_ARP) logg("updateMACVendorRecords(): %s does not exist", FTLfiles.macvendordb);
if(config.debug & DEBUG_ARP)
logg("updateMACVendorRecords(): %s does not exist", FTLfiles.macvendordb);
return;
}

View File

@@ -20,8 +20,11 @@ static void initSlot(const unsigned int index, const time_t timestamp)
{
// Possible debug printing
if(config.debug & DEBUG_OVERTIME)
{
logg("initSlot(%u, %lu): Zeroing overTime slot", index, timestamp);
}
// Initialize overTime entry
overTime[index].magic = MAGICBYTE;
overTime[index].timestamp = timestamp;
overTime[index].total = 0;
@@ -31,11 +34,18 @@ static void initSlot(const unsigned int index, const time_t timestamp)
// Zero all query types
for(unsigned int queryType = 0; queryType < TYPE_MAX-1; queryType++)
{
overTime[index].querytypedata[queryType] = 0;
}
// Zero overTime counter for all known clients
for(int clientID = 0; clientID < counters->clients; clientID++)
clients[clientID].overTime[index] = 0;
{
// Get client pointer
clientsData* client = getClient(clientID, true);
// Set overTime data to zero
client->overTime[index] = 0;
}
}
void initOverTime(void)
@@ -50,11 +60,11 @@ void initOverTime(void)
if(config.debug & DEBUG_OVERTIME)
logg("initOverTime(): Initializing %i slots from %lu to %lu", OVERTIME_SLOTS, timestamp-OVERTIME_SLOTS*OVERTIME_INTERVAL, timestamp);
// Iterate over overTime and initialize it
// Iterate over overTime
for(int i = OVERTIME_SLOTS-1; i >= 0 ; i--)
{
// Initialize onerTime slot
initSlot(i, timestamp);
// Prepare for next iteration
timestamp -= OVERTIME_INTERVAL;
}
@@ -87,7 +97,10 @@ unsigned int getOverTimeID(time_t timestamp)
}
if(config.debug & DEBUG_OVERTIME)
{
// Debug output
logg("getOverTimeID(%lu): %i", timestamp, id);
}
return (unsigned int) id;
}
@@ -111,7 +124,10 @@ void moveOverTimeMemory(const time_t mintime)
const unsigned int remainingSlots = OVERTIME_SLOTS - moveOverTime;
if(config.debug & DEBUG_OVERTIME)
logg("moveOverTimeMemory(): IS: %lu, SHOULD: %lu, MOVING: %u", oldestOverTimeIS, oldestOverTimeSHOULD, moveOverTime);
{
logg("moveOverTimeMemory(): IS: %lu, SHOULD: %lu, MOVING: %u",
oldestOverTimeIS, oldestOverTimeSHOULD, moveOverTime);
}
// Check if the move over amount is valid. This prevents errors if the
// function is called before GC is necessary.
@@ -119,29 +135,41 @@ void moveOverTimeMemory(const time_t mintime)
{
// Move overTime memory
if(config.debug & DEBUG_OVERTIME)
logg("moveOverTimeMemory(): Moving overTime %u - %u to 0 - %u", moveOverTime, moveOverTime+remainingSlots, remainingSlots);
memmove(&overTime[0], &overTime[moveOverTime], remainingSlots*sizeof(*overTime));
{
logg("moveOverTimeMemory(): Moving overTime %u - %u to 0 - %u",
moveOverTime, moveOverTime+remainingSlots, remainingSlots);
}
// Move overTime memory forward to update data structure
memmove(&overTime[0],
&overTime[moveOverTime],
remainingSlots*sizeof(*overTime));
// Correct time indices of queries. This is necessary because we just moved the slot this index points to
for(int queryID = 0; queryID < counters->queries; queryID++)
{
// Get query pointer
queriesData* query = getQuery(queryID, true);
// Check if the index would become negative if we adjusted it
if(((int)queries[queryID].timeidx - (int)moveOverTime) < 0)
if(((int)query->timeidx - (int)moveOverTime) < 0)
{
// This should never happen, but we print a warning if it still happens
// We don't do anything in this case
logg("WARN: moveOverTimeMemory(): overTime time index correction failed (%i: %u / %u)", queryID, queries[queryID].timeidx, moveOverTime);
logg("WARN: moveOverTimeMemory(): overTime time index correction failed (%i: %u / %u)",
queryID, query->timeidx, moveOverTime);
}
else
{
queries[queryID].timeidx -= moveOverTime;
query->timeidx -= moveOverTime;
}
}
// Move client-specific overTime memory
for(int clientID = 0; clientID < counters->clients; clientID++)
{
memmove(&clients[clientID].overTime[0], &clients[clientID].overTime[moveOverTime], remainingSlots*sizeof(int));
memmove(&(getClient(clientID, true)->overTime[0]),
&(getClient(clientID, true)->overTime[moveOverTime]),
remainingSlots*sizeof(int));
}
// Iterate over new overTime region and initialize it

View File

@@ -152,11 +152,13 @@ void free_regex(void)
// Must reevaluate regex filters after having reread the regex filter
// We reset all regex status to unknown to have them being reevaluated
if(counters->domains > 0)
validate_access("domains", counters->domains-1, false, __LINE__, __FUNCTION__, __FILE__);
for(int i=0; i < counters->domains; i++)
{
domains[i].regexmatch = REGEX_UNKNOWN;
// Get domain pointer
domainsData* domain = getDomain(i, true);
// Reset regexmatch to unknown
domain->regexmatch = REGEX_UNKNOWN;
}
}

View File

@@ -108,14 +108,14 @@ void resolveClients(const bool onlynew)
unlock_shm();
for(int clientID = 0; clientID < clientscount; clientID++)
{
// Memory validation
validate_access("clients", clientID, true, __LINE__, __FUNCTION__, __FILE__);
// Get client pointer
clientsData* client = getClient(clientID, true);
// Memory access needs to get locked
lock_shm();
bool newflag = clients[clientID].new;
size_t ippos = clients[clientID].ippos;
size_t oldnamepos = clients[clientID].namepos;
bool newflag = client->new;
size_t ippos = client->ippos;
size_t oldnamepos = client->namepos;
unlock_shm();
// If onlynew flag is set, we will only resolve new clients
@@ -128,10 +128,9 @@ void resolveClients(const bool onlynew)
lock_shm();
// Store obtained host name (may be unchanged)
clients[clientID].namepos = newnamepos;
client->namepos = newnamepos;
// Mark entry as not new
clients[clientID].new = false;
client->new = false;
unlock_shm();
}
}
@@ -145,14 +144,14 @@ void resolveForwardDestinations(const bool onlynew)
unlock_shm();
for(int forwardID = 0; forwardID < forwardedcount; forwardID++)
{
// Memory validation
validate_access("forwarded", forwardID, true, __LINE__, __FUNCTION__, __FILE__);
// Get forward pointer
forwardedData* forward = getForward(forwardID, true);
// Memory access needs to get locked
lock_shm();
bool newflag = forwarded[forwardID].new;
size_t ippos = forwarded[forwardID].ippos;
size_t oldnamepos = forwarded[forwardID].namepos;
bool newflag = forward->new;
size_t ippos = forward->ippos;
size_t oldnamepos = forward->namepos;
unlock_shm();
// If onlynew flag is set, we will only resolve new upstream destinations
@@ -165,9 +164,9 @@ void resolveForwardDestinations(const bool onlynew)
lock_shm();
// Store obtained host name (may be unchanged)
forwarded[forwardID].namepos = newnamepos;
forward->namepos = newnamepos;
// Mark entry as not new
forwarded[forwardID].new = false;
forward->new = false;
unlock_shm();
}
}

View File

@@ -7,7 +7,10 @@
*
* This file is copyright under the latest version of the EUPL.
* Please see LICENSE file for your rights under this license. */
#ifndef ROUTINES_H
#define ROUTINES_H
// daemon.c
void go_daemon(void);
void timer_start(const int i);
double timer_elapsed_msec(const int i);
@@ -16,11 +19,13 @@ void savepid(void);
char * getUserName(void);
void removepid(void);
// log.c
void open_FTL_log(const bool test);
void logg(const char* format, ...) __attribute__ ((format (gnu_printf, 1, 2)));
void log_counter_info(void);
void format_memory_size(char *prefix, unsigned long int bytes, double *formated);
void log_FTL_version(bool crashreport);
void get_timestr(char *timestring, const time_t timein);
// datastructure.c
void strtolower(char *str);
@@ -95,7 +100,6 @@ char *FTLstrdup(const char *src, const char *file, const char *function, const i
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)));
void FTLfree(void *ptr, const char* file, const char *function, const int line);
void validate_access(const char * name, int pos, bool testmagic, const int line, const char * function, const char * file);
int main_dnsmasq(int argc, const char ** argv);
@@ -160,3 +164,4 @@ bool gravityDB_getTable(unsigned char list);
const char* gravityDB_getDomain(void);
void gravityDB_finalizeTable(void);
int gravityDB_count(unsigned char list);
#endif // ROUTINES_H

182
shmem.c
View File

@@ -36,6 +36,12 @@ static SharedMemory shm_forwarded = { 0 };
static SharedMemory shm_overTime = { 0 };
static SharedMemory shm_settings = { 0 };
// Variable size array structs
static queriesData *queries = NULL;
static clientsData *clients = NULL;
static domainsData *domains = NULL;
static forwardedData *forwarded = NULL;
typedef struct {
pthread_mutex_t lock;
bool waitingForLock;
@@ -128,14 +134,14 @@ static pthread_mutex_t create_mutex(void) {
static void remap_shm(void)
{
// Remap shared object pointers which might have changed
realloc_shm(&shm_queries, counters->queries_MAX*sizeof(queriesDataStruct), false);
queries = (queriesDataStruct*)shm_queries.ptr;
realloc_shm(&shm_domains, counters->domains_MAX*sizeof(domainsDataStruct), false);
domains = (domainsDataStruct*)shm_domains.ptr;
realloc_shm(&shm_clients, counters->clients_MAX*sizeof(clientsDataStruct), false);
clients = (clientsDataStruct*)shm_clients.ptr;
realloc_shm(&shm_forwarded, counters->forwarded_MAX*sizeof(forwardedDataStruct), false);
forwarded = (forwardedDataStruct*)shm_forwarded.ptr;
realloc_shm(&shm_queries, counters->queries_MAX*sizeof(queriesData), false);
queries = (queriesData*)shm_queries.ptr;
realloc_shm(&shm_domains, counters->domains_MAX*sizeof(domainsData), false);
domains = (domainsData*)shm_domains.ptr;
realloc_shm(&shm_clients, counters->clients_MAX*sizeof(clientsData), false);
clients = (clientsData*)shm_clients.ptr;
realloc_shm(&shm_forwarded, counters->forwarded_MAX*sizeof(forwardedData), false);
forwarded = (forwardedData*)shm_forwarded.ptr;
realloc_shm(&shm_strings, counters->strings_MAX, false);
// strings are not exposed by a global pointer
@@ -224,35 +230,35 @@ bool init_shmem(void)
/****************************** shared domains struct ******************************/
// Try to create shared memory object
shm_domains = create_shm(SHARED_DOMAINS_NAME, pagesize*sizeof(domainsDataStruct));
domains = (domainsDataStruct*)shm_domains.ptr;
shm_domains = create_shm(SHARED_DOMAINS_NAME, pagesize*sizeof(domainsData));
domains = (domainsData*)shm_domains.ptr;
counters->domains_MAX = pagesize;
/****************************** shared clients struct ******************************/
size_t size = get_optimal_object_size(sizeof(clientsDataStruct), 1);
size_t size = get_optimal_object_size(sizeof(clientsData), 1);
// Try to create shared memory object
shm_clients = create_shm(SHARED_CLIENTS_NAME, size*sizeof(clientsDataStruct));
clients = (clientsDataStruct*)shm_clients.ptr;
shm_clients = create_shm(SHARED_CLIENTS_NAME, size*sizeof(clientsData));
clients = (clientsData*)shm_clients.ptr;
counters->clients_MAX = size;
/****************************** shared forwarded struct ******************************/
size = get_optimal_object_size(sizeof(forwardedDataStruct), 1);
size = get_optimal_object_size(sizeof(forwardedData), 1);
// Try to create shared memory object
shm_forwarded = create_shm(SHARED_FORWARDED_NAME, size*sizeof(forwardedDataStruct));
forwarded = (forwardedDataStruct*)shm_forwarded.ptr;
shm_forwarded = create_shm(SHARED_FORWARDED_NAME, size*sizeof(forwardedData));
forwarded = (forwardedData*)shm_forwarded.ptr;
counters->forwarded_MAX = size;
/****************************** shared queries struct ******************************/
// Try to create shared memory object
shm_queries = create_shm(SHARED_QUERIES_NAME, pagesize*sizeof(queriesDataStruct));
queries = (queriesDataStruct*)shm_queries.ptr;
shm_queries = create_shm(SHARED_QUERIES_NAME, pagesize*sizeof(queriesData));
queries = (queriesData*)shm_queries.ptr;
counters->queries_MAX = pagesize;
/****************************** shared overTime struct ******************************/
size = get_optimal_object_size(sizeof(overTimeDataStruct), OVERTIME_SLOTS);
size = get_optimal_object_size(sizeof(overTimeData), OVERTIME_SLOTS);
// Try to create shared memory object
shm_overTime = create_shm(SHARED_OVERTIME_NAME, size*sizeof(overTimeDataStruct));
overTime = (overTimeDataStruct*)shm_overTime.ptr;
shm_overTime = create_shm(SHARED_OVERTIME_NAME, size*sizeof(overTimeData));
overTime = (overTimeData*)shm_overTime.ptr;
initOverTime();
return true;
@@ -348,25 +354,25 @@ void *enlarge_shmem_struct(const char type)
case QUERIES:
sharedMemory = &shm_queries;
allocation_step = pagesize;
sizeofobj = sizeof(queriesDataStruct);
sizeofobj = sizeof(queriesData);
counter = &counters->queries_MAX;
break;
case CLIENTS:
sharedMemory = &shm_clients;
allocation_step = get_optimal_object_size(sizeof(clientsDataStruct), 1);
sizeofobj = sizeof(clientsDataStruct);
allocation_step = get_optimal_object_size(sizeof(clientsData), 1);
sizeofobj = sizeof(clientsData);
counter = &counters->clients_MAX;
break;
case DOMAINS:
sharedMemory = &shm_domains;
allocation_step = pagesize;
sizeofobj = sizeof(domainsDataStruct);
sizeofobj = sizeof(domainsData);
counter = &counters->domains_MAX;
break;
case FORWARDED:
sharedMemory = &shm_forwarded;
allocation_step = get_optimal_object_size(sizeof(forwardedDataStruct), 1);
sizeofobj = sizeof(forwardedDataStruct);
allocation_step = get_optimal_object_size(sizeof(forwardedData), 1);
sizeofobj = sizeof(forwardedData);
counter = &counters->forwarded_MAX;
break;
default:
@@ -515,3 +521,125 @@ static size_t get_optimal_object_size(const size_t objsize, const size_t minsize
return optsize;
}
}
void memory_check(int which)
{
switch(which)
{
case QUERIES:
if(counters->queries >= counters->queries_MAX-1)
{
// Have to reallocate shared memory
queries = enlarge_shmem_struct(QUERIES);
if(queries == NULL)
{
logg("FATAL: Memory allocation failed! Exiting");
exit(EXIT_FAILURE);
}
}
break;
case FORWARDED:
if(counters->forwarded >= counters->forwarded_MAX-1)
{
// Have to reallocate shared memory
forwarded = enlarge_shmem_struct(FORWARDED);
if(forwarded == NULL)
{
logg("FATAL: Memory allocation failed! Exiting");
exit(EXIT_FAILURE);
}
}
break;
case CLIENTS:
if(counters->clients >= counters->clients_MAX-1)
{
// Have to reallocate shared memory
clients = enlarge_shmem_struct(CLIENTS);
if(clients == NULL)
{
logg("FATAL: Memory allocation failed! Exiting");
exit(EXIT_FAILURE);
}
}
break;
case DOMAINS:
if(counters->domains >= counters->domains_MAX-1)
{
// Have to reallocate shared memory
domains = enlarge_shmem_struct(DOMAINS);
if(domains == NULL)
{
logg("FATAL: Memory allocation failed! Exiting");
exit(EXIT_FAILURE);
}
}
break;
default:
/* That cannot happen */
logg("Fatal error in memory_check(%i)", which);
exit(EXIT_FAILURE);
break;
}
}
static inline bool check_range(int ID, int MAXID, const char* type, int line, const char * function, const char * file)
{
if(ID < 0 || ID > MAXID)
{
// Check bounds
logg("FATAL: Trying to access %s ID %i, but maximum is %i", type, ID, MAXID);
logg(" found in %s() (%s:%i)", function, file, line);
return false;
}
// Everything okay
return true;
}
static inline bool check_magic(int ID, bool checkMagic, unsigned char magic, const char* type, int line, const char * function, const char * file)
{
if(checkMagic && magic != MAGICBYTE)
{
// Check magic only if requested (skipped for new entries which are uninitialized)
logg("FATAL: Trying to access %s ID %i, but magic byte is %x", type, ID, magic);
logg(" found in %s() (%s:%i)", function, file, line);
return false;
}
// Everything okay
return true;
}
queriesData* _getQuery(int queryID, bool checkMagic, int line, const char * function, const char * file)
{
if(check_range(queryID, counters->queries_MAX, "query", line, function, file) &&
check_magic(queryID, checkMagic, queries[queryID].magic, "query", line, function, file))
return &queries[queryID];
else
return NULL;
}
clientsData* _getClient(int clientID, bool checkMagic, int line, const char * function, const char * file)
{
if(check_range(clientID, counters->clients_MAX, "client", line, function, file) &&
check_magic(clientID, checkMagic, clients[clientID].magic, "client", line, function, file))
return &clients[clientID];
else
return NULL;
}
domainsData* _getDomain(int domainID, bool checkMagic, int line, const char * function, const char * file)
{
if(check_range(domainID, counters->domains_MAX, "domain", line, function, file) &&
check_magic(domainID, checkMagic, domains[domainID].magic, "domain", line, function, file))
return &domains[domainID];
else
return NULL;
}
forwardedData* _getForward(int forwardID, bool checkMagic, int line, const char * function, const char * file)
{
if(check_range(forwardID, counters->forwarded_MAX, "forward", line, function, file) &&
check_magic(forwardID, checkMagic, forwarded[forwardID].magic, "forward", line, function, file))
return &forwarded[forwardID];
else
return NULL;
}

8745
sqlite3.c

File diff suppressed because it is too large Load Diff

View File

@@ -123,9 +123,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
#define SQLITE_VERSION "3.27.1"
#define SQLITE_VERSION_NUMBER 3027001
#define SQLITE_SOURCE_ID "2019-02-08 13:17:39 0eca3dd3d38b31c92b49ca2d311128b74584714d9e7de895b1a6286ef959a1dd"
#define SQLITE_VERSION "3.28.0"
#define SQLITE_VERSION_NUMBER 3028000
#define SQLITE_SOURCE_ID "2019-04-16 19:49:53 884b4b7e502b4e991677b53971277adfaf0a04a284f8e483e2553d0f83156b50"
/*
** CAPI3REF: Run-Time Library Version Numbers
@@ -189,6 +189,9 @@ SQLITE_API int sqlite3_libversion_number(void);
#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS
SQLITE_API int sqlite3_compileoption_used(const char *zOptName);
SQLITE_API const char *sqlite3_compileoption_get(int N);
#else
# define sqlite3_compileoption_used(X) 0
# define sqlite3_compileoption_get(X) ((void*)0)
#endif
/*
@@ -2086,8 +2089,8 @@ struct sqlite3_mem_methods {
**
** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]]
** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt>
** <dd> ^This option is used to enable or disable the two-argument
** version of the [fts3_tokenizer()] function which is part of the
** <dd> ^This option is used to enable or disable the
** [fts3_tokenizer()] function which is part of the
** [FTS3] full-text search engine extension.
** There should be two additional arguments.
** The first argument is an integer which is 0 to disable fts3_tokenizer() or
@@ -2199,6 +2202,17 @@ struct sqlite3_mem_methods {
** <li> Direct writes to [shadow tables].
** </ul>
** </dd>
**
** [[SQLITE_DBCONFIG_WRITABLE_SCHEMA]] <dt>SQLITE_DBCONFIG_WRITABLE_SCHEMA</dt>
** <dd>The SQLITE_DBCONFIG_WRITABLE_SCHEMA option activates or deactivates the
** "writable_schema" flag. This has the same effect and is logically equivalent
** to setting [PRAGMA writable_schema=ON] or [PRAGMA writable_schema=OFF].
** The first argument to this setting is an integer which is 0 to disable
** the writable_schema, positive to enable writable_schema, or negative to
** leave the setting unchanged. The second parameter is a pointer to an
** integer into which is written 0 or 1 to indicate whether the writable_schema
** is enabled or disabled following this call.
** </dd>
** </dl>
*/
#define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */
@@ -2212,7 +2226,8 @@ struct sqlite3_mem_methods {
#define SQLITE_DBCONFIG_TRIGGER_EQP 1008 /* int int* */
#define SQLITE_DBCONFIG_RESET_DATABASE 1009 /* int int* */
#define SQLITE_DBCONFIG_DEFENSIVE 1010 /* int int* */
#define SQLITE_DBCONFIG_MAX 1010 /* Largest DBCONFIG */
#define SQLITE_DBCONFIG_WRITABLE_SCHEMA 1011 /* int int* */
#define SQLITE_DBCONFIG_MAX 1011 /* Largest DBCONFIG */
/*
** CAPI3REF: Enable Or Disable Extended Result Codes
@@ -2369,7 +2384,7 @@ SQLITE_API int sqlite3_changes(sqlite3*);
** not. ^Changes to a view that are intercepted by INSTEAD OF triggers
** are not counted.
**
** This the [sqlite3_total_changes(D)] interface only reports the number
** The [sqlite3_total_changes(D)] interface only reports the number
** of rows that changed due to SQL statement run against database
** connection D. Any changes by other database connections are ignored.
** To detect changes against a database file from other database
@@ -3894,6 +3909,18 @@ SQLITE_API const char *sqlite3_normalized_sql(sqlite3_stmt *pStmt);
*/
SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt);
/*
** CAPI3REF: Query The EXPLAIN Setting For A Prepared Statement
** METHOD: sqlite3_stmt
**
** ^The sqlite3_stmt_isexplain(S) interface returns 1 if the
** prepared statement S is an EXPLAIN statement, or 2 if the
** statement S is an EXPLAIN QUERY PLAN.
** ^The sqlite3_stmt_isexplain(S) interface returns 0 if S is
** an ordinary statement or a NULL pointer.
*/
SQLITE_API int sqlite3_stmt_isexplain(sqlite3_stmt *pStmt);
/*
** CAPI3REF: Determine If A Prepared Statement Has Been Reset
** METHOD: sqlite3_stmt
@@ -4033,7 +4060,9 @@ typedef struct sqlite3_context sqlite3_context;
** ^The fifth argument to the BLOB and string binding interfaces
** is a destructor used to dispose of the BLOB or
** string after SQLite has finished with it. ^The destructor is called
** to dispose of the BLOB or string even if the call to bind API fails.
** to dispose of the BLOB or string even if the call to the bind API fails,
** except the destructor is not called if the third parameter is a NULL
** pointer or the fourth parameter is negative.
** ^If the fifth argument is
** the special value [SQLITE_STATIC], then SQLite assumes that the
** information is in static, unmanaged space and does not need to be freed.
@@ -4950,6 +4979,8 @@ SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int6
** <tr><td><b>sqlite3_value_nochange&nbsp;&nbsp;</b>
** <td>&rarr;&nbsp;&nbsp;<td>True if the column is unchanged in an UPDATE
** against a virtual table.
** <tr><td><b>sqlite3_value_frombind&nbsp;&nbsp;</b>
** <td>&rarr;&nbsp;&nbsp;<td>True if value originated from a [bound parameter]
** </table></blockquote>
**
** <b>Details:</b>
@@ -5011,6 +5042,11 @@ SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int6
** than within an [xUpdate] method call for an UPDATE statement, then
** the return value is arbitrary and meaningless.
**
** ^The sqlite3_value_frombind(X) interface returns non-zero if the
** value X originated from one of the [sqlite3_bind_int|sqlite3_bind()]
** interfaces. ^If X comes from an SQL literal value, or a table column,
** and expression, then sqlite3_value_frombind(X) returns zero.
**
** Please pay particular attention to the fact that the pointer returned
** from [sqlite3_value_blob()], [sqlite3_value_text()], or
** [sqlite3_value_text16()] can be invalidated by a subsequent call to
@@ -5056,6 +5092,7 @@ SQLITE_API int sqlite3_value_bytes16(sqlite3_value*);
SQLITE_API int sqlite3_value_type(sqlite3_value*);
SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*);
SQLITE_API int sqlite3_value_nochange(sqlite3_value*);
SQLITE_API int sqlite3_value_frombind(sqlite3_value*);
/*
** CAPI3REF: Finding The Subtype Of SQL Values
@@ -5791,7 +5828,7 @@ SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*);
** associated with database N of connection D. ^The main database file
** has the name "main". If there is no attached database N on the database
** connection D, or if database N is a temporary or in-memory database, then
** a NULL pointer is returned.
** this function will return either a NULL pointer or an empty string.
**
** ^The filename returned by this function is the output of the
** xFullPathname method of the [VFS]. ^In other words, the filename
@@ -10892,7 +10929,7 @@ SQLITE_API int sqlite3rebaser_configure(
** in size. This function allocates and populates a buffer with a copy
** of the changeset rebased rebased according to the configuration of the
** rebaser object passed as the first argument. If successful, (*ppOut)
** is set to point to the new buffer containing the rebased changset and
** is set to point to the new buffer containing the rebased changeset and
** (*pnOut) to its size in bytes and SQLITE_OK returned. It is the
** responsibility of the caller to eventually free the new buffer using
** sqlite3_free(). Otherwise, if an error occurs, (*ppOut) and (*pnOut)
@@ -11301,7 +11338,7 @@ struct Fts5PhraseIter {
** Save the pointer passed as the second argument as the extension functions
** "auxiliary data". The pointer may then be retrieved by the current or any
** future invocation of the same fts5 extension function made as part of
** of the same MATCH query using the xGetAuxdata() API.
** the same MATCH query using the xGetAuxdata() API.
**
** Each extension function is allocated a single auxiliary data slot for
** each FTS query (MATCH expression). If the extension function is invoked
@@ -11316,7 +11353,7 @@ struct Fts5PhraseIter {
** The xDelete callback, if one is specified, is also invoked on the
** auxiliary data pointer after the FTS5 query has finished.
**
** If an error (e.g. an OOM condition) occurs within this function, an
** If an error (e.g. an OOM condition) occurs within this function,
** the auxiliary data is set to NULL and an error code returned. If the
** xDelete parameter was not NULL, it is invoked on the auxiliary data
** pointer before returning.