Compare commits

..
Author SHA1 Message Date
Simon Kelley d05dd58de1 Fix wrong reply to simple name when --domain-needed set and no servers configured.
Also return REFUSED and not SERVFAIL when out of memory.

Thanks to Allain Legacy for problem report.
2016-01-19 21:23:30 +00:00
Simon Kelley f7443d76f7 Fix problems in last commit when DNSSEC not enabled. 2016-01-19 20:29:57 +00:00
Simon Kelley f344dbc622 Complete DNSSEC server-selection code and set conntrack on DNSSEC queries. 2016-01-18 18:11:54 +00:00
Simon Kelley f4d0c660ca Fix sporadic crash in find_mac() - hwlen must be zero for empty entries. 2016-01-18 12:51:08 +00:00
Simon Kelley 1801a29226 Fix botch in forward.c flags code.
Thanks to Matthias Anfree for spotting this.
2016-01-17 21:53:57 +00:00
Simon Kelley 92be34a407 Complete work to allow DNSSEC validation with private DNS servers. 2016-01-16 18:39:54 +00:00
6 changed files with 173 additions and 41 deletions

No files matched your search

+9
View File
@@ -22,6 +22,15 @@ version 2.76
reading a hosts-file fails. Thanks to André Glüpker reading a hosts-file fails. Thanks to André Glüpker
for the patch. for the patch.
Fix wrong answer to simple name query when --domain-needed
set, but no upstream servers configured. Dnsmasq returned
REFUSED, in this case, when it should be the same as when
upstream servers are configured - NOERROR. Thanks to
Allain Legacy for spotting the problem.
Return REFUSED when running out of forwarding table slots,
not SERVFAIL.
version 2.75 version 2.75
Fix reversion on 2.74 which caused 100% CPU use when a Fix reversion on 2.74 which caused 100% CPU use when a
+4 -1
View File
@@ -405,7 +405,10 @@ xxx.internal.thekelleys.org.uk at 192.168.1.1 then giving the flag
.B -S /internal.thekelleys.org.uk/192.168.1.1 .B -S /internal.thekelleys.org.uk/192.168.1.1
will send all queries for will send all queries for
internal machines to that nameserver, everything else will go to the internal machines to that nameserver, everything else will go to the
servers in /etc/resolv.conf. An empty domain specification, servers in /etc/resolv.conf. DNSSEC validation is turned off for such
private nameservers, UNLESS a
.B --trust-anchor
is specified for the domain in question. An empty domain specification,
.B // .B //
has the special meaning of "unqualified names only" ie names without any has the special meaning of "unqualified names only" ie names without any
dots in them. A non-standard port may be specified as dots in them. A non-standard port may be specified as
+1
View File
@@ -188,6 +188,7 @@ int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy, time_t now)
arps = arp; arps = arp;
arp->status = ARP_EMPTY; arp->status = ARP_EMPTY;
arp->family = addr->sa.sa_family; arp->family = addr->sa.sa_family;
arp->hwlen = 0;
if (addr->sa.sa_family == AF_INET) if (addr->sa.sa_family == AF_INET)
arp->addr.addr.addr4.s_addr = addr->in.sin_addr.s_addr; arp->addr.addr.addr4.s_addr = addr->in.sin_addr.s_addr;
+152 -36
View File
@@ -151,7 +151,7 @@ static unsigned int search_servers(time_t now, struct all_addr **addrpp, unsigne
hostname_isequal(matchstart, serv->domain) && hostname_isequal(matchstart, serv->domain) &&
(domainlen == 0 || namelen == domainlen || *(matchstart-1) == '.' )) (domainlen == 0 || namelen == domainlen || *(matchstart-1) == '.' ))
{ {
if (serv->flags & SERV_NO_REBIND) if ((serv->flags & SERV_NO_REBIND) && norebind)
*norebind = 1; *norebind = 1;
else else
{ {
@@ -249,9 +249,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
(void)do_bit; (void)do_bit;
/* may be no servers available. */ /* may be no servers available. */
if (!daemon->servers) if (forward || (hash && (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, hash))))
forward = NULL;
else if (forward || (hash && (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, hash))))
{ {
/* If we didn't get an answer advertising a maximal packet in EDNS, /* If we didn't get an answer advertising a maximal packet in EDNS,
fall back to 1280, which should work everywhere on IPv6. fall back to 1280, which should work everywhere on IPv6.
@@ -331,12 +329,12 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
do_dnssec = type & SERV_DO_DNSSEC; do_dnssec = type & SERV_DO_DNSSEC;
type &= ~SERV_DO_DNSSEC;
#endif #endif
type &= ~SERV_DO_DNSSEC;
if (!flags && !(forward = get_new_frec(now, NULL, 0))) if (daemon->servers && !flags)
/* table full - server failure. */ forward = get_new_frec(now, NULL, 0);
flags = F_NEG; /* table full - flags == 0, return REFUSED */
if (forward) if (forward)
{ {
@@ -644,7 +642,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
return resize_packet(header, n, pheader, plen); return resize_packet(header, n, pheader, plen);
/* Complain loudly if the upstream server is non-recursive. */ /* Complain loudly if the upstream server is non-recursive. */
if (!(header->hb4 & HB4_RA) && RCODE(header) == NOERROR && ntohs(header->ancount) == 0 && if (!(header->hb4 & HB4_RA) && RCODE(header) == NOERROR &&
server && !(server->flags & SERV_WARNED_RECURSIVE)) server && !(server->flags & SERV_WARNED_RECURSIVE))
{ {
prettyprint_addr(&server->addr, daemon->namebuff); prettyprint_addr(&server->addr, daemon->namebuff);
@@ -810,9 +808,9 @@ void reply_query(int fd, int family, time_t now)
{ {
header->hb3 &= ~(HB3_QR | HB3_AA | HB3_TC); header->hb3 &= ~(HB3_QR | HB3_AA | HB3_TC);
header->hb4 &= ~(HB4_RA | HB4_RCODE | HB4_CD | HB4_AD); header->hb4 &= ~(HB4_RA | HB4_RCODE | HB4_CD | HB4_AD);
if (forward->flags |= FREC_CHECKING_DISABLED) if (forward->flags & FREC_CHECKING_DISABLED)
header->hb4 |= HB4_CD; header->hb4 |= HB4_CD;
if (forward->flags |= FREC_AD_QUESTION) if (forward->flags & FREC_AD_QUESTION)
header->hb4 |= HB4_AD; header->hb4 |= HB4_AD;
if (forward->flags & FREC_DO_QUESTION) if (forward->flags & FREC_DO_QUESTION)
add_do_bit(header, nn, (unsigned char *)pheader + plen); add_do_bit(header, nn, (unsigned char *)pheader + plen);
@@ -923,12 +921,48 @@ void reply_query(int fd, int family, time_t now)
status = STAT_ABANDONED; status = STAT_ABANDONED;
else else
{ {
int fd; int fd, type = SERV_DO_DNSSEC;
struct frec *next = new->next; struct frec *next = new->next;
char *domain;
*new = *forward; /* copy everything, then overwrite */ *new = *forward; /* copy everything, then overwrite */
new->next = next; new->next = next;
new->blocking_query = NULL; new->blocking_query = NULL;
/* 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, daemon->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;
}
new->sentto = server; new->sentto = server;
new->rfd4 = NULL; new->rfd4 = NULL;
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
new->rfd6 = NULL; new->rfd6 = NULL;
@@ -982,6 +1016,15 @@ void reply_query(int fd, int family, time_t now)
if (fd != -1) if (fd != -1)
{ {
#ifdef HAVE_CONNTRACK
/* Copy connection mark of incoming query to outgoing connection. */
if (option_bool(OPT_CONNTRACK))
{
unsigned int mark;
if (get_incoming_mark(&orig->source, &orig->dest, 0, &mark))
setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
}
#endif
while (retry_send(sendto(fd, (char *)header, nn, 0, while (retry_send(sendto(fd, (char *)header, nn, 0,
&server->addr.sa, &server->addr.sa,
sa_len(&server->addr)))); sa_len(&server->addr))));
@@ -1044,7 +1087,7 @@ void reply_query(int fd, int family, time_t now)
else else
header->hb4 &= ~HB4_CD; header->hb4 &= ~HB4_CD;
if ((nn = process_reply(header, now, server, (size_t)n, check_rebind, no_cache_dnssec, cache_secure, bogusanswer, if ((nn = process_reply(header, now, forward->sentto, (size_t)n, check_rebind, no_cache_dnssec, cache_secure, bogusanswer,
forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION, forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION,
forward->flags & FREC_ADDED_PHEADER, forward->flags & FREC_HAS_SUBNET, &forward->source))) forward->flags & FREC_ADDED_PHEADER, forward->flags & FREC_HAS_SUBNET, &forward->source)))
{ {
@@ -1405,20 +1448,24 @@ void receive_query(struct listener *listen, time_t now)
} }
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
static int tcp_key_recurse(time_t now, int status, struct dns_header *header, size_t n,
int class, char *name, char *keyname, struct server *server, int *keycount)
{
/* Recurse up the key heirarchy */ /* Recurse up the key heirarchy */
static int tcp_key_recurse(time_t now, int status, struct dns_header *header, size_t n,
int class, char *name, char *keyname, struct server *server,
int have_mark, unsigned int mark, int *keycount)
{
int new_status; int new_status;
unsigned char *packet = NULL; unsigned char *packet = NULL;
size_t m;
unsigned char *payload = NULL; unsigned char *payload = NULL;
struct dns_header *new_header = NULL; struct dns_header *new_header = NULL;
u16 *length = NULL; u16 *length = NULL;
unsigned char c1, c2;
while (1) while (1)
{ {
int type = SERV_DO_DNSSEC;
char *domain;
size_t m;
unsigned char c1, c2;
/* limit the amount of work we do, to avoid cycling forever on loops in the DNS */ /* limit the amount of work we do, to avoid cycling forever on loops in the DNS */
if (--(*keycount) == 0) if (--(*keycount) == 0)
new_status = STAT_ABANDONED; new_status = STAT_ABANDONED;
@@ -1453,6 +1500,67 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
*length = htons(m); *length = htons(m);
/* 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)
{
new_status = STAT_ABANDONED;
break;
}
#ifdef HAVE_CONNTRACK
/* Copy connection mark of incoming query to outgoing connection. */
if (have_mark)
setsockopt(server->tcpfd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
#endif
if (!local_bind(server->tcpfd, &server->source_addr, server->interface, 1) ||
connect(server->tcpfd, &server->addr.sa, sa_len(&server->addr)) == -1)
{
close(server->tcpfd);
server->tcpfd = -1;
new_status = STAT_ABANDONED;
break;
}
}
}
}
if (!read_write(server->tcpfd, packet, m + sizeof(u16), 0) || if (!read_write(server->tcpfd, packet, m + sizeof(u16), 0) ||
!read_write(server->tcpfd, &c1, 1, 1) || !read_write(server->tcpfd, &c1, 1, 1) ||
!read_write(server->tcpfd, &c2, 1, 1) || !read_write(server->tcpfd, &c2, 1, 1) ||
@@ -1464,7 +1572,7 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
m = (c1 << 8) | c2; m = (c1 << 8) | c2;
new_status = tcp_key_recurse(now, new_status, new_header, m, class, name, keyname, server, keycount); new_status = tcp_key_recurse(now, new_status, new_header, m, class, name, keyname, server, have_mark, mark, keycount);
if (new_status != STAT_OK) if (new_status != STAT_OK)
break; break;
@@ -1508,10 +1616,31 @@ unsigned char *tcp_request(int confd, time_t now,
socklen_t peer_len = sizeof(union mysockaddr); socklen_t peer_len = sizeof(union mysockaddr);
int query_count = 0; int query_count = 0;
unsigned char *pheader; unsigned char *pheader;
unsigned int mark = 0;
int have_mark = 0;
(void)mark;
(void)have_mark;
if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) == -1) if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) == -1)
return packet; return packet;
#ifdef HAVE_CONNTRACK
/* Get connection mark of incoming query to set on outgoing connections. */
if (option_bool(OPT_CONNTRACK))
{
struct all_addr local;
#ifdef HAVE_IPV6
if (local_addr->sa.sa_family == AF_INET6)
local.addr.addr6 = local_addr->in6.sin6_addr;
else
#endif
local.addr.addr4 = local_addr->in.sin_addr;
have_mark = get_incoming_mark(&peer_addr, &local, 1, &mark);
}
#endif
/* We can be configured to only accept queries from at-most-one-hop-away addresses. */ /* We can be configured to only accept queries from at-most-one-hop-away addresses. */
if (option_bool(OPT_LOCAL_SERVICE)) if (option_bool(OPT_LOCAL_SERVICE))
{ {
@@ -1637,7 +1766,7 @@ unsigned char *tcp_request(int confd, time_t now,
{ {
unsigned int flags = 0; unsigned int flags = 0;
struct all_addr *addrp = NULL; struct all_addr *addrp = NULL;
int type = 0; int type = SERV_DO_DNSSEC;
char *domain = NULL; char *domain = NULL;
size_t new_size = add_edns0_config(header, size, ((unsigned char *) header) + 65536, &peer_addr, now, &check_subnet); size_t new_size = add_edns0_config(header, size, ((unsigned char *) header) + 65536, &peer_addr, now, &check_subnet);
@@ -1650,9 +1779,7 @@ unsigned char *tcp_request(int confd, time_t now,
if (gotname) if (gotname)
flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind); flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);
#ifdef HAVE_DNSSEC
type &= ~SERV_DO_DNSSEC; type &= ~SERV_DO_DNSSEC;
#endif
if (type != 0 || option_bool(OPT_ORDER) || !daemon->last_server) if (type != 0 || option_bool(OPT_ORDER) || !daemon->last_server)
last_server = daemon->servers; last_server = daemon->servers;
@@ -1700,20 +1827,8 @@ unsigned char *tcp_request(int confd, time_t now,
#ifdef HAVE_CONNTRACK #ifdef HAVE_CONNTRACK
/* Copy connection mark of incoming query to outgoing connection. */ /* Copy connection mark of incoming query to outgoing connection. */
if (option_bool(OPT_CONNTRACK)) if (have_mark)
{
unsigned int mark;
struct all_addr local;
#ifdef HAVE_IPV6
if (local_addr->sa.sa_family == AF_INET6)
local.addr.addr6 = local_addr->in6.sin6_addr;
else
#endif
local.addr.addr4 = local_addr->in.sin_addr;
if (get_incoming_mark(&peer_addr, &local, 1, &mark))
setsockopt(last_server->tcpfd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int)); setsockopt(last_server->tcpfd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
}
#endif #endif
if ((!local_bind(last_server->tcpfd, &last_server->source_addr, last_server->interface, 1) || if ((!local_bind(last_server->tcpfd, &last_server->source_addr, last_server->interface, 1) ||
@@ -1774,7 +1889,8 @@ unsigned char *tcp_request(int confd, time_t now,
if (option_bool(OPT_DNSSEC_VALID) && !checking_disabled && (last_server->flags & SERV_DO_DNSSEC)) if (option_bool(OPT_DNSSEC_VALID) && !checking_disabled && (last_server->flags & SERV_DO_DNSSEC))
{ {
int keycount = DNSSEC_WORK; /* Limit to number of DNSSEC questions, to catch loops and avoid filling cache. */ int keycount = DNSSEC_WORK; /* Limit to number of DNSSEC questions, to catch loops and avoid filling cache. */
int status = tcp_key_recurse(now, STAT_OK, header, m, 0, daemon->namebuff, daemon->keyname, last_server, &keycount); int status = tcp_key_recurse(now, STAT_OK, header, m, 0, daemon->namebuff, daemon->keyname,
last_server, have_mark, mark, &keycount);
char *result, *domain = "result"; char *result, *domain = "result";
if (status == STAT_ABANDONED) if (status == STAT_ABANDONED)
+6 -1
View File
@@ -1442,7 +1442,9 @@ void check_servers(void)
if (!(serv->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND))) if (!(serv->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND)))
{ {
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && (serv->flags & SERV_HAS_DOMAIN)) if (option_bool(OPT_DNSSEC_VALID))
{
if (serv->flags & SERV_HAS_DOMAIN)
{ {
struct ds_config *ds; struct ds_config *ds;
char *domain = serv->domain; char *domain = serv->domain;
@@ -1458,6 +1460,9 @@ void check_servers(void)
if (!ds) if (!ds)
serv->flags &= ~SERV_DO_DNSSEC; serv->flags &= ~SERV_DO_DNSSEC;
} }
else if (serv->flags & SERV_FOR_NODOTS)
serv->flags &= ~SERV_DO_DNSSEC;
}
#endif #endif
port = prettyprint_addr(&serv->addr, daemon->namebuff); port = prettyprint_addr(&serv->addr, daemon->namebuff);
+1 -3
View File
@@ -896,9 +896,7 @@ size_t setup_reply(struct dns_header *header, size_t qlen,
header->nscount = htons(0); header->nscount = htons(0);
header->arcount = htons(0); header->arcount = htons(0);
header->ancount = htons(0); /* no answers unless changed below */ header->ancount = htons(0); /* no answers unless changed below */
if (flags == F_NEG) if (flags == F_NOERR)
SET_RCODE(header, SERVFAIL); /* couldn't get memory */
else if (flags == F_NOERR)
SET_RCODE(header, NOERROR); /* empty domain */ SET_RCODE(header, NOERROR); /* empty domain */
else if (flags == F_NXDOMAIN) else if (flags == F_NXDOMAIN)
SET_RCODE(header, NXDOMAIN); SET_RCODE(header, NXDOMAIN);