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.
When DNSEC validation is enabled, but a query is not validated
because it gets forwarded to a non-DNSEC-capable upstream
server, the rr_status array is not correctly cleared, with
the effect that the answer may be maked as DNSSEC validated
if the immediately preceding query was DNS signed and validated.
When using PXE proxy-DHCP, dnsmasq supplies PXE information to
the client, which also talks to another "normal" DHCP server
for address allocation and similar. The normal DHCP server may
be on the local network, but it may also be remote, and accessed via
a DHCP relay. This change allows dnsmasq to act as both a
PXE proxy-DHCP server AND a DHCP relay for the same network.
This acts almost exactly like --dhcp-option except that the defined option
is only sent when replying to PXE clients. More importantly, these
options are sent in reply PXE clients when dnsmasq in acting in PXE
proxy mode. In PXE proxy mode, the set of options sent is defined by
the PXE standard and the normal set of options is not sent. This config
allows arbitrary options in PXE-proxy replies. A typical use-case is
to send option 175 to iPXE. Thanks to Jason Berry for finding the
requirement for this.
A bug in gentoo linux https://bugs.gentoo.org/945183 reported that dnsmasq 2.90 fails to compile with GCC 15.
The issue is that while previous versions of GCC defaulted to the C17 standard and C23 could be selected with
"-std=c23" or "-std=gnu23", GCC 15 defaults to C23. In C23 incompatible pointer types are an error instead of
a warning, so the "int (*callback)()" incomplete prototypes cause errors.
For example, compiling dnsmasq 2.90 with gcc 14.2.1 and "-std=gnu23" fails with errors such as:
lease.c: In function `lease_find_interfaces':
lease.c:467:34: warning: passing argument 3 of `iface_enumerate' from incompatible pointer type [-Wincompatible-pointer-types[https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html#index-Wincompatible-pointer-types]]
467 | iface_enumerate(AF_INET, &now, find_interface_v4);
| ^~~~~~~~~~~~~~~~~
| |
| int (*)(struct in_addr, int, char *, struct in_addr, struct in_addr, void *)
In file included from lease.c:17:
dnsmasq.h:1662:50: note: expected `int (*)(void)' but argument is of type `int (*)(struct in_addr, int, char *, struct in_addr, struct in_addr, void *)'
1662 | int iface_enumerate(int family, void *parm, int (callback)());
| ~~~~~^~~~~~~~~~~
This patch uses a typedef'ed union of pointer types to get type checking of the pointers. If that's too complicated,
another way might be to use (void *) casts to disable type checking.
Also, some of the IPv6 callbacks had "int preferred, int valid" and some had
"unsigned int preferred, unsigned int valid". This patch changes them all to "unsigned int"
so they're the same and to avoid casting "u32" to "int", eg:
u32 preferred = 0xffffffff;
callback(..., (int)preferred, ...)
Even if those cast values aren't used in the callback, casting u32 to "int" feels bad, especially if "int" is 32 bits.
When deriving a domain name from an IPv6 address, an address
such as 1234:: would become 1234--.example.com, which is
not legal in IDNA2008. Stop using the :: compression method,
so 1234:: becomes
1234-0000-0000-0000-0000-0000-0000-0000.example.com
Timeouts for TCP connections to non-responive servers are very long.
This in not appropriate for DNS connections.
Set timeouts for connection setup, sending data and recieving data.
The timeouts for connection setup and sending data are set at 5 seconds.
For recieving the reply this is doubled, to take into account the
time for usptream to actually get the answer.
Thanks to Petr Menšík for pointing out this problem, and finding a better
and more portable solution than the one in place heretofore.
In the post 2020 flag-day world, we limit UDP packets to 1232 bytes
which can go anywhere, so the dodgy code to try and determine the
functional maxmimum packet size on the path from upstream servers
is obsolete.
When doing DNSSEC validation, a single downstream query may
trigger many upstream queries. On an unreliable network, there
may not be enough downstream retries to ensure that all these
queries complete.
This was kinda strange before, with a lot of cargo-cult copied code,
and no clear strategy.
Now it works like this:
When talking upstream we always add a pseudoheader, and set the
UDP packet size to --edns-packet-max unless we've had problems
talking to a server, when it's reduced to 1280 if that fixes things.
Answering queries from downstream, we get the answer (either from
upstream or local data) If local data won't fit the advertised size
(or 512 if there's not pseudoheader) return truncated. If upstream
returns truncated, do likewise. If upstream is OK, but the answer is
too big for downstream, truncate the answer.
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.
Now we're always saving the query, this is no longer necessary,
and allows the removal of a lot of quite hairy code.
Much more code removal to come, once the TCP code path is also purged.
A relatively common situation is that the reply to a downstream query
will fit in a UDP packet when no DNSSEC RRs are present, but overflows
when the RRSIGS, NSEC ect are added. This extends the automatic
move from UDP to TCP to downstream queries which get truncated replies,
in the hope that once stripped of the DNSSEC RRs, the reply can be returned
via UDP, nwithout making the downstream retry with TCP.
If the downstream sets the DO bit, (ie it wants the DNSSEC RRs, then
this path is not taken, since the downstream will have to get a truncated
repsonse and retry to get a correct answer.
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.
The msg_controllen field passed to sendmsg is computed using the
CMSG_SPACE(), which is correct, but CMSG_SPACE() can include
padding bytes at the end of the passed structure if they are required
to align subsequent structures in the buffer. Make sure these
bytes are zeroed to avoid passing uninitiased memory to the kernel,
even though it will never touch these bytes.
Also tidy up the mashalling code in send_from to use pointers to
the structure being filled out, rather than a temporary copy which
then gets memcpy()'d into place. The DHCP sendmsg code has always
worked like this.
Thanks to Dominik Derigs for running Memcheck as submitting the
initial patch.
CAP_NET_ADMIN is needed in the DHCPv4 code to place entries into
the ARP cache. If it's configured to unconditionally broadcast
to unconfigured clients, it never touches the ARP cache and
doesn't need CAP_NET_ADMIN.
Thanks to Martin Ivičič <max.enhanced@gmail.com> for prompting this.
In generalising the RR filter code, the Dbus methods
controlling filtering A and AAAA records
got severely broken. This, and the previous commit,
fixes things.