Commit 361dfe51 authored by Simon Kelley's avatar Simon Kelley

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.
parent 68f6312d
......@@ -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;
......
......@@ -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,43 +1505,41 @@ 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)
if (search_servers(now, NULL, F_QUERY, keyname, &type, &domain, NULL) != 0)
{
struct server *start = server, *new_server = NULL;
new_status = STAT_ABANDONED;
break;
}
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)))
if (!firstsendto)
firstsendto = server;
else
{
new_server = start;
if (server == start)
if (!(server = server->next))
server = daemon->servers;
if (server == firstsendto)
{
new_server = NULL;
/* can't find server to accept our query. */
new_status = STAT_ABANDONED;
break;
}
}
if (!(start = start->next))
start = daemon->servers;
if (start == server)
break;
}
if (type != (server->flags & SERV_TYPE) ||
(type == SERV_HAS_DOMAIN && !hostname_isequal(domain, server->domain)) ||
(server->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP)))
continue;
if (new_server)
{
server = new_server;
retry:
/* may need to make new connection. */
if (server->tcpfd == -1)
{
if ((server->tcpfd = socket(server->addr.sa.sa_family, SOCK_STREAM, 0)) == -1)
{
new_status = STAT_ABANDONED;
break;
}
continue; /* No good, next server */
#ifdef HAVE_CONNTRACK
/* Copy connection mark of incoming query to outgoing connection. */
......@@ -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)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment