mirror of
https://github.com/pi-hole/FTL.git
synced 2024-10-26 16:52:18 +02:00
Merge pull request #253 from pi-hole/FTLDNS-resolve
Internal name resolving functionality
This commit is contained in:
@@ -159,12 +159,16 @@ typedef struct {
|
||||
int count;
|
||||
int failed;
|
||||
char *ip;
|
||||
char *name;
|
||||
bool new;
|
||||
} forwardedDataStruct;
|
||||
|
||||
typedef struct {
|
||||
unsigned char magic;
|
||||
int count;
|
||||
char *ip;
|
||||
char *name;
|
||||
bool new;
|
||||
} clientsDataStruct;
|
||||
|
||||
typedef struct {
|
||||
|
||||
@@ -14,7 +14,7 @@ DNSMASQOPTS = -DHAVE_DNSSEC -DHAVE_DNSSEC_STATIC -DNO_FORK
|
||||
# Flags for compiling with libidn2: -DHAVE_LIBIDN2 -DIDN2_VERSION_NUMBER=0x02000003
|
||||
|
||||
FTLDEPS = FTL.h routines.h version.h api.h dnsmasq_interface.h
|
||||
FTLOBJ = main.o memory.o log.o daemon.o datastructure.o signals.o socket.o request.o grep.o setupVars.o args.o threads.o gc.o config.o database.o msgpack.o api.o dnsmasq_interface.o
|
||||
FTLOBJ = main.o memory.o log.o daemon.o datastructure.o signals.o socket.o request.o grep.o setupVars.o args.o threads.o gc.o config.o database.o msgpack.o api.o dnsmasq_interface.o resolve.o
|
||||
|
||||
DNSMASQDEPS = config.h dhcp-protocol.h dns-protocol.h radv-protocol.h dhcp6-protocol.h dnsmasq.h ip6addr.h
|
||||
DNSMASQOBJ = arp.o dbus.o domain.o lease.o outpacket.o rrfilter.o auth.o dhcp6.o edns0.o log.o poll.o slaac.o blockdata.o dhcp.o forward.o loop.o radv.o tables.o bpf.o dhcp-common.o helper.o netlink.o rfc1035.o tftp.o cache.o dnsmasq.o inotify.o network.o rfc2131.o util.o conntrack.o dnssec.o ipset.o option.o rfc3315.o crypto.o
|
||||
|
||||
@@ -380,20 +380,28 @@ void getTopClients(char *client_message, int *sock)
|
||||
validate_access("clients", j, true, __LINE__, __FUNCTION__, __FILE__);
|
||||
|
||||
// Skip this client if there is a filter on it
|
||||
if(excludeclients != NULL && insetupVarsArray(clients[j].ip))
|
||||
if(excludeclients != NULL &&
|
||||
(insetupVarsArray(clients[j].ip) || insetupVarsArray(clients[j].name)))
|
||||
continue;
|
||||
|
||||
// Hidden domain, probably due to privacy level. Skip this in the top lists
|
||||
// Hidden client, probably due to privacy level. Skip this in the top lists
|
||||
if(strcmp(clients[j].ip, "0.0.0.0") == 0)
|
||||
continue;
|
||||
|
||||
// Only return name if available
|
||||
char *name;
|
||||
if(clients[j].name != NULL)
|
||||
name = clients[j].name;
|
||||
else
|
||||
name = "";
|
||||
|
||||
// Return this client if either
|
||||
// - "withzero" option is set, and/or
|
||||
// - the client made at least one query within the most recent 24 hours
|
||||
if(includezeroclients || clients[j].count > 0)
|
||||
{
|
||||
if(istelnet[*sock])
|
||||
ssend(*sock,"%i %i %s\n",n,clients[j].count,clients[j].ip);
|
||||
ssend(*sock,"%i %i %s %s\n",n,clients[j].count,clients[j].ip,name);
|
||||
else
|
||||
{
|
||||
if(!pack_str32(*sock, "") || !pack_str32(*sock, clients[j].ip))
|
||||
@@ -437,7 +445,8 @@ void getForwardDestinations(char *client_message, int *sock)
|
||||
}
|
||||
}
|
||||
|
||||
if(sort) {
|
||||
if(sort)
|
||||
{
|
||||
// Add "local " forward destination
|
||||
temparray[counters.forwarded][0] = counters.forwarded;
|
||||
temparray[counters.forwarded][1] = counters.cached + counters.blocked;
|
||||
@@ -451,7 +460,7 @@ void getForwardDestinations(char *client_message, int *sock)
|
||||
// Loop over available forward destinations
|
||||
for(i=0; i < min(counters.forwarded+1, 10); i++)
|
||||
{
|
||||
char *ip;
|
||||
char *ip, *name;
|
||||
float percentage = 0.0f;
|
||||
|
||||
// Get sorted indices
|
||||
@@ -465,6 +474,7 @@ void getForwardDestinations(char *client_message, int *sock)
|
||||
if(j == counters.forwarded)
|
||||
{
|
||||
ip = strdup("local");
|
||||
name = ip;
|
||||
|
||||
if(totalqueries > 0)
|
||||
// Whats the percentage of (cached + blocked) queries on the total amount of queries?
|
||||
@@ -477,6 +487,12 @@ void getForwardDestinations(char *client_message, int *sock)
|
||||
validate_access("forwarded", j, true, __LINE__, __FUNCTION__, __FILE__);
|
||||
ip = forwarded[j].ip;
|
||||
|
||||
// Only return name if available
|
||||
if(forwarded[j].name != NULL)
|
||||
name = forwarded[j].name;
|
||||
else
|
||||
name = "";
|
||||
|
||||
// Math explanation:
|
||||
// A single query may result in requests being forwarded to multiple destinations
|
||||
// Hence, in order to be able to give percentages here, we have to normalize the
|
||||
@@ -503,7 +519,7 @@ void getForwardDestinations(char *client_message, int *sock)
|
||||
if(percentage > 0.0f)
|
||||
{
|
||||
if(istelnet[*sock])
|
||||
ssend(*sock, "%i %.2f %s\n", i, percentage, ip);
|
||||
ssend(*sock, "%i %.2f %s %s\n", i, percentage, ip, name);
|
||||
else
|
||||
{
|
||||
if(!pack_str32(*sock, "") || !pack_str32(*sock, ip))
|
||||
@@ -515,7 +531,10 @@ void getForwardDestinations(char *client_message, int *sock)
|
||||
|
||||
// Free previously allocated memory only if we allocated it
|
||||
if(allocated)
|
||||
{
|
||||
free(ip);
|
||||
//free(name); // This is just the same as ip
|
||||
}
|
||||
}
|
||||
|
||||
if(debugclients)
|
||||
@@ -681,12 +700,18 @@ void getAllQueries(char *client_message, int *sock)
|
||||
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)
|
||||
if(strcmp(clients[queries[i].clientID].ip, clientname) != 0 &&
|
||||
strcmp(clients[queries[i].clientID].name, clientname) != 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
char *domain = domains[queries[i].domainID].domain;
|
||||
char *client = clients[queries[i].clientID].ip;
|
||||
char *client;
|
||||
if(clients[queries[i].clientID].name != NULL &&
|
||||
strlen(clients[queries[i].clientID].name) > 0)
|
||||
client = clients[queries[i].clientID].name;
|
||||
else
|
||||
client = clients[queries[i].clientID].ip;
|
||||
|
||||
unsigned long delay = queries[i].response;
|
||||
// Check if received (delay should be smaller than 30min)
|
||||
@@ -967,7 +992,8 @@ void getClientsOverTime(int *sock)
|
||||
{
|
||||
validate_access("clients", i, true, __LINE__, __FUNCTION__, __FILE__);
|
||||
// Check if this client should be skipped
|
||||
if(insetupVarsArray(clients[i].ip))
|
||||
if(insetupVarsArray(clients[i].ip) ||
|
||||
insetupVarsArray(clients[i].name))
|
||||
skipclient[i] = true;
|
||||
}
|
||||
}
|
||||
@@ -1039,7 +1065,8 @@ void getClientNames(int *sock)
|
||||
{
|
||||
validate_access("clients", i, true, __LINE__, __FUNCTION__, __FILE__);
|
||||
// Check if this client should be skipped
|
||||
if(insetupVarsArray(clients[i].ip))
|
||||
if(insetupVarsArray(clients[i].ip) ||
|
||||
insetupVarsArray(clients[i].name))
|
||||
skipclient[i] = true;
|
||||
}
|
||||
}
|
||||
@@ -1051,8 +1078,14 @@ void getClientNames(int *sock)
|
||||
if(skipclient[i])
|
||||
continue;
|
||||
|
||||
char *client;
|
||||
if(clients[i].name != NULL && strlen(clients[i].name) > 0)
|
||||
client = clients[i].name;
|
||||
else
|
||||
client = clients[i].ip;
|
||||
|
||||
if(istelnet[*sock])
|
||||
ssend(*sock, "%i %i %s\n", i, clients[i].count, clients[i].ip);
|
||||
ssend(*sock, "%i %i %s\n", i, clients[i].count, client);
|
||||
else {
|
||||
if(!pack_str32(*sock, "") || !pack_str32(*sock, clients[i].ip))
|
||||
return;
|
||||
|
||||
@@ -555,6 +555,10 @@ void *DB_thread(void *val)
|
||||
// Update lastDBsave timer
|
||||
lastDBsave = time(NULL) - time(NULL)%config.DBinterval;
|
||||
|
||||
// This has to be run outside of the thread locks
|
||||
// to prevent locking the resolver
|
||||
resolveNewClients();
|
||||
|
||||
// Lock FTL's data structure, since it is
|
||||
// likely that it will be changed here
|
||||
enable_thread_lock();
|
||||
|
||||
@@ -111,6 +111,12 @@ int findForwardID(const char * forward, bool count)
|
||||
forwarded[forwardID].ip = strdup(forward);
|
||||
memory.forwardedips += (strlen(forward) + 1) * sizeof(char);
|
||||
forwarded[forwardID].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].name = NULL;
|
||||
// Increase counter by one
|
||||
counters.forwarded++;
|
||||
|
||||
@@ -191,6 +197,12 @@ int findClientID(const char *client)
|
||||
// Store client IP - no need to check for NULL here as it doesn't harm
|
||||
clients[clientID].ip = strdup(client);
|
||||
memory.clientips += (strlen(client) + 1) * sizeof(char);
|
||||
// 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].name = NULL;
|
||||
// Increase counter by one
|
||||
counters.clients++;
|
||||
|
||||
|
||||
@@ -166,6 +166,11 @@ void *GC_thread(void *val)
|
||||
|
||||
// Release thread lock
|
||||
disable_thread_lock();
|
||||
|
||||
// Reresolve client hostnames to account for changes
|
||||
// Have to this outside of the thread lock
|
||||
// to prevent locking of the resolver
|
||||
reresolveHostnames();
|
||||
}
|
||||
sleepms(100);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
/* 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
|
||||
* DNS Client Implementation
|
||||
*
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
#include "FTL.h"
|
||||
|
||||
char *resolveHostname(const char *addr)
|
||||
{
|
||||
// Get host name
|
||||
struct hostent *he = NULL;
|
||||
char *hostname = NULL;;
|
||||
bool IPv6 = false;
|
||||
|
||||
// Check if this is a hidden client
|
||||
// if so, return "hidden" as hostname
|
||||
if(strcmp(addr, "0.0.0.0") == 0)
|
||||
{
|
||||
hostname = strdup("hidden");
|
||||
//if(hostname == NULL) return NULL;
|
||||
return hostname;
|
||||
}
|
||||
|
||||
// Test if we want to resolve an IPv6 address
|
||||
if(strstr(addr,":") != NULL)
|
||||
{
|
||||
IPv6 = true;
|
||||
}
|
||||
|
||||
if(IPv6 && config.resolveIPv6) // Resolve IPv6 address only if requested
|
||||
{
|
||||
struct in6_addr ipaddr;
|
||||
inet_pton(AF_INET6, addr, &ipaddr);
|
||||
he = gethostbyaddr(&ipaddr, sizeof ipaddr, AF_INET6);
|
||||
}
|
||||
else if(!IPv6 && config.resolveIPv4) // Resolve IPv4 address only if requested
|
||||
{
|
||||
struct in_addr ipaddr;
|
||||
inet_pton(AF_INET, addr, &ipaddr);
|
||||
he = gethostbyaddr(&ipaddr, sizeof ipaddr, AF_INET);
|
||||
}
|
||||
|
||||
if(he == NULL)
|
||||
{
|
||||
// No hostname found
|
||||
hostname = strdup("");
|
||||
//if(hostname == NULL) return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Return hostname copied to new memory location
|
||||
hostname = strdup(he->h_name);
|
||||
if(hostname == NULL) return NULL;
|
||||
// Convert hostname to lower case
|
||||
strtolower(hostname);
|
||||
}
|
||||
return hostname;
|
||||
}
|
||||
|
||||
// This routine is run *after* garbage cleaning (default interval is once per hour)
|
||||
// to account for possibly updated hostnames
|
||||
void reresolveHostnames(void)
|
||||
{
|
||||
int clientID;
|
||||
for(clientID = 0; clientID < counters.clients; clientID++)
|
||||
{
|
||||
// Memory validation
|
||||
validate_access("clients", clientID, true, __LINE__, __FUNCTION__, __FILE__);
|
||||
|
||||
// Process this client only if it has at least one active query in the log
|
||||
if(clients[clientID].count < 1)
|
||||
continue;
|
||||
|
||||
// Get client hostname
|
||||
char *hostname = resolveHostname(clients[clientID].ip);
|
||||
if(strlen(hostname) > 0)
|
||||
{
|
||||
// Delete possibly already existing hostname pointer before storing new data
|
||||
if(clients[clientID].name != NULL)
|
||||
{
|
||||
free(clients[clientID].name);
|
||||
clients[clientID].name = NULL;
|
||||
}
|
||||
|
||||
// Store client hostname
|
||||
clients[clientID].name = strdup(hostname);
|
||||
}
|
||||
free(hostname);
|
||||
}
|
||||
}
|
||||
|
||||
// This routine is run *before* saving to the database (default interval is once per minute)
|
||||
// to account for new clients (and forward destinations)
|
||||
void resolveNewClients(void)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < counters.clients; i++)
|
||||
{
|
||||
// Memory validation
|
||||
validate_access("clients", i, true, __LINE__, __FUNCTION__, __FILE__);
|
||||
|
||||
// Only try to resolve new clients
|
||||
// Note that it can happen that we are not able to find hostnames but we don't
|
||||
// want to try to resolve them every minute in this case.
|
||||
if(clients[i].new)
|
||||
{
|
||||
clients[i].name = resolveHostname(clients[i].ip);
|
||||
clients[i].new = false;
|
||||
}
|
||||
}
|
||||
for(i = 0; i < counters.forwarded; i++)
|
||||
{
|
||||
// Memory validation
|
||||
validate_access("forwarded", i, true, __LINE__, __FUNCTION__, __FILE__);
|
||||
|
||||
// Only try to resolve new forward destinations
|
||||
if(forwarded[i].new)
|
||||
{
|
||||
forwarded[i].name = resolveHostname(forwarded[i].ip);
|
||||
forwarded[i].new = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -100,3 +100,7 @@ int main_dnsmasq(int argc, char **argv);
|
||||
|
||||
// signals.c
|
||||
void handle_signals(void);
|
||||
|
||||
// resolve.c
|
||||
void resolveNewClients(void);
|
||||
void reresolveHostnames(void);
|
||||
|
||||
@@ -165,6 +165,11 @@ void clearSetupVarsArray(void)
|
||||
bool insetupVarsArray(char * str)
|
||||
{
|
||||
int i;
|
||||
// Check for possible NULL pointer
|
||||
// (this is valid input, e.g. if clients[i].name is unspecified)
|
||||
if(str == NULL)
|
||||
return false;
|
||||
|
||||
// Loop over all entries in setupVarsArray
|
||||
for (i = 0; i < setupVarsElements; ++i)
|
||||
if(setupVarsArray[i][0] == '*')
|
||||
|
||||
Reference in New Issue
Block a user