mirror of
https://github.com/pi-hole/dnsmasq.git
synced 2025-12-19 18:28:25 +00:00
Improve connection handling when talking to TCP upsteam servers.
Specifically, be prepared to open a new connection when we want to make multiple queries but the upstream server accepts fewer queries per connection.
This commit is contained in:
@@ -65,6 +65,11 @@ version 2.77
|
||||
Thanks to Kevin Darbyshire-Bryant and Eric Luehrsen
|
||||
for pushing this.
|
||||
|
||||
Improve connection handling when talking to TCP upsteam
|
||||
servers. Specifically, be prepared to open a new TCP
|
||||
connection when we want to make multiple queries
|
||||
but the upstream server accepts fewer queries per connection.
|
||||
|
||||
|
||||
version 2.76
|
||||
Include 0.0.0.0/8 in DNS rebind checks. This range
|
||||
|
||||
@@ -485,6 +485,7 @@ union mysockaddr {
|
||||
#define SERV_FROM_FILE 4096 /* read from --servers-file */
|
||||
#define SERV_LOOP 8192 /* server causes forwarding loop */
|
||||
#define SERV_DO_DNSSEC 16384 /* Validate DNSSEC when using this server */
|
||||
#define SERV_GOT_TCP 32768 /* Got some data from the TCP connection */
|
||||
|
||||
struct serverfd {
|
||||
int fd;
|
||||
|
||||
101
src/forward.c
101
src/forward.c
@@ -1466,6 +1466,7 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
|
||||
char *domain;
|
||||
size_t m;
|
||||
unsigned char c1, c2;
|
||||
struct server *firstsendto = NULL;
|
||||
|
||||
/* limit the amount of work we do, to avoid cycling forever on loops in the DNS */
|
||||
if (--(*keycount) == 0)
|
||||
@@ -1504,44 +1505,42 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
|
||||
/* Find server to forward to. This will normally be the
|
||||
same as for the original query, but may be another if
|
||||
servers for domains are involved. */
|
||||
if (search_servers(now, NULL, F_QUERY, keyname, &type, &domain, NULL) == 0)
|
||||
{
|
||||
struct server *start = server, *new_server = NULL;
|
||||
type &= ~SERV_DO_DNSSEC;
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (type == (start->flags & SERV_TYPE) &&
|
||||
(type != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)) &&
|
||||
!(start->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP)))
|
||||
{
|
||||
new_server = start;
|
||||
if (server == start)
|
||||
{
|
||||
new_server = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(start = start->next))
|
||||
start = daemon->servers;
|
||||
if (start == server)
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (new_server)
|
||||
{
|
||||
server = new_server;
|
||||
/* may need to make new connection. */
|
||||
if (server->tcpfd == -1)
|
||||
{
|
||||
if ((server->tcpfd = socket(server->addr.sa.sa_family, SOCK_STREAM, 0)) == -1)
|
||||
if (search_servers(now, NULL, F_QUERY, keyname, &type, &domain, NULL) != 0)
|
||||
{
|
||||
new_status = STAT_ABANDONED;
|
||||
break;
|
||||
}
|
||||
|
||||
type &= ~SERV_DO_DNSSEC;
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (!firstsendto)
|
||||
firstsendto = server;
|
||||
else
|
||||
{
|
||||
if (!(server = server->next))
|
||||
server = daemon->servers;
|
||||
if (server == firstsendto)
|
||||
{
|
||||
/* can't find server to accept our query. */
|
||||
new_status = STAT_ABANDONED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (type != (server->flags & SERV_TYPE) ||
|
||||
(type == SERV_HAS_DOMAIN && !hostname_isequal(domain, server->domain)) ||
|
||||
(server->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP)))
|
||||
continue;
|
||||
|
||||
retry:
|
||||
/* may need to make new connection. */
|
||||
if (server->tcpfd == -1)
|
||||
{
|
||||
if ((server->tcpfd = socket(server->addr.sa.sa_family, SOCK_STREAM, 0)) == -1)
|
||||
continue; /* No good, next server */
|
||||
|
||||
#ifdef HAVE_CONNTRACK
|
||||
/* Copy connection mark of incoming query to outgoing connection. */
|
||||
if (have_mark)
|
||||
@@ -1553,27 +1552,34 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
|
||||
{
|
||||
close(server->tcpfd);
|
||||
server->tcpfd = -1;
|
||||
new_status = STAT_ABANDONED;
|
||||
break;
|
||||
continue; /* No good, next server */
|
||||
}
|
||||
|
||||
server->flags &= ~SERV_GOT_TCP;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!read_write(server->tcpfd, packet, m + sizeof(u16), 0) ||
|
||||
!read_write(server->tcpfd, &c1, 1, 1) ||
|
||||
!read_write(server->tcpfd, &c2, 1, 1) ||
|
||||
!read_write(server->tcpfd, payload, (c1 << 8) | c2, 1))
|
||||
{
|
||||
new_status = STAT_ABANDONED;
|
||||
break;
|
||||
close(server->tcpfd);
|
||||
server->tcpfd = -1;
|
||||
/* We get data then EOF, reopen connection to same server,
|
||||
else try next. This avoids DoS from a server which accepts
|
||||
connections and then closes them. */
|
||||
if (server->flags & SERV_GOT_TCP)
|
||||
goto retry;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
|
||||
m = (c1 << 8) | c2;
|
||||
server->flags |= SERV_GOT_TCP;
|
||||
|
||||
m = (c1 << 8) | c2;
|
||||
new_status = tcp_key_recurse(now, new_status, new_header, m, class, name, keyname, server, have_mark, mark, keycount);
|
||||
break;
|
||||
}
|
||||
|
||||
if (new_status != STAT_OK)
|
||||
break;
|
||||
@@ -1821,6 +1827,7 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
(last_server->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP)))
|
||||
continue;
|
||||
|
||||
retry:
|
||||
if (last_server->tcpfd == -1)
|
||||
{
|
||||
if ((last_server->tcpfd = socket(last_server->addr.sa.sa_family, SOCK_STREAM, 0)) == -1)
|
||||
@@ -1840,6 +1847,9 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
continue;
|
||||
}
|
||||
|
||||
last_server->flags &= ~SERV_GOT_TCP;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID) && (last_server->flags & SERV_DO_DNSSEC))
|
||||
{
|
||||
@@ -1857,7 +1867,6 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
header->hb4 |= HB4_CD;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
*length = htons(size);
|
||||
|
||||
@@ -1872,9 +1881,17 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
{
|
||||
close(last_server->tcpfd);
|
||||
last_server->tcpfd = -1;
|
||||
/* We get data then EOF, reopen connection to same server,
|
||||
else try next. This avoids DoS from a server which accepts
|
||||
connections and then closes them. */
|
||||
if (last_server->flags & SERV_GOT_TCP)
|
||||
goto retry;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
|
||||
last_server->flags |= SERV_GOT_TCP;
|
||||
|
||||
m = (c1 << 8) | c2;
|
||||
|
||||
if (last_server->addr.sa.sa_family == AF_INET)
|
||||
|
||||
Reference in New Issue
Block a user