Compare commits

...
11 Commits
Author SHA1 Message Date
Simon Kelley da58455508 Tweak 7d915a0bb9
A downstream query may have gone to TCP, not just DNSSEC queries.
2025-01-13 11:03:30 +00:00
Simon Kelley b915c9a661 Attempt to keep running if a child process dies.
If a child process dies unexpectedly, log the error and
try and tidy up so the main process continues to run and
doesn't block awaiting the dead child.
2025-01-13 10:56:19 +00:00
Simon Kelley 424aaa0f9d Fix another 509afcd1d2 SNAFU 2025-01-13 10:32:55 +00:00
Andrew Sayers c72c895869 Improve "no upstream servers configured" when D-Bus is enabled
Print a specific INFO message instead of a generic WARNING message,
so users know what to do.

Starting dnsmasq without upstream servers indicates a problem by default,
but is perfectly normal with D-Bus enabled.  For example, NetworkManager
starts dnsmasq with no upstream servers, then immediately populates it
over D-Bus.
2025-01-12 22:32:32 +00:00
Simon Kelley b7156116c2 Fix SNAFU in 509afcd1d2 2025-01-12 22:28:12 +00:00
Simon Kelley 7d915a0bb9 Don't do retries over UDP when we've sent the query by TCP. 2025-01-12 22:02:05 +00:00
Simon Kelley 509afcd1d2 Refactor poll() loop.
Handling events on file descriptors can result in new file
descriptors being created or old ones being deleted. As such
the results of the last call to poll() become invalid in subtle
ways.

After handling each file descriptor in  check_dns_listeners()
return, to go around the poll() loop again and get valid data
for the new situation.

Thanks to Dominik Derigs for his indefatigable sleuthing of this one.
2025-01-12 21:36:09 +00:00
Simon Kelley 51343bd9a2 Treat replies with CD flag set the same for UDP and TCP code paths. 2025-01-12 16:25:07 +00:00
Simon Kelley b58276a73c Return EDE OTHER error when DNSSEC validation abandoned.
This distinguishes the case where we found a message was bogus
from cases where the process failed.
2025-01-12 16:00:09 +00:00
Matthias Andree f162d344c0 cache: Fix potential NULL deref in arcane situations. 2025-01-08 23:34:12 +00:00
Simon Kelley 0003db15cb Fix crash introduced in 6656790f24 2025-01-07 23:08:35 +00:00
3 changed files with 318 additions and 247 deletions

No files matched your search

+1 -1
View File
@@ -479,7 +479,7 @@ static struct crec *cache_scan_free(char *name, union all_addr *addr, unsigned s
if ((crecp->flags & F_FORWARD) && hostname_isequal(cache_get_name(crecp), name))
{
int rrmatch = 0;
if (crecp->flags & flags & F_RR)
if (addr && (crecp->flags & flags & F_RR))
{
unsigned short rrc = (crecp->flags & F_KEYTAG) ? crecp->addr.rrblock.rrtype : crecp->addr.rrdata.rrtype;
unsigned short rra = (flags & F_KEYTAG) ? addr->rrblock.rrtype : addr->rrdata.rrtype;
+278 -222
View File
@@ -32,6 +32,7 @@ static volatile int pipewrite;
static void set_dns_listeners(void);
static void set_tftp_listeners(void);
static void check_dns_listeners(time_t now);
static void do_tcp_connection(struct listener *listener, time_t now, int slot);
static void sig_handler(int sig);
static void async_event(int pipe, time_t now);
static void fatal_event(struct event_desc *ev, char *msg);
@@ -961,7 +962,14 @@ int main (int argc, char **argv)
my_syslog(LOG_WARNING, _("warning: ignoring resolv-file flag because no-resolv is set"));
daemon->resolv_files = NULL;
if (!daemon->servers)
my_syslog(LOG_WARNING, _("warning: no upstream servers configured"));
{
#ifdef HAVE_DBUS
if (option_bool(OPT_DBUS))
my_syslog(LOG_INFO, _("no upstream servers configured - please set them from DBus"));
else
#endif
my_syslog(LOG_WARNING, _("warning: no upstream servers configured"));
}
}
if (daemon->max_logs != 0)
@@ -1476,7 +1484,7 @@ static void async_event(int pipe, time_t now)
{
pid_t p;
struct event_desc ev;
int i, check = 0;
int wstatus, i, check = 0;
char *msg;
/* NOTE: the memory used to return msg is leaked: use msgs in events only
@@ -1539,7 +1547,7 @@ static void async_event(int pipe, time_t now)
case EVENT_CHILD:
/* See Stevens 5.10 */
while ((p = waitpid(-1, NULL, WNOHANG)) != 0)
while ((p = waitpid(-1, &wstatus, WNOHANG)) != 0)
if (p == -1)
{
if (errno != EINTR)
@@ -1550,6 +1558,20 @@ static void async_event(int pipe, time_t now)
if (daemon->tcp_pids[i] == p)
{
daemon->tcp_pids[i] = 0;
if (!WIFEXITED(wstatus))
{
/* If a helper process dies, (eg with SIGSEV)
log that and attempt to patch things up so that the
parent can continue to function. */
my_syslog(LOG_WARNING, _("TCP helper process %u died unexpectedly"), (unsigned int)p);
if (daemon->tcp_pipes[i] != -1)
{
close(daemon->tcp_pipes[i]);
daemon->tcp_pipes[i] = -1;
}
}
/* tcp_pipes == -1 && tcp_pids == 0 required to free slot */
if (daemon->tcp_pipes[i] == -1)
daemon->metrics[METRIC_TCP_CONNECTIONS]--;
@@ -1832,247 +1854,281 @@ static void check_dns_listeners(time_t now)
struct listener *listener;
struct randfd_list *rfl;
int i;
int pipefd[2];
/* Note that handling events here can create or destroy fds and
render the result of the last poll() call invalid. Once
we find an fd that needs service, do it, then return to go around the
poll() loop again. This avoid really, really, wierd bugs. */
if (!option_bool(OPT_DEBUG))
for (i = 0; i < daemon->max_procs; i++)
if (daemon->tcp_pipes[i] != -1 &&
poll_check(daemon->tcp_pipes[i], POLLIN | POLLHUP))
{
/* Races. The child process can die before we read all of the data from the
pipe, or vice versa. Therefore send tcp_pids to zero when we wait() the
process, and tcp_pipes to -1 and close the FD when we read the last
of the data - indicated by cache_recv_insert returning zero.
The order of these events is indeterminate, and both are needed
to free the process slot. Once the child process has gone, poll()
returns POLLHUP, not POLLIN, so have to check for both here. */
if (!cache_recv_insert(now, daemon->tcp_pipes[i]))
{
close(daemon->tcp_pipes[i]);
daemon->tcp_pipes[i] = -1;
/* tcp_pipes == -1 && tcp_pids == 0 required to free slot */
if (daemon->tcp_pids[i] == 0)
daemon->metrics[METRIC_TCP_CONNECTIONS]--;
}
return;
}
for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
if (poll_check(serverfdp->fd, POLLIN))
reply_query(serverfdp->fd, now);
{
reply_query(serverfdp->fd, now);
return;
}
for (i = 0; i < daemon->numrrand; i++)
if (daemon->randomsocks[i].refcount != 0 &&
poll_check(daemon->randomsocks[i].fd, POLLIN))
reply_query(daemon->randomsocks[i].fd, now);
{
reply_query(daemon->randomsocks[i].fd, now);
return;
}
/* Check overflow random sockets too. */
for (rfl = daemon->rfl_poll; rfl; rfl = rfl->next)
if (poll_check(rfl->rfd->fd, POLLIN))
reply_query(rfl->rfd->fd, now);
/* Races. The child process can die before we read all of the data from the
pipe, or vice versa. Therefore send tcp_pids to zero when we wait() the
process, and tcp_pipes to -1 and close the FD when we read the last
of the data - indicated by cache_recv_insert returning zero.
The order of these events is indeterminate, and both are needed
to free the process slot. Once the child process has gone, poll()
returns POLLHUP, not POLLIN, so have to check for both here. */
if (!option_bool(OPT_DEBUG))
for (i = 0; i < daemon->max_procs; i++)
if (daemon->tcp_pipes[i] != -1 &&
poll_check(daemon->tcp_pipes[i], POLLIN | POLLHUP) &&
!cache_recv_insert(now, daemon->tcp_pipes[i]))
{
close(daemon->tcp_pipes[i]);
daemon->tcp_pipes[i] = -1;
/* tcp_pipes == -1 && tcp_pids == 0 required to free slot */
if (daemon->tcp_pids[i] == 0)
daemon->metrics[METRIC_TCP_CONNECTIONS]--;
}
{
reply_query(rfl->rfd->fd, now);
return;
}
for (listener = daemon->listeners; listener; listener = listener->next)
{
if (listener->fd != -1 && poll_check(listener->fd, POLLIN))
if (listener->fd != -1 && poll_check(listener->fd, POLLIN))
{
receive_query(listener, now);
/* check to see if we have a free tcp process slot.
Note that we can't assume that because we had
at least one a poll() time, that we still do.
There may be more waiting connections after
poll() returns then free process slots. */
for (i = daemon->max_procs - 1; i >= 0; i--)
if (daemon->tcp_pids[i] == 0 && daemon->tcp_pipes[i] == -1)
break;
return;
}
/* check to see if we have a free tcp process slot.
Note that we can't assume that because we had
at least one a poll() time, that we still do.
There may be more waiting connections after
poll() returns then free process slots. */
for (i = daemon->max_procs - 1; i >= 0; i--)
if (daemon->tcp_pids[i] == 0 && daemon->tcp_pipes[i] == -1)
break;
if (listener->tcpfd != -1 && i >= 0 && poll_check(listener->tcpfd, POLLIN))
if (i >= 0)
for (listener = daemon->listeners; listener; listener = listener->next)
if (listener->tcpfd != -1 && poll_check(listener->tcpfd, POLLIN))
{
int confd, client_ok = 1;
struct irec *iface = NULL;
pid_t p;
union mysockaddr tcp_addr;
socklen_t tcp_len = sizeof(union mysockaddr);
do_tcp_connection(listener, now, i);
return;
}
}
while ((confd = accept(listener->tcpfd, NULL, NULL)) == -1 && errno == EINTR);
if (confd == -1)
continue;
if (getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) == -1)
{
close(confd);
continue;
}
/* Make sure that the interface list is up-to-date.
We do this here as we may need the results below, and
the DNS code needs them for --interface-name stuff.
static void do_tcp_connection(struct listener *listener, time_t now, int slot)
{
int confd, client_ok = 1;
struct irec *iface = NULL;
pid_t p;
union mysockaddr tcp_addr;
socklen_t tcp_len = sizeof(union mysockaddr);
unsigned char a = 0, *buff;
struct server *s;
int flags, auth_dns;
struct in_addr netmask;
int pipefd[2];
Multiple calls to enumerate_interfaces() per select loop are
inhibited, so calls to it in the child process (which doesn't select())
have no effect. This avoids two processes reading from the same
netlink fd and screwing the pooch entirely.
*/
enumerate_interfaces(0);
while ((confd = accept(listener->tcpfd, NULL, NULL)) == -1 && errno == EINTR);
if (confd == -1)
return;
if (getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) == -1)
{
closeconandreturn:
shutdown(confd, SHUT_RDWR);
close(confd);
return;
}
/* Make sure that the interface list is up-to-date.
We do this here as we may need the results below, and
the DNS code needs them for --interface-name stuff.
Multiple calls to enumerate_interfaces() per select loop are
inhibited, so calls to it in the child process (which doesn't select())
have no effect. This avoids two processes reading from the same
netlink fd and screwing the pooch entirely.
*/
enumerate_interfaces(0);
if (option_bool(OPT_NOWILD))
iface = listener->iface; /* May be NULL */
else
{
int if_index;
char intr_name[IF_NAMESIZE];
/* if we can find the arrival interface, check it's one that's allowed */
if ((if_index = tcp_interface(confd, tcp_addr.sa.sa_family)) != 0 &&
indextoname(listener->tcpfd, if_index, intr_name))
{
union all_addr addr;
if (option_bool(OPT_NOWILD))
iface = listener->iface; /* May be NULL */
else
{
int if_index;
char intr_name[IF_NAMESIZE];
/* if we can find the arrival interface, check it's one that's allowed */
if ((if_index = tcp_interface(confd, tcp_addr.sa.sa_family)) != 0 &&
indextoname(listener->tcpfd, if_index, intr_name))
{
union all_addr addr;
if (tcp_addr.sa.sa_family == AF_INET6)
addr.addr6 = tcp_addr.in6.sin6_addr;
else
addr.addr4 = tcp_addr.in.sin_addr;
for (iface = daemon->interfaces; iface; iface = iface->next)
if (iface->index == if_index &&
iface->addr.sa.sa_family == tcp_addr.sa.sa_family)
break;
if (!iface && !loopback_exception(listener->tcpfd, tcp_addr.sa.sa_family, &addr, intr_name))
client_ok = 0;
}
if (option_bool(OPT_CLEVERBIND))
iface = listener->iface; /* May be NULL */
else
{
/* Check for allowed interfaces when binding the wildcard address:
we do this by looking for an interface with the same address as
the local address of the TCP connection, then looking to see if that's
an allowed interface. As a side effect, we get the netmask of the
interface too, for localisation. */
for (iface = daemon->interfaces; iface; iface = iface->next)
if (sockaddr_isequal(&iface->addr, &tcp_addr))
break;
if (!iface)
client_ok = 0;
}
}
if (!client_ok)
{
shutdown(confd, SHUT_RDWR);
close(confd);
}
else if (!option_bool(OPT_DEBUG) && pipe(pipefd) == 0 && (p = fork()) != 0)
{
close(pipefd[1]); /* parent needs read pipe end. */
if (p == -1)
close(pipefd[0]);
else
{
#ifdef HAVE_LINUX_NETWORK
/* The child process inherits the netlink socket,
which it never uses, but when the parent (us)
uses it in the future, the answer may go to the
child, resulting in the parent blocking
forever awaiting the result. To avoid this
the child closes the netlink socket, but there's
a nasty race, since the parent may use netlink
before the child has done the close.
To avoid this, the parent blocks here until a
single byte comes back up the pipe, which
is sent by the child after it has closed the
netlink socket. */
unsigned char a;
read_write(pipefd[0], &a, 1, RW_READ);
#endif
/* i holds index of free slot */
daemon->tcp_pids[i] = p;
daemon->tcp_pipes[i] = pipefd[0];
daemon->metrics[METRIC_TCP_CONNECTIONS]++;
if (daemon->metrics[METRIC_TCP_CONNECTIONS] > daemon->max_procs_used)
daemon->max_procs_used = daemon->metrics[METRIC_TCP_CONNECTIONS];
}
close(confd);
/* The child can use up to TCP_MAX_QUERIES ids, so skip that many. */
daemon->log_id += TCP_MAX_QUERIES;
#ifdef HAVE_DNSSEC
/* It can do more if making DNSSEC queries too. */
if (option_bool(OPT_DNSSEC_VALID))
daemon->log_id += daemon->limit[LIMIT_WORK];
#endif
}
if (tcp_addr.sa.sa_family == AF_INET6)
addr.addr6 = tcp_addr.in6.sin6_addr;
else
{
unsigned char *buff;
struct server *s;
int flags;
struct in_addr netmask;
int auth_dns;
if (iface)
{
netmask = iface->netmask;
auth_dns = iface->dns_auth;
}
else
{
netmask.s_addr = 0;
auth_dns = 0;
}
/* Arrange for SIGALRM after CHILD_LIFETIME seconds to
terminate the process. */
if (!option_bool(OPT_DEBUG))
{
#ifdef HAVE_LINUX_NETWORK
/* See comment above re: netlink socket. */
unsigned char a = 0;
close(daemon->netlinkfd);
read_write(pipefd[1], &a, 1, RW_WRITE);
#endif
alarm(CHILD_LIFETIME);
close(pipefd[0]); /* close read end in child. */
daemon->pipe_to_parent = pipefd[1];
}
/* The connected socket inherits non-blocking
attribute from the listening socket.
Reset that here. */
if ((flags = fcntl(confd, F_GETFL, 0)) != -1)
while(retry_send(fcntl(confd, F_SETFL, flags & ~O_NONBLOCK)));
buff = tcp_request(confd, now, &tcp_addr, netmask, auth_dns);
if (buff)
free(buff);
for (s = daemon->servers; s; s = s->next)
if (s->tcpfd != -1)
{
shutdown(s->tcpfd, SHUT_RDWR);
close(s->tcpfd);
s->tcpfd = -1;
}
if (!option_bool(OPT_DEBUG))
{
close(daemon->pipe_to_parent);
flush_log();
_exit(0);
}
}
addr.addr4 = tcp_addr.in.sin_addr;
for (iface = daemon->interfaces; iface; iface = iface->next)
if (iface->index == if_index &&
iface->addr.sa.sa_family == tcp_addr.sa.sa_family)
break;
if (!iface && !loopback_exception(listener->tcpfd, tcp_addr.sa.sa_family, &addr, intr_name))
client_ok = 0;
}
if (option_bool(OPT_CLEVERBIND))
iface = listener->iface; /* May be NULL */
else
{
/* Check for allowed interfaces when binding the wildcard address:
we do this by looking for an interface with the same address as
the local address of the TCP connection, then looking to see if that's
an allowed interface. As a side effect, we get the netmask of the
interface too, for localisation. */
for (iface = daemon->interfaces; iface; iface = iface->next)
if (sockaddr_isequal(&iface->addr, &tcp_addr))
break;
if (!iface)
client_ok = 0;
}
}
if (!client_ok)
goto closeconandreturn;
if (!option_bool(OPT_DEBUG))
{
if (pipe(pipefd) == -1)
goto closeconandreturn; /* pipe failed */
if ((p = fork()) == -1)
{
/* fork failed */
close(pipefd[0]);
close(pipefd[1]);
goto closeconandreturn;
}
if (p != 0)
{
/* fork() done: parent side */
close(pipefd[1]); /* parent needs read pipe end. */
#ifdef HAVE_LINUX_NETWORK
/* The child process inherits the netlink socket,
which it never uses, but when the parent (us)
uses it in the future, the answer may go to the
child, resulting in the parent blocking
forever awaiting the result. To avoid this
the child closes the netlink socket, but there's
a nasty race, since the parent may use netlink
before the child has done the close.
To avoid this, the parent blocks here until a
single byte comes back up the pipe, which
is sent by the child after it has closed the
netlink socket. */
read_write(pipefd[0], &a, 1, RW_READ);
#endif
daemon->tcp_pids[slot] = p;
daemon->tcp_pipes[slot] = pipefd[0];
daemon->metrics[METRIC_TCP_CONNECTIONS]++;
if (daemon->metrics[METRIC_TCP_CONNECTIONS] > daemon->max_procs_used)
daemon->max_procs_used = daemon->metrics[METRIC_TCP_CONNECTIONS];
close(confd);
/* The child can use up to TCP_MAX_QUERIES ids, so skip that many. */
daemon->log_id += TCP_MAX_QUERIES;
#ifdef HAVE_DNSSEC
/* It can do more if making DNSSEC queries too. */
if (option_bool(OPT_DNSSEC_VALID))
daemon->log_id += daemon->limit[LIMIT_WORK];
#endif
return;
}
}
if (iface)
{
netmask = iface->netmask;
auth_dns = iface->dns_auth;
}
else
{
netmask.s_addr = 0;
auth_dns = 0;
}
/* Arrange for SIGALRM after CHILD_LIFETIME seconds to
terminate the process. */
if (!option_bool(OPT_DEBUG))
{
#ifdef HAVE_LINUX_NETWORK
/* See comment above re: netlink socket. */
close(daemon->netlinkfd);
read_write(pipefd[1], &a, 1, RW_WRITE);
#endif
alarm(CHILD_LIFETIME);
close(pipefd[0]); /* close read end in child. */
daemon->pipe_to_parent = pipefd[1];
}
/* The connected socket inherits non-blocking
attribute from the listening socket.
Reset that here. */
if ((flags = fcntl(confd, F_GETFL, 0)) != -1)
while(retry_send(fcntl(confd, F_SETFL, flags & ~O_NONBLOCK)));
buff = tcp_request(confd, now, &tcp_addr, netmask, auth_dns);
if (buff)
free(buff);
for (s = daemon->servers; s; s = s->next)
if (s->tcpfd != -1)
{
shutdown(s->tcpfd, SHUT_RDWR);
close(s->tcpfd);
s->tcpfd = -1;
}
if (!option_bool(OPT_DEBUG))
{
close(daemon->pipe_to_parent);
flush_log();
_exit(0);
}
}
#ifdef HAVE_DNSSEC
/* If a DNSSEC query over UDP returns a truncated answer,
we swap to the TCP path. This routine is responsible for forking
+39 -24
View File
@@ -163,10 +163,10 @@ static int domain_no_rebind(char *domain)
return 0;
}
static int forward_query(int udpfd, union mysockaddr *udpaddr,
union all_addr *dst_addr, unsigned int dst_iface,
struct dns_header *header, size_t plen, size_t replylimit, time_t now,
struct frec *forward, unsigned int fwd_flags, int fast_retry)
static void forward_query(int udpfd, union mysockaddr *udpaddr,
union all_addr *dst_addr, unsigned int dst_iface,
struct dns_header *header, size_t plen, size_t replylimit, time_t now,
struct frec *forward, unsigned int fwd_flags, int fast_retry)
{
unsigned int flags = 0;
int is_dnssec = forward && (forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY));
@@ -190,6 +190,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
{
old_src = 1;
old_reply = 1;
fwd_flags = forward->flags;
}
else if (gotname && (forward = lookup_frec(daemon->namebuff, C_IN, (int)rrtype, -1, fwd_flags,
FREC_CHECKING_DISABLED | FREC_AD_QUESTION | FREC_DO_QUESTION |
@@ -253,7 +254,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
it's safe to wait for the reply from the first without
forwarding the second. */
if (difftime(now, forward->time) < 2)
return 0;
return;
}
/* use our id when resending */
@@ -373,6 +374,10 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
while (forward->blocking_query)
forward = forward->blocking_query;
/* Don't retry if we've already sent it via TCP. */
if (forward->flags & FREC_GONE_TO_TCP)
return;
if (forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY))
{
/* log_id should match previous DNSSEC query. */
@@ -385,8 +390,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
/* Find suitable servers: should never fail. */
if (!filter_servers(forward->sentto->arrayposn, F_DNSSECOK, &first, &last))
return 0;
return;
is_dnssec = 1;
forward->forwardall = 1;
}
@@ -501,8 +506,9 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
if (forwarded || is_dnssec)
{
daemon->metrics[METRIC_DNS_QUERIES_FORWARDED]++;
forward->forward_timestamp = dnsmasq_milliseconds();
return 1;
return;
}
/* could not send on, prepare to return */
@@ -514,9 +520,9 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
if (udpfd != -1)
{
if (!(plen = make_local_answer(flags, gotname, plen, header, daemon->namebuff, (char *)(header + replylimit), first, last, ede)))
return 0;
return;
if (forward->flags & FREC_HAS_PHEADER)
if (fwd_flags & FREC_HAS_PHEADER)
{
u16 swap = htons((u16)ede);
@@ -542,7 +548,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
send_from(udpfd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND), (char *)header, plen, udpaddr, dst_addr, dst_iface);
}
return 0;
daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]++;
return;
}
/* Check if any frecs need to do a retry, and action that if so.
@@ -779,10 +786,9 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
header->ancount = htons(0);
header->nscount = htons(0);
header->arcount = htons(0);
ede = EDE_DNSSEC_BOGUS;
}
}
else if (!(header->hb4 & HB4_CD) && ad_reqd && cache_secure)
else if (ad_reqd && cache_secure)
header->hb4 |= HB4_AD;
/* If the requestor didn't set the DO bit, don't return DNSSEC info. */
@@ -1243,20 +1249,25 @@ void return_reply(time_t now, struct frec *forward, struct dns_header *header, s
char *result, *domain = "result";
union all_addr a;
a.log.ede = ede = errflags_to_ede(status);
ede = errflags_to_ede(status);
if (STAT_ISEQUAL(status, STAT_ABANDONED))
{
result = "ABANDONED";
status = STAT_BOGUS;
if (ede == EDE_UNSET)
ede = EDE_OTHER;
}
else
result = (STAT_ISEQUAL(status, STAT_SECURE) ? "SECURE" : (STAT_ISEQUAL(status, STAT_INSECURE) ? "INSECURE" : "BOGUS"));
if (STAT_ISEQUAL(status, STAT_SECURE))
cache_secure = 1;
else if (STAT_ISEQUAL(status, STAT_BOGUS))
{
if (ede == EDE_UNSET)
ede = EDE_DNSSEC_BOGUS;
no_cache_dnssec = 1;
bogusanswer = 1;
@@ -1264,6 +1275,7 @@ void return_reply(time_t now, struct frec *forward, struct dns_header *header, s
domain = daemon->namebuff;
}
a.log.ede = ede;
log_query(F_SECSTAT, domain, &a, result, 0);
}
}
@@ -1845,11 +1857,8 @@ void receive_query(struct listener *listen, time_t now)
blockdata_free(saved_question);
saved_question = NULL;
if (forward_query(fd, &source_addr, &dst_addr, if_index, header, (size_t)n,
udp_size, now, NULL, fwd_flags, 0))
daemon->metrics[METRIC_DNS_QUERIES_FORWARDED]++;
else
daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]++;
forward_query(fd, &source_addr, &dst_addr, if_index, header, (size_t)n,
udp_size, now, NULL, fwd_flags, 0);
}
blockdata_free(saved_question);
@@ -2394,9 +2403,8 @@ unsigned char *tcp_request(int confd, time_t now,
size = saved_size;
/* save state of "cd" flag in query */
if ((checking_disabled = header->hb4 & HB4_CD))
no_cache_dnssec = 1;
checking_disabled = header->hb4 & HB4_CD;
if (lookup_domain(daemon->namebuff, gotname, &first, &last))
flags = is_local_answer(now, first, daemon->namebuff);
else
@@ -2447,7 +2455,9 @@ unsigned char *tcp_request(int confd, time_t now,
/* Clear this in case we don't call tcp_key_recurse() below */
memset(daemon->rr_status, 0, sizeof(*daemon->rr_status) * daemon->rr_status_sz);
if (!checking_disabled && (master->flags & SERV_DO_DNSSEC))
if (checking_disabled || (header->hb4 & HB4_CD))
no_cache_dnssec = 1;
else if (master->flags & SERV_DO_DNSSEC)
{
int keycount = daemon->limit[LIMIT_WORK]; /* Limit to number of DNSSEC questions, to catch loops and avoid filling cache. */
int validatecount = daemon->limit[LIMIT_CRYPTO];
@@ -2456,12 +2466,14 @@ unsigned char *tcp_request(int confd, time_t now,
char *result, *domain = "result";
union all_addr a;
a.log.ede = ede = errflags_to_ede(status);
ede = errflags_to_ede(status);
if (STAT_ISEQUAL(status, STAT_ABANDONED))
{
result = "ABANDONED";
status = STAT_BOGUS;
if (ede == EDE_UNSET)
ede = EDE_OTHER;
}
else
result = (STAT_ISEQUAL(status, STAT_SECURE) ? "SECURE" : (STAT_ISEQUAL(status, STAT_INSECURE) ? "INSECURE" : "BOGUS"));
@@ -2470,6 +2482,8 @@ unsigned char *tcp_request(int confd, time_t now,
cache_secure = 1;
else if (STAT_ISEQUAL(status, STAT_BOGUS))
{
if (ede == EDE_UNSET)
ede = EDE_DNSSEC_BOGUS;
no_cache_dnssec = 1;
bogusanswer = 1;
@@ -2477,6 +2491,7 @@ unsigned char *tcp_request(int confd, time_t now,
domain = daemon->namebuff;
}
a.log.ede = ede;
log_query(F_SECSTAT, domain, &a, result, 0);
if ((daemon->limit[LIMIT_CRYPTO] - validatecount) > (int)daemon->metrics[METRIC_CRYPTO_HWM])