Merge pull request #1986 from pi-hole/fix/retry_truncated

Internal name resolution over UDP with fallback to TCP
This commit is contained in:
Dominik
2024-06-02 21:14:53 +02:00
committed by GitHub
4 changed files with 50 additions and 66 deletions
+1 -1
View File
@@ -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);
+12 -9
View File
@@ -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
View File
@@ -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
View File
@@ -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));