mirror of
https://github.com/pi-hole/FTL.git
synced 2025-12-20 02:19:21 +00:00
Merge branch 'development' into new/gravityDB
Signed-off-by: DL6ER <dl6er@dl6er.de>
This commit is contained in:
30
FTL.h
30
FTL.h
@@ -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
|
||||
|
||||
5
Makefile
5
Makefile
@@ -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
422
api.c
@@ -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
4
api.h
@@ -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
|
||||
|
||||
110
database.c
110
database.c
@@ -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;
|
||||
|
||||
142
datastructure.c
142
datastructure.c
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
79
gc.c
@@ -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
9
log.c
@@ -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
100
memory.c
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
48
overTime.c
48
overTime.c
@@ -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
|
||||
|
||||
8
regex.c
8
regex.c
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
29
resolve.c
29
resolve.c
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
182
shmem.c
@@ -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;
|
||||
}
|
||||
|
||||
61
sqlite3.h
61
sqlite3.h
@@ -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 </b>
|
||||
** <td>→ <td>True if the column is unchanged in an UPDATE
|
||||
** against a virtual table.
|
||||
** <tr><td><b>sqlite3_value_frombind </b>
|
||||
** <td>→ <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.
|
||||
|
||||
Reference in New Issue
Block a user