mirror of
https://github.com/pi-hole/FTL.git
synced 2024-10-26 16:52:18 +02:00
Merge pull request #1986 from pi-hole/fix/retry_truncated
Internal name resolution over UDP with fallback to TCP
This commit is contained in:
+1
-1
@@ -513,7 +513,7 @@ void parse_args(int argc, char* argv[])
|
||||
// Create a socket
|
||||
struct sockaddr_in dest;
|
||||
const int sock = create_socket(tcp, &dest);
|
||||
char *name = resolveHostname(sock, &dest, tcp, argv[2], true);
|
||||
char *name = resolveHostname(sock, tcp, &dest, argv[2], true, NULL);
|
||||
|
||||
// Close the socket
|
||||
close(sock);
|
||||
|
||||
@@ -2009,7 +2009,7 @@ char *__attribute__((malloc)) getNameFromIP(sqlite3 *db, const char *ipaddr)
|
||||
// Check if we want to resolve host names
|
||||
if(!resolve_this_name(ipaddr))
|
||||
{
|
||||
log_debug(DEBUG_DATABASE, "getNameFromIP(\"%s\") - configured to not resolve host name", ipaddr);
|
||||
log_debug(DEBUG_RESOLVER, "getNameFromIP(\"%s\") - configured to not resolve host name", ipaddr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -2056,6 +2056,8 @@ char *__attribute__((malloc)) getNameFromIP(sqlite3 *db, const char *ipaddr)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
log_debug(DEBUG_RESOLVER, "Check for a host name associated with IP address %s", ipaddr);
|
||||
|
||||
char *name = NULL;
|
||||
rc = sqlite3_step(stmt);
|
||||
if(rc == SQLITE_ROW)
|
||||
@@ -2063,7 +2065,7 @@ char *__attribute__((malloc)) getNameFromIP(sqlite3 *db, const char *ipaddr)
|
||||
// Database record found (result might be empty)
|
||||
name = strdup((char*)sqlite3_column_text(stmt, 0));
|
||||
|
||||
log_debug(DEBUG_DATABASE, "Found database host name (same address) %s -> %s", ipaddr, name);
|
||||
log_debug(DEBUG_RESOLVER, "Found database host name (same address) %s -> %s", ipaddr, name);
|
||||
}
|
||||
else if(rc != SQLITE_DONE)
|
||||
{
|
||||
@@ -2084,6 +2086,8 @@ char *__attribute__((malloc)) getNameFromIP(sqlite3 *db, const char *ipaddr)
|
||||
return name;
|
||||
}
|
||||
|
||||
log_debug(DEBUG_RESOLVER, " ---> not found");
|
||||
|
||||
// Nothing found for the exact IP address
|
||||
// Check for a host name associated with the same device (but another IP address)
|
||||
querystr = "SELECT name FROM network_addresses "
|
||||
@@ -2114,6 +2118,8 @@ char *__attribute__((malloc)) getNameFromIP(sqlite3 *db, const char *ipaddr)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
log_debug(DEBUG_RESOLVER, "Checking for a host name associated with the same device (but another IP address)");
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
if(rc == SQLITE_ROW)
|
||||
{
|
||||
@@ -2153,8 +2159,6 @@ char *__attribute__((malloc)) getNameFromMAC(const char *client)
|
||||
if(FTLDBerror())
|
||||
return NULL;
|
||||
|
||||
log_debug(DEBUG_DATABASE,"Looking up host name for %s", client);
|
||||
|
||||
// Open pihole-FTL.db database file
|
||||
sqlite3 *db = NULL;
|
||||
if((db = dbopen(false, false)) == NULL)
|
||||
@@ -2192,6 +2196,8 @@ char *__attribute__((malloc)) getNameFromMAC(const char *client)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
log_debug(DEBUG_RESOLVER, "Check for a host name associated with MAC address %s", client);
|
||||
|
||||
char *name = NULL;
|
||||
rc = sqlite3_step(stmt);
|
||||
if(rc == SQLITE_ROW)
|
||||
@@ -2262,11 +2268,8 @@ char *__attribute__((malloc)) getIfaceFromIP(sqlite3 *db, const char *ipaddr)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(config.debug.resolver.v.b)
|
||||
{
|
||||
log_debug(DEBUG_RESOLVER, "getIfaceFromIP(): \"%s\" with ? = \"%s\"",
|
||||
querystr, ipaddr);
|
||||
}
|
||||
log_debug(DEBUG_DATABASE, "getIfaceFromIP(): \"%s\" with ? = \"%s\"",
|
||||
querystr, ipaddr);
|
||||
|
||||
// Bind ipaddr to prepared statement
|
||||
if((rc = sqlite3_bind_text(stmt, 1, ipaddr, -1, SQLITE_STATIC)) != SQLITE_OK)
|
||||
|
||||
+35
-54
@@ -261,7 +261,8 @@ int create_socket(bool tcp, struct sockaddr_in *dest)
|
||||
}
|
||||
|
||||
// Perform a name lookup by sending a packet to ourselves
|
||||
static char *__attribute__((malloc)) ngethostbyname(const int sock, struct sockaddr_in *dest, const bool tcp, const char *host, const char *ipaddr)
|
||||
static char *__attribute__((malloc)) ngethostbyname(const int sock, const bool tcp, struct sockaddr_in *dest,
|
||||
const char *host, const char *ipaddr, bool *truncated)
|
||||
{
|
||||
uint8_t buf[4096] = { 0 }; // buffer for DNS query
|
||||
uint8_t *qname = NULL, *reader = NULL;
|
||||
@@ -385,8 +386,9 @@ static char *__attribute__((malloc)) ngethostbyname(const int sock, struct socka
|
||||
// Abort if the query was not successful
|
||||
if(dns->tc != 0)
|
||||
{
|
||||
log_debug(DEBUG_RESOLVER, "Internal name lookup for %s was unsuccessful: DNS response was truncated",
|
||||
ipaddr);
|
||||
log_debug(DEBUG_RESOLVER, " --> DNS response truncated");
|
||||
if(truncated != NULL)
|
||||
*truncated = true;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -556,8 +558,8 @@ static void __attribute__((nonnull(1,3))) name_toDNS(unsigned char *dns, const s
|
||||
*dns++='\0';
|
||||
}
|
||||
|
||||
char *__attribute__((malloc)) resolveHostname(const int sock, struct sockaddr_in *dest,
|
||||
const bool tcp, const char *addr, const bool force)
|
||||
char *__attribute__((malloc)) resolveHostname(const int sock, const bool tcp, struct sockaddr_in *dest,
|
||||
const char *addr, const bool force, bool *truncated)
|
||||
{
|
||||
// Get host name
|
||||
char *hostn = NULL;
|
||||
@@ -680,11 +682,17 @@ char *__attribute__((malloc)) resolveHostname(const int sock, struct sockaddr_in
|
||||
// Get host name by making a reverse lookup to ourselves (server at 127.0.0.1 with port 53)
|
||||
// We implement a minimalistic resolver here as we cannot rely on the system resolver using whatever
|
||||
// nameserver we configured in /etc/resolv.conf
|
||||
return ngethostbyname(sock, dest, tcp, inaddr, addr);
|
||||
hostn = ngethostbyname(sock, tcp, dest, inaddr, addr, truncated);
|
||||
|
||||
// Free allocated memory
|
||||
free(inaddr);
|
||||
|
||||
// Return obtained host name
|
||||
return hostn;
|
||||
}
|
||||
|
||||
// Resolve upstream destination host names
|
||||
static size_t resolveAndAddHostname(const int sock, struct sockaddr_in *dest, const bool tcp,
|
||||
static size_t resolveAndAddHostname(const int udp_sock, struct sockaddr_in *dest,
|
||||
size_t ippos, size_t oldnamepos, bool *success)
|
||||
{
|
||||
// Get IP and host name strings. They are cloned in case shared memory is
|
||||
@@ -710,12 +718,21 @@ static size_t resolveAndAddHostname(const int sock, struct sockaddr_in *dest, co
|
||||
|
||||
// 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(sock, dest, tcp, ipaddr, false);
|
||||
bool truncated = false;
|
||||
char *newname = resolveHostname(udp_sock, false, dest, ipaddr, false, &truncated);
|
||||
if(newname == NULL && truncated)
|
||||
{
|
||||
// Retry with TCP if UDP failed due to truncation (RFC 7766)
|
||||
const int tcp_sock = create_socket(true, dest);
|
||||
newname = resolveHostname(tcp_sock, true, dest, ipaddr, false, NULL);
|
||||
close(tcp_sock);
|
||||
}
|
||||
|
||||
if(newname == NULL)
|
||||
{
|
||||
// We could not resolve the hostname, so we keep the old one
|
||||
// and mark the entry as not new
|
||||
log_debug(DEBUG_RESOLVER, " ---> \"%s\" (failed to resolve)", oldname);
|
||||
log_debug(DEBUG_RESOLVER, " ---> \"%s\" (failed to resolve via UDP, too)", oldname);
|
||||
|
||||
// Free allocated memory
|
||||
*success = false;
|
||||
@@ -776,17 +793,15 @@ static void resolveClients(const bool onlynew, const bool force_refreshing)
|
||||
unlock_shm();
|
||||
|
||||
// Create DNS client socket
|
||||
const bool tcp = true;
|
||||
struct sockaddr_in dest = { 0 };
|
||||
int sock = create_socket(tcp, &dest);
|
||||
if(sock < 0)
|
||||
const int udp_sock = create_socket(false, &dest);
|
||||
if(udp_sock < 0)
|
||||
{
|
||||
log_err("Unable to create DNS resolver socket, client host name resolution failed");
|
||||
return;
|
||||
}
|
||||
|
||||
int skipped = 0;
|
||||
unsigned int queries = 0u;
|
||||
for(int clientID = 0; clientID < clientscount; clientID++)
|
||||
{
|
||||
// Memory access needs to get locked
|
||||
@@ -874,25 +889,9 @@ static void resolveClients(const bool onlynew, const bool force_refreshing)
|
||||
continue;
|
||||
}
|
||||
|
||||
// We need to reconnect after a certain number of queries due to
|
||||
// dnsmasq-internal limits
|
||||
if(tcp && ++queries > TCP_MAX_QUERIES - 1)
|
||||
{
|
||||
close(sock);
|
||||
sock = create_socket(tcp, &dest);
|
||||
if(sock < 0)
|
||||
{
|
||||
log_err("Unable to recreate to DNS resolver socket, client host name resolution failed");
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset query counter
|
||||
queries = 0;
|
||||
}
|
||||
|
||||
// Obtain/update hostname of this client
|
||||
bool success = true;
|
||||
size_t newnamepos = resolveAndAddHostname(sock, &dest, tcp, ippos, oldnamepos, &success);
|
||||
size_t newnamepos = resolveAndAddHostname(udp_sock, &dest, ippos, oldnamepos, &success);
|
||||
|
||||
lock_shm();
|
||||
// Get client pointer for the second time (writing data)
|
||||
@@ -933,7 +932,7 @@ static void resolveClients(const bool onlynew, const bool force_refreshing)
|
||||
}
|
||||
|
||||
// Close socket
|
||||
close(sock);
|
||||
close(udp_sock);
|
||||
|
||||
log_debug(DEBUG_RESOLVER, "%i / %i client host names resolved",
|
||||
clientscount-skipped, clientscount);
|
||||
@@ -949,17 +948,15 @@ static void resolveUpstreams(const bool onlynew)
|
||||
unlock_shm();
|
||||
|
||||
// Create socket
|
||||
const bool tcp = true;
|
||||
struct sockaddr_in dest = { 0 };
|
||||
int sock = create_socket(tcp, &dest);
|
||||
if(sock < 0)
|
||||
const int udp_sock = create_socket(false, &dest);
|
||||
if(udp_sock < 0)
|
||||
{
|
||||
log_err("Unable to create DNS resolver socket, upstream host name resolution failed");
|
||||
log_err("Unable to create DNS resolver socket, client host name resolution failed");
|
||||
return;
|
||||
}
|
||||
|
||||
int skipped = 0;
|
||||
unsigned int queries = 0u;
|
||||
for(int upstreamID = 0; upstreamID < upstreams; upstreamID++)
|
||||
{
|
||||
// Memory access needs to get locked
|
||||
@@ -1004,25 +1001,9 @@ static void resolveUpstreams(const bool onlynew)
|
||||
continue;
|
||||
}
|
||||
|
||||
// We need to reconnect after a certain number of queries due to
|
||||
// dnsmasq-internal limits
|
||||
if(tcp && ++queries > TCP_MAX_QUERIES - 1)
|
||||
{
|
||||
close(sock);
|
||||
sock = create_socket(tcp, &dest);
|
||||
if(sock < 0)
|
||||
{
|
||||
log_err("Unable to recreate to DNS resolver socket, client host name resolution failed");
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset query counter
|
||||
queries = 0;
|
||||
}
|
||||
|
||||
// Obtain/update hostname of this client
|
||||
bool success = true;
|
||||
size_t newnamepos = resolveAndAddHostname(sock, &dest, tcp, ippos, oldnamepos, &success);
|
||||
size_t newnamepos = resolveAndAddHostname(udp_sock, &dest, ippos, oldnamepos, &success);
|
||||
|
||||
lock_shm();
|
||||
// Get upstream pointer for the second time (writing data)
|
||||
@@ -1062,7 +1043,7 @@ static void resolveUpstreams(const bool onlynew)
|
||||
}
|
||||
|
||||
// Close socket
|
||||
close(sock);
|
||||
close(udp_sock);
|
||||
|
||||
log_debug(DEBUG_RESOLVER, "%i / %i upstream server host names resolved",
|
||||
upstreams-skipped, upstreams);
|
||||
|
||||
+2
-2
@@ -12,8 +12,8 @@
|
||||
|
||||
void *DNSclient_thread(void *val);
|
||||
int create_socket(bool tcp, struct sockaddr_in *dest);
|
||||
char *resolveHostname(const int sock, struct sockaddr_in *dest, const bool tcp,
|
||||
const char *addr, const bool force) __attribute__((malloc));
|
||||
char *resolveHostname(const int sock, const bool tcp, struct sockaddr_in *dest,
|
||||
const char *addr, const bool force, bool *truncated) __attribute__((malloc));
|
||||
bool resolve_names(void) __attribute__((pure));
|
||||
bool resolve_this_name(const char *ipaddr) __attribute__((pure));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user