diff --git a/FTL.h b/FTL.h index b4003422..ae2ed6b6 100644 --- a/FTL.h +++ b/FTL.h @@ -169,6 +169,8 @@ typedef struct { int forwardnum; int *forwarddata; int *querytypedata; + int clientnum; + int *clientdata; } overTimeDataStruct; typedef struct { @@ -179,6 +181,7 @@ typedef struct { int forwardedips; int forwardednames; int forwarddata; + int clientdata; int querytypedata; } memoryStruct; diff --git a/parser.c b/parser.c index f9020587..8c998187 100644 --- a/parser.c +++ b/parser.c @@ -272,6 +272,8 @@ void process_pihole_log(int file) overTime[timeidx].forwardnum = 0; overTime[timeidx].forwarddata = NULL; overTime[timeidx].querytypedata = calloc(2, sizeof(int)); + overTime[timeidx].clientnum = 0; + overTime[timeidx].clientdata = NULL; memory.querytypedata += 2*sizeof(int); counters.overTime++; @@ -600,6 +602,27 @@ void process_pihole_log(int file) break; } + // Determine if there is enough space for saving the current + // clientID in the overTime data structure, allocate space otherwise + validate_access("overTime", timeidx, true, __LINE__, __FUNCTION__, __FILE__); + if(overTime[timeidx].clientnum <= clientID) + { + // Reallocate more space for clientdata + overTime[timeidx].clientdata = realloc(overTime[timeidx].clientdata, (clientID+1)*sizeof(*overTime[timeidx].clientdata)); + // Initialize new data fields with zeroes + for(i = overTime[timeidx].clientnum; i <= clientID; i++) + { + overTime[timeidx].clientdata[i] = 0; + memory.clientdata++; + } + // Update counter + overTime[timeidx].clientnum = clientID + 1; + } + + // Update overTime data structure with the new client + validate_access_oTcl(timeidx, clientID, __LINE__, __FUNCTION__, __FILE__); + overTime[timeidx].clientdata[clientID]++; + // Free allocated memory free(client); free(domain); @@ -655,11 +678,13 @@ void process_pihole_log(int file) overTime[timeidx].forwardnum = 0; overTime[timeidx].forwarddata = NULL; overTime[timeidx].querytypedata = calloc(2, sizeof(int)); + overTime[timeidx].clientnum = 0; + overTime[timeidx].clientdata = NULL; memory.querytypedata += 2*sizeof(int); counters.overTime++; } // Determine if there is enough space for saving the current - // forwardID in the overTime data structure -allocate space otherwise + // forwardID in the overTime data structure, allocate space otherwise validate_access("overTime", timeidx, true, __LINE__, __FUNCTION__, __FILE__); if(overTime[timeidx].forwardnum <= forwardID) { @@ -1041,7 +1066,18 @@ void validate_access_oTfd(int timeidx, int pos, int line, const char * function, if(pos >= limit || pos < 0) { logg("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); - logg("FATAL ERROR: Trying to access overTime.forwardata[%i], but maximum is %i", pos, limit); + logg("FATAL ERROR: Trying to access overTime.forwarddata[%i], but maximum is %i", pos, limit); + logg(" found in %s() (line %i) in %s", function, line, file); + } +} + +void validate_access_oTcl(int timeidx, int pos, int line, const char * function, const char * file) +{ + int limit = overTime[timeidx].clientnum; + if(pos >= limit || pos < 0) + { + logg("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + logg("FATAL ERROR: Trying to access overTime.clientdata[%i], but maximum is %i", pos, limit); logg(" found in %s() (line %i) in %s", function, line, file); } } diff --git a/request.c b/request.c index 21296f3c..8373cff2 100644 --- a/request.c +++ b/request.c @@ -30,6 +30,8 @@ void getClientID(int *sock); void getQueryTypesOverTime(int *sock); void getVersion(int *sock); void getDBstats(int *sock); +void getClientsOverTime(int *sock); +void getClientNames(int *sock); void process_request(char *client_message, int *sock) { @@ -113,6 +115,16 @@ void process_request(char *client_message, int *sock) processed = true; getDBstats(sock); } + else if(command(client_message, ">ClientsoverTime")) + { + processed = true; + getClientsOverTime(sock); + } + else if(command(client_message, ">client-names")) + { + processed = true; + getClientNames(sock); + } // Test only at the end if we want to quit or kill // so things can be processed before @@ -439,7 +451,7 @@ void getTopClients(char *client_message, int *sock) // Sort temporary array qsort(temparray, counters.clients, sizeof(int[2]), cmpasc); - // Get domains which the user doesn't want to see + // Get clients which the user doesn't want to see char * excludeclients = read_setupVarsconf("API_EXCLUDE_CLIENTS"); if(excludeclients != NULL) { @@ -1026,3 +1038,116 @@ void getDBstats(int *sock) if(debugclients) logg("Sent DB info to client, ID: %i", *sock); } + +void getClientsOverTime(int *sock) +{ + char server_message[SOCKETBUFFERLEN]; + int i, sendit = -1; + + for(i = 0; i < counters.overTime; i++) + { + validate_access("overTime", i, true, __LINE__, __FUNCTION__, __FILE__); + if((overTime[i].total > 0 || overTime[i].blocked > 0)) + { + sendit = i; + break; + } + } + if(sendit < 0) + return; + + // Get clients which the user doesn't want to see + char * excludeclients = read_setupVarsconf("API_EXCLUDE_CLIENTS"); + // Array of clients to be skipped in the output + // if skipclient[i] == true then this client should be hidden from + // returned data. We initialize it with false + bool skipclient[counters.clients]; + memset(skipclient, false, counters.clients*sizeof(bool)); + + if(excludeclients != NULL) + { + getSetupVarsArray(excludeclients); + + for(i=0; i < counters.clients; i++) + { + validate_access("clients", i, true, __LINE__, __FUNCTION__, __FILE__); + // Check if this client should be skipped + if(insetupVarsArray(clients[i].ip) || insetupVarsArray(clients[i].name)) + { + skipclient[i] = true; + } + } + } + + // Main return loop + for(i = sendit; i < counters.overTime; i++) + { + validate_access("overTime", i, true, __LINE__, __FUNCTION__, __FILE__); + sprintf(server_message, "%i", overTime[i].timestamp); + + // Loop over forward destinations to generate output to be sent to the client + int j; + for(j = 0; j < counters.clients; j++) + { + int thisclient = 0; + + if(skipclient[j]) + continue; + + if(j < overTime[i].clientnum) + { + // This client entry does already exist at this timestamp + // -> use counter of requests sent to this destination + thisclient = overTime[i].clientdata[j]; + } + + sprintf(server_message + strlen(server_message), " %i", thisclient); + } + + sprintf(server_message + strlen(server_message), "\n"); + swrite(server_message, *sock); + } + + if(excludeclients != NULL) + clearSetupVarsArray(); +} + +void getClientNames(int *sock) +{ + char server_message[SOCKETBUFFERLEN]; + int i; + + // Get clients which the user doesn't want to see + char * excludeclients = read_setupVarsconf("API_EXCLUDE_CLIENTS"); + // Array of clients to be skipped in the output + // if skipclient[i] == true then this client should be hidden from + // returned data. We initialize it with false + bool skipclient[counters.clients]; + memset(skipclient, false, counters.clients*sizeof(bool)); + + if(excludeclients != NULL) + { + getSetupVarsArray(excludeclients); + + for(i=0; i < counters.clients; i++) + { + validate_access("clients", i, true, __LINE__, __FUNCTION__, __FILE__); + // Check if this client should be skipped + + } + } + + // Loop over clients to generate output to be sent to the client + for(i = 0; i < counters.clients; i++) + { + validate_access("clients", i, true, __LINE__, __FUNCTION__, __FILE__); + if(insetupVarsArray(clients[i].ip) || insetupVarsArray(clients[i].name)) + continue; + + sprintf(server_message,"%i %i %s %s\n", i, clients[i].count, clients[i].ip, clients[i].name); + swrite(server_message, *sock); + } + + if(excludeclients != NULL) + clearSetupVarsArray(); +} diff --git a/routines.h b/routines.h index f2f03983..fba6a5de 100644 --- a/routines.h +++ b/routines.h @@ -31,6 +31,7 @@ void process_pihole_log(int file); void *pihole_log_thread(void *val); void validate_access(const char * name, int pos, bool testmagic, int line, const char * function, const char * file); void validate_access_oTfd(int timeidx, int pos, int line, const char * function, const char * file); +void validate_access_oTcl(int timeidx, int pos, int line, const char * function, const char * file); void reresolveHostnames(void); void pihole_log_flushed(bool message);