Files
FTL/request.c
T
Mcat12 1d29630db2 Implement API JSON output for summary
Currently using the cJSON library, as it's entirely contained
within one source and one header file and is very easy to use.
By adding on the API output formats to the previous raw socket
code, we can simply change the output format when the response
type is `API` instead of `SOCKET`. As shown in the implemented
summary API code, this should be very simple for most commands.
2017-06-12 21:32:53 -04:00

901 lines
23 KiB
C

/* Pi-hole: A black hole for Internet advertisements
* (c) 2017 Pi-hole, LLC (https://pi-hole.net)
* Network-wide ad blocking via your own hardware.
*
* FTL Engine
* Socket request handling routines
*
* This file is copyright under the latest version of the EUPL.
* Please see LICENSE file for your rights under this license. */
#include "FTL.h"
#include "version.h"
#include "cJSON.h"
// Private
#define min(a,b) ({ __typeof__ (a) _a = (a); __typeof__ (b) _b = (b); _a < _b ? _a : _b; })
#define max(a,b) ({ __typeof__ (a) _a = (a); __typeof__ (b) _b = (b); _a > _b ? _a : _b; })
// Local prototypes
void getStats(int *sock, char type);
void getOverTime(int *sock, char type);
void getTopDomains (char *client_message, int *sock, char type);
void getTopClients(char *client_message, int *sock, char type);
void getForwardDestinations(int *sock, char type);
void getForwardNames(int *sock, char type);
void getQueryTypes(int *sock, char type);
void getAllQueries(char *client_message, int *sock, char type);
void getRecentBlocked(char *client_message, int *sock, char type);
void getMemoryUsage(int *sock, char type);
void getForwardDestinationsOverTime(int *sock, char type);
void getClientID(int *sock, char type);
void getQueryTypesOverTime(int *sock, char type);
void getVersion(int *sock, char type);
void getDBstats(int *sock, char type);
void process_socket_request(char *client_message, int *sock, char type)
{
char EOT[2];
EOT[0] = 0x04;
EOT[1] = 0x00;
bool processed = false;
if(command(client_message, ">stats"))
{
processed = true;
getStats(sock, type);
}
else if(command(client_message, ">overTime"))
{
processed = true;
getOverTime(sock, type);
}
else if(command(client_message, ">top-domains") || command(client_message, ">top-ads"))
{
processed = true;
getTopDomains(client_message, sock, type);
}
else if(command(client_message, ">top-clients"))
{
processed = true;
getTopClients(client_message, sock, type);
}
else if(command(client_message, ">forward-dest"))
{
processed = true;
getForwardDestinations(sock, type);
}
else if(command(client_message, ">forward-names"))
{
processed = true;
getForwardNames(sock, type);
}
else if(command(client_message, ">querytypes"))
{
processed = true;
getQueryTypes(sock, type);
}
else if(command(client_message, ">getallqueries"))
{
processed = true;
getAllQueries(client_message, sock, type);
}
else if(command(client_message, ">recentBlocked"))
{
processed = true;
getRecentBlocked(client_message, sock, type);
}
else if(command(client_message, ">memory"))
{
processed = true;
getMemoryUsage(sock, type);
}
else if(command(client_message, ">clientID"))
{
processed = true;
getClientID(sock, type);
}
else if(command(client_message, ">ForwardedoverTime"))
{
processed = true;
getForwardDestinationsOverTime(sock, type);
}
else if(command(client_message, ">QueryTypesoverTime"))
{
processed = true;
getQueryTypesOverTime(sock, type);
}
else if(command(client_message, ">version"))
{
processed = true;
getVersion(sock, type);
}
else if(command(client_message, ">dbstats"))
{
processed = true;
getDBstats(sock, type);
}
// End of queryable commands
if(processed)
{
// Send EOM
seom(*sock);
}
// Test only at the end if we want to quit or kill
// so things can be processed before
if(command(client_message, ">quit") || command(client_message, EOT))
{
processed = true;
if(debugclients)
logg("Client wants to disconnect, ID: %i",*sock);
close(*sock);
*sock = 0;
}
else if(command(client_message, ">kill"))
{
processed = true;
logg("FTL killed by client ID: %i",*sock);
killed = 1;
}
if(!processed)
{
ssend(*sock,"unknown command: %s\n",client_message);
}
}
bool command(char *client_message, const char* cmd)
{
if(strstr(client_message,cmd) != NULL)
return true;
else
return false;
}
void sendAPIResponse(int sock, char *content) {
ssend(
sock,
"HTTP/1.0 200 OK\nServer: FTL\nCache-Control: no-cache\n"
"Content-Type: application/json\nContent-Length: %i\n\n%s",
strlen(content),
content
);
}
// void formatNumber(bool raw, int n, char* buffer)
// {
// if(raw)
// {
// // Don't change number, echo string
// sprintf(buffer, "%d", n);
// }
// else
// {
// // Insert thousand separator
// if(n < 0) {
// sprintf(buffer, "-");
// n = -n;
// }
// else
// {
// // Empty buffer
// buffer[0] = '\0';
// }
// int a[20] = { 0 };
// int *pa = a;
// while(n > 0) {
// *++pa = n % 1000;
// n /= 1000;
// }
// sprintf(buffer, "%s%d", buffer, *pa);
// while(pa > a + 1) {
// sprintf(buffer, "%s,%03d", buffer, *--pa);
// }
// }
// }
/* qsort comparision function (count field), sort ASC */
int cmpasc(const void *a, const void *b)
{
int *elem1 = (int*)a;
int *elem2 = (int*)b;
if (elem1[1] < elem2[1])
return -1;
else if (elem1[1] > elem2[1])
return 1;
else
return 0;
}
// qsort subroutine, sort DESC
int cmpdesc(const void *a, const void *b)
{
int *elem1 = (int*)a;
int *elem2 = (int*)b;
if (elem1[1] > elem2[1])
return -1;
else if (elem1[1] < elem2[1])
return 1;
else
return 0;
}
void getStats(int *sock, char type)
{
int blocked = counters.blocked + counters.wildcardblocked;
int total = counters.queries - counters.invalidqueries;
float percentage = 0.0;
// Avoid 1/0 condition
if(total > 0)
{
percentage = 1e2*blocked/total;
}
if(type == SOCKET) {
ssend(*sock, "domains_being_blocked %i\ndns_queries_today %i\nads_blocked_today %i\nads_percentage_today %f\n", \
counters.gravity, total, blocked, percentage);
ssend(*sock, "unique_domains %i\nqueries_forwarded %i\nqueries_cached %i\n", \
counters.domains, counters.forwardedqueries, counters.cached);
}
else if(type == API) {
cJSON *response = cJSON_CreateObject();
cJSON_AddNumberToObject(response, "domains_being_blocked", counters.gravity);
cJSON_AddNumberToObject(response, "dns_queries_today", total);
cJSON_AddNumberToObject(response, "ads_blocked_today", blocked);
cJSON_AddNumberToObject(response, "ads_percentage_today", percentage);
cJSON_AddNumberToObject(response, "unique_domains", counters.domains);
cJSON_AddNumberToObject(response, "queries_forwarded", counters.forwardedqueries);
cJSON_AddNumberToObject(response, "queries_cached", counters.cached);
sendAPIResponse(*sock, cJSON_Print(response));
}
if(debugclients)
logg("Sent stats data to client, ID: %i", *sock);
}
void getOverTime(int *sock, char type)
{
int i;
bool sendit = false;
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)
{
sendit = true;
}
if(sendit)
{
ssend(*sock,"%i %i %i\n",overTime[i].timestamp,overTime[i].total,overTime[i].blocked);
}
}
if(debugclients)
logg("Sent overTime data to client, ID: %i", *sock);
}
void getTopDomains(char *client_message, int *sock, char type)
{
int i, temparray[counters.domains][2], count=10, num;
bool blocked = command(client_message, ">top-ads"), audit = false, desc = false;
// Exit before processing any data if requested via config setting
if(!config.query_display)
return;
// Match both top-domains and top-ads
if(sscanf(client_message, ">%*[^(](%i)", &num) > 0)
{
// User wants a different number of requests
count = num;
}
// Apply Audit Log filtering?
if(command(client_message, " for audit"))
{
audit = true;
}
// Sort in descending order?
if(command(client_message, " desc"))
{
desc = true;
}
for(i=0; i < counters.domains; i++)
{
validate_access("domains", i, true, __LINE__, __FUNCTION__, __FILE__);
temparray[i][0] = i;
if(blocked)
temparray[i][1] = domains[i].blockedcount;
else
// Count only permitted queries
temparray[i][1] = (domains[i].count - domains[i].blockedcount);
}
// Sort temporary array
if(desc)
qsort(temparray, counters.domains, sizeof(int[2]), cmpdesc);
else
qsort(temparray, counters.domains, sizeof(int[2]), cmpasc);
// Get filter
char * filter = read_setupVarsconf("API_QUERY_LOG_SHOW");
bool showpermitted = true, showblocked = true;
if(filter != NULL)
{
if((strcmp(filter, "permittedonly")) == 0)
{
showblocked = false;
}
else if((strcmp(filter, "blockedonly")) == 0)
{
showpermitted = false;
}
else if((strcmp(filter, "nothing")) == 0)
{
showpermitted = false;
showblocked = false;
}
}
clearSetupVarsArray();
// Get domains which the user doesn't want to see
char * excludedomains = NULL;
if(!audit)
{
excludedomains = read_setupVarsconf("API_EXCLUDE_DOMAINS");
if(excludedomains != NULL)
{
getSetupVarsArray(excludedomains);
if(debugclients)
logg("Excluding %i domains from being displayed", setupVarsElements);
}
}
int skip = 0;
for(i=0; i < min(counters.domains, count+skip); i++)
{
// Get sorted indices
int j = temparray[counters.domains-i-1][0];
validate_access("domains", j, true, __LINE__, __FUNCTION__, __FILE__);
// Skip this domain if there is a filter on it
if(excludedomains != NULL)
{
if(insetupVarsArray(domains[j].domain))
{
skip++;
continue;
}
}
// Skip this domain if already included in audit
if(audit && countlineswith(domains[j].domain, files.auditlist) > 0)
{
skip++;
continue;
}
if(blocked && showblocked && domains[j].blockedcount > 0)
{
if(audit && domains[j].wildcard)
ssend(*sock,"%i %i %s wildcard\n",i,domains[j].blockedcount,domains[j].domain);
else
ssend(*sock,"%i %i %s\n",i,domains[j].blockedcount,domains[j].domain);
}
else if(!blocked && showpermitted && (domains[j].count - domains[j].blockedcount) > 0)
{
ssend(*sock,"%i %i %s\n",i,(domains[j].count - domains[j].blockedcount),domains[j].domain);
}
}
if(excludedomains != NULL)
clearSetupVarsArray();
if(debugclients)
{
if(blocked)
logg("Sent top ads list data to client, ID: %i", *sock);
else
logg("Sent top domains list data to client, ID: %i", *sock);
}
}
void getTopClients(char *client_message, int *sock, char type)
{
int i, temparray[counters.clients][2], count=10, num;
if(sscanf(client_message, ">%*[^(](%i)", &num) > 0)
{
// User wants a different number of requests
count = num;
}
for(i=0; i < counters.clients; i++)
{
validate_access("clients", i, true, __LINE__, __FUNCTION__, __FILE__);
temparray[i][0] = i;
temparray[i][1] = clients[i].count;
}
// Sort temporary array
qsort(temparray, counters.clients, sizeof(int[2]), cmpasc);
// Get domains which the user doesn't want to see
char * excludeclients = read_setupVarsconf("API_EXCLUDE_CLIENTS");
if(excludeclients != NULL)
{
getSetupVarsArray(excludeclients);
if(debugclients)
logg("Excluding %i clients from being displayed", setupVarsElements);
}
int skip = 0;
for(i=0; i < min(counters.clients, count+skip); i++)
{
// Get sorted indices
int j = temparray[counters.clients-i-1][0];
validate_access("clients", j, true, __LINE__, __FUNCTION__, __FILE__);
// Skip this client if there is a filter on it
if(excludeclients != NULL)
{
if(insetupVarsArray(clients[j].ip) ||
insetupVarsArray(clients[j].name))
{
skip++;
continue;
}
}
if(clients[j].count > 0)
{
ssend(*sock,"%i %i %s %s\n",i,clients[j].count,clients[j].ip,clients[j].name);
}
}
if(excludeclients != NULL)
clearSetupVarsArray();
if(debugclients)
logg("Sent top clients data to client, ID: %i", *sock);
}
void getForwardDestinations(int *sock, char type)
{
bool allocated = false;
int i, temparray[counters.forwarded+1][2];
for(i=0; i < counters.forwarded; i++)
{
validate_access("forwarded", i, true, __LINE__, __FUNCTION__, __FILE__);
temparray[i][0] = i;
temparray[i][1] = forwarded[i].count;
}
// Add "local " forward destination
temparray[counters.forwarded][0] = counters.forwarded;
temparray[counters.forwarded][1] = counters.cached + counters.blocked;
// Sort temporary array in descending order
qsort(temparray, counters.forwarded+1, sizeof(int[2]), cmpdesc);
// Loop over available forward destinations
for(i=0; i < min(counters.forwarded+1, 10); i++)
{
char *name, *ip;
int count;
// Get sorted indices
int j = temparray[i][0];
// Is this the "local" forward destination?
if(j == counters.forwarded)
{
ip = calloc(4,1);
strcpy(ip, "::1");
name = calloc(6,1);
strcpy(name, "local");
count = counters.cached + counters.blocked;
allocated = true;
}
else
{
validate_access("forwarded", j, true, __LINE__, __FUNCTION__, __FILE__);
ip = forwarded[j].ip;
name = forwarded[j].name;
count = forwarded[j].count;
allocated = false;
}
// Send data if count > 0
if(count > 0)
{
ssend(*sock,"%i %i %s %s\n",i,count,ip,name);
}
// Free previously allocated memory only if we allocated it
if(allocated)
{
free(ip);
free(name);
}
}
if(debugclients)
logg("Sent forward destination data to client, ID: %i", *sock);
}
void getForwardNames(int *sock, char type)
{
int i;
for(i=0; i < counters.forwarded; i++)
{
validate_access("forwarded", i, true, __LINE__, __FUNCTION__, __FILE__);
// Get sorted indices
ssend(*sock,"%i %i %s %s\n",i,forwarded[i].count,forwarded[i].ip,forwarded[i].name);
}
// Add "local" forward destination
ssend(*sock,"%i %i ::1 local\n",counters.forwarded,counters.cached);
if(debugclients)
logg("Sent forward destination names to client, ID: %i", *sock);
}
void getQueryTypes(int *sock, char type)
{
ssend(*sock,"A (IPv4): %i\nAAAA (IPv6): %i\n",counters.IPv4,counters.IPv6);
if(debugclients)
logg("Sent query type data to client, ID: %i", *sock);
}
void getAllQueries(char *client_message, int *sock, char type)
{
// Exit before processing any data if requested via config setting
if(!config.query_display)
return;
// Do we want a more specific version of this command (domain/client/time interval filtered)?
int from = 0, until = 0;
bool filtertime = false;
if(command(client_message, ">getallqueries-time"))
{
// Get from to until boundaries
sscanf(client_message, ">getallqueries-time %i %i",&from, &until);
if(debugclients)
{
logg("Showing only limited time interval starting at ",from);
logg("Showing only limited time interval ending at ",until);
}
filtertime = true;
}
char *domainname;
bool filterdomainname = false;
if(command(client_message, ">getallqueries-domain"))
{
domainname = calloc(128, sizeof(char));
// Get domain name we want to see only (limit length to 127 chars)
sscanf(client_message, ">getallqueries-domain %127s", domainname);
if(debugclients)
logg("Showing only queries with domain %s", domainname);
filterdomainname = true;
}
char *clientname;
bool filterclientname = false;
if(command(client_message, ">getallqueries-client"))
{
clientname = calloc(128, sizeof(char));
// Get client name we want to see only (limit length to 127 chars)
sscanf(client_message, ">getallqueries-client %127s", clientname);
if(debugclients)
logg("Showing only queries with client %s", clientname);
filterclientname = true;
}
int ibeg = 0, num;
// Test for integer that specifies number of entries to be shown
if(sscanf(client_message, ">%*[^(](%i)", &num) > 0)
{
// User wants a different number of requests
// Don't allow a start index that is smaller than zero
ibeg = counters.queries-num;
if(ibeg < 0)
ibeg = 0;
if(debugclients)
logg("Showing only limited amount of queries: ",num);
}
// Get potentially existing filtering flags
char * filter = read_setupVarsconf("API_QUERY_LOG_SHOW");
bool showpermitted = true, showblocked = true;
if(filter != NULL)
{
if((strcmp(filter, "permittedonly")) == 0)
{
showblocked = false;
}
else if((strcmp(filter, "blockedonly")) == 0)
{
showpermitted = false;
}
else if((strcmp(filter, "nothing")) == 0)
{
showpermitted = false;
showblocked = false;
}
}
clearSetupVarsArray();
// Get privacy mode flag
char * privacy = read_setupVarsconf("API_PRIVACY_MODE");
bool privacymode = false;
if(privacy != NULL)
if(getSetupVarsBool(privacy))
privacymode = true;
clearSetupVarsArray();
if(debugclients)
{
if(showpermitted)
logg("Showing permitted queries");
else
logg("Hiding permitted queries");
if(showblocked)
logg("Showing blocked queries");
else
logg("Hiding blocked queries");
if(privacymode)
logg("Privacy mode enabled");
}
int i;
for(i=ibeg; i < counters.queries; i++)
{
validate_access("queries", i, true, __LINE__, __FUNCTION__, __FILE__);
// Check if this query has been removed due to garbage collection
if(!queries[i].valid) continue;
validate_access("domains", queries[i].domainID, true, __LINE__, __FUNCTION__, __FILE__);
validate_access("clients", queries[i].clientID, true, __LINE__, __FUNCTION__, __FILE__);
char type[5];
if(queries[i].type == 1)
{
strcpy(type,"IPv4");
}
else
{
strcpy(type,"IPv6");
}
if((queries[i].status == 1 || queries[i].status == 4) && !showblocked)
continue;
if((queries[i].status == 2 || queries[i].status == 3) && !showpermitted)
continue;
if(filtertime)
{
// Skip those entries which so not meet the requested timeframe
if(from > queries[i].timestamp || queries[i].timestamp > until)
continue;
}
if(filterdomainname)
{
// Skip if domain name is not identical with what the user wants to see
if(strcmp(domains[queries[i].domainID].domain, domainname) != 0)
continue;
}
if(filterclientname)
{
// Skip if client name and IP are not identical with what the user wants to see
if((strcmp(clients[queries[i].clientID].ip, clientname) != 0) &&
(strcmp(clients[queries[i].clientID].name, clientname) != 0))
continue;
}
if(!privacymode)
{
if(strlen(clients[queries[i].clientID].name) > 0)
ssend(*sock,"%i %s %s %s %i\n",queries[i].timestamp,type,domains[queries[i].domainID].domain,clients[queries[i].clientID].name,queries[i].status);
else
ssend(*sock,"%i %s %s %s %i\n",queries[i].timestamp,type,domains[queries[i].domainID].domain,clients[queries[i].clientID].ip,queries[i].status);
}
else
{
ssend(*sock,"%i %s %s hidden %i\n",queries[i].timestamp,type,domains[queries[i].domainID].domain,queries[i].status);
}
}
// Free allocated memory
if(filterclientname)
free(clientname);
if(filterdomainname)
free(domainname);
if(debugclients)
logg("Sent all queries data to client, ID: %i", *sock);
}
void getRecentBlocked(char *client_message, int *sock, char type)
{
int i, num=1;
// Exit before processing any data if requested via config setting
if(!config.query_display)
return;
// Test for integer that specifies number of entries to be shown
if(sscanf(client_message, ">%*[^(](%i)", &num) > 0)
{
// User wants a different number of requests
if(num >= counters.queries)
num = 0;
if(debugclients)
logg("Showing several blocked domains ",num);
}
// Find most recent query with either status 1 (blocked)
// or status 4 (wildcard blocked)
int found = 0;
for(i = counters.queries - 1; i > 0 ; i--)
{
validate_access("queries", i, true, __LINE__, __FUNCTION__, __FILE__);
// Check if this query has been removed due to garbage collection
if(!queries[i].valid) continue;
if(queries[i].status == 1 || queries[i].status == 4)
{
found++;
ssend(*sock,"%s\n",domains[queries[i].domainID].domain);
}
if(found >= num)
{
break;
}
}
}
void getMemoryUsage(int *sock, char type)
{
unsigned long int structbytes = sizeof(countersStruct) + sizeof(ConfigStruct) + counters.queries_MAX*sizeof(queriesDataStruct) + counters.forwarded_MAX*sizeof(forwardedDataStruct) + counters.clients_MAX*sizeof(clientsDataStruct) + counters.domains_MAX*sizeof(domainsDataStruct) + counters.overTime_MAX*sizeof(overTimeDataStruct) + (counters.wildcarddomains)*sizeof(*wildcarddomains);
char *structprefix = calloc(2, sizeof(char));
double formated = 0.0;
format_memory_size(structprefix, structbytes, &formated);
ssend(*sock,"memory allocated for internal data structure: %lu bytes (%.2f %sB)\n",structbytes,formated,structprefix);
free(structprefix);
unsigned long int dynamicbytes = memory.wildcarddomains + memory.domainnames + memory.clientips + memory.clientnames + memory.forwardedips + memory.forwardednames + memory.forwarddata;
char *dynamicprefix = calloc(2, sizeof(char));
format_memory_size(dynamicprefix, dynamicbytes, &formated);
ssend(*sock,"dynamically allocated allocated memory used for strings: %lu bytes (%.2f %sB)\n",dynamicbytes,formated,dynamicprefix);
free(dynamicprefix);
unsigned long int totalbytes = structbytes + dynamicbytes;
char *totalprefix = calloc(2, sizeof(char));
format_memory_size(totalprefix, totalbytes, &formated);
ssend(*sock,"Sum: %lu bytes (%.2f %sB)\n",totalbytes,formated,totalprefix);
free(totalprefix);
if(debugclients)
logg("Sent memory data to client, ID: %i", *sock);
}
void getForwardDestinationsOverTime(int *sock, char type)
{
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 > -1)
{
for(i = sendit; i < counters.overTime; i++)
{
validate_access("overTime", i, true, __LINE__, __FUNCTION__, __FILE__);
ssend(*sock, "%i", overTime[i].timestamp);
int j;
for(j = 0; j < counters.forwarded; j++)
{
int k;
if(j < overTime[i].forwardnum)
k = overTime[i].forwarddata[j];
else
k = 0;
ssend(*sock, " %i", k);
}
ssend(*sock, " %i\n", overTime[i].cached + overTime[i].blocked);
}
}
if(debugclients)
logg("Sent overTime forwarded data to client, ID: %i", *sock);
}
void getClientID(int *sock, char type)
{
ssend(*sock,"%i\n", *sock);
if(debugclients)
logg("Sent client ID to client, ID: %i", *sock);
}
void getQueryTypesOverTime(int *sock, char type)
{
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 > -1)
{
for(i = sendit; i < counters.overTime; i++)
{
validate_access("overTime", i, true, __LINE__, __FUNCTION__, __FILE__);
ssend(*sock, "%i %i %i\n", overTime[i].timestamp,overTime[i].querytypedata[0],overTime[i].querytypedata[1]);
}
}
if(debugclients)
logg("Sent overTime query types data to client, ID: %i", *sock);
}
void getVersion(int *sock, char type)
{
ssend(*sock,"version %s\ntag %s\nbranch %s\ndate %s\n", GIT_VERSION, GIT_TAG, GIT_BRANCH, GIT_DATE);
if(debugclients)
logg("Sent version info to client, ID: %i", *sock);
}
void getDBstats(int *sock, char type)
{
// Get file details
struct stat st;
long int filesize = 0;
if(stat(FTLfiles.db, &st) != 0)
// stat() failed (maybe the file does not exist?)
filesize = -1;
else
filesize = st.st_size;
char *prefix = calloc(2, sizeof(char));
double formated = 0.0;
format_memory_size(prefix, filesize, &formated);
ssend(*sock,"queries in database: %i\ndatabase filesize: %.2f %sB\nSQLite version: %s\n", get_number_of_queries_in_DB(), formated, prefix, sqlite3_libversion());
if(debugclients)
logg("Sent DB info to client, ID: %i", *sock);
}