FTL/resolve.c

215 lines
5.5 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
* 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"
#include "shmem.h"
static 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;
}
// Resolve upstream destination host names
static size_t resolveAndAddHostname(size_t ippos, size_t oldnamepos)
{
// Get IP and host name strings. They are cloned in case shared memory is
// resized before the next lock
lock_shm();
char* ipaddr = strdup(getstr(ippos));
char* oldname = strdup(getstr(oldnamepos));
unlock_shm();
// Important: Don't hold a lock while resolving as the main thread
// (dnsmasq) needs to be operable during the call to resolveHostname()
char* newname = resolveHostname(ipaddr);
// Only store new newname if it is valid and differs from oldname
// We do not need to check for oldname == NULL as names are
// always initialized with an empty string at position 0
if(newname != NULL && strcmp(oldname, newname) != 0)
{
lock_shm();
size_t newnamepos = addstr(newname);
// newname has already been checked against NULL
// so we can safely free it
free(newname);
free(ipaddr);
free(oldname);
unlock_shm();
return newnamepos;
}
else if(config.debug & DEBUG_SHMEM)
{
// Debugging output
logg("Not adding \"%s\" to buffer (unchanged)", oldname);
}
free(ipaddr);
free(oldname);
// Not changed, return old namepos
return oldnamepos;
}
// Resolve client host names
void resolveClients(const bool onlynew)
{
// Lock counter access here, we use a copy in the following loop
lock_shm();
int clientscount = counters->clients;
unlock_shm();
for(int clientID = 0; clientID < clientscount; clientID++)
{
// Get client pointer
clientsData* client = getClient(clientID, true);
// Memory access needs to get locked
lock_shm();
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
// If not, we will try to re-resolve all known clients
if(onlynew && !newflag)
continue;
// Obtain/update hostname of this client
size_t newnamepos = resolveAndAddHostname(ippos, oldnamepos);
lock_shm();
// Store obtained host name (may be unchanged)
client->namepos = newnamepos;
// Mark entry as not new
client->new = false;
unlock_shm();
}
}
// Resolve upstream destination host names
void resolveForwardDestinations(const bool onlynew)
{
// Lock counter access here, we use a copy in the following loop
lock_shm();
int forwardedcount = counters->forwarded;
unlock_shm();
for(int forwardID = 0; forwardID < forwardedcount; forwardID++)
{
// Get forward pointer
forwardedData* forward = getForward(forwardID, true);
// Memory access needs to get locked
lock_shm();
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
// If not, we will try to re-resolve all known upstream destinations
if(onlynew && !newflag)
continue;
// Obtain/update hostname of this client
size_t newnamepos = resolveAndAddHostname(ippos, oldnamepos);
lock_shm();
// Store obtained host name (may be unchanged)
forward->namepos = newnamepos;
// Mark entry as not new
forward->new = false;
unlock_shm();
}
}
void *DNSclient_thread(void *val)
{
// Set thread name
prctl(PR_SET_NAME, "DNS client", 0, 0, 0);
while(!killed)
{
// Run every minute to resolve only new clients and upstream servers
if(time(NULL) % RESOLVE_INTERVAL == 0)
{
// Try to resolve new client host names (onlynew=true)
resolveClients(true);
// Try to resolve new upstream destination host names (onlynew=true)
resolveForwardDestinations(true);
// Prevent immediate re-run of this routine
sleepms(500);
}
// Run every hour to update possibly changed client host names
if(time(NULL) % RERESOLVE_INTERVAL == 0)
{
// Try to resolve all client host names (onlynew=false)
resolveClients(false);
// Try to resolve all upstream destination host names (onlynew=false)
resolveForwardDestinations(false);
// Prevent immediate re-run of this routine
sleepms(500);
}
// Idle for 0.5 sec before checking again the time criteria
sleepms(500);
}
return NULL;
}