forked from pi-hole/FTL
-
Notifications
You must be signed in to change notification settings - Fork 0
/
resolve.c
215 lines (188 loc) · 5.7 KB
/
resolve.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
/* 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(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++)
{
// Memory validation
validate_access("clients", clientID, true, __LINE__, __FUNCTION__, __FILE__);
// 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;
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)
clients[clientID].namepos = newnamepos;
// Mark entry as not new
clients[clientID].new = false;
unlock_shm();
}
}
// Resolve upstream destination host names
void resolveForwardDestinations(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++)
{
// Memory validation
validate_access("forwarded", forwardID, true, __LINE__, __FUNCTION__, __FILE__);
// 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;
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)
forwarded[forwardID].namepos = newnamepos;
// Mark entry as not new
forwarded[forwardID].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;
}