servers for plain names (server=//1.2.3.4) as domain-specific.
This means that if we fail to get a DS record for such a query,
it gets given the benefit of the doubt.
Updates 57f0489f38
The bug occurs when we ask lookup_domain() for a server for a domain
which is not a general upstream server, by setting F_DOMAINSRV in the flags.
If there are no possible servers, because there are no upstream servers
defined (for instance, at startup) then the code steps off the end of an
array and SEGVs.
The bug has been latent for some time, but
3e659bd4ec added a new call to lookup_domain()
which can actually trigger the bug if DNSSEC is enabled and a certain
amount of bad luck ensues.
Thanks to the testers extraordinaire at PiHole for reporting this.
Consider what happens when the same domain appears in
--address and --server.
This commit fixes the order, I think correctly like this:
highest to lowest priority.
--address with a IPv4 or IPv6 address (as long as the query matches the type)
--address with # for all-zeros, as long as the query is A or AAAA)
--address with no address, which returns NXDOMAIN or NOERROR for all types.
--server with address set to # to use the unqualified servers.
--server with matching domain.
--server without domain or from /etc/resolv.conf.
Note that the above is only valid when same domain appears.
The domain being matched is determined first, and has a higher
priority, so you can send google.com to a server and force com
to return NXDOMAIN and for google.com the server config will
override the address config, because there's a longer match.
Commit 3e659bd4ec removed the concept of
an usptream DNS server which is capable of DNSSEC: they are all
(at least in theory) now usable. As a very unfortunate side-effect,
this removed the filter that ensured that dnssec_server() ONLY
returns servers, and not domains with literal addresses.
If we try and do DNSSEC queries for a domain, and there's
a --address line which matches the domain, then dnssec_server()
will return that. This would break DNSSEC validation, but that's
turns out not to matter, because under these circumstances
dnssec_server() will probably return an out-of-bounds index into
the servers[] array, and the process dies with SIGSEGV.
Many thanks to the hard workers at the Tomato project who
found this bug and provided enough information to diagnose it.
This should just work in all cases now. If the normal chain-of-trust exists into
the delegated domain then whether the domain is signed or not, DNSSEC
validation will function normally. In the case the delgated domain
is an "overlay" on top of the global DNS and no NS and/or DS records
exist connecting it to the global dns, then if the domain is
unsigned the situation will be handled by synthesising a
proof-of-non-existance-of-DS for the domain and queries will be
answered unvalidated; this action will be logged. A signed domain
without chain-of-trust can be validated if a suitable trust-anchor
is provided using --trust-anchor.
Thanks to Uwe Kleine-König for prompting this change, and contributing
valuable insights into what could be improved.
We can't answer and shouldn't forward non-QUERY DNS requests.
This patch fixes handling such requests from TCP connections; before
the connection would be closed without reply.
It also changes the RCODE in the answer from REFUSED to NOTIMP and
provides clearer logging.
We only need to order two server records on the ->serial field.
Literal address records are smaller and don't have
this field and don't need to be ordered on it.
To actually provoke this bug seems to need the same server-literal
to be repeated twice, eg --address=/a/1.1.1.1 --address-/a/1.1.1.1
which is clearly rare in the wild, but if it did exist it could
provoke a SIGSEV. Thanks to Daniel Rhea for fuzzing this one.
Heretofore, when a validating the result of an external query triggers
a DNSKEY or DS query and the result of that query is truncated, dnsmasq
has forced the whole validation process to move to TCP by returning a
truncated reply to the original requestor. This forces the original
requestor to retry the query in TCP mode, and the DNSSEC subqueries
also get made via TCP and everything works.
Note that in general the actual answer being validated is not large
enough to trigger truncation, and there's no reason not to return that
answer via UDP if we can validate it successfully. It follows that
a substandard client which can't do TCP queries will still work if the
answer could be returned via UDP, but fails if it gets an artifically
truncated answer and cannot move to TCP.
This patch teaches dnsmasq to move to TCP for DNSSEC queries when
validating UDP answers. That makes the substandard clients mentioned
above work, and saves a round trip even for clients that can do TCP.
If there exists a --address=/<domain>/ or --server=/<domain>/#
configuration but no upstream server config unqualified by
domain then when a query which doesnt match the domain is
recieved it will use the qualfied server config and in the process
possibly make an out-of-bounds memory access.
Thanks to Daniel Danzberger for spotting the bug.
When re-reading upstream servers from /etc/resolv.conf or other
sources that can change dnsmasq tries to avoid memory fragmentation by
re-using existing records that are being re-read unchanged. This
involves seaching all the server records for each new one installed.
During startup this search is pointless, and can cause long start
times with thousands of --server options because the work needed is
O(n^2). Handle this case more intelligently. Thanks to Ye Zhou for
spotting the problem and an initial patch.
A bug, introduced in 2.87, which could result in DNS
servers being removed from the configuration when reloading
server configuration from DBus, or re-reading /etc/resolv.conf
Only servers from the same source should be replaced, but some
servers from other sources (ie hard coded or another dynamic source)
could mysteriously disappear.
The 2.86 upstream server rewrite severely broke re-reading
of server configuration. It would get everyting right the first
time, but on re-reading /etc/resolv.conf or --servers-file
or setting things with DBUS, the results were just wrong.
This should put things right again.
The domain-match rewrite didn't take into account
that domain names are case-insensitive, so things like
--address=/Example.com/.....
didn't work correctly.
The 2.86 domain matching rewrite failed to take into account the possibilty that
server=/example.com/#
could be combined with, for example
address=/example.com/1.2.3.4
resulting in the struct server datastructure for the former getting passed
to forward_query(), rapidly followed by a SEGV.
This fix makes server=/example.com/# a fully fledged member of the
priority list, which is now IPv6 addr, IPv4 addr, all zero return,
resolvconf servers, upstream servers, no-data return
Thanks to dl6er@dl6er.de for finding and characterising the bug.
This patch also changes the method of calling querystr() such that
it is only called when logging is enabled, to eliminate any
possible performance problems from searching the larger table.
Make --server=/example.com/1.2.3.4 take priority over
--server=/example.com/ (AKA --address=/example.com/ or --local=/example.com/)
This corrects a regression in the domain-match rewrite, and appears
to be the more useful order. It got swapped because I didn't consider
that both could usefully co-exist.
The code which checked for a possible local answer to a domain,
like --address=/example.com/1.2.3.4 could return false positives,
causing upstream NXDOMAIN replies to be rewritten as NOERROR.
Thanks to Dominik DL6ER for the bug report and analysis.
Domain patterns in --address, --server and --local have, for many years,
matched complete labels only, so
--server=/google.com/1.2.3.4
will apply to google.com and www.google.com but NOT supergoogle.com
This commit introduces an optional '*' at the LHS of the domain string which
changes this behaviour so as to include substring matches _within_ labels. So,
--server=/*google.com/1.2.3.4
applies to google.com, www.google.com AND supergoogle.com.
For reasons unknown, I (srk) assumed that the orginal
substring domain matching algorithm was still in use,
where example.com would match eg. sexample.com
In fact the far more sensible label-based match, where
example.com (or .example.com) matches example.com and
www.example.com, but not sexample.com, has been in use
since release 2.22. This commit implements the 2.22 to 2.85
behaviour in the new domain-search code.
Thanks to Kevin Darbyshire-Bryant for spotting my mistake.
The optimisation based on the assumption that
"try now points to the last domain that sorts before the query"
fails in the specific edge case that the query sorts before
_any_ of the server domains. Handle this case.
Thanks to Xingcong Li for finding a test case for this bug.
In the specific case of configuring an A record for a domain
address=/example.com/1.2.3.4
queries for *example.com for any other type will now return
NOERR, and not the previous erroneous NXDOMAIN. The same thing
applies for
address=/example.com/::1:2:3:4
address=/example.com/#