This used to have a global limit, but that has a problem when using
different servers for different upstream domains. Queries which are
routed by domain to an upstream server which is not responding will
build up and trigger the limit, which breaks DNS service for all other
domains which could be handled by other servers. The change is to make
the limit per server-group, where a server group is the set of servers
configured for a particular domain. In the common case, where only
default servers are declared, there is no effective change.
This should be largely transparent, but it drastically
improves performance and reduces memory foot-print when
configuring large numbers domains of the form
local=/adserver.com/
or
local=/adserver.com/#
Lookup times now grow as log-to-base-2 of the number of domains,
rather than greater than linearly, as before.
The change makes multiple addresses associated with a domain work
address=/example.com/1.2.3.4
address=/example.com/5.6.7.8
It also handles multiple upstream servers for a domain better; using
the same try/retry alogrithms as non domain-specific servers. This
also applies to DNSSEC-generated queries.
Finally, some of the oldest and gnarliest code in dnsmasq has had
a significant clean-up. It's far from perfect, but it _is_ better.
If two queries arrive a second or so apart, they cannot be a try and
a retry from the same client (retries are at least three seconds apart.)
It's therefore safe not to forward the second query, but answer them
both when the reply arrives for the first.
This changes the behaviour introduced in
141a26f979
We re-introduce the distinction between a query
which is retried from the same source, and one which is
repeated from different sources.
In the later case, we still forward the query, to avoid
problems when the reply to the first query is lost
(see f8cf456920) but we suppress the behaviour
that's used on a retry, when the query is sent to
all available servers in parallel.
Retry -> all servers.
Repeat -> next server.
This avoids a significant increase in upstream traffic on
busy instances which see lots of queries for common names.
It does mean the clients which repeat queries from new source ports,
rather than retrying them from the same source port, will see
different behaviour, but it in fact restores the pre-2.83 behaviour,
so it's not expected to be a practical problem.
One change to server_test_type forgot to set SERV_DO_DNSSEC. One new
place still can be reused.
Fixes commit e10a9239e1, thanks to
Xingcong Li for spotting it.
One part in dnssec retry path did not dump sent retry into dump file.
Make sure it is dumped all times it is sent by common function shared on
multiple places. Reduce a bit also server sending.
CVE-2021-3448 applies.
It's possible to specify the source address or interface to be
used when contacting upstream nameservers: server=8.8.8.8@1.2.3.4
or server=8.8.8.8@1.2.3.4#66 or server=8.8.8.8@eth0, and all of
these have, until now, used a single socket, bound to a fixed
port. This was originally done to allow an error (non-existent
interface, or non-local address) to be detected at start-up. This
means that any upstream servers specified in such a way don't use
random source ports, and are more susceptible to cache-poisoning
attacks.
We now use random ports where possible, even when the
source is specified, so server=8.8.8.8@1.2.3.4 or
server=8.8.8.8@eth0 will use random source
ports. server=8.8.8.8@1.2.3.4#66 or any use of --query-port will
use the explicitly configured port, and should only be done with
understanding of the security implications.
Note that this change changes non-existing interface, or non-local
source address errors from fatal to run-time. The error will be
logged and communiction with the server not possible.
Remove distinction between retry with same QID/SP and
retry for same query with different QID/SP. If the
QID/SP are the same as an existing one, simply retry,
if a new QID/SP is seen, add to the list to be replied to.
The new logic in 2.83/2.84 which merges distinct requests for the
same domain causes problems with clients which do retries as distinct
requests (differing IDs and/or source ports.) The retries just get
piggy-backed on the first, failed, request.
The logic is now changed so that distinct requests for repeated
queries still get merged into a single ID/source port, but they now
always trigger a re-try upstream.
Thanks to Nicholas Mu for his analysis.
If identical queries from IPv4 and IPv6 sources are combined by the
new code added in 15b60ddf93 then replies
can end up being sent via the wrong family of socket. The ->fd
should be per query, not per-question.
In bind-interfaces mode, this could also result in replies being sent
via the wrong socket even when IPv4/IPV6 issues are not in play.
If we add the EDNS client subnet option, or the client's
MAC address, then the reply we get back may very depending on
that. Since the cache is ignorant of such things, it's not safe to
cache such replies. This patch determines when a dangerous EDNS
option is being added and disables caching.
Note that for much the same reason, we can't combine multiple
queries for the same question when dangerous EDNS options are
being added, and the code now handles that in the same way. This
query combining is required for security against cache poisoning,
so disabling the cache has a security function as well as a
correctness one.
Previously, such queries would all be forwarded
independently. This is, in theory, inefficent but in practise
not a problem, _except_ that is means that an answer for any
of the forwarded queries will be accepted and cached.
An attacker can send a query multiple times, and for each repeat,
another {port, ID} becomes capable of accepting the answer he is
sending in the blind, to random IDs and ports. The chance of a
succesful attack is therefore multiplied by the number of repeats
of the query. The new behaviour detects repeated queries and
merely stores the clients sending repeats so that when the
first query completes, the answer can be sent to all the
clients who asked. Refer: CERT VU#434904.
Use the SHA-256 hash function to verify that DNS answers
received are for the questions originally asked. This replaces
the slightly insecure SHA-1 (when compiled with DNSSEC) or
the very insecure CRC32 (otherwise). Refer: CERT VU#434904.
At any time, dnsmasq will have a set of sockets open, bound to
random ports, on which it sends queries to upstream nameservers.
This patch fixes the existing problem that a reply for ANY in-flight
query would be accepted via ANY open port, which increases the
chances of an attacker flooding answers "in the blind" in an
attempt to poison the DNS cache. CERT VU#434904 refers.
A call to get_new_frec() for a DNSSEC query could manage to
free the original frec that we're doing the DNSSEC query to validate.
Bad things then happen.
This requires that the original frec is old, so it doesn't happen
in practice. I found it when running under gdb, and there have been
reports of SEGV associated with large system-clock warps which are
probably the same thing.
Hello,
My home network has a DNS search domain of home.arpa and my machine's dnsmasq
instance is configured with:
server=/home.arpa/192.168.0.1
server=//192.168.0.1
stop-dns-rebind
rebind-domain-ok=home.arpa
rebind-domain-ok=// # Match unqualified domains
Querying my router's FQDN works as expected:
dnsmasq: query[A] gateway.home.arpa from 127.0.0.1
dnsmasq: forwarded gateway.home.arpa to 192.168.0.1
dnsmasq: reply gateway.home.arpa is 192.168.0.1
But using an unqualified domain name does not:
dnsmasq: query[A] gateway from 127.0.0.1
dnsmasq: forwarded gateway to 192.168.0.1
dnsmasq: possible DNS-rebind attack detected: gateway
The attached patch addresses this issue by checking for SERV_NO_REBIND when
handling dotless domains.
>From 0460b07108b009cff06e29eac54910ec2e7fafce Mon Sep 17 00:00:00 2001
From: guns <self@sungpae.com>
Date: Mon, 30 Dec 2019 16:34:23 -0600
Subject: [PATCH] Check for SERV_NO_REBIND on unqualified domains
Some REFUSED answers to DNSSEC-originated queries would
bypass the DNSSEC code entirely, and be returned as answers
to the original query. In the process, they'd mess up datastructures
so that a retry of the original query would crash dnsmasq.
In a reply proving that a DS doesn't exist, it doesn't matter if RRs
in the auth section _other_ than NSEC/NSEC3 are not signed. We can't
set the AD flag when returning the query, but it still proves
that the DS doesn't exist for internal use.
As one of the RRs which may not be signed is the SOA record, use the
TTL of the NSEC record to cache the negative result, not one
derived from the SOA.
Thanks to Tore Anderson for spotting and diagnosing the bug.
msg_controllen should be set using CMSG_SPACE() to account for padding.
RFC3542 provides more details:
While sending an application may or may not include padding at the end
of last ancillary data in msg_controllen and implementations must
accept both as valid.
At least OpenBSD rejects control messages if msg_controllen doesn't
account for padding, so use CMSG_SPACE() for maximal portability. This
is consistent with the example provided in the Linux cmsg(3) manpage.
The above is intended to increase robustness, but actually does the
opposite. The problem is that by ignoring SERVFAIL messages and hoping
for a better answer from another of the servers we've forwarded to,
we become vulnerable in the case that one or more of the configured
servers is down or not responding.
Consider the case that a domain is indeed BOGUS, and we've send the
query to n servers. With 68f6312d4b
we ignore the first n-1 SERVFAIL replies, and only return the
final n'th answer to the client. Now, if one of the servers we are
forwarding to is down, then we won't get all n replies, and the
client will never get an answer! This is a far more likely scenario
than a temporary SERVFAIL from only one of a set of notionally identical
servers, so, on the ground of robustness, we have to believe
any SERVFAIL answers we get, and return them to the client.
The client could be using the same recursive servers we are,
so it should, in theory, retry on SERVFAIL anyway.
This was the source of a large number of #ifdefs, originally
included for use with old embedded libc versions. I'm
sure no-one wants or needs IPv6-free code these days, so this
is a move towards more maintainable code.