Compare commits
129 Commits
v2.83
...
v2.86test6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8389b943d3 | ||
|
|
f2266d9678 | ||
|
|
56bd806978 | ||
|
|
ac7eeea44d | ||
|
|
b741059549 | ||
|
|
cbd984287f | ||
|
|
32e15c3f45 | ||
|
|
f0dc324e35 | ||
|
|
f83c6cf51a | ||
|
|
c068b3ae2f | ||
|
|
adf9dec1e6 | ||
|
|
767d9cbd96 | ||
|
|
e7ccd95c04 | ||
|
|
719f79a8fd | ||
|
|
96f6444958 | ||
|
|
df25f204ba | ||
|
|
8acdc3ede7 | ||
|
|
857b445522 | ||
|
|
5bcca1219a | ||
|
|
4558c26fcd | ||
|
|
a92c6d77dc | ||
|
|
0c95a5ff53 | ||
|
|
cb6d06bb54 | ||
|
|
3ef955c85a | ||
|
|
5e95c16c32 | ||
|
|
4205e2ebcf | ||
|
|
9d806c51c2 | ||
|
|
a38bb31727 | ||
|
|
8a1ef367e2 | ||
|
|
1291865c92 | ||
|
|
a9ebbee7b6 | ||
|
|
06df5ad7d0 | ||
|
|
66b863c989 | ||
|
|
c9efe8e5e1 | ||
|
|
d515223bb5 | ||
|
|
b908f4334b | ||
|
|
6261aba026 | ||
|
|
85bc7534da | ||
|
|
1b30fd1732 | ||
|
|
8c9196bff8 | ||
|
|
b1daf44954 | ||
|
|
11c52d032b | ||
|
|
be291d979d | ||
|
|
6d1edd8d32 | ||
|
|
25ff956c7d | ||
|
|
38179500f8 | ||
|
|
5f7be5f0d6 | ||
|
|
627056febb | ||
|
|
cbd76447fd | ||
|
|
a60a233329 | ||
|
|
a0a3b8ad3e | ||
|
|
d0ae3f5a4d | ||
|
|
6860cf932b | ||
|
|
0276e0805b | ||
|
|
06ff3d8a26 | ||
|
|
1a3b69aa56 | ||
|
|
8237d06ab7 | ||
|
|
1c9f136b57 | ||
|
|
5ab7e4a475 | ||
|
|
3236f358f8 | ||
|
|
4a6550d69a | ||
|
|
ff523d0c67 | ||
|
|
3c93e8eb41 | ||
|
|
88a482fdb9 | ||
|
|
12a9aa7c62 | ||
|
|
50ccf9c585 | ||
|
|
d100eb05a3 | ||
|
|
10d8b5f001 | ||
|
|
ffd3ceb856 | ||
|
|
d942aa9321 | ||
|
|
6469fefe89 | ||
|
|
b082842ee7 | ||
|
|
3573ca0eec | ||
|
|
ad90eb075d | ||
|
|
d55e2d086d | ||
|
|
fe9c966a49 | ||
|
|
9f20afb1a8 | ||
|
|
f61afcfc70 | ||
|
|
961daf8f92 | ||
|
|
64a16cb376 | ||
|
|
ea6b0b2665 | ||
|
|
89df73ac05 | ||
|
|
7d3f3c9983 | ||
|
|
1bdbea2461 | ||
|
|
dfb1f7ccf1 | ||
|
|
b5d1b20727 | ||
|
|
2a407a76be | ||
|
|
d1640a6338 | ||
|
|
26b5c40d95 | ||
|
|
0b3ecf7432 | ||
|
|
8f9bd61505 | ||
|
|
ea28d0ef8a | ||
|
|
4a8c098840 | ||
|
|
ffa4628faa | ||
|
|
e10a9239e1 | ||
|
|
51f7bc924c | ||
|
|
6c0bf79078 | ||
|
|
4b03170920 | ||
|
|
1de6bbc108 | ||
|
|
023ace8e54 | ||
|
|
74d4fcd756 | ||
|
|
9eaa91bfc3 | ||
|
|
484bd75ce4 | ||
|
|
4c30e9602b | ||
|
|
b260d222af | ||
|
|
6528d62cd2 | ||
|
|
b7cf754f6f | ||
|
|
14e3f6ba19 | ||
|
|
a8c1474562 | ||
|
|
8b8a4148ec | ||
|
|
9e147480ed | ||
|
|
4c0aecc685 | ||
|
|
d556b8a5d5 | ||
|
|
e7c0d7b348 | ||
|
|
17360439dc | ||
|
|
9e169a9bea | ||
|
|
305cb79c57 | ||
|
|
141a26f979 | ||
|
|
cfcafdd27c | ||
|
|
f1204a875e | ||
|
|
20295012b8 | ||
|
|
807e82343a | ||
|
|
c8e8f5c204 | ||
|
|
a69b017902 | ||
|
|
e75069f79a | ||
|
|
3f535da79e | ||
|
|
8ebdc364af | ||
|
|
12af2b171d | ||
|
|
04490bf622 |
171
CHANGELOG
171
CHANGELOG
@@ -1,3 +1,170 @@
|
||||
version 2.86
|
||||
Handle DHCPREBIND requests in the DHCPv6 server code.
|
||||
Thanks to Aichun Li for spotting this ommision, and the initial
|
||||
patch.
|
||||
|
||||
Fix bug which caused dnsmasq to lose track of processes forked
|
||||
to handle TCP DNS connections under heavy load. The code
|
||||
checked that at least one free process table slot was
|
||||
available before listening on TCP sockets, but didn't take
|
||||
into account that more than one TCP connection could
|
||||
arrive, so that check was not sufficient to ensure that
|
||||
there would be slots for all new processes. It compounded
|
||||
this error by silently failing to store the process when
|
||||
it did run out of slots. Even when this bug is triggered,
|
||||
all the right things happen, and answers are still returned.
|
||||
Only under very exceptional circumstances, does the bug
|
||||
manifest itself: see
|
||||
https://lists.thekelleys.org.uk/pipermail/dnsmasq-discuss/2021q2/014976.html
|
||||
Thanks to Tijs Van Buggenhout for finding the conditions under
|
||||
which the bug manifests itself, and then working out
|
||||
exactly what was going on.
|
||||
|
||||
Major rewrite of the DNS server and domain handling code.
|
||||
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.
|
||||
|
||||
Revise resource handling for number of concurrent DNS queries. 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.
|
||||
|
||||
Improve efficiency of DNSSEC. The sharing point for DNSSEC RR data
|
||||
used to be when it entered the cache, having been validated. After
|
||||
that queries requiring the KEY or DS records would share the cached
|
||||
values. There is a common case in dual-stack hosts that queries for
|
||||
A and AAAA records for the same domain are made simultaneously.
|
||||
If required keys were not in the cache, this would result in two
|
||||
requests being sent upstream for the same key data (and all the
|
||||
subsequent chain-of-trust queries.) Now we combine these requests
|
||||
and elide the duplicates, resulting in fewer queries upstream
|
||||
and better performance. To keep a better handle on what's
|
||||
going on, the "extra" logging mode has been modified to associate
|
||||
queries and answers for DNSSEC queries in the same way as ordinary
|
||||
queries. The requesting address and port have been removed from
|
||||
DNSSEC logging lines, since this is no longer strictly defined.
|
||||
|
||||
Connection track mark based DNS query filtering. Thanks to
|
||||
Etan Kissling for implementing this It extends query filtering
|
||||
support beyond what is currently possible
|
||||
with the `--ipset` configuration option, by adding support for:
|
||||
1) Specifying allowlists on a per-client basis, based on their
|
||||
associated Linux connection track mark.
|
||||
2) Dynamic configuration of allowlists via Ubus.
|
||||
3) Reporting when a DNS query resolves or is rejected via Ubus.
|
||||
4) DNS name patterns containing wildcards.
|
||||
Disallowed queries are not forwarded; they are rejected
|
||||
with a REFUSED error code.
|
||||
|
||||
Allow smaller then 64 prefix lengths in synth-domain, with caveats.
|
||||
--synth-domain=1234:4567::/56,example.com is now valid.
|
||||
|
||||
Make domains generated by --synth-domain appear in replies
|
||||
when in authoritative mode.
|
||||
|
||||
|
||||
version 2.85
|
||||
Fix problem with DNS retries in 2.83/2.84.
|
||||
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.
|
||||
|
||||
Tweak sort order of tags in get-version. v2.84 sorts
|
||||
before v2.83, but v2.83 sorts before v2.83rc1 and 2.83rc1
|
||||
sorts before v2.83test1. This fixes the problem which lead
|
||||
to 2.84 announcing itself as 2.84rc2.
|
||||
|
||||
Avoid treating a --dhcp-host which has an IPv6 address
|
||||
as eligible for use with DHCPv4 on the grounds that it has
|
||||
no address, and vice-versa. Thanks to Viktor Papp for
|
||||
spotting the problem. (This bug was fixed was back in 2.67, and
|
||||
then regressed in 2.81).
|
||||
|
||||
Add --dynamic-host option: A and AAAA records which take their
|
||||
network part from the network of a local interface. Useful
|
||||
for routers with dynamically prefixes. Thanks
|
||||
to Fred F for the suggestion.
|
||||
|
||||
Teach --bogus-nxdomain and --ignore-address to take an IPv4 subnet.
|
||||
|
||||
Use random source ports where possible if source
|
||||
addresses/interfaces in use.
|
||||
CVE-2021-3448 applies. Thanks to Petr Menšík for spotting this.
|
||||
It's possible to specify the source address or interface to be
|
||||
used when contacting upstream name servers: 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 communication with the server not possible.
|
||||
|
||||
Change the method of allocation of random source ports for DNS.
|
||||
Previously, without min-port or max-port configured, dnsmasq would
|
||||
default to the compiled in defaults for those, which are 1024 and
|
||||
65535. Now, when neither are configured, it defaults instead to
|
||||
the kernel's ephemeral port range, which is typically
|
||||
32768 to 60999 on Linux systems. This change eliminates the
|
||||
possibility that dnsmasq may be using a registered port > 1024
|
||||
when a long-running daemon starts up and wishes to claim it.
|
||||
This change does likely slightly reduce the number of random ports
|
||||
and therefore the protection from reply spoofing. The older
|
||||
behaviour can be restored using the min-port and max-port config
|
||||
switches should that be a concern.
|
||||
|
||||
Scale the size of the DNS random-port pool based on the
|
||||
value of the --dns-forward-max configuration.
|
||||
|
||||
Tweak TFTP code to check sender of all received packets, as
|
||||
specified in RFC 1350 para 4.
|
||||
|
||||
|
||||
version 2.84
|
||||
Fix a problem, introduced in 2.83, which could see DNS replies
|
||||
being sent via the wrong socket. On machines running both
|
||||
IPv4 and IPv6 this could result in sporadic messages of
|
||||
the form "failed to send packet: Network is unreachable" and
|
||||
the lost of the query. Since the error is sporadic and of
|
||||
low probability, the client retry would normally succeed.
|
||||
|
||||
Change HAVE_NETTLEHASH compile-time to HAVE_CRYPTOHASH.
|
||||
|
||||
|
||||
version 2.83
|
||||
Use the values of --min-port and --max-port in outgoing
|
||||
TCP connections to upstream DNS servers.
|
||||
@@ -19,13 +186,13 @@ version 2.83
|
||||
|
||||
Handle multiple identical near simultaneous DNS queries better.
|
||||
Previously, such queries would all be forwarded
|
||||
independently. This is, in theory, inefficent but in practise
|
||||
independently. This is, in theory, inefficient 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
|
||||
successful 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
|
||||
|
||||
2
FAQ
2
FAQ
@@ -236,7 +236,7 @@ Q: What network types are supported by the DHCP server?
|
||||
A: Ethernet (and 802.11 wireless) are supported on all platforms. On
|
||||
Linux all network types (including FireWire) are supported.
|
||||
|
||||
Q: What are these strange "bind-interface" and "bind-dynamic" options?
|
||||
Q: What are these strange "bind-interfaces" and "bind-dynamic" options?
|
||||
|
||||
A: Dnsmasq from v2.63 can operate in one of three different "networking
|
||||
modes". This is unfortunate as it requires users configuring dnsmasq
|
||||
|
||||
8
Makefile
8
Makefile
@@ -1,4 +1,4 @@
|
||||
# dnsmasq is Copyright (c) 2000-2016 Simon Kelley
|
||||
# dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -63,8 +63,10 @@ ct_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_CONNTRACK $(PKG_CON
|
||||
lua_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LUASCRIPT $(PKG_CONFIG) --cflags lua5.2`
|
||||
lua_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LUASCRIPT $(PKG_CONFIG) --libs lua5.2`
|
||||
nettle_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC $(PKG_CONFIG) --cflags 'nettle hogweed' \
|
||||
HAVE_CRYPTOHASH $(PKG_CONFIG) --cflags nettle \
|
||||
HAVE_NETTLEHASH $(PKG_CONFIG) --cflags nettle`
|
||||
nettle_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC $(PKG_CONFIG) --libs 'nettle hogweed' \
|
||||
HAVE_CRYPTOHASH $(PKG_CONFIG) --libs nettle \
|
||||
HAVE_NETTLEHASH $(PKG_CONFIG) --libs nettle`
|
||||
gmp_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC NO_GMP --copy -lgmp`
|
||||
sunos_libs = `if uname | grep SunOS >/dev/null 2>&1; then echo -lsocket -lnsl -lposix4; fi`
|
||||
@@ -77,10 +79,10 @@ copts_conf = .copts_$(sum)
|
||||
objs = cache.o rfc1035.o util.o option.o forward.o network.o \
|
||||
dnsmasq.o dhcp.o lease.o rfc2131.o netlink.o dbus.o bpf.o \
|
||||
helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o \
|
||||
dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o \
|
||||
dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o pattern.o \
|
||||
domain.o dnssec.o blockdata.o tables.o loop.o inotify.o \
|
||||
poll.o rrfilter.o edns0.o arp.o crypto.o dump.o ubus.o \
|
||||
metrics.o hash_questions.o
|
||||
metrics.o hash-questions.o domain-match.o
|
||||
|
||||
hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \
|
||||
dns-protocol.h radv-protocol.h ip6addr.h metrics.h
|
||||
|
||||
@@ -11,7 +11,8 @@ LOCAL_SRC_FILES := bpf.c cache.c dbus.c dhcp.c dnsmasq.c \
|
||||
radv.c slaac.c auth.c ipset.c domain.c \
|
||||
dnssec.c dnssec-openssl.c blockdata.c tables.c \
|
||||
loop.c inotify.c poll.c rrfilter.c edns0.c arp.c \
|
||||
crypto.c dump.c ubus.c metrics.c hash_questions.c
|
||||
crypto.c dump.c ubus.c metrics.c hash-questions.c \
|
||||
domain-match.c
|
||||
|
||||
LOCAL_MODULE := dnsmasq
|
||||
|
||||
|
||||
@@ -9,7 +9,10 @@
|
||||
# If we can find one which matches $v[0-9].* then we assume it's
|
||||
# a version-number tag, else we just use the whole string.
|
||||
# If there is more than one v[0-9].* tag, sort them and use the
|
||||
# first. This favours, eg v2.63 over 2.63rc6.
|
||||
# first. The insane arguments to the sort command are to ensure
|
||||
# that, eg v2.64 comes before v2.63, but v2.63 comes before v2.63rc1
|
||||
# and v2.63rc1 comes before v2.63test1
|
||||
|
||||
|
||||
# Change directory to the toplevel source directory.
|
||||
if test -z "$1" || ! test -d "$1" || ! cd "$1"; then
|
||||
@@ -28,7 +31,7 @@ else
|
||||
vers=`cat $1/VERSION | sed 's/[(), ]/,/ g' | tr ',' '\n' | grep ^v[0-9]`
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "${vers}" | sort -r | head -n 1 | sed 's/^v//'
|
||||
echo "${vers}" | sort -k1.2,1.5Vr -k1.6,1.6 -k1.8,1.9Vr -k1.10,1.11Vr | head -n 1 | sed 's/^v//'
|
||||
else
|
||||
cat $1/VERSION
|
||||
fi
|
||||
|
||||
40
debian/changelog
vendored
40
debian/changelog
vendored
@@ -1,3 +1,41 @@
|
||||
dnsmasq (2.86-1) unstable; urgency=low
|
||||
|
||||
* Fix debian/changelog format error. (closes: #986626)
|
||||
|
||||
-- Simon Kelley <simon@thekelleys.org.uk> Thu, 08 Apr 2021 22:39:00 +0100
|
||||
|
||||
dnsmasq (2.85-1) unstable; urgency=low
|
||||
|
||||
* New upstream.
|
||||
* Includes fix to CVE-2021-3448.
|
||||
* Fix manpage typos. (closes: #986150)
|
||||
|
||||
-- Simon Kelley <simon@thekelleys.org.uk> Sat, 03 Apr 2021 22:17:23 +0100
|
||||
|
||||
dnsmasq (2.84-1.2) unstable; urgency=medium
|
||||
|
||||
* Non-maintainer upload.
|
||||
* Bump old-version in dpkg-maintscript-helper dir_to_symlink calls to also
|
||||
clean up after upgrades to an earlier version in testing.
|
||||
|
||||
-- Andreas Beckmann <anbe@debian.org> Thu, 01 Apr 2021 16:01:51 +0200
|
||||
|
||||
dnsmasq (2.84-1.1) unstable; urgency=medium
|
||||
|
||||
* Non-maintainer upload.
|
||||
* Fix symlink to directory conversion for /usr/share/doc/dnsmasq.
|
||||
This is achieved by directly calling dpkg-maintscript-helper in the preinst,
|
||||
postinst, and postrm scripts, since the package does not use debhelper.
|
||||
(Closes: #985282)
|
||||
|
||||
-- Sébastien Villemot <sebastien@debian.org> Sun, 28 Mar 2021 10:55:07 +0200
|
||||
|
||||
dnsmasq (2.84-1) unstable; urgency=low
|
||||
|
||||
* New upstream.
|
||||
|
||||
-- Simon Kelley <simon@thekelleys.org.uk> Sun, 24 Jan 2021 22:02:01 +0000
|
||||
|
||||
dnsmasq (2.83-1) unstable; urgency=high
|
||||
|
||||
* New upstream.
|
||||
@@ -1043,7 +1081,7 @@ dnsmasq (2.6-2) unstable; urgency=low
|
||||
* Added note about the --bind-interfaces option to
|
||||
readme.Debian (closes: #241700)
|
||||
|
||||
-- Simon Kelley <simon@thekelleys.org.uk> Tues, 13 Apr 2004 18:37:55 +0000
|
||||
-- Simon Kelley <simon@thekelleys.org.uk> Tue, 13 Apr 2004 18:37:55 +0000
|
||||
|
||||
dnsmasq (2.6-1) unstable; urgency=low
|
||||
|
||||
|
||||
2
debian/copyright
vendored
2
debian/copyright
vendored
@@ -1,4 +1,4 @@
|
||||
dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
It was downloaded from: http://www.thekelleys.org.uk/dnsmasq/
|
||||
|
||||
|
||||
3
debian/postinst
vendored
3
debian/postinst
vendored
@@ -1,6 +1,9 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
# /usr/share/doc/dnsmasq was a symlink in versions < 2.81-1 (see #985282)
|
||||
dpkg-maintscript-helper symlink_to_dir /usr/share/doc/dnsmasq dnsmasq-base 2.84-1.2~ dnsmasq -- "$@"
|
||||
|
||||
# Code copied from dh_systemd_enable ----------------------
|
||||
# This will only remove masks created by d-s-h on package removal.
|
||||
deb-systemd-helper unmask dnsmasq.service >/dev/null || true
|
||||
|
||||
3
debian/postrm
vendored
3
debian/postrm
vendored
@@ -1,6 +1,9 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
# /usr/share/doc/dnsmasq was a symlink in versions < 2.81-1 (see #985282)
|
||||
dpkg-maintscript-helper symlink_to_dir /usr/share/doc/dnsmasq dnsmasq-base 2.84-1.2~ dnsmasq -- "$@"
|
||||
|
||||
if [ purge = "$1" ]; then
|
||||
update-rc.d dnsmasq remove >/dev/null
|
||||
fi
|
||||
|
||||
5
debian/preinst
vendored
Normal file
5
debian/preinst
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
# /usr/share/doc/dnsmasq was a symlink in versions < 2.81-1 (see #985282)
|
||||
dpkg-maintscript-helper symlink_to_dir /usr/share/doc/dnsmasq dnsmasq-base 2.84-1.2~ dnsmasq -- "$@"
|
||||
2
debian/rules
vendored
2
debian/rules
vendored
@@ -176,7 +176,7 @@ binary-indep: checkroot
|
||||
-d debian/trees/daemon/usr/lib/tmpfiles.d \
|
||||
-d debian/trees/daemon/etc/insserv.conf.d
|
||||
install -m 644 debian/conffiles debian/trees/daemon/DEBIAN
|
||||
install -m 755 debian/postinst debian/postrm debian/prerm debian/trees/daemon/DEBIAN
|
||||
install -m 755 debian/postinst debian/postrm debian/preinst debian/prerm debian/trees/daemon/DEBIAN
|
||||
if ! dpkg-vendor --derives-from Ubuntu; then \
|
||||
rm -f debian/dnsmasq.postinst.debhelper debian/dnsmasq.postrm.debhelper; \
|
||||
dh_runit -pdnsmasq -Pdebian/trees/daemon; \
|
||||
|
||||
120
man/dnsmasq.8
120
man/dnsmasq.8
@@ -135,6 +135,9 @@ running, will go exclusively to the file.) When logging to a file,
|
||||
dnsmasq will close and reopen the file when it receives SIGUSR2. This
|
||||
allows the log file to be rotated without stopping dnsmasq.
|
||||
.TP
|
||||
.B --log-debug
|
||||
Enable extra logging intended for debugging rather than information.
|
||||
.TP
|
||||
.B --log-async[=<lines>]
|
||||
Enable asynchronous logging and optionally set the limit on the
|
||||
number of lines
|
||||
@@ -181,7 +184,7 @@ OS: this was the default behaviour in versions prior to 2.43.
|
||||
.B --min-port=<port>
|
||||
Do not use ports less than that given as source for outbound DNS
|
||||
queries. Dnsmasq picks random ports as source for outbound queries:
|
||||
when this option is given, the ports used will always to larger
|
||||
when this option is given, the ports used will always be larger
|
||||
than that specified. Useful for systems behind firewalls. If not specified,
|
||||
defaults to 1024.
|
||||
.TP
|
||||
@@ -296,7 +299,7 @@ option requires non-standard networking APIs and it is only available
|
||||
under Linux. On other platforms it falls-back to \fB--bind-interfaces\fP mode.
|
||||
.TP
|
||||
.B \-y, --localise-queries
|
||||
Return answers to DNS queries from /etc/hosts and \fB--interface-name\fP which depend on the interface over which the query was
|
||||
Return answers to DNS queries from /etc/hosts and \fB--interface-name\fP and \fB--dynamic-host\fP which depend on the interface over which the query was
|
||||
received. If a name has more than one address associated with
|
||||
it, and at least one of those addresses is on the same subnet as the
|
||||
interface to which the query was sent, then return only the
|
||||
@@ -323,17 +326,17 @@ are re-written. So
|
||||
.B --alias=192.168.0.10-192.168.0.40,10.0.0.0,255.255.255.0
|
||||
maps 192.168.0.10->192.168.0.40 to 10.0.0.10->10.0.0.40
|
||||
.TP
|
||||
.B \-B, --bogus-nxdomain=<ipaddr>
|
||||
Transform replies which contain the IP address given into "No such
|
||||
domain" replies. This is intended to counteract a devious move made by
|
||||
.B \-B, --bogus-nxdomain=<ipaddr>[/prefix]
|
||||
Transform replies which contain the specified address or subnet into "No such
|
||||
domain" replies. IPv4 and IPv6 are supported. This is intended to counteract a devious move made by
|
||||
Verisign in September 2003 when they started returning the address of
|
||||
an advertising web page in response to queries for unregistered names,
|
||||
instead of the correct NXDOMAIN response. This option tells dnsmasq to
|
||||
fake the correct response when it sees this behaviour. As at Sept 2003
|
||||
the IP address being returned by Verisign is 64.94.110.11
|
||||
.TP
|
||||
.B --ignore-address=<ipaddr>
|
||||
Ignore replies to A-record queries which include the specified address.
|
||||
.B --ignore-address=<ipaddr>[/prefix]
|
||||
Ignore replies to A or AAAA queries which include the specified address or subnet.
|
||||
No error is generated, dnsmasq simply continues to listen for another reply.
|
||||
This is useful to defeat blocking strategies which rely on quickly supplying a
|
||||
forged answer to a DNS request for certain domain, before the correct answer can arrive.
|
||||
@@ -368,7 +371,10 @@ provides service at that name, rather than the default which is
|
||||
.TP
|
||||
.B --enable-ubus[=<service-name>]
|
||||
Enable dnsmasq UBus interface. It sends notifications via UBus on
|
||||
DHCPACK and DHCPRELEASE events. Furthermore it offers metrics.
|
||||
DHCPACK and DHCPRELEASE events. Furthermore it offers metrics
|
||||
and allows configuration of Linux connection track mark based filtering.
|
||||
When DNS query filtering based on Linux connection track marks is enabled
|
||||
UBus notifications are generated for each resolved or filtered DNS query.
|
||||
Requires that dnsmasq has been built with UBus support. If the service
|
||||
name is given, dnsmasq provides service at that namespace, rather than
|
||||
the default which is
|
||||
@@ -428,7 +434,7 @@ Tells dnsmasq to never forward A or AAAA queries for plain names, without dots
|
||||
or domain parts, to upstream nameservers. If the name is not known
|
||||
from /etc/hosts or DHCP then a "not found" answer is returned.
|
||||
.TP
|
||||
.B \-S, --local, --server=[/[<domain>]/[domain/]][<ipaddr>[#<port>]][@<source-ip>|<interface>[#<port>]]
|
||||
.B \-S, --local, --server=[/[<domain>]/[domain/]][<ipaddr>[#<port>]][@<interface>][@<source-ip>[#<port>]]
|
||||
Specify IP address of upstream servers directly. Setting this flag does
|
||||
not suppress reading of /etc/resolv.conf, use \fB--no-resolv\fP to do that. If one or more
|
||||
optional domains are given, that server is used only for those domains
|
||||
@@ -454,13 +460,22 @@ repeated domain or ipaddr parts as required.
|
||||
More specific domains take precedence over less specific domains, so:
|
||||
.B --server=/google.com/1.2.3.4
|
||||
.B --server=/www.google.com/2.3.4.5
|
||||
will send queries for *.google.com to 1.2.3.4, except *www.google.com,
|
||||
which will go to 2.3.4.5
|
||||
will send queries for google.com and gmail.google.com to 1.2.3.4, but www.google.com
|
||||
will go to 2.3.4.5
|
||||
|
||||
Matching of domains is normally done on complete labels, so /google.com/ matches google.com and www.google.com
|
||||
but NOT supergoogle.com. This can be overridden with a * at the start of a pattern only: /*google.com/
|
||||
will match google.com and www.google.com AND supergoogle.com. The non-wildcard form has priority, so
|
||||
if /google.com/ and /*google.com/ are both specified then google.com and www.google.com will match /google.com/
|
||||
and /*google.com/ will only match supergoogle.com.
|
||||
|
||||
For historical reasons, the pattern /.google.com/ is equivalent to /google.com/ if you wish to match any subdomain
|
||||
of google.com but NOT google.com itself, use /*.google.com/
|
||||
|
||||
The special server address '#' means, "use the standard servers", so
|
||||
.B --server=/google.com/1.2.3.4
|
||||
.B --server=/www.google.com/#
|
||||
will send queries for *.google.com to 1.2.3.4, except *www.google.com which will
|
||||
will send queries for google.com and its subdomains to 1.2.3.4, except www.google.com (and its subdomains) which will
|
||||
be forwarded as usual.
|
||||
|
||||
Also permitted is a -S
|
||||
@@ -489,7 +504,7 @@ source address specified but the port may be specified directly as
|
||||
part of the source address. Forcing queries to an interface is not
|
||||
implemented on all platforms supported by dnsmasq.
|
||||
.TP
|
||||
.B --rev-server=<ip-address>/<prefix-len>[,<ipaddr>][#<port>][@<source-ip>|<interface>[#<port>]]
|
||||
.B --rev-server=<ip-address>/<prefix-len>[,<ipaddr>][#<port>][@<interface>][@<source-ip>[#<port>]]
|
||||
This is functionally the same as
|
||||
.B --server,
|
||||
but provides some syntactic sugar to make specifying address-to-name queries easier. For example
|
||||
@@ -507,7 +522,7 @@ To include multiple IP addresses for a single query, use
|
||||
Note that /etc/hosts and DHCP leases override this for individual
|
||||
names. A common use of this is to redirect the entire doubleclick.net
|
||||
domain to some friendly local web server to avoid banner ads. The
|
||||
domain specification works in the same was as for \fB--server\fP, with
|
||||
domain specification works in the same way as for \fB--server\fP, with
|
||||
the additional facility that \fB/#/\fP matches any domain. Thus
|
||||
\fB--address=/#/1.2.3.4\fP will always return \fB1.2.3.4\fP for any
|
||||
query not answered from \fB/etc/hosts\fP or DHCP and not sent to an
|
||||
@@ -533,6 +548,32 @@ These IP sets must already exist. See
|
||||
.BR ipset (8)
|
||||
for more details.
|
||||
.TP
|
||||
.B --connmark-allowlist-enable[=<mask>]
|
||||
Enables filtering of incoming DNS queries with associated Linux connection track marks
|
||||
according to individual allowlists configured via a series of \fB--connmark-allowlist\fP
|
||||
options. Disallowed queries are not forwarded; they are rejected with a REFUSED error code.
|
||||
DNS queries are only allowed if they do not have an associated Linux connection
|
||||
track mark, or if the queried domains match the configured DNS patterns for the
|
||||
associated Linux connection track mark. If no allowlist is configured for a
|
||||
Linux connection track mark, all DNS queries associated with that mark are rejected.
|
||||
If a mask is specified, Linux connection track marks are first bitwise ANDed
|
||||
with the given mask before being processed.
|
||||
.TP
|
||||
.B --connmark-allowlist=<connmark>[/<mask>][,<pattern>[/<pattern>...]]
|
||||
Configures the DNS patterns that are allowed in DNS queries associated with
|
||||
the given Linux connection track mark.
|
||||
If a mask is specified, Linux connection track marks are first bitwise ANDed
|
||||
with the given mask before they are compared to the given connection track mark.
|
||||
Patterns follow the syntax of DNS names, but additionally allow the wildcard
|
||||
character "*" to be used up to twice per label to match 0 or more characters
|
||||
within that label. Note that the wildcard never matches a dot (e.g., "*.example.com"
|
||||
matches "api.example.com" but not "api.us.example.com"). Patterns must be
|
||||
fully qualified, i.e., consist of at least two labels. The final label must not be
|
||||
fully numeric, and must not be the "local" pseudo-TLD. A pattern must end with at least
|
||||
two literal (non-wildcard) labels.
|
||||
Instead of a pattern, "*" can be specified to disable allowlist filtering
|
||||
for a given Linux connection track mark entirely.
|
||||
.TP
|
||||
.B \-m, --mx-host=<mx name>[[,<hostname>],<preference>]
|
||||
Return an MX record named <mx name> pointing to the given hostname (if
|
||||
given), or
|
||||
@@ -591,6 +632,12 @@ If the time-to-live is given, it overrides the default, which is zero
|
||||
or the value of \fB--local-ttl\fP. The value is a positive integer and gives
|
||||
the time-to-live in seconds.
|
||||
.TP
|
||||
.B --dynamic-host=<name>,[IPv4-address],[IPv6-address],<interface>
|
||||
Add A, AAAA and PTR records to the DNS in the same subnet as the specified interface. The address is derived from the network part of each address associated with the interface, and the host part from the specified address. For example
|
||||
.B --dynamic-host=example.com,0.0.0.8,eth0
|
||||
will, when eth0 has the address 192.168.78.x and netmask 255.255.255.0 give the
|
||||
name example.com an A record for 192.168.78.8. The same principle applies to IPv6 addresses. Note that if an interface has more than one address, more than one A or AAAA record will be created. The TTL of the records is always zero, and any changes to interface addresses will be immediately reflected in them.
|
||||
.TP
|
||||
.B \-Y, --txt-record=<name>[[,<text>],<text>]
|
||||
Return a TXT DNS record. The value of TXT record is a set of strings,
|
||||
so any number may be included, delimited by commas; use quotes to put
|
||||
@@ -658,7 +705,8 @@ configured a zero is added in front of the label. ::1 becomes 0--1.
|
||||
V4 mapped IPv6 addresses, which have a representation like ::ffff:1.2.3.4 are handled specially, and become like 0--ffff-1-2-3-4
|
||||
|
||||
The address range can be of the form
|
||||
<ip address>,<ip address> or <ip address>/<netmask> in both forms of the option.
|
||||
<start address>,<end address> or <ip address>/<prefix-length> in both forms of the option. For IPv6 the start and end addresses
|
||||
must fall in the same /64 network, or prefix-length must be greater than or equal to 64 except that shorter prefix lengths than 64 are allowed only if non-sequential names are in use.
|
||||
.TP
|
||||
.B --dumpfile=<path/to/file>
|
||||
Specify the location of a pcap-format file which dnsmasq uses to dump copies of network packets for debugging purposes. If the file exists when dnsmasq starts, it is not deleted; new packets are added to the end.
|
||||
@@ -702,7 +750,13 @@ will add the /24 and /96 subnets of the requestor for IPv4 and IPv6 requestors,
|
||||
will add 1.2.3.0/24 for IPv4 requestors and ::/0 for IPv6 requestors.
|
||||
.B --add-subnet=1.2.3.4/24,1.2.3.4/24
|
||||
will add 1.2.3.0/24 for both IPv4 and IPv6 requestors.
|
||||
|
||||
.TP
|
||||
.B --umbrella[=deviceid:<deviceid>[,orgid:<orgid>]]
|
||||
Embeds the requestor's IP address in DNS queries forwarded upstream.
|
||||
If device id or organization id are specified, the information is
|
||||
included in the forwarded queries and may be able to be used in
|
||||
filtering policies and reporting. The order of the deviceid and orgid
|
||||
attributes is irrelevant, but must be separated by a comma.
|
||||
.TP
|
||||
.B \-c, --cache-size=<cachesize>
|
||||
Set the size of dnsmasq's cache. The default is 150 names. Setting the cache size to zero disables caching. Note: huge cache size impacts performance.
|
||||
@@ -716,7 +770,8 @@ identical queries without forwarding them again.
|
||||
Set the maximum number of concurrent DNS queries. The default value is
|
||||
150, which should be fine for most setups. The only known situation
|
||||
where this needs to be increased is when using web-server log file
|
||||
resolvers, which can generate large numbers of concurrent queries.
|
||||
resolvers, which can generate large numbers of concurrent queries. This
|
||||
parameter actually controls the number of concurrent queries per server group, where a server group is the set of server(s) associated with a single domain. So if a domain has it's own server via --server=/example.com/1.2.3.4 and 1.2.3.4 is not responding, but queries for *.example.com cannot go elsewhere, then other queries will not be affected. On configurations with many such server groups and tight resources, this value may need to be reduced.
|
||||
.TP
|
||||
.B --dnssec
|
||||
Validate DNS replies and cache DNSSEC data. When forwarding DNS queries, dnsmasq requests the
|
||||
@@ -860,7 +915,7 @@ in
|
||||
.B --dhcp-host
|
||||
options. If the lease time is given, then leases
|
||||
will be given for that length of time. The lease time is in seconds,
|
||||
or minutes (eg 45m) or hours (eg 1h) or "infinite". If not given,
|
||||
or minutes (eg 45m) or hours (eg 1h) or days (2d) or weeks (1w) or "infinite". If not given,
|
||||
the default lease time is one hour for IPv4 and one day for IPv6. The
|
||||
minimum lease time is two minutes. For IPv6 ranges, the lease time
|
||||
maybe "deprecated"; this sets the preferred lifetime sent in a DHCP
|
||||
@@ -1045,6 +1100,19 @@ option, but aliases are possible by using CNAMEs. (See
|
||||
.B --cname
|
||||
).
|
||||
|
||||
More than one
|
||||
.B --dhcp-host
|
||||
can be associated (by name, hardware address or UID) with a host. Which one is used
|
||||
(and therefore which address is allocated by DHCP and appears in the DNS) depends
|
||||
on the subnet on which the host last obtained a DHCP lease:
|
||||
the
|
||||
.B --dhcp-host
|
||||
with an address within the subnet is used. If more than one address is within the subnet,
|
||||
the result is undefined. A corollary to this is that the name associated with a host using
|
||||
.B --dhcp-host
|
||||
does not appear in the DNS until the host obtains a DHCP lease.
|
||||
|
||||
|
||||
The special keyword "ignore"
|
||||
tells dnsmasq to never offer a DHCP lease to a machine. The machine
|
||||
can be specified by hardware address, client ID or hostname, for
|
||||
@@ -1436,7 +1504,7 @@ functions when supported by a suitable DHCP server.
|
||||
This specifies a boot option which may appear in a PXE boot menu. <CSA> is
|
||||
client system type, only services of the correct type will appear in a
|
||||
menu. The known types are x86PC, PC98, IA64_EFI, Alpha, Arc_x86,
|
||||
Intel_Lean_Client, IA32_EFI, X86-64_EFI, Xscale_EFI, BC_EFI, ARM32_EFI and ARM64_EFI; an
|
||||
Intel_Lean_Client, IA32_EFI, x86-64_EFI, Xscale_EFI, BC_EFI, ARM32_EFI and ARM64_EFI; an
|
||||
integer may be used for other types. The
|
||||
parameter after the menu text may be a file name, in which case dnsmasq acts as a
|
||||
boot server and directs the PXE client to download the file by TFTP,
|
||||
@@ -1495,7 +1563,6 @@ instance
|
||||
will enable dnsmasq to also provide proxy PXE service to those PXE clients with
|
||||
.I HW-Client
|
||||
in as their identifier.
|
||||
>>>>>>> 907def3... pxe: support pxe clients with custom vendor-class
|
||||
.TP
|
||||
.B \-X, --dhcp-lease-max=<number>
|
||||
Limits dnsmasq to the specified maximum number of DHCP leases. The
|
||||
@@ -1546,10 +1613,11 @@ tried. This flag disables this check. Use with caution.
|
||||
Extra logging for DHCP: log all the options sent to DHCP clients and
|
||||
the tags used to determine them.
|
||||
.TP
|
||||
.B --quiet-dhcp, --quiet-dhcp6, --quiet-ra
|
||||
.B --quiet-dhcp, --quiet-dhcp6, --quiet-ra, --quiet-tftp
|
||||
Suppress logging of the routine operation of these protocols. Errors and
|
||||
problems will still be logged. \fB--quiet-dhcp\fP and quiet-dhcp6 are
|
||||
over-ridden by \fB--log-dhcp\fP.
|
||||
problems will still be logged. \fB--quiet-tftp\fP does not consider file not
|
||||
found to be an error. \fB--quiet-dhcp\fP and quiet-dhcp6 are over-ridden by
|
||||
\fB--log-dhcp\fP.
|
||||
.TP
|
||||
.B \-l, --dhcp-leasefile=<path>
|
||||
Use the specified file to store DHCP lease information.
|
||||
@@ -2351,6 +2419,10 @@ following data is used to populate the authoritative zone.
|
||||
.B --mx-host, --srv-host, --dns-rr, --txt-record, --naptr-record, --caa-record,
|
||||
as long as the record names are in the authoritative domain.
|
||||
.PP
|
||||
.B --synth-domain
|
||||
as long as the domain is in the authoritative zone and, for
|
||||
reverse (PTR) queries, the address is in the relevant subnet.
|
||||
.PP
|
||||
.B --cname
|
||||
as long as the record name is in the authoritative domain. If the
|
||||
target of the CNAME is unqualified, then it is qualified with the
|
||||
@@ -2367,6 +2439,8 @@ IPv4 and IPv6 addresses from /etc/hosts (and
|
||||
.B --host-record
|
||||
and
|
||||
.B --interface-name
|
||||
and
|
||||
.B ---dynamic-host
|
||||
provided the address falls into one of the subnets specified in the
|
||||
.B --auth-zone.
|
||||
.PP
|
||||
|
||||
1043
po/pt_BR.po
1043
po/pt_BR.po
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -230,5 +230,3 @@ int do_arp_script_run(void)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
44
src/auth.c
44
src/auth.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -105,7 +105,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
int nameoffset, axfroffset = 0;
|
||||
int q, anscount = 0, authcount = 0;
|
||||
struct crec *crecp;
|
||||
int auth = !local_query, trunc = 0, nxdomain = 1, soa = 0, ns = 0, axfr = 0;
|
||||
int auth = !local_query, trunc = 0, nxdomain = 1, soa = 0, ns = 0, axfr = 0, out_of_zone = 0;
|
||||
struct auth_zone *zone = NULL;
|
||||
struct addrlist *subnet = NULL;
|
||||
char *cut;
|
||||
@@ -146,6 +146,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
if (qclass != C_IN)
|
||||
{
|
||||
auth = 0;
|
||||
out_of_zone = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -159,6 +160,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
|
||||
if (!zone)
|
||||
{
|
||||
out_of_zone = 1;
|
||||
auth = 0;
|
||||
continue;
|
||||
}
|
||||
@@ -253,6 +255,17 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
|
||||
} while ((crecp = cache_find_by_addr(crecp, &addr, now, flag)));
|
||||
|
||||
if (!found && is_rev_synth(flag, &addr, name) && (local_query || in_zone(zone, name, NULL)))
|
||||
{
|
||||
log_query(F_CONFIG | F_REVERSE | flag, name, &addr, NULL);
|
||||
found = 1;
|
||||
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
daemon->auth_ttl, NULL,
|
||||
T_PTR, C_IN, "d", name))
|
||||
anscount++;
|
||||
}
|
||||
|
||||
if (found)
|
||||
nxdomain = 0;
|
||||
else
|
||||
@@ -273,6 +286,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
|
||||
if (!zone)
|
||||
{
|
||||
out_of_zone = 1;
|
||||
auth = 0;
|
||||
continue;
|
||||
}
|
||||
@@ -400,6 +414,17 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
anscount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found && is_name_synthetic(flag, name, &addr) )
|
||||
{
|
||||
found = 1;
|
||||
nxdomain = 0;
|
||||
|
||||
log_query(F_FORWARD | F_CONFIG | flag, name, &addr, NULL);
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
daemon->auth_ttl, NULL, qtype, C_IN, qtype == T_A ? "4" : "6", &addr))
|
||||
anscount++;
|
||||
}
|
||||
|
||||
if (!cut)
|
||||
{
|
||||
@@ -855,10 +880,22 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
SET_RCODE(header, NXDOMAIN);
|
||||
else
|
||||
SET_RCODE(header, NOERROR); /* no error */
|
||||
|
||||
header->ancount = htons(anscount);
|
||||
header->nscount = htons(authcount);
|
||||
header->arcount = htons(0);
|
||||
|
||||
if (!local_query && out_of_zone)
|
||||
{
|
||||
SET_RCODE(header, REFUSED);
|
||||
header->ancount = htons(0);
|
||||
header->nscount = htons(0);
|
||||
addr.log.rcode = REFUSED;
|
||||
addr.log.ede = EDE_NOT_AUTH;
|
||||
log_query(F_UPSTREAM | F_RCODE, "error", &addr, NULL);
|
||||
return resize_packet(header, ansp - (unsigned char *)header, NULL, 0);
|
||||
}
|
||||
|
||||
/* Advertise our packet size limit in our reply */
|
||||
if (have_pseudoheader)
|
||||
return add_pseudoheader(header, ansp - (unsigned char *)header, (unsigned char *)limit, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0);
|
||||
@@ -867,6 +904,3 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -174,4 +174,3 @@ struct blockdata *blockdata_read(int fd, size_t len)
|
||||
{
|
||||
return blockdata_alloc_real(fd, NULL, len);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -440,5 +440,3 @@ void route_sock(void)
|
||||
}
|
||||
|
||||
#endif /* HAVE_BSD_NETWORK */
|
||||
|
||||
|
||||
|
||||
105
src/cache.c
105
src/cache.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -1602,21 +1602,18 @@ int cache_make_stat(struct txt_record *t)
|
||||
case TXT_STAT_SERVERS:
|
||||
/* sum counts from different records for same server */
|
||||
for (serv = daemon->servers; serv; serv = serv->next)
|
||||
serv->flags &= ~SERV_COUNTED;
|
||||
serv->flags &= ~SERV_MARK;
|
||||
|
||||
for (serv = daemon->servers; serv; serv = serv->next)
|
||||
if (!(serv->flags &
|
||||
(SERV_NO_ADDR | SERV_LITERAL_ADDRESS | SERV_COUNTED | SERV_USE_RESOLV | SERV_NO_REBIND)))
|
||||
if (!(serv->flags & SERV_MARK))
|
||||
{
|
||||
char *new, *lenp;
|
||||
int port, newlen, bytes_avail, bytes_needed;
|
||||
unsigned int queries = 0, failed_queries = 0;
|
||||
for (serv1 = serv; serv1; serv1 = serv1->next)
|
||||
if (!(serv1->flags &
|
||||
(SERV_NO_ADDR | SERV_LITERAL_ADDRESS | SERV_COUNTED | SERV_USE_RESOLV | SERV_NO_REBIND)) &&
|
||||
sockaddr_isequal(&serv->addr, &serv1->addr))
|
||||
if (!(serv1->flags & SERV_MARK) && sockaddr_isequal(&serv->addr, &serv1->addr))
|
||||
{
|
||||
serv1->flags |= SERV_COUNTED;
|
||||
serv1->flags |= SERV_MARK;
|
||||
queries += serv1->queries;
|
||||
failed_queries += serv1->failed_queries;
|
||||
}
|
||||
@@ -1644,6 +1641,7 @@ int cache_make_stat(struct txt_record *t)
|
||||
}
|
||||
t->txt = (unsigned char *)buff;
|
||||
t->len = p - buff;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1686,27 +1684,24 @@ void dump_cache(time_t now)
|
||||
|
||||
/* sum counts from different records for same server */
|
||||
for (serv = daemon->servers; serv; serv = serv->next)
|
||||
serv->flags &= ~SERV_COUNTED;
|
||||
serv->flags &= ~SERV_MARK;
|
||||
|
||||
for (serv = daemon->servers; serv; serv = serv->next)
|
||||
if (!(serv->flags &
|
||||
(SERV_NO_ADDR | SERV_LITERAL_ADDRESS | SERV_COUNTED | SERV_USE_RESOLV | SERV_NO_REBIND)))
|
||||
if (!(serv->flags & SERV_MARK))
|
||||
{
|
||||
int port;
|
||||
unsigned int queries = 0, failed_queries = 0;
|
||||
for (serv1 = serv; serv1; serv1 = serv1->next)
|
||||
if (!(serv1->flags &
|
||||
(SERV_NO_ADDR | SERV_LITERAL_ADDRESS | SERV_COUNTED | SERV_USE_RESOLV | SERV_NO_REBIND)) &&
|
||||
sockaddr_isequal(&serv->addr, &serv1->addr))
|
||||
if (!(serv1->flags & SERV_MARK) && sockaddr_isequal(&serv->addr, &serv1->addr))
|
||||
{
|
||||
serv1->flags |= SERV_COUNTED;
|
||||
serv1->flags |= SERV_MARK;
|
||||
queries += serv1->queries;
|
||||
failed_queries += serv1->failed_queries;
|
||||
}
|
||||
port = prettyprint_addr(&serv->addr, daemon->addrbuff);
|
||||
my_syslog(LOG_INFO, _("server %s#%d: queries sent %u, retried or failed %u"), daemon->addrbuff, port, queries, failed_queries);
|
||||
}
|
||||
|
||||
|
||||
if (option_bool(OPT_DEBUG) || option_bool(OPT_LOG))
|
||||
{
|
||||
struct crec *cache ;
|
||||
@@ -1867,10 +1862,44 @@ char *querystr(char *desc, unsigned short type)
|
||||
return buff ? buff : "";
|
||||
}
|
||||
|
||||
static char *edestr(int ede)
|
||||
{
|
||||
switch (ede)
|
||||
{
|
||||
case EDE_OTHER: return "other";
|
||||
case EDE_USUPDNSKEY: return "unsupported DNSKEY algorithm";
|
||||
case EDE_USUPDS: return "unsupported DS digest";
|
||||
case EDE_STALE: return "stale answer";
|
||||
case EDE_FORGED: return "forged";
|
||||
case EDE_DNSSEC_IND: return "DNSSEC indeterminate";
|
||||
case EDE_DNSSEC_BOGUS: return "DNSSEC bogus";
|
||||
case EDE_SIG_EXP: return "DNSSEC signature expired";
|
||||
case EDE_SIG_NYV: return "DNSSEC sig not yet valid";
|
||||
case EDE_NO_DNSKEY: return "DNSKEY missing";
|
||||
case EDE_NO_RRSIG: return "RRSIG missing";
|
||||
case EDE_NO_ZONEKEY: return "no zone key bit set";
|
||||
case EDE_NO_NSEC: return "NSEC(3) missing";
|
||||
case EDE_CACHED_ERR: return "cached error";
|
||||
case EDE_NOT_READY: return "not ready";
|
||||
case EDE_BLOCKED: return "blocked";
|
||||
case EDE_CENSORED: return "censored";
|
||||
case EDE_FILTERED: return "filtered";
|
||||
case EDE_PROHIBITED: return "prohibited";
|
||||
case EDE_STALE_NXD: return "stale NXDOMAIN";
|
||||
case EDE_NOT_AUTH: return "not authoritative";
|
||||
case EDE_NOT_SUP: return "not supported";
|
||||
case EDE_NO_AUTH: return "no reachable authority";
|
||||
case EDE_NETERR: return "network error";
|
||||
case EDE_INVALID_DATA: return "invalid data";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg)
|
||||
{
|
||||
char *source, *dest = daemon->addrbuff;
|
||||
char *verb = "is";
|
||||
char *extra = "";
|
||||
|
||||
if (!option_bool(OPT_LOG))
|
||||
return;
|
||||
@@ -1885,14 +1914,20 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg)
|
||||
{
|
||||
unsigned int rcode = addr->log.rcode;
|
||||
|
||||
if (rcode == SERVFAIL)
|
||||
dest = "SERVFAIL";
|
||||
else if (rcode == REFUSED)
|
||||
dest = "REFUSED";
|
||||
else if (rcode == NOTIMP)
|
||||
dest = "not implemented";
|
||||
else
|
||||
sprintf(daemon->addrbuff, "%u", rcode);
|
||||
if (rcode == SERVFAIL)
|
||||
dest = "SERVFAIL";
|
||||
else if (rcode == REFUSED)
|
||||
dest = "REFUSED";
|
||||
else if (rcode == NOTIMP)
|
||||
dest = "not implemented";
|
||||
else
|
||||
sprintf(daemon->addrbuff, "%u", rcode);
|
||||
|
||||
if (addr->log.ede != EDE_UNSET)
|
||||
{
|
||||
extra = daemon->addrbuff;
|
||||
sprintf(extra, " (EDE: %s)", edestr(addr->log.ede));
|
||||
}
|
||||
}
|
||||
else
|
||||
inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
|
||||
@@ -1938,7 +1973,15 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg)
|
||||
else if (flags & F_UPSTREAM)
|
||||
source = "reply";
|
||||
else if (flags & F_SECSTAT)
|
||||
source = "validation";
|
||||
{
|
||||
if (addr && addr->log.ede != EDE_UNSET && option_bool(OPT_EXTRALOG))
|
||||
{
|
||||
extra = daemon->addrbuff;
|
||||
sprintf(extra, " (EDE: %s)", edestr(addr->log.ede));
|
||||
}
|
||||
source = "validation";
|
||||
dest = arg;
|
||||
}
|
||||
else if (flags & F_AUTH)
|
||||
source = "auth";
|
||||
else if (flags & F_SERVER)
|
||||
@@ -1971,14 +2014,14 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg)
|
||||
|
||||
if (option_bool(OPT_EXTRALOG))
|
||||
{
|
||||
int port = prettyprint_addr(daemon->log_source_addr, daemon->addrbuff2);
|
||||
if (flags & F_NOEXTRA)
|
||||
my_syslog(LOG_INFO, "* %s/%u %s %s %s %s", daemon->addrbuff2, port, source, name, verb, dest);
|
||||
my_syslog(LOG_INFO, "%u %s %s %s %s%s", daemon->log_display_id, source, name, verb, dest, extra);
|
||||
else
|
||||
my_syslog(LOG_INFO, "%u %s/%u %s %s %s %s", daemon->log_display_id, daemon->addrbuff2, port, source, name, verb, dest);
|
||||
{
|
||||
int port = prettyprint_addr(daemon->log_source_addr, daemon->addrbuff2);
|
||||
my_syslog(LOG_INFO, "%u %s/%u %s %s %s %s%s", daemon->log_display_id, daemon->addrbuff2, port, source, name, verb, dest, extra);
|
||||
}
|
||||
}
|
||||
else
|
||||
my_syslog(LOG_INFO, "%s %s %s %s", source, name, verb, dest);
|
||||
my_syslog(LOG_INFO, "%s %s %s %s%s", source, name, verb, dest, extra);
|
||||
}
|
||||
|
||||
|
||||
|
||||
21
src/config.h
21
src/config.h
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -23,13 +23,12 @@
|
||||
#define SAFE_PKTSZ 1280 /* "go anywhere" UDP packet size */
|
||||
#define KEYBLOCK_LEN 40 /* choose to minimise fragmentation when storing DNSSEC keys */
|
||||
#define DNSSEC_WORK 50 /* Max number of queries to validate one question */
|
||||
#define TIMEOUT 10 /* drop UDP queries after TIMEOUT seconds */
|
||||
#define TIMEOUT 10 /* drop UDP queries after TIMEOUT seconds */
|
||||
#define FORWARD_TEST 50 /* try all servers every 50 queries */
|
||||
#define FORWARD_TIME 20 /* or 20 seconds */
|
||||
#define UDP_TEST_TIME 60 /* How often to reset our idea of max packet size. */
|
||||
#define SERVERS_LOGGED 30 /* Only log this many servers when logging state */
|
||||
#define LOCALS_LOGGED 8 /* Only log this many local addresses when logging state */
|
||||
#define RANDOM_SOCKS 64 /* max simultaneous random ports */
|
||||
#define LEASE_RETRY 60 /* on error, retry writing leasefile after LEASE_RETRY seconds */
|
||||
#define CACHESIZ 150 /* default cache size */
|
||||
#define TTL_FLOOR_LIMIT 3600 /* don't allow --min-cache-ttl to raise TTL above this under any circumstances */
|
||||
@@ -120,8 +119,8 @@ HAVE_AUTH
|
||||
define this to include the facility to act as an authoritative DNS
|
||||
server for one or more zones.
|
||||
|
||||
HAVE_NETTLEHASH
|
||||
include just hash function from nettle, but no DNSSEC.
|
||||
HAVE_CRYPTOHASH
|
||||
include just hash function from crypto library, but no DNSSEC.
|
||||
|
||||
HAVE_DNSSEC
|
||||
include DNSSEC validator.
|
||||
@@ -144,6 +143,7 @@ NO_SCRIPT
|
||||
NO_LARGEFILE
|
||||
NO_AUTH
|
||||
NO_DUMPFILE
|
||||
NO_LOOP
|
||||
NO_INOTIFY
|
||||
these are available to explicitly disable compile time options which would
|
||||
otherwise be enabled automatically or which are enabled by default
|
||||
@@ -190,7 +190,7 @@ RESOLVFILE
|
||||
/* #define HAVE_IDN */
|
||||
/* #define HAVE_LIBIDN2 */
|
||||
/* #define HAVE_CONNTRACK */
|
||||
/* #define HAVE_NETTLEHASH */
|
||||
/* #define HAVE_CRYPTOHASH */
|
||||
/* #define HAVE_DNSSEC */
|
||||
|
||||
|
||||
@@ -424,10 +424,10 @@ static char *compile_opts =
|
||||
"no-"
|
||||
#endif
|
||||
"auth "
|
||||
#if !defined(HAVE_NETTLEHASH) && !defined(HAVE_DNSSEC)
|
||||
#if !defined(HAVE_CRYPTOHASH) && !defined(HAVE_DNSSEC)
|
||||
"no-"
|
||||
#endif
|
||||
"nettlehash "
|
||||
"cryptohash "
|
||||
#ifndef HAVE_DNSSEC
|
||||
"no-"
|
||||
#endif
|
||||
@@ -448,7 +448,4 @@ static char *compile_opts =
|
||||
#endif
|
||||
"dumpfile";
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#endif /* defined(HAVE_DHCP) */
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -82,7 +82,4 @@ static int callback(enum nf_conntrack_msg_type type, struct nf_conntrack *ct, vo
|
||||
return NFCT_CB_CONTINUE;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#endif /* HAVE_CONNTRACK */
|
||||
|
||||
111
src/crypto.c
111
src/crypto.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -16,21 +16,34 @@
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
#if defined(HAVE_DNSSEC) || defined(HAVE_CRYPTOHASH)
|
||||
|
||||
/* Minimal version of nettle */
|
||||
|
||||
/* bignum.h includes version.h and works on
|
||||
earlier releases of nettle which don't have version.h */
|
||||
#include <nettle/bignum.h>
|
||||
#if !defined(NETTLE_VERSION_MAJOR)
|
||||
# define NETTLE_VERSION_MAJOR 2
|
||||
# define NETTLE_VERSION_MINOR 0
|
||||
#endif
|
||||
#define MIN_VERSION(major, minor) ((NETTLE_VERSION_MAJOR == (major) && NETTLE_VERSION_MINOR >= (minor)) || \
|
||||
(NETTLE_VERSION_MAJOR > (major)))
|
||||
|
||||
#endif /* defined(HAVE_DNSSEC) || defined(HAVE_CRYPTOHASH) */
|
||||
|
||||
#if defined(HAVE_DNSSEC)
|
||||
#include <nettle/rsa.h>
|
||||
#include <nettle/ecdsa.h>
|
||||
#include <nettle/ecc-curve.h>
|
||||
#if MIN_VERSION(3, 1)
|
||||
#include <nettle/eddsa.h>
|
||||
#if NETTLE_VERSION_MAJOR == 3 && NETTLE_VERSION_MINOR >= 6
|
||||
#endif
|
||||
#if MIN_VERSION(3, 6)
|
||||
# include <nettle/gostdsa.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_DNSSEC) || defined(HAVE_NETTLEHASH)
|
||||
#include <nettle/nettle-meta.h>
|
||||
#include <nettle/bignum.h>
|
||||
|
||||
#if MIN_VERSION(3, 1)
|
||||
/* Implement a "hash-function" to the nettle API, which simply returns
|
||||
the input data, concatenated into a single, statically maintained, buffer.
|
||||
|
||||
@@ -84,7 +97,6 @@ static void null_hash_update(void *ctxv, size_t length, const uint8_t *src)
|
||||
ctx->len += length;
|
||||
}
|
||||
|
||||
|
||||
static void null_hash_digest(void *ctx, size_t length, uint8_t *dst)
|
||||
{
|
||||
(void)length;
|
||||
@@ -103,35 +115,7 @@ static struct nettle_hash null_hash = {
|
||||
(nettle_hash_digest_func *) null_hash_digest
|
||||
};
|
||||
|
||||
/* Find pointer to correct hash function in nettle library */
|
||||
const struct nettle_hash *hash_find(char *name)
|
||||
{
|
||||
if (!name)
|
||||
return NULL;
|
||||
|
||||
/* We provide a "null" hash which returns the input data as digest. */
|
||||
if (strcmp(null_hash.name, name) == 0)
|
||||
return &null_hash;
|
||||
|
||||
/* libnettle >= 3.4 provides nettle_lookup_hash() which avoids nasty ABI
|
||||
incompatibilities if sizeof(nettle_hashes) changes between library
|
||||
versions. It also #defines nettle_hashes, so use that to tell
|
||||
if we have the new facilities. */
|
||||
|
||||
#ifdef nettle_hashes
|
||||
return nettle_lookup_hash(name);
|
||||
#else
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; nettle_hashes[i]; i++)
|
||||
if (strcmp(nettle_hashes[i]->name, name) == 0)
|
||||
return nettle_hashes[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
#endif /* MIN_VERSION(3, 1) */
|
||||
|
||||
/* expand ctx and digest memory allocations if necessary and init hash function */
|
||||
int hash_init(const struct nettle_hash *hash, void **ctxp, unsigned char **digestp)
|
||||
@@ -171,10 +155,6 @@ int hash_init(const struct nettle_hash *hash, void **ctxp, unsigned char **diges
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
|
||||
static int dnsmasq_rsa_verify(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len,
|
||||
unsigned char *digest, size_t digest_len, int algo)
|
||||
{
|
||||
@@ -238,7 +218,7 @@ static int dnsmasq_ecdsa_verify(struct blockdata *key_data, unsigned int key_len
|
||||
static struct ecc_point *key_256 = NULL, *key_384 = NULL;
|
||||
static mpz_t x, y;
|
||||
static struct dsa_signature *sig_struct;
|
||||
#if NETTLE_VERSION_MAJOR == 3 && NETTLE_VERSION_MINOR < 4
|
||||
#if !MIN_VERSION(3, 4)
|
||||
#define nettle_get_secp_256r1() (&nettle_secp_256r1)
|
||||
#define nettle_get_secp_384r1() (&nettle_secp_384r1)
|
||||
#endif
|
||||
@@ -301,7 +281,7 @@ static int dnsmasq_ecdsa_verify(struct blockdata *key_data, unsigned int key_len
|
||||
return nettle_ecdsa_verify(key, digest_len, digest, sig_struct);
|
||||
}
|
||||
|
||||
#if NETTLE_VERSION_MAJOR == 3 && NETTLE_VERSION_MINOR >= 6
|
||||
#if MIN_VERSION(3, 6)
|
||||
static int dnsmasq_gostdsa_verify(struct blockdata *key_data, unsigned int key_len,
|
||||
unsigned char *sig, size_t sig_len,
|
||||
unsigned char *digest, size_t digest_len, int algo)
|
||||
@@ -342,6 +322,7 @@ static int dnsmasq_gostdsa_verify(struct blockdata *key_data, unsigned int key_l
|
||||
}
|
||||
#endif
|
||||
|
||||
#if MIN_VERSION(3, 1)
|
||||
static int dnsmasq_eddsa_verify(struct blockdata *key_data, unsigned int key_len,
|
||||
unsigned char *sig, size_t sig_len,
|
||||
unsigned char *digest, size_t digest_len, int algo)
|
||||
@@ -368,7 +349,7 @@ static int dnsmasq_eddsa_verify(struct blockdata *key_data, unsigned int key_len
|
||||
((struct null_hash_digest *)digest)->buff,
|
||||
sig);
|
||||
|
||||
#if NETTLE_VERSION_MAJOR == 3 && NETTLE_VERSION_MINOR >= 6
|
||||
#if MIN_VERSION(3, 6)
|
||||
case 16:
|
||||
if (key_len != ED448_KEY_SIZE ||
|
||||
sig_len != ED448_SIGNATURE_SIZE)
|
||||
@@ -384,6 +365,7 @@ static int dnsmasq_eddsa_verify(struct blockdata *key_data, unsigned int key_len
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int (*verify_func(int algo))(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len,
|
||||
unsigned char *digest, size_t digest_len, int algo)
|
||||
@@ -399,16 +381,18 @@ static int (*verify_func(int algo))(struct blockdata *key_data, unsigned int key
|
||||
case 5: case 7: case 8: case 10:
|
||||
return dnsmasq_rsa_verify;
|
||||
|
||||
#if NETTLE_VERSION_MAJOR == 3 && NETTLE_VERSION_MINOR >= 6
|
||||
#if MIN_VERSION(3, 6)
|
||||
case 12:
|
||||
return dnsmasq_gostdsa_verify;
|
||||
#endif
|
||||
|
||||
case 13: case 14:
|
||||
return dnsmasq_ecdsa_verify;
|
||||
|
||||
|
||||
#if MIN_VERSION(3, 1)
|
||||
case 15: case 16:
|
||||
return dnsmasq_eddsa_verify;
|
||||
#endif
|
||||
}
|
||||
|
||||
return NULL;
|
||||
@@ -479,4 +463,37 @@ char *nsec3_digest_name(int digest)
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* defined(HAVE_DNSSEC) */
|
||||
|
||||
#if defined(HAVE_DNSSEC) || defined(HAVE_CRYPTOHASH)
|
||||
/* Find pointer to correct hash function in nettle library */
|
||||
const struct nettle_hash *hash_find(char *name)
|
||||
{
|
||||
if (!name)
|
||||
return NULL;
|
||||
|
||||
#if MIN_VERSION(3,1) && defined(HAVE_DNSSEC)
|
||||
/* We provide a "null" hash which returns the input data as digest. */
|
||||
if (strcmp(null_hash.name, name) == 0)
|
||||
return &null_hash;
|
||||
#endif
|
||||
|
||||
/* libnettle >= 3.4 provides nettle_lookup_hash() which avoids nasty ABI
|
||||
incompatibilities if sizeof(nettle_hashes) changes between library
|
||||
versions. */
|
||||
#if MIN_VERSION(3, 4)
|
||||
return nettle_lookup_hash(name);
|
||||
#else
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; nettle_hashes[i]; i++)
|
||||
if (strcmp(nettle_hashes[i]->name, name) == 0)
|
||||
return nettle_hashes[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* defined(HAVE_DNSSEC) || defined(HAVE_CRYPTOHASH) */
|
||||
|
||||
20
src/dbus.c
20
src/dbus.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -215,7 +215,7 @@ static void dbus_read_servers(DBusMessage *message)
|
||||
domain = NULL;
|
||||
|
||||
if (!skip)
|
||||
add_update_server(SERV_FROM_DBUS, &addr, &source_addr, NULL, domain);
|
||||
add_update_server(SERV_FROM_DBUS, &addr, &source_addr, NULL, domain, NULL);
|
||||
|
||||
} while (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING);
|
||||
}
|
||||
@@ -276,7 +276,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
|
||||
{
|
||||
const char *str = NULL;
|
||||
union mysockaddr addr, source_addr;
|
||||
int flags = 0;
|
||||
u16 flags = 0;
|
||||
char interface[IF_NAMESIZE];
|
||||
char *str_addr, *str_domain = NULL;
|
||||
|
||||
@@ -361,10 +361,6 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
|
||||
strcpy(str_addr, str);
|
||||
}
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
memset(&source_addr, 0, sizeof(source_addr));
|
||||
memset(&interface, 0, sizeof(interface));
|
||||
|
||||
/* parse the IP address */
|
||||
if ((addr_err = parse_server(str_addr, &addr, &source_addr, (char *) &interface, &flags)))
|
||||
{
|
||||
@@ -377,7 +373,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
|
||||
/* 0.0.0.0 for server address == NULL, for Dbus */
|
||||
if (addr.in.sin_family == AF_INET &&
|
||||
addr.in.sin_addr.s_addr == 0)
|
||||
flags |= SERV_NO_ADDR;
|
||||
flags |= SERV_LITERAL_ADDRESS;
|
||||
|
||||
if (strings)
|
||||
{
|
||||
@@ -392,7 +388,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
|
||||
else
|
||||
p = NULL;
|
||||
|
||||
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str_domain);
|
||||
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str_domain, NULL);
|
||||
} while ((str_domain = p));
|
||||
}
|
||||
else
|
||||
@@ -407,7 +403,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
|
||||
dbus_message_iter_get_basic(&string_iter, &str);
|
||||
dbus_message_iter_next (&string_iter);
|
||||
|
||||
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str);
|
||||
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str, NULL);
|
||||
} while (dbus_message_iter_get_arg_type(&string_iter) == DBUS_TYPE_STRING);
|
||||
}
|
||||
|
||||
@@ -416,7 +412,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
|
||||
}
|
||||
|
||||
cleanup_servers();
|
||||
|
||||
|
||||
if (dup)
|
||||
free(dup);
|
||||
|
||||
@@ -715,7 +711,7 @@ DBusHandlerResult message_handler(DBusConnection *connection,
|
||||
if (new_servers)
|
||||
{
|
||||
my_syslog(LOG_INFO, _("setting upstream servers from DBus"));
|
||||
check_servers();
|
||||
check_servers(0);
|
||||
if (option_bool(OPT_RELOAD))
|
||||
clear_cache = 1;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -280,31 +280,29 @@ static int is_config_in_context(struct dhcp_context *context, struct dhcp_config
|
||||
{
|
||||
if (!context) /* called via find_config() from lease_update_from_configs() */
|
||||
return 1;
|
||||
|
||||
if (!(config->flags & (CONFIG_ADDR | CONFIG_ADDR6)))
|
||||
return 1;
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
if (context->flags & CONTEXT_V6)
|
||||
{
|
||||
struct addrlist *addr_list;
|
||||
|
||||
if (!(config->flags & CONFIG_ADDR6))
|
||||
return 1;
|
||||
|
||||
for (; context; context = context->current)
|
||||
for (addr_list = config->addr6; addr_list; addr_list = addr_list->next)
|
||||
{
|
||||
if ((addr_list->flags & ADDRLIST_WILDCARD) && context->prefix == 64)
|
||||
return 1;
|
||||
|
||||
if (is_same_net6(&addr_list->addr.addr6, &context->start6, context->prefix))
|
||||
return 1;
|
||||
}
|
||||
if (config->flags & CONFIG_ADDR6)
|
||||
for (; context; context = context->current)
|
||||
for (addr_list = config->addr6; addr_list; addr_list = addr_list->next)
|
||||
{
|
||||
if ((addr_list->flags & ADDRLIST_WILDCARD) && context->prefix == 64)
|
||||
return 1;
|
||||
|
||||
if (is_same_net6(&addr_list->addr.addr6, &context->start6, context->prefix))
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
if (!(config->flags & CONFIG_ADDR))
|
||||
return 1;
|
||||
|
||||
for (; context; context = context->current)
|
||||
if ((config->flags & CONFIG_ADDR) && is_same_net(config->addr, context->start, context->netmask))
|
||||
return 1;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -72,4 +72,3 @@
|
||||
#define DHCP6NOBINDING 3
|
||||
#define DHCP6NOTONLINK 4
|
||||
#define DHCP6USEMULTI 5
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -825,6 +825,4 @@ void dhcp_construct_contexts(time_t now)
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* HAVE_DHCP6 */
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -80,8 +80,41 @@
|
||||
|
||||
#define EDNS0_OPTION_MAC 65001 /* dyndns.org temporary assignment */
|
||||
#define EDNS0_OPTION_CLIENT_SUBNET 8 /* IANA */
|
||||
#define EDNS0_OPTION_EDE 15 /* IANA - RFC 8914 */
|
||||
#define EDNS0_OPTION_NOMDEVICEID 65073 /* Nominum temporary assignment */
|
||||
#define EDNS0_OPTION_NOMCPEID 65074 /* Nominum temporary assignment */
|
||||
#define EDNS0_OPTION_UMBRELLA 20292 /* Cisco Umbrella temporary assignment */
|
||||
|
||||
/* RFC-8914 extended errors, negative values are our definitions */
|
||||
#define EDE_UNSET -1 /* No extended DNS error available */
|
||||
#define EDE_OTHER 0 /* Other */
|
||||
#define EDE_USUPDNSKEY 1 /* Unsupported DNSKEY algo */
|
||||
#define EDE_USUPDS 2 /* Unsupported DS Digest */
|
||||
#define EDE_STALE 3 /* Stale answer */
|
||||
#define EDE_FORGED 4 /* Forged answer */
|
||||
#define EDE_DNSSEC_IND 5 /* DNSSEC Indeterminate */
|
||||
#define EDE_DNSSEC_BOGUS 6 /* DNSSEC Bogus */
|
||||
#define EDE_SIG_EXP 7 /* Signature Expired */
|
||||
#define EDE_SIG_NYV 8 /* Signature Not Yet Valid */
|
||||
#define EDE_NO_DNSKEY 9 /* DNSKEY missing */
|
||||
#define EDE_NO_RRSIG 10 /* RRSIGs missing */
|
||||
#define EDE_NO_ZONEKEY 11 /* No Zone Key Bit Set */
|
||||
#define EDE_NO_NSEC 12 /* NSEC Missing */
|
||||
#define EDE_CACHED_ERR 13 /* Cached Error */
|
||||
#define EDE_NOT_READY 14 /* Not Ready */
|
||||
#define EDE_BLOCKED 15 /* Blocked */
|
||||
#define EDE_CENSORED 16 /* Censored */
|
||||
#define EDE_FILTERED 17 /* Filtered */
|
||||
#define EDE_PROHIBITED 18 /* Prohibited */
|
||||
#define EDE_STALE_NXD 19 /* Stale NXDOMAIN */
|
||||
#define EDE_NOT_AUTH 20 /* Not Authoritative */
|
||||
#define EDE_NOT_SUP 21 /* Not Supported */
|
||||
#define EDE_NO_AUTH 22 /* No Reachable Authority */
|
||||
#define EDE_NETERR 23 /* Network error */
|
||||
#define EDE_INVALID_DATA 24 /* Invalid Data */
|
||||
|
||||
|
||||
|
||||
|
||||
struct dns_header {
|
||||
u16 id;
|
||||
|
||||
203
src/dnsmasq.c
203
src/dnsmasq.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -24,7 +24,7 @@ struct daemon *daemon;
|
||||
static volatile pid_t pid = 0;
|
||||
static volatile int pipewrite;
|
||||
|
||||
static int set_dns_listeners(time_t now);
|
||||
static void set_dns_listeners(void);
|
||||
static void check_dns_listeners(time_t now);
|
||||
static void sig_handler(int sig);
|
||||
static void async_event(int pipe, time_t now);
|
||||
@@ -135,6 +135,13 @@ int main (int argc, char **argv)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS)
|
||||
/* CONNTRACK UBUS code uses this buffer, so if not allocated above,
|
||||
we need to allocate it here. */
|
||||
if (option_bool(OPT_CMARK_ALST_EN) && !daemon->workspacename)
|
||||
daemon->workspacename = safe_malloc(MAXDNAME);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DHCP
|
||||
if (!daemon->lease_file)
|
||||
{
|
||||
@@ -237,9 +244,16 @@ int main (int argc, char **argv)
|
||||
die(_("Ubus not available: set HAVE_UBUS in src/config.h"), NULL, EC_BADCONF);
|
||||
#endif
|
||||
|
||||
/* Handle only one of min_port/max_port being set. */
|
||||
if (daemon->min_port != 0 && daemon->max_port == 0)
|
||||
daemon->max_port = MAX_PORT;
|
||||
|
||||
if (daemon->max_port != 0 && daemon->min_port == 0)
|
||||
daemon->min_port = MIN_PORT;
|
||||
|
||||
if (daemon->max_port < daemon->min_port)
|
||||
die(_("max_port cannot be smaller than min_port"), NULL, EC_BADCONF);
|
||||
|
||||
|
||||
now = dnsmasq_time();
|
||||
|
||||
if (daemon->auth_zones)
|
||||
@@ -390,8 +404,16 @@ int main (int argc, char **argv)
|
||||
if (daemon->port != 0)
|
||||
{
|
||||
cache_init();
|
||||
|
||||
blockdata_init();
|
||||
hash_questions_init();
|
||||
|
||||
/* Scale random socket pool by ftabsize, but
|
||||
limit it based on available fds. */
|
||||
daemon->numrrand = daemon->ftabsize/2;
|
||||
if (daemon->numrrand > max_fd/3)
|
||||
daemon->numrrand = max_fd/3;
|
||||
/* safe_malloc returns zero'd memory */
|
||||
daemon->randomsocks = safe_malloc(daemon->numrrand * sizeof(struct randfd));
|
||||
}
|
||||
|
||||
#ifdef HAVE_INOTIFY
|
||||
@@ -415,8 +437,6 @@ int main (int argc, char **argv)
|
||||
#ifdef HAVE_DBUS
|
||||
{
|
||||
char *err;
|
||||
daemon->dbus = NULL;
|
||||
daemon->watches = NULL;
|
||||
if ((err = dbus_init()))
|
||||
die(_("DBus error: %s"), err, EC_MISC);
|
||||
}
|
||||
@@ -427,8 +447,9 @@ int main (int argc, char **argv)
|
||||
if (option_bool(OPT_UBUS))
|
||||
#ifdef HAVE_UBUS
|
||||
{
|
||||
daemon->ubus = NULL;
|
||||
ubus_init();
|
||||
char *err;
|
||||
if ((err = ubus_init()))
|
||||
die(_("UBus error: %s"), err, EC_MISC);
|
||||
}
|
||||
#else
|
||||
die(_("UBus not available: set HAVE_UBUS in src/config.h"), NULL, EC_BADCONF);
|
||||
@@ -980,7 +1001,7 @@ int main (int argc, char **argv)
|
||||
a single file will be sent to may clients (the file only needs
|
||||
one fd). */
|
||||
|
||||
max_fd -= 30; /* use other than TFTP */
|
||||
max_fd -= 30 + daemon->numrrand; /* use other than TFTP */
|
||||
|
||||
if (max_fd < 0)
|
||||
max_fd = 5;
|
||||
@@ -1010,7 +1031,7 @@ int main (int argc, char **argv)
|
||||
close(err_pipe[1]);
|
||||
|
||||
if (daemon->port != 0)
|
||||
check_servers();
|
||||
check_servers(0);
|
||||
|
||||
pid = getpid();
|
||||
|
||||
@@ -1025,16 +1046,10 @@ int main (int argc, char **argv)
|
||||
|
||||
while (1)
|
||||
{
|
||||
int t, timeout = -1;
|
||||
int timeout = -1;
|
||||
|
||||
poll_reset();
|
||||
|
||||
/* if we are out of resources, find how long we have to wait
|
||||
for some to come free, we'll loop around then and restart
|
||||
listening for queries */
|
||||
if ((t = set_dns_listeners(now)) != 0)
|
||||
timeout = t * 1000;
|
||||
|
||||
/* Whilst polling for the dbus, or doing a tftp transfer, wake every quarter second */
|
||||
if (daemon->tftp_trans ||
|
||||
(option_bool(OPT_DBUS) && !daemon->dbus))
|
||||
@@ -1044,15 +1059,18 @@ int main (int argc, char **argv)
|
||||
else if (is_dad_listeners())
|
||||
timeout = 1000;
|
||||
|
||||
#ifdef HAVE_DBUS
|
||||
set_dbus_listeners();
|
||||
#endif
|
||||
set_dns_listeners();
|
||||
|
||||
#ifdef HAVE_DBUS
|
||||
if (option_bool(OPT_DBUS))
|
||||
set_dbus_listeners();
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_UBUS
|
||||
if (option_bool(OPT_UBUS))
|
||||
set_ubus_listeners();
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef HAVE_DHCP
|
||||
if (daemon->dhcp || daemon->relay4)
|
||||
{
|
||||
@@ -1172,28 +1190,44 @@ int main (int argc, char **argv)
|
||||
|
||||
#ifdef HAVE_DBUS
|
||||
/* if we didn't create a DBus connection, retry now. */
|
||||
if (option_bool(OPT_DBUS) && !daemon->dbus)
|
||||
if (option_bool(OPT_DBUS))
|
||||
{
|
||||
char *err;
|
||||
if ((err = dbus_init()))
|
||||
my_syslog(LOG_WARNING, _("DBus error: %s"), err);
|
||||
if (daemon->dbus)
|
||||
my_syslog(LOG_INFO, _("connected to system DBus"));
|
||||
if (!daemon->dbus)
|
||||
{
|
||||
char *err = dbus_init();
|
||||
|
||||
if (daemon->dbus)
|
||||
my_syslog(LOG_INFO, _("connected to system DBus"));
|
||||
else if (err)
|
||||
{
|
||||
my_syslog(LOG_ERR, _("DBus error: %s"), err);
|
||||
reset_option_bool(OPT_DBUS); /* fatal error, stop trying. */
|
||||
}
|
||||
}
|
||||
|
||||
check_dbus_listeners();
|
||||
}
|
||||
check_dbus_listeners();
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_UBUS
|
||||
/* if we didn't create a UBus connection, retry now. */
|
||||
if (option_bool(OPT_UBUS))
|
||||
{
|
||||
/* if we didn't create a UBus connection, retry now. */
|
||||
if (!daemon->ubus)
|
||||
{
|
||||
ubus_init();
|
||||
}
|
||||
{
|
||||
if (!daemon->ubus)
|
||||
{
|
||||
char *err = ubus_init();
|
||||
|
||||
check_ubus_listeners();
|
||||
}
|
||||
if (daemon->ubus)
|
||||
my_syslog(LOG_INFO, _("connected to system UBus"));
|
||||
else if (err)
|
||||
{
|
||||
my_syslog(LOG_ERR, _("UBus error: %s"), err);
|
||||
reset_option_bool(OPT_UBUS); /* fatal error, stop trying. */
|
||||
}
|
||||
}
|
||||
|
||||
check_ubus_listeners();
|
||||
}
|
||||
#endif
|
||||
|
||||
check_dns_listeners(now);
|
||||
@@ -1426,7 +1460,7 @@ static void async_event(int pipe, time_t now)
|
||||
}
|
||||
|
||||
if (check)
|
||||
check_servers();
|
||||
check_servers(0);
|
||||
}
|
||||
|
||||
#ifdef HAVE_DHCP
|
||||
@@ -1625,7 +1659,7 @@ static void poll_resolv(int force, int do_reload, time_t now)
|
||||
{
|
||||
my_syslog(LOG_INFO, _("reading %s"), latest->name);
|
||||
warned = 0;
|
||||
check_servers();
|
||||
check_servers(0);
|
||||
if (option_bool(OPT_RELOAD) && do_reload)
|
||||
clear_cache_and_reload(now);
|
||||
}
|
||||
@@ -1668,11 +1702,12 @@ void clear_cache_and_reload(time_t now)
|
||||
#endif
|
||||
}
|
||||
|
||||
static int set_dns_listeners(time_t now)
|
||||
static void set_dns_listeners(void)
|
||||
{
|
||||
struct serverfd *serverfdp;
|
||||
struct listener *listener;
|
||||
int wait = 0, i;
|
||||
struct randfd_list *rfl;
|
||||
int i;
|
||||
|
||||
#ifdef HAVE_TFTP
|
||||
int tftp = 0;
|
||||
@@ -1685,66 +1720,68 @@ static int set_dns_listeners(time_t now)
|
||||
}
|
||||
#endif
|
||||
|
||||
/* will we be able to get memory? */
|
||||
if (daemon->port != 0)
|
||||
get_new_frec(now, &wait, NULL);
|
||||
|
||||
for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
|
||||
poll_listen(serverfdp->fd, POLLIN);
|
||||
|
||||
if (daemon->port != 0 && !daemon->osport)
|
||||
for (i = 0; i < RANDOM_SOCKS; i++)
|
||||
if (daemon->randomsocks[i].refcount != 0)
|
||||
poll_listen(daemon->randomsocks[i].fd, POLLIN);
|
||||
|
||||
for (i = 0; i < daemon->numrrand; i++)
|
||||
if (daemon->randomsocks[i].refcount != 0)
|
||||
poll_listen(daemon->randomsocks[i].fd, POLLIN);
|
||||
|
||||
/* Check overflow random sockets too. */
|
||||
for (rfl = daemon->rfl_poll; rfl; rfl = rfl->next)
|
||||
poll_listen(rfl->rfd->fd, POLLIN);
|
||||
|
||||
/* check to see if we have free tcp process slots. */
|
||||
for (i = MAX_PROCS - 1; i >= 0; i--)
|
||||
if (daemon->tcp_pids[i] == 0 && daemon->tcp_pipes[i] == -1)
|
||||
break;
|
||||
|
||||
for (listener = daemon->listeners; listener; listener = listener->next)
|
||||
{
|
||||
/* only listen for queries if we have resources */
|
||||
if (listener->fd != -1 && wait == 0)
|
||||
if (listener->fd != -1)
|
||||
poll_listen(listener->fd, POLLIN);
|
||||
|
||||
/* death of a child goes through the select loop, so
|
||||
we don't need to explicitly arrange to wake up here */
|
||||
if (listener->tcpfd != -1)
|
||||
for (i = 0; i < MAX_PROCS; i++)
|
||||
if (daemon->tcp_pids[i] == 0 && daemon->tcp_pipes[i] == -1)
|
||||
{
|
||||
poll_listen(listener->tcpfd, POLLIN);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
/* Only listen for TCP connections when a process slot
|
||||
is available. Death of a child goes through the select loop, so
|
||||
we don't need to explicitly arrange to wake up here,
|
||||
we'll be called again when a slot becomes available. */
|
||||
if (listener->tcpfd != -1 && i >= 0)
|
||||
poll_listen(listener->tcpfd, POLLIN);
|
||||
|
||||
#ifdef HAVE_TFTP
|
||||
/* tftp == 0 in single-port mode. */
|
||||
if (tftp <= daemon->tftp_max && listener->tftpfd != -1)
|
||||
poll_listen(listener->tftpfd, POLLIN);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
if (!option_bool(OPT_DEBUG))
|
||||
for (i = 0; i < MAX_PROCS; i++)
|
||||
if (daemon->tcp_pipes[i] != -1)
|
||||
poll_listen(daemon->tcp_pipes[i], POLLIN);
|
||||
|
||||
return wait;
|
||||
}
|
||||
|
||||
static void check_dns_listeners(time_t now)
|
||||
{
|
||||
struct serverfd *serverfdp;
|
||||
struct listener *listener;
|
||||
struct randfd_list *rfl;
|
||||
int i;
|
||||
int pipefd[2];
|
||||
|
||||
for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
|
||||
if (poll_check(serverfdp->fd, POLLIN))
|
||||
reply_query(serverfdp->fd, serverfdp->source_addr.sa.sa_family, now);
|
||||
reply_query(serverfdp->fd, now);
|
||||
|
||||
if (daemon->port != 0 && !daemon->osport)
|
||||
for (i = 0; i < RANDOM_SOCKS; i++)
|
||||
if (daemon->randomsocks[i].refcount != 0 &&
|
||||
poll_check(daemon->randomsocks[i].fd, POLLIN))
|
||||
reply_query(daemon->randomsocks[i].fd, daemon->randomsocks[i].family, now);
|
||||
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);
|
||||
|
||||
/* 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
|
||||
@@ -1773,7 +1810,16 @@ static void check_dns_listeners(time_t now)
|
||||
tftp_request(listener, now);
|
||||
#endif
|
||||
|
||||
if (listener->tcpfd != -1 && poll_check(listener->tcpfd, POLLIN))
|
||||
/* 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 = 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))
|
||||
{
|
||||
int confd, client_ok = 1;
|
||||
struct irec *iface = NULL;
|
||||
@@ -1863,7 +1909,6 @@ static void check_dns_listeners(time_t now)
|
||||
close(pipefd[0]);
|
||||
else
|
||||
{
|
||||
int i;
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
/* The child process inherits the netlink socket,
|
||||
which it never uses, but when the parent (us)
|
||||
@@ -1883,13 +1928,9 @@ static void check_dns_listeners(time_t now)
|
||||
read_write(pipefd[0], &a, 1, 1);
|
||||
#endif
|
||||
|
||||
for (i = 0; i < MAX_PROCS; i++)
|
||||
if (daemon->tcp_pids[i] == 0 && daemon->tcp_pipes[i] == -1)
|
||||
{
|
||||
daemon->tcp_pids[i] = p;
|
||||
daemon->tcp_pipes[i] = pipefd[0];
|
||||
break;
|
||||
}
|
||||
/* i holds index of free slot */
|
||||
daemon->tcp_pids[i] = p;
|
||||
daemon->tcp_pipes[i] = pipefd[0];
|
||||
}
|
||||
close(confd);
|
||||
|
||||
@@ -2068,7 +2109,7 @@ int delay_dhcp(time_t start, int sec, int fd, uint32_t addr, unsigned short id)
|
||||
poll_reset();
|
||||
if (fd != -1)
|
||||
poll_listen(fd, POLLIN);
|
||||
set_dns_listeners(now);
|
||||
set_dns_listeners();
|
||||
set_log_writer();
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
|
||||
242
src/dnsmasq.h
242
src/dnsmasq.h
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -14,7 +14,7 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define COPYRIGHT "Copyright (c) 2000-2020 Simon Kelley"
|
||||
#define COPYRIGHT "Copyright (c) 2000-2021 Simon Kelley"
|
||||
|
||||
/* We do defines that influence behavior of stdio.h, so complain
|
||||
if included too early. */
|
||||
@@ -95,11 +95,7 @@ typedef unsigned long long u64;
|
||||
#if defined(HAVE_SOLARIS_NETWORK)
|
||||
# include <sys/sockio.h>
|
||||
#endif
|
||||
#if defined(HAVE_POLL_H)
|
||||
# include <poll.h>
|
||||
#else
|
||||
# include <sys/poll.h>
|
||||
#endif
|
||||
#include <poll.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/un.h>
|
||||
@@ -111,6 +107,7 @@ typedef unsigned long long u64;
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
@@ -157,7 +154,11 @@ extern int capget(cap_user_header_t header, cap_user_data_t data);
|
||||
#include <priv.h>
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_DNSSEC) || defined(HAVE_NETTLEHASH)
|
||||
/* Backwards compat with 2.83 */
|
||||
#if defined(HAVE_NETTLEHASH)
|
||||
# define HAVE_CRYPTOHASH
|
||||
#endif
|
||||
#if defined(HAVE_DNSSEC) || defined(HAVE_CRYPTOHASH)
|
||||
# include <nettle/nettle-meta.h>
|
||||
#endif
|
||||
|
||||
@@ -269,7 +270,12 @@ struct event_desc {
|
||||
#define OPT_IGNORE_CLID 59
|
||||
#define OPT_SINGLE_PORT 60
|
||||
#define OPT_LEASE_RENEW 61
|
||||
#define OPT_LAST 62
|
||||
#define OPT_LOG_DEBUG 62
|
||||
#define OPT_UMBRELLA 63
|
||||
#define OPT_UMBRELLA_DEVID 64
|
||||
#define OPT_CMARK_ALST_EN 65
|
||||
#define OPT_QUIET_TFTP 66
|
||||
#define OPT_LAST 67
|
||||
|
||||
#define OPTION_BITS (sizeof(unsigned int)*8)
|
||||
#define OPTION_SIZE ( (OPT_LAST/OPTION_BITS)+((OPT_LAST%OPTION_BITS)!=0) )
|
||||
@@ -277,11 +283,13 @@ struct event_desc {
|
||||
#define option_val(x) ((1u) << ((x) % OPTION_BITS))
|
||||
#define option_bool(x) (option_var(x) & option_val(x))
|
||||
|
||||
/* extra flags for my_syslog, we use a couple of facilities since they are known
|
||||
not to occupy the same bits as priorities, no matter how syslog.h is set up. */
|
||||
/* extra flags for my_syslog, we use facilities since they are known
|
||||
not to occupy the same bits as priorities, no matter how syslog.h is set up.
|
||||
MS_DEBUG messages are suppressed unless --log-debug is set. */
|
||||
#define MS_TFTP LOG_USER
|
||||
#define MS_DHCP LOG_DAEMON
|
||||
#define MS_SCRIPT LOG_MAIL
|
||||
#define MS_DEBUG LOG_NEWS
|
||||
|
||||
/* Note that this is used widely as a container for IPv4/IPv6 addresses,
|
||||
so for that reason, was well as to avoid wasting memory in almost every
|
||||
@@ -317,12 +325,14 @@ union all_addr {
|
||||
/* for log_query */
|
||||
struct {
|
||||
unsigned short keytag, algo, digest, rcode;
|
||||
int ede;
|
||||
} log;
|
||||
};
|
||||
|
||||
|
||||
struct bogus_addr {
|
||||
struct in_addr addr;
|
||||
int is6, prefix;
|
||||
union all_addr addr;
|
||||
struct bogus_addr *next;
|
||||
};
|
||||
|
||||
@@ -423,10 +433,17 @@ struct host_record {
|
||||
struct host_record *next;
|
||||
};
|
||||
|
||||
#define IN4 1
|
||||
#define IN6 2
|
||||
#define INP4 4
|
||||
#define INP6 8
|
||||
|
||||
struct interface_name {
|
||||
char *name; /* domain name */
|
||||
char *intr; /* interface name */
|
||||
int family; /* AF_INET, AF_INET6 or zero for both */
|
||||
int flags;
|
||||
struct in_addr proto4;
|
||||
struct in6_addr proto6;
|
||||
struct addrlist *addr;
|
||||
struct interface_name *next;
|
||||
};
|
||||
@@ -486,7 +503,7 @@ struct crec {
|
||||
#define F_NO_RR (1u<<25)
|
||||
#define F_IPSET (1u<<26)
|
||||
#define F_NOEXTRA (1u<<27)
|
||||
#define F_SERVFAIL (1u<<28) /* currently unused. */
|
||||
#define F_DOMAINSRV (1u<<28)
|
||||
#define F_RCODE (1u<<29)
|
||||
#define F_SRV (1u<<30)
|
||||
|
||||
@@ -512,19 +529,20 @@ union mysockaddr {
|
||||
#define IFACE_PERMANENT 4
|
||||
|
||||
|
||||
#define SERV_FROM_RESOLV 1 /* 1 for servers from resolv, 0 for command line. */
|
||||
#define SERV_NO_ADDR 2 /* no server, this domain is local only */
|
||||
#define SERV_LITERAL_ADDRESS 4 /* addr is the answer, not the server */
|
||||
#define SERV_HAS_DOMAIN 8 /* server for one domain only */
|
||||
/* The actual values here matter, since we sort on them to get records in the order
|
||||
IPv6 addr, IPv4 addr, all zero return, no-data return, send upstream. */
|
||||
#define SERV_LITERAL_ADDRESS 1 /* addr is the answer, or NoDATA is the answer, depending on the next three flags */
|
||||
#define SERV_ALL_ZEROS 2 /* return all zeros for A and AAAA */
|
||||
#define SERV_4ADDR 4 /* addr is IPv4 */
|
||||
#define SERV_6ADDR 8 /* addr is IPv6 */
|
||||
#define SERV_HAS_SOURCE 16 /* source address defined */
|
||||
#define SERV_FOR_NODOTS 32 /* server for names with no domain part only */
|
||||
#define SERV_WARNED_RECURSIVE 64 /* avoid warning spam */
|
||||
#define SERV_FROM_DBUS 128 /* 1 if source is DBus */
|
||||
#define SERV_MARK 256 /* for mark-and-delete */
|
||||
#define SERV_TYPE (SERV_HAS_DOMAIN | SERV_FOR_NODOTS)
|
||||
#define SERV_COUNTED 512 /* workspace for log code */
|
||||
#define SERV_MARK 256 /* for mark-and-delete and log code */
|
||||
#define SERV_WILDCARD 512 /* domain has leading '*' */
|
||||
#define SERV_USE_RESOLV 1024 /* forward this domain in the normal way */
|
||||
#define SERV_NO_REBIND 2048 /* inhibit dns-rebind protection */
|
||||
#define SERV_FROM_RESOLV 2048 /* 1 for servers from resolv, 0 for command line. */
|
||||
#define SERV_FROM_FILE 4096 /* read from --servers-file */
|
||||
#define SERV_LOOP 8192 /* server causes forwarding loop */
|
||||
#define SERV_DO_DNSSEC 16384 /* Validate DNSSEC when using this server */
|
||||
@@ -539,22 +557,56 @@ struct serverfd {
|
||||
};
|
||||
|
||||
struct randfd {
|
||||
struct server *serv;
|
||||
int fd;
|
||||
unsigned short refcount, family;
|
||||
unsigned short refcount; /* refcount == 0xffff means overflow record. */
|
||||
};
|
||||
|
||||
|
||||
struct randfd_list {
|
||||
struct randfd *rfd;
|
||||
struct randfd_list *next;
|
||||
};
|
||||
|
||||
|
||||
struct server {
|
||||
u16 flags, domain_len;
|
||||
char *domain;
|
||||
struct server *next;
|
||||
int serial, arrayposn;
|
||||
int last_server;
|
||||
union mysockaddr addr, source_addr;
|
||||
char interface[IF_NAMESIZE+1];
|
||||
unsigned int ifindex; /* corresponding to interface, above */
|
||||
struct serverfd *sfd;
|
||||
char *domain; /* set if this server only handles a domain. */
|
||||
int flags, tcpfd, edns_pktsz;
|
||||
int tcpfd, edns_pktsz;
|
||||
time_t pktsz_reduced;
|
||||
unsigned int queries, failed_queries;
|
||||
time_t forwardtime;
|
||||
int forwardcount;
|
||||
#ifdef HAVE_LOOP
|
||||
u32 uid;
|
||||
#endif
|
||||
struct server *next;
|
||||
};
|
||||
|
||||
/* First four fields must match struct server in next three definitions.. */
|
||||
struct serv_addr4 {
|
||||
u16 flags, domain_len;
|
||||
char *domain;
|
||||
struct server *next;
|
||||
struct in_addr addr;
|
||||
};
|
||||
|
||||
struct serv_addr6 {
|
||||
u16 flags, domain_len;
|
||||
char *domain;
|
||||
struct server *next;
|
||||
struct in6_addr addr;
|
||||
};
|
||||
|
||||
struct serv_local {
|
||||
u16 flags, domain_len;
|
||||
char *domain;
|
||||
struct server *next;
|
||||
};
|
||||
|
||||
struct ipsets {
|
||||
@@ -563,6 +615,12 @@ struct ipsets {
|
||||
struct ipsets *next;
|
||||
};
|
||||
|
||||
struct allowlist {
|
||||
u32 mark, mask;
|
||||
char **patterns;
|
||||
struct allowlist *next;
|
||||
};
|
||||
|
||||
struct irec {
|
||||
union mysockaddr addr;
|
||||
struct in_addr netmask; /* only valid for IPv4 */
|
||||
@@ -632,17 +690,28 @@ struct hostsfile {
|
||||
#define DUMP_BOGUS 0x0040
|
||||
#define DUMP_SEC_BOGUS 0x0080
|
||||
|
||||
|
||||
/* DNSSEC status values. */
|
||||
#define STAT_SECURE 1
|
||||
#define STAT_INSECURE 2
|
||||
#define STAT_BOGUS 3
|
||||
#define STAT_NEED_DS 4
|
||||
#define STAT_NEED_KEY 5
|
||||
#define STAT_TRUNCATED 6
|
||||
#define STAT_SECURE_WILDCARD 7
|
||||
#define STAT_OK 8
|
||||
#define STAT_ABANDONED 9
|
||||
#define STAT_SECURE 0x10000
|
||||
#define STAT_INSECURE 0x20000
|
||||
#define STAT_BOGUS 0x30000
|
||||
#define STAT_NEED_DS 0x40000
|
||||
#define STAT_NEED_KEY 0x50000
|
||||
#define STAT_TRUNCATED 0x60000
|
||||
#define STAT_SECURE_WILDCARD 0x70000
|
||||
#define STAT_OK 0x80000
|
||||
#define STAT_ABANDONED 0x90000
|
||||
|
||||
#define DNSSEC_FAIL_NYV 0x0001 /* key not yet valid */
|
||||
#define DNSSEC_FAIL_EXP 0x0002 /* key expired */
|
||||
#define DNSSEC_FAIL_INDET 0x0004 /* indetermined */
|
||||
#define DNSSEC_FAIL_NOKEYSUP 0x0008 /* no supported key algo. */
|
||||
#define DNSSEC_FAIL_NOSIG 0x0010 /* No RRsigs */
|
||||
#define DNSSEC_FAIL_NOZONE 0x0020 /* No Zone bit set */
|
||||
#define DNSSEC_FAIL_NONSEC 0x0040 /* No NSEC */
|
||||
#define DNSSEC_FAIL_NODSSUP 0x0080 /* no supported DS algo. */
|
||||
#define DNSSEC_FAIL_NOKEY 0x0100 /* no DNSKEY */
|
||||
|
||||
#define STAT_ISEQUAL(a, b) (((a) & 0xffff0000) == (b))
|
||||
|
||||
#define FREC_NOREBIND 1
|
||||
#define FREC_CHECKING_DISABLED 2
|
||||
@@ -664,14 +733,14 @@ struct frec {
|
||||
union mysockaddr source;
|
||||
union all_addr dest;
|
||||
unsigned int iface, log_id;
|
||||
int fd;
|
||||
unsigned short orig_id;
|
||||
struct frec_src *next;
|
||||
} frec_src;
|
||||
struct server *sentto; /* NULL means free */
|
||||
struct randfd *rfd4;
|
||||
struct randfd *rfd6;
|
||||
struct randfd_list *rfds;
|
||||
unsigned short new_id;
|
||||
int fd, forwardall, flags;
|
||||
int forwardall, flags;
|
||||
time_t time;
|
||||
unsigned char *hash[HASH_SIZE];
|
||||
#ifdef HAVE_DNSSEC
|
||||
@@ -679,6 +748,7 @@ struct frec {
|
||||
struct blockdata *stash; /* Saved reply, whilst we validate */
|
||||
size_t stash_len;
|
||||
struct frec *dependent; /* Query awaiting internally-generated DNSKEY or DS query */
|
||||
struct frec *next_dependent; /* list of above. */
|
||||
struct frec *blocking_query; /* Query which is blocking us. */
|
||||
#endif
|
||||
struct frec *next;
|
||||
@@ -891,10 +961,10 @@ struct dhcp_bridge {
|
||||
};
|
||||
|
||||
struct cond_domain {
|
||||
char *domain, *prefix;
|
||||
char *domain, *prefix; /* prefix is text-prefix on domain name */
|
||||
struct in_addr start, end;
|
||||
struct in6_addr start6, end6;
|
||||
int is6, indexed;
|
||||
int is6, indexed, prefixlen;
|
||||
struct cond_domain *next;
|
||||
};
|
||||
|
||||
@@ -1035,8 +1105,12 @@ extern struct daemon {
|
||||
char *lease_change_command;
|
||||
struct iname *if_names, *if_addrs, *if_except, *dhcp_except, *auth_peers, *tftp_interfaces;
|
||||
struct bogus_addr *bogus_addr, *ignore_addr;
|
||||
struct server *servers;
|
||||
struct server *servers, *local_domains, **serverarray, *no_rebind;
|
||||
int server_has_wildcard;
|
||||
int serverarraysz, serverarrayhwm;
|
||||
struct ipsets *ipsets;
|
||||
u32 allowlist_mask;
|
||||
struct allowlist *allowlists;
|
||||
int log_fac; /* log facility */
|
||||
char *log_file; /* optional log file */
|
||||
int max_logs; /* queue limit */
|
||||
@@ -1044,6 +1118,9 @@ extern struct daemon {
|
||||
int port, query_port, min_port, max_port;
|
||||
unsigned long local_ttl, neg_ttl, max_ttl, min_cache_ttl, max_cache_ttl, auth_ttl, dhcp_ttl, use_dhcp_ttl;
|
||||
char *dns_client_id;
|
||||
u32 umbrella_org;
|
||||
u32 umbrella_asset;
|
||||
u8 umbrella_device[8];
|
||||
struct hostsfile *addn_hosts;
|
||||
struct dhcp_context *dhcp, *dhcp6;
|
||||
struct ra_interface *ra_interfaces;
|
||||
@@ -1104,16 +1181,15 @@ extern struct daemon {
|
||||
struct serverfd *sfds;
|
||||
struct irec *interfaces;
|
||||
struct listener *listeners;
|
||||
struct server *last_server;
|
||||
time_t forwardtime;
|
||||
int forwardcount;
|
||||
struct server *srv_save; /* Used for resend on DoD */
|
||||
size_t packet_len; /* " " */
|
||||
struct randfd *rfd_save; /* " " */
|
||||
int fd_save; /* " " */
|
||||
pid_t tcp_pids[MAX_PROCS];
|
||||
int tcp_pipes[MAX_PROCS];
|
||||
int pipe_to_parent;
|
||||
struct randfd randomsocks[RANDOM_SOCKS];
|
||||
int numrrand;
|
||||
struct randfd *randomsocks;
|
||||
struct randfd_list *rfl_spare, *rfl_poll;
|
||||
int v6pktinfo;
|
||||
struct addrlist *interface_addrs; /* list of all addresses/prefix lengths associated with all local interfaces */
|
||||
int log_id, log_display_id; /* ids of transactions for logging */
|
||||
@@ -1220,18 +1296,19 @@ unsigned char *skip_questions(struct dns_header *header, size_t plen);
|
||||
unsigned char *skip_section(unsigned char *ansp, int count, struct dns_header *header, size_t plen);
|
||||
unsigned int extract_request(struct dns_header *header, size_t qlen,
|
||||
char *name, unsigned short *typep);
|
||||
size_t setup_reply(struct dns_header *header, size_t qlen,
|
||||
union all_addr *addrp, unsigned int flags,
|
||||
unsigned long ttl);
|
||||
void setup_reply(struct dns_header *header, unsigned int flags, int ede);
|
||||
int extract_addresses(struct dns_header *header, size_t qlen, char *name,
|
||||
time_t now, char **ipsets, int is_sign, int check_rebind,
|
||||
int no_cache_dnssec, int secure, int *doctored);
|
||||
#if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS)
|
||||
void report_addresses(struct dns_header *header, size_t len, u32 mark);
|
||||
#endif
|
||||
size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
struct in_addr local_addr, struct in_addr local_netmask,
|
||||
time_t now, int ad_reqd, int do_bit, int have_pseudoheader);
|
||||
int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
|
||||
struct bogus_addr *baddr, time_t now);
|
||||
int check_for_ignored_address(struct dns_header *header, size_t qlen, struct bogus_addr *baddr);
|
||||
time_t now);
|
||||
int check_for_ignored_address(struct dns_header *header, size_t qlen);
|
||||
int check_for_local_domain(char *name, time_t now);
|
||||
size_t resize_packet(struct dns_header *header, size_t plen,
|
||||
unsigned char *pheader, size_t hlen);
|
||||
@@ -1250,6 +1327,7 @@ int in_zone(struct auth_zone *zone, char *name, char **cut);
|
||||
#endif
|
||||
|
||||
/* dnssec.c */
|
||||
#ifdef HAVE_DNSSEC
|
||||
size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char *name, int class, int type, int edns_pktsz);
|
||||
int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class);
|
||||
int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class);
|
||||
@@ -1258,8 +1336,11 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
||||
int dnskey_keytag(int alg, int flags, unsigned char *key, int keylen);
|
||||
size_t filter_rrsigs(struct dns_header *header, size_t plen);
|
||||
int setup_timestamp(void);
|
||||
int errflags_to_ede(int status);
|
||||
#endif
|
||||
|
||||
/* hash_questions.c */
|
||||
void hash_questions_init(void);
|
||||
unsigned char *hash_questions(struct dns_header *header, size_t plen, char *name);
|
||||
|
||||
/* crypto.c */
|
||||
@@ -1284,12 +1365,13 @@ void safe_strncpy(char *dest, const char *src, size_t size);
|
||||
void safe_pipe(int *fd, int read_noblock);
|
||||
void *whine_malloc(size_t size);
|
||||
int sa_len(union mysockaddr *addr);
|
||||
int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2);
|
||||
int sockaddr_isequal(const union mysockaddr *s1, const union mysockaddr *s2);
|
||||
int hostname_isequal(const char *a, const char *b);
|
||||
int hostname_issubdomain(char *a, char *b);
|
||||
time_t dnsmasq_time(void);
|
||||
int netmask_length(struct in_addr mask);
|
||||
int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask);
|
||||
int is_same_net_prefix(struct in_addr a, struct in_addr b, int prefix);
|
||||
int is_same_net6(struct in6_addr *a, struct in6_addr *b, int prefixlen);
|
||||
u64 addr6part(struct in6_addr *addr);
|
||||
void setaddr6part(struct in6_addr *addr, u64 host);
|
||||
@@ -1331,37 +1413,28 @@ void set_option_bool(unsigned int opt);
|
||||
void reset_option_bool(unsigned int opt);
|
||||
struct hostsfile *expand_filelist(struct hostsfile *list);
|
||||
char *parse_server(char *arg, union mysockaddr *addr,
|
||||
union mysockaddr *source_addr, char *interface, int *flags);
|
||||
union mysockaddr *source_addr, char *interface, u16 *flags);
|
||||
int option_read_dynfile(char *file, int flags);
|
||||
|
||||
/* forward.c */
|
||||
void reply_query(int fd, int family, time_t now);
|
||||
void reply_query(int fd, time_t now);
|
||||
void receive_query(struct listener *listen, time_t now);
|
||||
unsigned char *tcp_request(int confd, time_t now,
|
||||
union mysockaddr *local_addr, struct in_addr netmask, int auth_dns);
|
||||
void server_gone(struct server *server);
|
||||
struct frec *get_new_frec(time_t now, int *wait, struct frec *force);
|
||||
int send_from(int fd, int nowild, char *packet, size_t len,
|
||||
union mysockaddr *to, union all_addr *source,
|
||||
unsigned int iface);
|
||||
void resend_query(void);
|
||||
struct randfd *allocate_rfd(int family);
|
||||
void free_rfd(struct randfd *rfd);
|
||||
int allocate_rfd(struct randfd_list **fdlp, struct server *serv);
|
||||
void free_rfds(struct randfd_list **fdlp);
|
||||
|
||||
/* network.c */
|
||||
int indextoname(int fd, int index, char *name);
|
||||
int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifindex, int is_tcp);
|
||||
int random_sock(int family);
|
||||
void pre_allocate_sfds(void);
|
||||
int reload_servers(char *fname);
|
||||
void mark_servers(int flag);
|
||||
void cleanup_servers(void);
|
||||
void add_update_server(int flags,
|
||||
union mysockaddr *addr,
|
||||
union mysockaddr *source_addr,
|
||||
const char *interface,
|
||||
const char *domain);
|
||||
void check_servers(void);
|
||||
void check_servers(int no_loop_call);
|
||||
int enumerate_interfaces(int reset);
|
||||
void create_wildcard_listeners(void);
|
||||
void create_bound_listeners(int dienow);
|
||||
@@ -1494,10 +1567,14 @@ void emit_dbus_signal(int action, struct dhcp_lease *lease, char *hostname);
|
||||
|
||||
/* ubus.c */
|
||||
#ifdef HAVE_UBUS
|
||||
void ubus_init(void);
|
||||
char *ubus_init(void);
|
||||
void set_ubus_listeners(void);
|
||||
void check_ubus_listeners(void);
|
||||
void ubus_event_bcast(const char *type, const char *mac, const char *ip, const char *name, const char *interface);
|
||||
# ifdef HAVE_CONNTRACK
|
||||
void ubus_event_bcast_connmark_allowlist_refused(u32 mark, const char *name);
|
||||
void ubus_event_bcast_connmark_allowlist_resolved(u32 mark, const char *pattern, const char *ip, u32 ttl);
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* ipset.c */
|
||||
@@ -1506,6 +1583,13 @@ void ipset_init(void);
|
||||
int add_to_ipset(const char *setname, const union all_addr *ipaddr, int flags, int remove);
|
||||
#endif
|
||||
|
||||
/* pattern.c */
|
||||
#ifdef HAVE_CONNTRACK
|
||||
int is_valid_dns_name(const char *value);
|
||||
int is_valid_dns_name_pattern(const char *value);
|
||||
int is_dns_name_matching_pattern(const char *name, const char *pattern);
|
||||
#endif
|
||||
|
||||
/* helper.c */
|
||||
#if defined(HAVE_SCRIPT)
|
||||
int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd);
|
||||
@@ -1671,3 +1755,23 @@ int do_arp_script_run(void);
|
||||
void dump_init(void);
|
||||
void dump_packet(int mask, void *packet, size_t len, union mysockaddr *src, union mysockaddr *dst);
|
||||
#endif
|
||||
|
||||
/* domain-match.c */
|
||||
void build_server_array(void);
|
||||
int lookup_domain(char *qdomain, int flags, int *lowout, int *highout);
|
||||
int filter_servers(int seed, int flags, int *lowout, int *highout);
|
||||
int is_local_answer(time_t now, int first, char *name);
|
||||
size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header *header,
|
||||
char *name, char *limit, int first, int last, int ede);
|
||||
int server_samegroup(struct server *a, struct server *b);
|
||||
#ifdef HAVE_DNSSEC
|
||||
int dnssec_server(struct server *server, char *keyname, int *firstp, int *lastp);
|
||||
#endif
|
||||
void mark_servers(int flag);
|
||||
void cleanup_servers(void);
|
||||
int add_update_server(int flags,
|
||||
union mysockaddr *addr,
|
||||
union mysockaddr *source_addr,
|
||||
const char *interface,
|
||||
const char *domain,
|
||||
union all_addr *local_addr);
|
||||
|
||||
233
src/dnssec.c
233
src/dnssec.c
@@ -215,14 +215,6 @@ static int is_check_date(unsigned long curtime)
|
||||
return !daemon->dnssec_no_time_check;
|
||||
}
|
||||
|
||||
/* Check whether today/now is between date_start and date_end */
|
||||
static int check_date_range(unsigned long curtime, u32 date_start, u32 date_end)
|
||||
{
|
||||
/* We must explicitly check against wanted values, because of SERIAL_UNDEF */
|
||||
return serial_compare_32(curtime, date_start) == SERIAL_GT
|
||||
&& serial_compare_32(curtime, date_end) == SERIAL_LT;
|
||||
}
|
||||
|
||||
/* Return bytes of canonicalised rrdata one by one.
|
||||
Init state->ip with the RR, and state->end with the end of same.
|
||||
Init state->op to NULL.
|
||||
@@ -334,37 +326,64 @@ static int sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int
|
||||
if (!CHECK_LEN(header, state2.ip, plen, rdlen2))
|
||||
return rrsetidx; /* short packet */
|
||||
state2.end = state2.ip + rdlen2;
|
||||
|
||||
while (1)
|
||||
{
|
||||
int ok1, ok2;
|
||||
|
||||
ok1 = get_rdata(header, plen, &state1);
|
||||
ok2 = get_rdata(header, plen, &state2);
|
||||
|
||||
if (!ok1 && !ok2)
|
||||
/* If the RR has no names in it then canonicalisation
|
||||
is the identity function and we can compare
|
||||
the RRs directly. If not we compare the
|
||||
canonicalised RRs one byte at a time. */
|
||||
if (*rr_desc == (u16)-1)
|
||||
{
|
||||
int rdmin = rdlen1 > rdlen2 ? rdlen2 : rdlen1;
|
||||
int cmp = memcmp(state1.ip, state2.ip, rdmin);
|
||||
|
||||
if (cmp > 0 || (cmp == 0 && rdlen1 > rdmin))
|
||||
{
|
||||
unsigned char *tmp = rrset[i+1];
|
||||
rrset[i+1] = rrset[i];
|
||||
rrset[i] = tmp;
|
||||
swap = 1;
|
||||
}
|
||||
else if (cmp == 0 && (rdlen1 == rdlen2))
|
||||
{
|
||||
/* Two RRs are equal, remove one copy. RFC 4034, para 6.3 */
|
||||
for (j = i+1; j < rrsetidx-1; j++)
|
||||
rrset[j] = rrset[j+1];
|
||||
rrsetidx--;
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
else if (ok1 && (!ok2 || *state1.op > *state2.op))
|
||||
{
|
||||
unsigned char *tmp = rrset[i+1];
|
||||
rrset[i+1] = rrset[i];
|
||||
rrset[i] = tmp;
|
||||
swap = 1;
|
||||
break;
|
||||
}
|
||||
else if (ok2 && (!ok1 || *state2.op > *state1.op))
|
||||
break;
|
||||
|
||||
/* arrive here when bytes are equal, go round the loop again
|
||||
and compare the next ones. */
|
||||
}
|
||||
else
|
||||
/* Comparing canonicalised RRs, byte-at-a-time. */
|
||||
while (1)
|
||||
{
|
||||
int ok1, ok2;
|
||||
|
||||
ok1 = get_rdata(header, plen, &state1);
|
||||
ok2 = get_rdata(header, plen, &state2);
|
||||
|
||||
if (!ok1 && !ok2)
|
||||
{
|
||||
/* Two RRs are equal, remove one copy. RFC 4034, para 6.3 */
|
||||
for (j = i+1; j < rrsetidx-1; j++)
|
||||
rrset[j] = rrset[j+1];
|
||||
rrsetidx--;
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
else if (ok1 && (!ok2 || *state1.op > *state2.op))
|
||||
{
|
||||
unsigned char *tmp = rrset[i+1];
|
||||
rrset[i+1] = rrset[i];
|
||||
rrset[i] = tmp;
|
||||
swap = 1;
|
||||
break;
|
||||
}
|
||||
else if (ok2 && (!ok1 || *state2.op > *state1.op))
|
||||
break;
|
||||
|
||||
/* arrive here when bytes are equal, go round the loop again
|
||||
and compare the next ones. */
|
||||
}
|
||||
}
|
||||
} while (swap);
|
||||
|
||||
@@ -507,7 +526,8 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
|
||||
struct crec *crecp = NULL;
|
||||
u16 *rr_desc = rrfilter_desc(type);
|
||||
u32 sig_expiration, sig_inception;
|
||||
|
||||
int failflags = DNSSEC_FAIL_NOSIG | DNSSEC_FAIL_NYV | DNSSEC_FAIL_EXP | DNSSEC_FAIL_NOKEYSUP;
|
||||
|
||||
unsigned long curtime = time(0);
|
||||
int time_check = is_check_date(curtime);
|
||||
|
||||
@@ -530,6 +550,8 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
|
||||
void *ctx;
|
||||
char *name_start;
|
||||
u32 nsigttl, ttl, orig_ttl;
|
||||
|
||||
failflags &= ~DNSSEC_FAIL_NOSIG;
|
||||
|
||||
p = sigs[j];
|
||||
GETLONG(ttl, p);
|
||||
@@ -547,12 +569,31 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
|
||||
if (!extract_name(header, plen, &p, keyname, 1, 0))
|
||||
return STAT_BOGUS;
|
||||
|
||||
if ((time_check && !check_date_range(curtime, sig_inception, sig_expiration)) ||
|
||||
labels > name_labels ||
|
||||
!(hash = hash_find(algo_digest_name(algo))) ||
|
||||
if (!time_check)
|
||||
failflags &= ~(DNSSEC_FAIL_NYV | DNSSEC_FAIL_EXP);
|
||||
else
|
||||
{
|
||||
/* We must explicitly check against wanted values, because of SERIAL_UNDEF */
|
||||
if (serial_compare_32(curtime, sig_inception) == SERIAL_LT)
|
||||
continue;
|
||||
else
|
||||
failflags &= ~DNSSEC_FAIL_NYV;
|
||||
|
||||
if (serial_compare_32(curtime, sig_expiration) == SERIAL_GT)
|
||||
continue;
|
||||
else
|
||||
failflags &= ~DNSSEC_FAIL_EXP;
|
||||
}
|
||||
|
||||
if (!(hash = hash_find(algo_digest_name(algo))))
|
||||
continue;
|
||||
else
|
||||
failflags &= ~DNSSEC_FAIL_NOKEYSUP;
|
||||
|
||||
if (labels > name_labels ||
|
||||
!hash_init(hash, &ctx, &digest))
|
||||
continue;
|
||||
|
||||
|
||||
/* OK, we have the signature record, see if the relevant DNSKEY is in the cache. */
|
||||
if (!key && !(crecp = cache_find_by_name(NULL, keyname, now, F_DNSKEY)))
|
||||
return STAT_NEED_KEY;
|
||||
@@ -703,7 +744,8 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
|
||||
}
|
||||
}
|
||||
|
||||
return STAT_BOGUS;
|
||||
/* If we reach this point, no verifying key was found */
|
||||
return STAT_BOGUS | failflags | DNSSEC_FAIL_NOKEY;
|
||||
}
|
||||
|
||||
|
||||
@@ -724,17 +766,18 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
|
||||
unsigned long ttl, sig_ttl;
|
||||
struct blockdata *key;
|
||||
union all_addr a;
|
||||
int failflags = DNSSEC_FAIL_NOSIG | DNSSEC_FAIL_NODSSUP | DNSSEC_FAIL_NOZONE | DNSSEC_FAIL_NOKEY;
|
||||
|
||||
if (ntohs(header->qdcount) != 1 ||
|
||||
RCODE(header) == SERVFAIL || RCODE(header) == REFUSED ||
|
||||
!extract_name(header, plen, &p, name, 1, 4))
|
||||
return STAT_BOGUS;
|
||||
return STAT_BOGUS | DNSSEC_FAIL_NOKEY;
|
||||
|
||||
GETSHORT(qtype, p);
|
||||
GETSHORT(qclass, p);
|
||||
|
||||
if (qtype != T_DNSKEY || qclass != class || ntohs(header->ancount) == 0)
|
||||
return STAT_BOGUS;
|
||||
return STAT_BOGUS | DNSSEC_FAIL_NOKEY;
|
||||
|
||||
/* See if we have cached a DS record which validates this key */
|
||||
if (!(crecp = cache_find_by_name(NULL, name, now, F_DS)))
|
||||
@@ -768,14 +811,17 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
|
||||
|
||||
GETSHORT(flags, p);
|
||||
if (*p++ != 3)
|
||||
return STAT_BOGUS;
|
||||
return STAT_BOGUS | DNSSEC_FAIL_NOKEY;
|
||||
algo = *p++;
|
||||
keytag = dnskey_keytag(algo, flags, p, rdlen - 4);
|
||||
key = NULL;
|
||||
|
||||
/* key must have zone key flag set */
|
||||
if (flags & 0x100)
|
||||
key = blockdata_alloc((char*)p, rdlen - 4);
|
||||
{
|
||||
key = blockdata_alloc((char*)p, rdlen - 4);
|
||||
failflags &= ~DNSSEC_FAIL_NOZONE;
|
||||
}
|
||||
|
||||
p = psave;
|
||||
|
||||
@@ -796,15 +842,23 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
|
||||
unsigned char *digest, *ds_digest;
|
||||
const struct nettle_hash *hash;
|
||||
int sigcnt, rrcnt;
|
||||
|
||||
int wire_len;
|
||||
|
||||
if (recp1->addr.ds.algo == algo &&
|
||||
recp1->addr.ds.keytag == keytag &&
|
||||
recp1->uid == (unsigned int)class &&
|
||||
(hash = hash_find(ds_digest_name(recp1->addr.ds.digest))) &&
|
||||
hash_init(hash, &ctx, &digest))
|
||||
|
||||
recp1->uid == (unsigned int)class)
|
||||
{
|
||||
int wire_len = to_wire(name);
|
||||
failflags &= ~DNSSEC_FAIL_NOKEY;
|
||||
|
||||
if (!(hash = hash_find(ds_digest_name(recp1->addr.ds.digest))))
|
||||
continue;
|
||||
else
|
||||
failflags &= ~DNSSEC_FAIL_NODSSUP;
|
||||
|
||||
if (!hash_init(hash, &ctx, &digest))
|
||||
continue;
|
||||
|
||||
wire_len = to_wire(name);
|
||||
|
||||
/* Note that digest may be different between DSs, so
|
||||
we can't move this outside the loop. */
|
||||
@@ -819,12 +873,23 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
|
||||
(ds_digest = blockdata_retrieve(recp1->addr.ds.keydata, recp1->addr.ds.keylen, NULL)) &&
|
||||
memcmp(ds_digest, digest, recp1->addr.ds.keylen) == 0 &&
|
||||
explore_rrset(header, plen, class, T_DNSKEY, name, keyname, &sigcnt, &rrcnt) &&
|
||||
sigcnt != 0 && rrcnt != 0 &&
|
||||
validate_rrset(now, header, plen, class, T_DNSKEY, sigcnt, rrcnt, name, keyname,
|
||||
NULL, key, rdlen - 4, algo, keytag, &sig_ttl) == STAT_SECURE)
|
||||
rrcnt != 0)
|
||||
{
|
||||
valid = 1;
|
||||
break;
|
||||
if (sigcnt == 0)
|
||||
continue;
|
||||
else
|
||||
failflags &= ~DNSSEC_FAIL_NOSIG;
|
||||
|
||||
rc = validate_rrset(now, header, plen, class, T_DNSKEY, sigcnt, rrcnt, name, keyname,
|
||||
NULL, key, rdlen - 4, algo, keytag, &sig_ttl);
|
||||
|
||||
failflags &= rc;
|
||||
|
||||
if (STAT_ISEQUAL(rc, STAT_SECURE))
|
||||
{
|
||||
valid = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -909,7 +974,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
|
||||
}
|
||||
|
||||
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DNSKEY");
|
||||
return STAT_BOGUS;
|
||||
return STAT_BOGUS | failflags;
|
||||
}
|
||||
|
||||
/* The DNS packet is expected to contain the answer to a DS query
|
||||
@@ -944,10 +1009,11 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
|
||||
else
|
||||
rc = dnssec_validate_reply(now, header, plen, name, keyname, NULL, 0, &neganswer, &nons, &neg_ttl);
|
||||
|
||||
if (rc == STAT_INSECURE)
|
||||
if (STAT_ISEQUAL(rc, STAT_INSECURE))
|
||||
{
|
||||
my_syslog(LOG_WARNING, _("Insecure DS reply received for %s, check domain configuration and upstream DNS server DNSSEC support"), name);
|
||||
rc = STAT_BOGUS;
|
||||
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS - not secure");
|
||||
return STAT_BOGUS | DNSSEC_FAIL_INDET;
|
||||
}
|
||||
|
||||
p = (unsigned char *)(header+1);
|
||||
@@ -957,13 +1023,13 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
|
||||
/* If the key needed to validate the DS is on the same domain as the DS, we'll
|
||||
loop getting nowhere. Stop that now. This can happen of the DS answer comes
|
||||
from the DS's zone, and not the parent zone. */
|
||||
if (rc == STAT_BOGUS || (rc == STAT_NEED_KEY && hostname_isequal(name, keyname)))
|
||||
if (STAT_ISEQUAL(rc, STAT_NEED_KEY) && hostname_isequal(name, keyname))
|
||||
{
|
||||
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS");
|
||||
return STAT_BOGUS;
|
||||
}
|
||||
|
||||
if (rc != STAT_SECURE)
|
||||
if (!STAT_ISEQUAL(rc, STAT_SECURE))
|
||||
return rc;
|
||||
|
||||
if (!neganswer)
|
||||
@@ -1429,7 +1495,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
|
||||
if (!(p = skip_name(nsecs[i], header, plen, 15)))
|
||||
return 0; /* bad packet */
|
||||
|
||||
p += 10; /* type, class, TTL, rdlen */
|
||||
p += 10; /* type, class, TTL, rdlen */
|
||||
algo = *p++;
|
||||
|
||||
if ((hash = hash_find(nsec3_digest_name(algo))))
|
||||
@@ -1932,15 +1998,15 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
||||
if (check_unsigned && i < ntohs(header->ancount))
|
||||
{
|
||||
rc = zone_status(name, class1, keyname, now);
|
||||
if (rc == STAT_SECURE)
|
||||
rc = STAT_BOGUS;
|
||||
if (STAT_ISEQUAL(rc, STAT_SECURE))
|
||||
rc = STAT_BOGUS | DNSSEC_FAIL_NOSIG;
|
||||
if (class)
|
||||
*class = class1; /* Class for NEED_DS or NEED_KEY */
|
||||
}
|
||||
else
|
||||
rc = STAT_INSECURE;
|
||||
|
||||
if (rc != STAT_INSECURE)
|
||||
if (!STAT_ISEQUAL(rc, STAT_INSECURE))
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
@@ -1951,7 +2017,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
||||
strcpy(daemon->workspacename, keyname);
|
||||
rc = zone_status(daemon->workspacename, class1, keyname, now);
|
||||
|
||||
if (rc == STAT_BOGUS || rc == STAT_NEED_KEY || rc == STAT_NEED_DS)
|
||||
if (STAT_ISEQUAL(rc, STAT_BOGUS) || STAT_ISEQUAL(rc, STAT_NEED_KEY) || STAT_ISEQUAL(rc, STAT_NEED_DS))
|
||||
{
|
||||
if (class)
|
||||
*class = class1; /* Class for NEED_DS or NEED_KEY */
|
||||
@@ -1959,13 +2025,13 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
||||
}
|
||||
|
||||
/* Zone is insecure, don't need to validate RRset */
|
||||
if (rc == STAT_SECURE)
|
||||
if (STAT_ISEQUAL(rc, STAT_SECURE))
|
||||
{
|
||||
unsigned long sig_ttl;
|
||||
rc = validate_rrset(now, header, plen, class1, type1, sigcnt,
|
||||
rrcnt, name, keyname, &wildname, NULL, 0, 0, 0, &sig_ttl);
|
||||
|
||||
if (rc == STAT_BOGUS || rc == STAT_NEED_KEY || rc == STAT_NEED_DS)
|
||||
if (STAT_ISEQUAL(rc, STAT_BOGUS) || STAT_ISEQUAL(rc, STAT_NEED_KEY) || STAT_ISEQUAL(rc, STAT_NEED_DS))
|
||||
{
|
||||
if (class)
|
||||
*class = class1; /* Class for DS or DNSKEY */
|
||||
@@ -1998,21 +2064,21 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
||||
Note that we may not yet have validated the NSEC/NSEC3 RRsets.
|
||||
That's not a problem since if the RRsets later fail
|
||||
we'll return BOGUS then. */
|
||||
if (rc == STAT_SECURE_WILDCARD &&
|
||||
if (STAT_ISEQUAL(rc, STAT_SECURE_WILDCARD) &&
|
||||
!prove_non_existence(header, plen, keyname, name, type1, class1, wildname, NULL, NULL))
|
||||
return STAT_BOGUS;
|
||||
return STAT_BOGUS | DNSSEC_FAIL_NONSEC;
|
||||
|
||||
rc = STAT_SECURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rc == STAT_INSECURE)
|
||||
if (STAT_ISEQUAL(rc, STAT_INSECURE))
|
||||
secure = STAT_INSECURE;
|
||||
}
|
||||
|
||||
/* OK, all the RRsets validate, now see if we have a missing answer or CNAME target. */
|
||||
if (secure == STAT_SECURE)
|
||||
if (STAT_ISEQUAL(secure, STAT_SECURE))
|
||||
for (j = 0; j <targetidx; j++)
|
||||
if ((p2 = targets[j]))
|
||||
{
|
||||
@@ -2030,16 +2096,16 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
||||
{
|
||||
/* Empty DS without NSECS */
|
||||
if (qtype == T_DS)
|
||||
return STAT_BOGUS;
|
||||
return STAT_BOGUS | DNSSEC_FAIL_NONSEC;
|
||||
|
||||
if ((rc = zone_status(name, qclass, keyname, now)) != STAT_SECURE)
|
||||
if (STAT_ISEQUAL((rc = zone_status(name, qclass, keyname, now)), STAT_SECURE))
|
||||
{
|
||||
if (class)
|
||||
*class = qclass; /* Class for NEED_DS or NEED_KEY */
|
||||
return rc;
|
||||
}
|
||||
|
||||
return STAT_BOGUS; /* signed zone, no NSECs */
|
||||
return STAT_BOGUS | DNSSEC_FAIL_NONSEC; /* signed zone, no NSECs */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2103,4 +2169,31 @@ size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char
|
||||
return ret;
|
||||
}
|
||||
|
||||
int errflags_to_ede(int status)
|
||||
{
|
||||
/* We can end up with more than one flag set for some errors,
|
||||
so this encodes a rough priority so the (eg) No sig is reported
|
||||
before no-unexpired-sig. */
|
||||
|
||||
if (status & DNSSEC_FAIL_NYV)
|
||||
return EDE_SIG_NYV;
|
||||
else if (status & DNSSEC_FAIL_EXP)
|
||||
return EDE_SIG_EXP;
|
||||
else if (status & DNSSEC_FAIL_NOKEYSUP)
|
||||
return EDE_USUPDNSKEY;
|
||||
else if (status & DNSSEC_FAIL_NOZONE)
|
||||
return EDE_NO_ZONEKEY;
|
||||
else if (status & DNSSEC_FAIL_NOKEY)
|
||||
return EDE_NO_DNSKEY;
|
||||
else if (status & DNSSEC_FAIL_NODSSUP)
|
||||
return EDE_USUPDS;
|
||||
else if (status & DNSSEC_FAIL_NONSEC)
|
||||
return EDE_NO_NSEC;
|
||||
else if (status & DNSSEC_FAIL_INDET)
|
||||
return EDE_DNSSEC_IND;
|
||||
else if (status & DNSSEC_FAIL_NOSIG)
|
||||
return EDE_NO_RRSIG;
|
||||
else
|
||||
return EDE_UNSET;
|
||||
}
|
||||
#endif /* HAVE_DNSSEC */
|
||||
|
||||
698
src/domain-match.c
Normal file
698
src/domain-match.c
Normal file
@@ -0,0 +1,698 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 dated June, 1991, or
|
||||
(at your option) version 3 dated 29 June, 2007.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
static int order(char *qdomain, size_t qlen, struct server *serv);
|
||||
static int order_qsort(const void *a, const void *b);
|
||||
static int order_servers(struct server *s, struct server *s2);
|
||||
|
||||
/* If the server is USE_RESOLV or LITERAL_ADDRES, it lives on the local_domains chain. */
|
||||
#define SERV_IS_LOCAL (SERV_USE_RESOLV | SERV_LITERAL_ADDRESS)
|
||||
|
||||
void build_server_array(void)
|
||||
{
|
||||
struct server *serv;
|
||||
int count = 0;
|
||||
|
||||
for (serv = daemon->servers; serv; serv = serv->next)
|
||||
#ifdef HAVE_LOOP
|
||||
if (!(serv->flags & SERV_LOOP))
|
||||
#endif
|
||||
{
|
||||
count++;
|
||||
if (serv->flags & SERV_WILDCARD)
|
||||
daemon->server_has_wildcard = 1;
|
||||
}
|
||||
|
||||
for (serv = daemon->local_domains; serv; serv = serv->next)
|
||||
{
|
||||
count++;
|
||||
if (serv->flags & SERV_WILDCARD)
|
||||
daemon->server_has_wildcard = 1;
|
||||
}
|
||||
|
||||
daemon->serverarraysz = count;
|
||||
|
||||
if (count > daemon->serverarrayhwm)
|
||||
{
|
||||
struct server **new;
|
||||
|
||||
count += 10; /* A few extra without re-allocating. */
|
||||
|
||||
if ((new = whine_malloc(count * sizeof(struct server *))))
|
||||
{
|
||||
if (daemon->serverarray)
|
||||
free(daemon->serverarray);
|
||||
|
||||
daemon->serverarray = new;
|
||||
daemon->serverarrayhwm = count;
|
||||
}
|
||||
}
|
||||
|
||||
count = 0;
|
||||
|
||||
for (serv = daemon->servers; serv; serv = serv->next)
|
||||
#ifdef HAVE_LOOP
|
||||
if (!(serv->flags & SERV_LOOP))
|
||||
#endif
|
||||
{
|
||||
daemon->serverarray[count] = serv;
|
||||
serv->serial = count;
|
||||
serv->last_server = -1;
|
||||
count++;
|
||||
}
|
||||
|
||||
for (serv = daemon->local_domains; serv; serv = serv->next, count++)
|
||||
daemon->serverarray[count] = serv;
|
||||
|
||||
qsort(daemon->serverarray, daemon->serverarraysz, sizeof(struct server *), order_qsort);
|
||||
|
||||
/* servers need the location in the array to find all the whole
|
||||
set of equivalent servers from a pointer to a single one. */
|
||||
for (count = 0; count < daemon->serverarraysz; count++)
|
||||
if (!(daemon->serverarray[count]->flags & SERV_IS_LOCAL))
|
||||
daemon->serverarray[count]->arrayposn = count;
|
||||
}
|
||||
|
||||
/* we're looking for the server whose domain is the longest exact match
|
||||
to the RH end of qdomain, or a local address if the flags match.
|
||||
Add '.' to the LHS of the query string so
|
||||
server=/.example.com/ works.
|
||||
|
||||
A flag of F_SERVER returns an upstream server only.
|
||||
A flag of F_DNSSECOK returns a DNSSEC capable server only and
|
||||
also disables NODOTS servers from consideration.
|
||||
A flag of F_DOMAINSRV returns a domain-specific server only.
|
||||
A flag of F_CONFIG returns anything that generates a local
|
||||
reply of IPv4 or IPV6.
|
||||
return 0 if nothing found, 1 otherwise.
|
||||
*/
|
||||
int lookup_domain(char *domain, int flags, int *lowout, int *highout)
|
||||
{
|
||||
int rc, crop_query, nodots;
|
||||
ssize_t qlen;
|
||||
int try, high, low = 0;
|
||||
int nlow = 0, nhigh = 0;
|
||||
char *cp, *qdomain = domain;
|
||||
|
||||
/* may be no configured servers. */
|
||||
if (daemon->serverarraysz == 0)
|
||||
return 0;
|
||||
|
||||
/* find query length and presence of '.' */
|
||||
for (cp = qdomain, nodots = 1, qlen = 0; *cp; qlen++, cp++)
|
||||
if (*cp == '.')
|
||||
nodots = 0;
|
||||
|
||||
/* Handle empty name, and searches for DNSSEC queries without
|
||||
diverting to NODOTS servers. */
|
||||
if (qlen == 0 || flags & F_DNSSECOK)
|
||||
nodots = 0;
|
||||
|
||||
/* Search shorter and shorter RHS substrings for a match */
|
||||
while (qlen >= 0)
|
||||
{
|
||||
/* Note that when we chop off a label, all the possible matches
|
||||
MUST be at a larger index than the nearest failing match with one more
|
||||
character, since the array is sorted longest to smallest. Hence
|
||||
we don't reset low to zero here, we can go further below and crop the
|
||||
search string to the size of the largest remaining server
|
||||
when this match fails. */
|
||||
high = daemon->serverarraysz;
|
||||
crop_query = 1;
|
||||
|
||||
/* binary search */
|
||||
while (1)
|
||||
{
|
||||
try = (low + high)/2;
|
||||
|
||||
if ((rc = order(qdomain, qlen, daemon->serverarray[try])) == 0)
|
||||
break;
|
||||
|
||||
if (rc < 0)
|
||||
{
|
||||
if (high == try)
|
||||
{
|
||||
/* qdomain is longer or same length as longest domain, and try == 0
|
||||
crop the query to the longest domain. */
|
||||
crop_query = qlen - daemon->serverarray[try]->domain_len;
|
||||
break;
|
||||
}
|
||||
high = try;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (low == try)
|
||||
{
|
||||
/* try now points to the last domain that sorts before the query, so
|
||||
we know that a substring of the query shorter than it is required to match, so
|
||||
find the largest domain that's shorter than try. Note that just going to
|
||||
try+1 is not optimal, consider searching bbb in (aaa,ccc,bb). try will point
|
||||
to aaa, since ccc sorts after bbb, but the first domain that has a chance to
|
||||
match is bb. So find the length of the first domain later than try which is
|
||||
is shorter than it.
|
||||
There's a nasty edge case when qdomain sorts before _any_ of the
|
||||
server domains, where try _doesn't point_ to the last domain that sorts
|
||||
before the query, since no such domain exists. In that case, the loop
|
||||
exits via the rc < 0 && high == try path above and this code is
|
||||
not executed. */
|
||||
ssize_t len, old = daemon->serverarray[try]->domain_len;
|
||||
while (++try != daemon->serverarraysz)
|
||||
{
|
||||
if (old != (len = daemon->serverarray[try]->domain_len))
|
||||
{
|
||||
crop_query = qlen - len;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
low = try;
|
||||
}
|
||||
};
|
||||
|
||||
if (rc == 0)
|
||||
{
|
||||
int found = 1;
|
||||
|
||||
if (daemon->server_has_wildcard)
|
||||
{
|
||||
/* if we have example.com and *example.com we need to check against *example.com,
|
||||
but the binary search may have found either. Use the fact that example.com is sorted before *example.com
|
||||
We favour example.com in the case that both match (ie www.example.com) */
|
||||
while (try != 0 && order(qdomain, qlen, daemon->serverarray[try-1]) == 0)
|
||||
try--;
|
||||
|
||||
if (!(qdomain == domain || *qdomain == 0 || *(qdomain-1) == '.'))
|
||||
{
|
||||
while (try < daemon->serverarraysz-1 && order(qdomain, qlen, daemon->serverarray[try+1]) == 0)
|
||||
try++;
|
||||
|
||||
if (!(daemon->serverarray[try]->flags & SERV_WILDCARD))
|
||||
found = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (found)
|
||||
{
|
||||
/* We've matched a setting which says to use servers without a domain.
|
||||
Continue the search with empty query */
|
||||
if (daemon->serverarray[try]->flags & SERV_USE_RESOLV)
|
||||
crop_query = qlen;
|
||||
else if (filter_servers(try, flags, &nlow, &nhigh))
|
||||
/* We have a match, but it may only be (say) an IPv6 address, and
|
||||
if the query wasn't for an AAAA record, it's no good, and we need
|
||||
to continue generalising */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* crop_query must be at least one always. */
|
||||
if (crop_query == 0)
|
||||
crop_query = 1;
|
||||
|
||||
/* strip chars off the query based on the largest possible remaining match,
|
||||
then continue to the start of the next label unless we have a wildcard
|
||||
domain somewhere, in which case we have to go one at a time. */
|
||||
qlen -= crop_query;
|
||||
qdomain += crop_query;
|
||||
if (!daemon->server_has_wildcard)
|
||||
while (qlen > 0 && (*(qdomain-1) != '.'))
|
||||
qlen--, qdomain++;
|
||||
}
|
||||
|
||||
/* domain has no dots, and we have at least one server configured to handle such,
|
||||
These servers always sort to the very end of the array.
|
||||
A configured server eg server=/lan/ will take precdence. */
|
||||
if (nodots &&
|
||||
(daemon->serverarray[daemon->serverarraysz-1]->flags & SERV_FOR_NODOTS) &&
|
||||
(nlow == nhigh || daemon->serverarray[nlow]->domain_len == 0))
|
||||
filter_servers(daemon->serverarraysz-1, flags, &nlow, &nhigh);
|
||||
|
||||
if (lowout)
|
||||
*lowout = nlow;
|
||||
|
||||
if (highout)
|
||||
*highout = nhigh;
|
||||
|
||||
if (nlow == nhigh)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Return first server in group of equivalent servers; this is the "master" record. */
|
||||
int server_samegroup(struct server *a, struct server *b)
|
||||
{
|
||||
return order_servers(a, b) == 0;
|
||||
}
|
||||
|
||||
int filter_servers(int seed, int flags, int *lowout, int *highout)
|
||||
{
|
||||
int nlow = seed, nhigh = seed;
|
||||
int i;
|
||||
|
||||
/* expand nlow and nhigh to cover all the records with the same domain
|
||||
nlow is the first, nhigh - 1 is the last. nlow=nhigh means no servers,
|
||||
which can happen below. */
|
||||
while (nlow > 0 && order_servers(daemon->serverarray[nlow-1], daemon->serverarray[nlow]) == 0)
|
||||
nlow--;
|
||||
|
||||
while (nhigh < daemon->serverarraysz-1 && order_servers(daemon->serverarray[nhigh], daemon->serverarray[nhigh+1]) == 0)
|
||||
nhigh++;
|
||||
|
||||
nhigh++;
|
||||
|
||||
#define SERV_LOCAL_ADDRESS (SERV_6ADDR | SERV_4ADDR | SERV_ALL_ZEROS)
|
||||
|
||||
if (flags & F_CONFIG)
|
||||
{
|
||||
/* We're just lookin for any matches that return an RR. */
|
||||
for (i = nlow; i < nhigh; i++)
|
||||
if (daemon->serverarray[i]->flags & SERV_LOCAL_ADDRESS)
|
||||
break;
|
||||
|
||||
/* failed, return failure. */
|
||||
if (i == nhigh)
|
||||
nhigh = nlow;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Now the servers are on order between low and high, in the order
|
||||
IPv6 addr, IPv4 addr, return zero for both, send upstream, no-data return.
|
||||
|
||||
See which of those match our query in that priority order and narrow (low, high) */
|
||||
|
||||
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_6ADDR); i++);
|
||||
|
||||
if (i != nlow && (flags & F_IPV6))
|
||||
nhigh = i;
|
||||
else
|
||||
{
|
||||
nlow = i;
|
||||
|
||||
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_4ADDR); i++);
|
||||
|
||||
if (i != nlow && (flags & F_IPV4))
|
||||
nhigh = i;
|
||||
else
|
||||
{
|
||||
nlow = i;
|
||||
|
||||
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_ALL_ZEROS); i++);
|
||||
|
||||
if (i != nlow && (flags & (F_IPV4 | F_IPV6)))
|
||||
nhigh = i;
|
||||
else
|
||||
{
|
||||
nlow = i;
|
||||
|
||||
/* now look for a server */
|
||||
for (i = nlow; i < nhigh && !(daemon->serverarray[i]->flags & SERV_LITERAL_ADDRESS); i++);
|
||||
|
||||
if (i != nlow)
|
||||
{
|
||||
/* If we want a server that can do DNSSEC, and this one can't,
|
||||
return nothing, similarly if were looking only for a server
|
||||
for a particular domain. */
|
||||
if ((flags & F_DNSSECOK) && !(daemon->serverarray[nlow]->flags & SERV_DO_DNSSEC))
|
||||
nlow = nhigh;
|
||||
else if ((flags & F_DOMAINSRV) && daemon->serverarray[nlow]->domain_len == 0)
|
||||
nlow = nhigh;
|
||||
else
|
||||
nhigh = i;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* --local=/domain/, only return if we don't need a server. */
|
||||
if (flags & (F_DNSSECOK | F_DOMAINSRV | F_SERVER))
|
||||
nhigh = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*lowout = nlow;
|
||||
*highout = nhigh;
|
||||
|
||||
return (nlow != nhigh);
|
||||
}
|
||||
|
||||
int is_local_answer(time_t now, int first, char *name)
|
||||
{
|
||||
int flags = 0;
|
||||
int rc = 0;
|
||||
|
||||
if ((flags = daemon->serverarray[first]->flags) & SERV_LITERAL_ADDRESS)
|
||||
{
|
||||
if (flags & SERV_4ADDR)
|
||||
rc = F_IPV4;
|
||||
else if (flags & SERV_6ADDR)
|
||||
rc = F_IPV6;
|
||||
else if (flags & SERV_ALL_ZEROS)
|
||||
rc = F_IPV4 | F_IPV6;
|
||||
else
|
||||
{
|
||||
/* argument first is the first struct server which matches the query type;
|
||||
now roll back to the server which is just the same domain, to check if that
|
||||
provides an answer of a different type. */
|
||||
|
||||
for (;first > 0 && order_servers(daemon->serverarray[first-1], daemon->serverarray[first]) == 0; first--);
|
||||
|
||||
if ((daemon->serverarray[first]->flags & SERV_LOCAL_ADDRESS) ||
|
||||
check_for_local_domain(name, now))
|
||||
rc = F_NOERR;
|
||||
else
|
||||
rc = F_NXDOMAIN;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header *header, char *name, char *limit, int first, int last, int ede)
|
||||
{
|
||||
int trunc = 0;
|
||||
unsigned char *p;
|
||||
int start;
|
||||
union all_addr addr;
|
||||
|
||||
if (flags & (F_NXDOMAIN | F_NOERR))
|
||||
log_query(flags | gotname | F_NEG | F_CONFIG | F_FORWARD, name, NULL, NULL);
|
||||
|
||||
setup_reply(header, flags, ede);
|
||||
|
||||
if (!(p = skip_questions(header, size)))
|
||||
return 0;
|
||||
|
||||
if (flags & gotname & F_IPV4)
|
||||
for (start = first; start != last; start++)
|
||||
{
|
||||
struct serv_addr4 *srv = (struct serv_addr4 *)daemon->serverarray[start];
|
||||
|
||||
if (srv->flags & SERV_ALL_ZEROS)
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
else
|
||||
addr.addr4 = srv->addr;
|
||||
|
||||
header->ancount = htons(ntohs(header->ancount) + 1);
|
||||
add_resource_record(header, limit, &trunc, sizeof(struct dns_header), &p, daemon->local_ttl, NULL, T_A, C_IN, "4", &addr);
|
||||
log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV6, name, (union all_addr *)&addr, NULL);
|
||||
}
|
||||
|
||||
if (flags & gotname & F_IPV6)
|
||||
for (start = first; start != last; start++)
|
||||
{
|
||||
struct serv_addr6 *srv = (struct serv_addr6 *)daemon->serverarray[start];
|
||||
|
||||
if (srv->flags & SERV_ALL_ZEROS)
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
else
|
||||
addr.addr6 = srv->addr;
|
||||
|
||||
header->ancount = htons(ntohs(header->ancount) + 1);
|
||||
add_resource_record(header, limit, &trunc, sizeof(struct dns_header), &p, daemon->local_ttl, NULL, T_AAAA, C_IN, "6", &addr);
|
||||
log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV4, name, (union all_addr *)&addr, NULL);
|
||||
}
|
||||
|
||||
if (trunc)
|
||||
header->hb3 |= HB3_TC;
|
||||
|
||||
return p - (unsigned char *)header;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
int dnssec_server(struct server *server, char *keyname, int *firstp, int *lastp)
|
||||
{
|
||||
int first, last, index;
|
||||
|
||||
/* Find server to send DNSSEC query to. This will normally be the
|
||||
same as for the original query, but may be another if
|
||||
servers for domains are involved. */
|
||||
if (!lookup_domain(keyname, F_DNSSECOK, &first, &last))
|
||||
return -1;
|
||||
|
||||
for (index = first; index != last; index++)
|
||||
if (daemon->serverarray[index] == server)
|
||||
break;
|
||||
|
||||
/* No match to server used for original query.
|
||||
Use newly looked up set. */
|
||||
if (index == last)
|
||||
index = daemon->serverarray[first]->last_server == -1 ?
|
||||
first : daemon->serverarray[first]->last_server;
|
||||
|
||||
if (firstp)
|
||||
*firstp = first;
|
||||
|
||||
if (lastp)
|
||||
*lastp = last;
|
||||
|
||||
return index;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* order by size, then by dictionary order */
|
||||
static int order(char *qdomain, size_t qlen, struct server *serv)
|
||||
{
|
||||
size_t dlen = 0;
|
||||
|
||||
/* servers for dotless names always sort last
|
||||
searched for name is never dotless. */
|
||||
if (serv->flags & SERV_FOR_NODOTS)
|
||||
return -1;
|
||||
|
||||
dlen = serv->domain_len;
|
||||
|
||||
if (qlen < dlen)
|
||||
return 1;
|
||||
|
||||
if (qlen > dlen)
|
||||
return -1;
|
||||
|
||||
return strcmp(qdomain, serv->domain);
|
||||
}
|
||||
|
||||
static int order_servers(struct server *s1, struct server *s2)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* need full comparison of dotless servers in
|
||||
order_qsort() and filter_servers() */
|
||||
|
||||
if (s1->flags & SERV_FOR_NODOTS)
|
||||
return (s2->flags & SERV_FOR_NODOTS) ? 0 : 1;
|
||||
|
||||
if ((rc = order(s1->domain, s1->domain_len, s2)) != 0)
|
||||
return rc;
|
||||
|
||||
/* For identical domains, sort wildcard ones first */
|
||||
if (s1->flags & SERV_WILDCARD)
|
||||
return (s2->flags & SERV_WILDCARD) ? 0 : 1;
|
||||
|
||||
return (s2->flags & SERV_WILDCARD) ? -1 : 0;
|
||||
}
|
||||
|
||||
static int order_qsort(const void *a, const void *b)
|
||||
{
|
||||
int rc;
|
||||
|
||||
struct server *s1 = *((struct server **)a);
|
||||
struct server *s2 = *((struct server **)b);
|
||||
|
||||
rc = order_servers(s1, s2);
|
||||
|
||||
/* Sort all literal NODATA and local IPV4 or IPV6 responses together,
|
||||
in a very specific order. We flip the SERV_LITERAL_ADDRESS bit
|
||||
so the order is IPv6 literal, IPv4 literal, all-zero literal,
|
||||
upstream server, NXDOMAIN literal. */
|
||||
if (rc == 0)
|
||||
rc = ((s2->flags & (SERV_LITERAL_ADDRESS | SERV_4ADDR | SERV_6ADDR | SERV_ALL_ZEROS)) ^ SERV_LITERAL_ADDRESS) -
|
||||
((s1->flags & (SERV_LITERAL_ADDRESS | SERV_4ADDR | SERV_6ADDR | SERV_ALL_ZEROS)) ^ SERV_LITERAL_ADDRESS);
|
||||
|
||||
/* Finally, order by appearance in /etc/resolv.conf etc, for --strict-order */
|
||||
if (rc == 0)
|
||||
if (!(s1->flags & SERV_LITERAL_ADDRESS))
|
||||
rc = s1->serial - s2->serial;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void mark_servers(int flag)
|
||||
{
|
||||
struct server *serv;
|
||||
|
||||
/* mark everything with argument flag */
|
||||
for (serv = daemon->servers; serv; serv = serv->next)
|
||||
if (serv->flags & flag)
|
||||
serv->flags |= SERV_MARK;
|
||||
else
|
||||
serv->flags &= ~SERV_MARK;
|
||||
|
||||
for (serv = daemon->local_domains; serv; serv = serv->next)
|
||||
if (serv->flags & flag)
|
||||
serv->flags |= SERV_MARK;
|
||||
else
|
||||
serv->flags &= ~SERV_MARK;
|
||||
}
|
||||
|
||||
void cleanup_servers(void)
|
||||
{
|
||||
struct server *serv, *tmp, **up;
|
||||
|
||||
/* unlink and free anything still marked. */
|
||||
for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp)
|
||||
{
|
||||
tmp = serv->next;
|
||||
if (serv->flags & SERV_MARK)
|
||||
{
|
||||
server_gone(serv);
|
||||
*up = serv->next;
|
||||
free(serv->domain);
|
||||
free(serv);
|
||||
}
|
||||
else
|
||||
up = &serv->next;
|
||||
}
|
||||
|
||||
for (serv = daemon->local_domains, up = &daemon->local_domains; serv; serv = tmp)
|
||||
{
|
||||
tmp = serv->next;
|
||||
if (serv->flags & SERV_MARK)
|
||||
{
|
||||
*up = serv->next;
|
||||
free(serv->domain);
|
||||
free(serv);
|
||||
}
|
||||
else
|
||||
up = &serv->next;
|
||||
}
|
||||
}
|
||||
|
||||
int add_update_server(int flags,
|
||||
union mysockaddr *addr,
|
||||
union mysockaddr *source_addr,
|
||||
const char *interface,
|
||||
const char *domain,
|
||||
union all_addr *local_addr)
|
||||
{
|
||||
struct server *serv = NULL;
|
||||
char *alloc_domain;
|
||||
|
||||
if (!domain)
|
||||
domain = "";
|
||||
|
||||
/* .domain == domain, for historical reasons. */
|
||||
if (*domain == '.')
|
||||
while (*domain == '.') domain++;
|
||||
else if (*domain == '*')
|
||||
{
|
||||
domain++;
|
||||
if (*domain != 0)
|
||||
flags |= SERV_WILDCARD;
|
||||
}
|
||||
|
||||
if (*domain == 0)
|
||||
alloc_domain = whine_malloc(1);
|
||||
else if (!(alloc_domain = canonicalise((char *)domain, NULL)))
|
||||
return 0;
|
||||
|
||||
/* See if there is a suitable candidate, and unmark
|
||||
only do this for forwarding servers, not
|
||||
address or local, to avoid delays on large numbers. */
|
||||
if (flags & SERV_IS_LOCAL)
|
||||
for (serv = daemon->servers; serv; serv = serv->next)
|
||||
if ((serv->flags & SERV_MARK) &&
|
||||
hostname_isequal(alloc_domain, serv->domain))
|
||||
break;
|
||||
|
||||
if (serv)
|
||||
{
|
||||
free(alloc_domain);
|
||||
alloc_domain = serv->domain;
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t size;
|
||||
|
||||
if (flags & SERV_LITERAL_ADDRESS)
|
||||
{
|
||||
if (flags & SERV_6ADDR)
|
||||
size = sizeof(struct serv_addr6);
|
||||
else if (flags & SERV_4ADDR)
|
||||
size = sizeof(struct serv_addr4);
|
||||
else
|
||||
size = sizeof(struct serv_local);
|
||||
}
|
||||
else
|
||||
size = sizeof(struct server);
|
||||
|
||||
if (!(serv = whine_malloc(size)))
|
||||
return 0;
|
||||
|
||||
if (flags & SERV_IS_LOCAL)
|
||||
{
|
||||
serv->next = daemon->local_domains;
|
||||
daemon->local_domains = serv;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct server *s;
|
||||
/* Add to the end of the chain, for order */
|
||||
if (!daemon->servers)
|
||||
daemon->servers = serv;
|
||||
else
|
||||
{
|
||||
for (s = daemon->servers; s->next; s = s->next);
|
||||
s->next = serv;
|
||||
}
|
||||
|
||||
serv->next = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(flags & SERV_IS_LOCAL))
|
||||
memset(serv, 0, sizeof(struct server));
|
||||
|
||||
serv->flags = flags;
|
||||
serv->domain = alloc_domain;
|
||||
serv->domain_len = strlen(alloc_domain);
|
||||
|
||||
if (flags & SERV_4ADDR)
|
||||
((struct serv_addr4*)serv)->addr = local_addr->addr4;
|
||||
|
||||
if (flags & SERV_6ADDR)
|
||||
((struct serv_addr6*)serv)->addr = local_addr->addr6;
|
||||
|
||||
if (!(flags & SERV_IS_LOCAL))
|
||||
{
|
||||
#ifdef HAVE_LOOP
|
||||
serv->uid = rand32();
|
||||
#endif
|
||||
|
||||
if (interface)
|
||||
safe_strncpy(serv->interface, interface, sizeof(serv->interface));
|
||||
if (addr)
|
||||
serv->addr = *addr;
|
||||
if (source_addr)
|
||||
serv->source_addr = *source_addr;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
70
src/domain.c
70
src/domain.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -18,8 +18,9 @@
|
||||
|
||||
|
||||
static struct cond_domain *search_domain(struct in_addr addr, struct cond_domain *c);
|
||||
static int match_domain(struct in_addr addr, struct cond_domain *c);
|
||||
static struct cond_domain *search_domain6(struct in6_addr *addr, struct cond_domain *c);
|
||||
|
||||
static int match_domain6(struct in6_addr *addr, struct cond_domain *c);
|
||||
|
||||
int is_name_synthetic(int flags, char *name, union all_addr *addr)
|
||||
{
|
||||
@@ -135,28 +136,9 @@ int is_name_synthetic(int flags, char *name, union all_addr *addr)
|
||||
}
|
||||
|
||||
if (hostname_isequal(c->domain, p+1) && inet_pton(prot, tail, addr))
|
||||
{
|
||||
if (prot == AF_INET)
|
||||
{
|
||||
if (!c->is6 &&
|
||||
ntohl(addr->addr4.s_addr) >= ntohl(c->start.s_addr) &&
|
||||
ntohl(addr->addr4.s_addr) <= ntohl(c->end.s_addr))
|
||||
found = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
u64 addrpart = addr6part(&addr->addr6);
|
||||
|
||||
if (c->is6 &&
|
||||
is_same_net6(&addr->addr6, &c->start6, 64) &&
|
||||
addrpart >= addr6part(&c->start6) &&
|
||||
addrpart <= addr6part(&c->end6))
|
||||
found = 1;
|
||||
}
|
||||
}
|
||||
|
||||
found = (prot == AF_INET) ? match_domain(addr->addr4, c) : match_domain6(&addr->addr6, c);
|
||||
}
|
||||
|
||||
|
||||
/* restore name */
|
||||
for (p = tail; *p; p++)
|
||||
if (*p == '.' || *p == ':')
|
||||
@@ -246,14 +228,22 @@ int is_rev_synth(int flag, union all_addr *addr, char *name)
|
||||
}
|
||||
|
||||
|
||||
static int match_domain(struct in_addr addr, struct cond_domain *c)
|
||||
{
|
||||
if (!c->is6 &&
|
||||
ntohl(addr.s_addr) >= ntohl(c->start.s_addr) &&
|
||||
ntohl(addr.s_addr) <= ntohl(c->end.s_addr))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct cond_domain *search_domain(struct in_addr addr, struct cond_domain *c)
|
||||
{
|
||||
for (; c; c = c->next)
|
||||
if (!c->is6 &&
|
||||
ntohl(addr.s_addr) >= ntohl(c->start.s_addr) &&
|
||||
ntohl(addr.s_addr) <= ntohl(c->end.s_addr))
|
||||
if (match_domain(addr, c))
|
||||
return c;
|
||||
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -267,16 +257,30 @@ char *get_domain(struct in_addr addr)
|
||||
return daemon->domain_suffix;
|
||||
}
|
||||
|
||||
|
||||
static struct cond_domain *search_domain6(struct in6_addr *addr, struct cond_domain *c)
|
||||
static int match_domain6(struct in6_addr *addr, struct cond_domain *c)
|
||||
{
|
||||
u64 addrpart = addr6part(addr);
|
||||
|
||||
if (c->is6)
|
||||
{
|
||||
if (c->prefixlen >= 64)
|
||||
{
|
||||
if (is_same_net6(addr, &c->start6, 64) &&
|
||||
addrpart >= addr6part(&c->start6) &&
|
||||
addrpart <= addr6part(&c->end6))
|
||||
return 1;
|
||||
}
|
||||
else if (is_same_net6(addr, &c->start6, c->prefixlen))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct cond_domain *search_domain6(struct in6_addr *addr, struct cond_domain *c)
|
||||
{
|
||||
for (; c; c = c->next)
|
||||
if (c->is6 &&
|
||||
is_same_net6(addr, &c->start6, 64) &&
|
||||
addrpart >= addr6part(&c->start6) &&
|
||||
addrpart <= addr6part(&c->end6))
|
||||
if (match_domain6(addr, c))
|
||||
return c;
|
||||
|
||||
return NULL;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
65
src/edns0.c
65
src/edns0.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -427,6 +427,66 @@ int check_source(struct dns_header *header, size_t plen, unsigned char *pseudohe
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* See https://docs.umbrella.com/umbrella-api/docs/identifying-dns-traffic for
|
||||
* detailed information on packet formating.
|
||||
*/
|
||||
#define UMBRELLA_VERSION 1
|
||||
#define UMBRELLA_TYPESZ 2
|
||||
|
||||
#define UMBRELLA_ASSET 0x0004
|
||||
#define UMBRELLA_ASSETSZ sizeof(daemon->umbrella_asset)
|
||||
#define UMBRELLA_ORG 0x0008
|
||||
#define UMBRELLA_ORGSZ sizeof(daemon->umbrella_org)
|
||||
#define UMBRELLA_IPV4 0x0010
|
||||
#define UMBRELLA_IPV6 0x0020
|
||||
#define UMBRELLA_DEVICE 0x0040
|
||||
#define UMBRELLA_DEVICESZ sizeof(daemon->umbrella_device)
|
||||
|
||||
struct umbrella_opt {
|
||||
u8 magic[4];
|
||||
u8 version;
|
||||
u8 flags;
|
||||
/* We have 4 possible fields since we'll never send both IPv4 and
|
||||
* IPv6, so using the larger of the two to calculate max buffer size.
|
||||
* Each field also has a type header. So the following accounts for
|
||||
* the type headers and each field size to get a max buffer size.
|
||||
*/
|
||||
u8 fields[4 * UMBRELLA_TYPESZ + UMBRELLA_ORGSZ + IN6ADDRSZ + UMBRELLA_DEVICESZ + UMBRELLA_ASSETSZ];
|
||||
};
|
||||
|
||||
static size_t add_umbrella_opt(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *source, int *cacheable)
|
||||
{
|
||||
*cacheable = 0;
|
||||
|
||||
struct umbrella_opt opt = {{"ODNS"}, UMBRELLA_VERSION, 0, {}};
|
||||
u8 *u = &opt.fields[0];
|
||||
|
||||
if (daemon->umbrella_org) {
|
||||
PUTSHORT(UMBRELLA_ORG, u);
|
||||
PUTLONG(daemon->umbrella_org, u);
|
||||
}
|
||||
|
||||
int family = source->sa.sa_family;
|
||||
PUTSHORT(family == AF_INET ? UMBRELLA_IPV4 : UMBRELLA_IPV6, u);
|
||||
int size = family == AF_INET ? INADDRSZ : IN6ADDRSZ;
|
||||
memcpy(u, get_addrp(source, family), size);
|
||||
u += size;
|
||||
|
||||
if (option_bool(OPT_UMBRELLA_DEVID)) {
|
||||
PUTSHORT(UMBRELLA_DEVICE, u);
|
||||
memcpy(u, (char *)&daemon->umbrella_device, UMBRELLA_DEVICESZ);
|
||||
u += UMBRELLA_DEVICESZ;
|
||||
}
|
||||
|
||||
if (daemon->umbrella_asset) {
|
||||
PUTSHORT(UMBRELLA_ASSET, u);
|
||||
PUTLONG(daemon->umbrella_asset, u);
|
||||
}
|
||||
|
||||
int len = u - &opt.magic[0];
|
||||
return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, EDNS0_OPTION_UMBRELLA, (unsigned char *)&opt, len, 0, 1);
|
||||
}
|
||||
|
||||
/* Set *check_subnet if we add a client subnet option, which needs to checked
|
||||
in the reply. Set *cacheable to zero if we add an option which the answer
|
||||
may depend on. */
|
||||
@@ -445,6 +505,9 @@ size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *l
|
||||
if (daemon->dns_client_id)
|
||||
plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMCPEID,
|
||||
(unsigned char *)daemon->dns_client_id, strlen(daemon->dns_client_id), 0, 1);
|
||||
|
||||
if (option_bool(OPT_UMBRELLA))
|
||||
plen = add_umbrella_opt(header, plen, limit, source, cacheable);
|
||||
|
||||
if (option_bool(OPT_CLIENT_SUBNET))
|
||||
{
|
||||
|
||||
2863
src/forward.c
2863
src/forward.c
File diff suppressed because it is too large
Load Diff
@@ -28,28 +28,28 @@
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
#if defined(HAVE_DNSSEC) || defined(HAVE_NETTLEHASH)
|
||||
#if defined(HAVE_DNSSEC) || defined(HAVE_CRYPTOHASH)
|
||||
|
||||
static const struct nettle_hash *hash;
|
||||
static void *ctx;
|
||||
static unsigned char *digest;
|
||||
|
||||
void hash_questions_init(void)
|
||||
{
|
||||
if (!(hash = hash_find("sha256")))
|
||||
die(_("Failed to create SHA-256 hash object"), NULL, EC_MISC);
|
||||
|
||||
ctx = safe_malloc(hash->context_size);
|
||||
digest = safe_malloc(hash->digest_size);
|
||||
}
|
||||
|
||||
unsigned char *hash_questions(struct dns_header *header, size_t plen, char *name)
|
||||
{
|
||||
int q;
|
||||
unsigned char *p = (unsigned char *)(header+1);
|
||||
const struct nettle_hash *hash;
|
||||
void *ctx;
|
||||
unsigned char *digest;
|
||||
|
||||
if (!(hash = hash_find("sha256")) || !hash_init(hash, &ctx, &digest))
|
||||
{
|
||||
/* don't think this can ever happen. */
|
||||
static unsigned char dummy[HASH_SIZE];
|
||||
static int warned = 0;
|
||||
|
||||
if (!warned)
|
||||
my_syslog(LOG_ERR, _("Failed to create SHA-256 hash object"));
|
||||
warned = 1;
|
||||
|
||||
return dummy;
|
||||
}
|
||||
|
||||
hash->init(ctx);
|
||||
|
||||
for (q = ntohs(header->qdcount); q != 0; q--)
|
||||
{
|
||||
char *cp, c;
|
||||
@@ -74,7 +74,7 @@ unsigned char *hash_questions(struct dns_header *header, size_t plen, char *name
|
||||
return digest;
|
||||
}
|
||||
|
||||
#else /* HAVE_DNSSEC */
|
||||
#else /* HAVE_DNSSEC || HAVE_CRYPTOHASH */
|
||||
|
||||
#define SHA256_BLOCK_SIZE 32 // SHA256 outputs a 32 byte digest
|
||||
typedef unsigned char BYTE; // 8-bit byte
|
||||
@@ -91,6 +91,9 @@ static void sha256_init(SHA256_CTX *ctx);
|
||||
static void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len);
|
||||
static void sha256_final(SHA256_CTX *ctx, BYTE hash[]);
|
||||
|
||||
void hash_questions_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
unsigned char *hash_questions(struct dns_header *header, size_t plen, char *name)
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -235,7 +235,6 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
|
||||
}
|
||||
else
|
||||
continue;
|
||||
|
||||
|
||||
/* stringify MAC into dhcp_buff */
|
||||
p = daemon->dhcp_buff;
|
||||
@@ -882,7 +881,4 @@ void helper_write(void)
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#endif /* HAVE_SCRIPT */
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -295,4 +295,3 @@ int inotify_check(time_t now)
|
||||
}
|
||||
|
||||
#endif /* INOTIFY */
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -31,4 +31,3 @@
|
||||
&& ((__const uint32_t *) (a))[1] == 0 \
|
||||
&& ((__const uint32_t *) (a))[2] == 0 \
|
||||
&& ((__const uint32_t *) (a))[3] == 0)
|
||||
|
||||
|
||||
11
src/lease.c
11
src/lease.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -378,7 +378,7 @@ void lease_update_file(time_t now)
|
||||
if (next_event == 0 || difftime(next_event, LEASE_RETRY + now) > 0.0)
|
||||
next_event = LEASE_RETRY + now;
|
||||
|
||||
my_syslog(MS_DHCP | LOG_ERR, _("failed to write %s: %s (retry in %us)"),
|
||||
my_syslog(MS_DHCP | LOG_ERR, _("failed to write %s: %s (retry in %u s)"),
|
||||
daemon->lease_file, strerror(err),
|
||||
(unsigned int)difftime(next_event, now));
|
||||
}
|
||||
@@ -1021,6 +1021,7 @@ void lease_set_hostname(struct dhcp_lease *lease, const char *name, int auth, ch
|
||||
}
|
||||
|
||||
kill_name(lease_tmp);
|
||||
lease_tmp->flags |= LEASE_CHANGED; /* run script on change */
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1201,8 +1202,4 @@ void lease_add_extradata(struct dhcp_lease *lease, unsigned char *data, unsigned
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
#endif /* HAVE_DHCP */
|
||||
|
||||
12
src/log.c
12
src/log.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -273,7 +273,7 @@ static void log_write(void)
|
||||
/* priority is one of LOG_DEBUG, LOG_INFO, LOG_NOTICE, etc. See sys/syslog.h.
|
||||
OR'd to priority can be MS_TFTP, MS_DHCP, ... to be able to do log separation between
|
||||
DNS, DHCP and TFTP services.
|
||||
*/
|
||||
If OR'd with MS_DEBUG, the messages are suppressed unless --log-debug is set. */
|
||||
void my_syslog(int priority, const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
@@ -290,7 +290,13 @@ void my_syslog(int priority, const char *format, ...)
|
||||
func = "-dhcp";
|
||||
else if ((LOG_FACMASK & priority) == MS_SCRIPT)
|
||||
func = "-script";
|
||||
|
||||
else if ((LOG_FACMASK & priority) == MS_DEBUG)
|
||||
{
|
||||
if (!option_bool(OPT_LOG_DEBUG))
|
||||
return;
|
||||
func = "-debug";
|
||||
}
|
||||
|
||||
#ifdef LOG_PRI
|
||||
priority = LOG_PRI(priority);
|
||||
#else
|
||||
|
||||
44
src/loop.c
44
src/loop.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -22,6 +22,7 @@ static ssize_t loop_make_probe(u32 uid);
|
||||
void loop_send_probes()
|
||||
{
|
||||
struct server *serv;
|
||||
struct randfd_list *rfds = NULL;
|
||||
|
||||
if (!option_bool(OPT_LOOP_DETECT))
|
||||
return;
|
||||
@@ -29,34 +30,29 @@ void loop_send_probes()
|
||||
/* Loop through all upstream servers not for particular domains, and send a query to that server which is
|
||||
identifiable, via the uid. If we see that query back again, then the server is looping, and we should not use it. */
|
||||
for (serv = daemon->servers; serv; serv = serv->next)
|
||||
if (!(serv->flags &
|
||||
(SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND | SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_LOOP)))
|
||||
if (strlen(serv->domain) == 0 &&
|
||||
!(serv->flags & (SERV_FOR_NODOTS)))
|
||||
{
|
||||
ssize_t len = loop_make_probe(serv->uid);
|
||||
int fd;
|
||||
struct randfd *rfd = NULL;
|
||||
|
||||
if (serv->sfd)
|
||||
fd = serv->sfd->fd;
|
||||
else
|
||||
{
|
||||
if (!(rfd = allocate_rfd(serv->addr.sa.sa_family)))
|
||||
continue;
|
||||
fd = rfd->fd;
|
||||
}
|
||||
serv->flags &= ~SERV_LOOP;
|
||||
|
||||
if ((fd = allocate_rfd(&rfds, serv)) == -1)
|
||||
continue;
|
||||
|
||||
while (retry_send(sendto(fd, daemon->packet, len, 0,
|
||||
&serv->addr.sa, sa_len(&serv->addr))));
|
||||
|
||||
free_rfd(rfd);
|
||||
}
|
||||
|
||||
free_rfds(&rfds);
|
||||
}
|
||||
|
||||
static ssize_t loop_make_probe(u32 uid)
|
||||
{
|
||||
struct dns_header *header = (struct dns_header *)daemon->packet;
|
||||
unsigned char *p = (unsigned char *)(header+1);
|
||||
|
||||
|
||||
/* packet buffer overwritten */
|
||||
daemon->srv_save = NULL;
|
||||
|
||||
@@ -102,15 +98,15 @@ int detect_loop(char *query, int type)
|
||||
uid = strtol(query, NULL, 16);
|
||||
|
||||
for (serv = daemon->servers; serv; serv = serv->next)
|
||||
if (!(serv->flags &
|
||||
(SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND | SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_LOOP)) &&
|
||||
uid == serv->uid)
|
||||
{
|
||||
serv->flags |= SERV_LOOP;
|
||||
check_servers(); /* log new state */
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (strlen(serv->domain) == 0 &&
|
||||
!(serv->flags & SERV_LOOP) &&
|
||||
uid == serv->uid)
|
||||
{
|
||||
serv->flags |= SERV_LOOP;
|
||||
check_servers(1); /* log new state - don't send more probes. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -41,19 +41,26 @@
|
||||
|
||||
#ifndef NDA_RTA
|
||||
# define NDA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg))))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Used to request refresh of addresses or routes just once,
|
||||
* when multiple changes might be announced. */
|
||||
enum async_states {
|
||||
STATE_NEWADDR = (1 << 0),
|
||||
STATE_NEWROUTE = (1 << 1),
|
||||
};
|
||||
|
||||
|
||||
static struct iovec iov;
|
||||
static u32 netlink_pid;
|
||||
|
||||
static void nl_async(struct nlmsghdr *h);
|
||||
static unsigned nl_async(struct nlmsghdr *h, unsigned state);
|
||||
static void nl_multicast_state(unsigned state);
|
||||
|
||||
char *netlink_init(void)
|
||||
{
|
||||
struct sockaddr_nl addr;
|
||||
socklen_t slen = sizeof(addr);
|
||||
int opt = 1;
|
||||
|
||||
addr.nl_family = AF_NETLINK;
|
||||
addr.nl_pad = 0;
|
||||
@@ -92,14 +99,10 @@ char *netlink_init(void)
|
||||
iov.iov_len = 100;
|
||||
iov.iov_base = safe_malloc(iov.iov_len);
|
||||
|
||||
if (daemon->kernel_version >= KERNEL_VERSION(2,6,30) &&
|
||||
setsockopt(daemon->netlinkfd, SOL_NETLINK, NETLINK_NO_ENOBUFS, &opt, sizeof(opt)) == -1)
|
||||
return _("warning: failed to set NETLINK_NO_ENOBUFS on netlink socket");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static ssize_t netlink_recv(void)
|
||||
static ssize_t netlink_recv(int flags)
|
||||
{
|
||||
struct msghdr msg;
|
||||
struct sockaddr_nl nladdr;
|
||||
@@ -115,7 +118,8 @@ static ssize_t netlink_recv(void)
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_flags = 0;
|
||||
|
||||
while ((rc = recvmsg(daemon->netlinkfd, &msg, MSG_PEEK | MSG_TRUNC)) == -1 && errno == EINTR);
|
||||
while ((rc = recvmsg(daemon->netlinkfd, &msg, flags | MSG_PEEK | MSG_TRUNC)) == -1 &&
|
||||
errno == EINTR);
|
||||
|
||||
/* make buffer big enough */
|
||||
if (rc != -1 && (msg.msg_flags & MSG_TRUNC))
|
||||
@@ -132,7 +136,7 @@ static ssize_t netlink_recv(void)
|
||||
|
||||
/* read it for real */
|
||||
msg.msg_flags = 0;
|
||||
while ((rc = recvmsg(daemon->netlinkfd, &msg, 0)) == -1 && errno == EINTR);
|
||||
while ((rc = recvmsg(daemon->netlinkfd, &msg, flags)) == -1 && errno == EINTR);
|
||||
|
||||
/* Make sure this is from the kernel */
|
||||
if (rc == -1 || nladdr.nl_pid == 0)
|
||||
@@ -151,7 +155,9 @@ static ssize_t netlink_recv(void)
|
||||
|
||||
|
||||
/* family = AF_UNSPEC finds ARP table entries.
|
||||
family = AF_LOCAL finds MAC addresses. */
|
||||
family = AF_LOCAL finds MAC addresses.
|
||||
returns 0 on failure, 1 on success, -1 when restart is required
|
||||
*/
|
||||
int iface_enumerate(int family, void *parm, int (*callback)())
|
||||
{
|
||||
struct sockaddr_nl addr;
|
||||
@@ -159,6 +165,7 @@ int iface_enumerate(int family, void *parm, int (*callback)())
|
||||
ssize_t len;
|
||||
static unsigned int seq = 0;
|
||||
int callback_ok = 1;
|
||||
unsigned state = 0;
|
||||
|
||||
struct {
|
||||
struct nlmsghdr nlh;
|
||||
@@ -170,7 +177,6 @@ int iface_enumerate(int family, void *parm, int (*callback)())
|
||||
|
||||
addr.nl_family = AF_NETLINK;
|
||||
|
||||
again:
|
||||
if (family == AF_UNSPEC)
|
||||
req.nlh.nlmsg_type = RTM_GETNEIGH;
|
||||
else if (family == AF_LOCAL)
|
||||
@@ -193,12 +199,12 @@ int iface_enumerate(int family, void *parm, int (*callback)())
|
||||
|
||||
while (1)
|
||||
{
|
||||
if ((len = netlink_recv()) == -1)
|
||||
if ((len = netlink_recv(0)) == -1)
|
||||
{
|
||||
if (errno == ENOBUFS)
|
||||
{
|
||||
sleep(1);
|
||||
goto again;
|
||||
nl_multicast_state(state);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -207,7 +213,7 @@ int iface_enumerate(int family, void *parm, int (*callback)())
|
||||
if (h->nlmsg_pid != netlink_pid || h->nlmsg_type == NLMSG_ERROR)
|
||||
{
|
||||
/* May be multicast arriving async */
|
||||
nl_async(h);
|
||||
state = nl_async(h, state);
|
||||
}
|
||||
else if (h->nlmsg_seq != seq)
|
||||
{
|
||||
@@ -341,26 +347,28 @@ int iface_enumerate(int family, void *parm, int (*callback)())
|
||||
}
|
||||
}
|
||||
|
||||
void netlink_multicast(void)
|
||||
static void nl_multicast_state(unsigned state)
|
||||
{
|
||||
ssize_t len;
|
||||
struct nlmsghdr *h;
|
||||
int flags;
|
||||
|
||||
do {
|
||||
/* don't risk blocking reading netlink messages here. */
|
||||
while ((len = netlink_recv(MSG_DONTWAIT)) != -1)
|
||||
|
||||
/* don't risk blocking reading netlink messages here. */
|
||||
if ((flags = fcntl(daemon->netlinkfd, F_GETFL)) == -1 ||
|
||||
fcntl(daemon->netlinkfd, F_SETFL, flags | O_NONBLOCK) == -1)
|
||||
return;
|
||||
|
||||
if ((len = netlink_recv()) != -1)
|
||||
for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len))
|
||||
nl_async(h);
|
||||
|
||||
/* restore non-blocking status */
|
||||
fcntl(daemon->netlinkfd, F_SETFL, flags);
|
||||
for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len))
|
||||
state = nl_async(h, state);
|
||||
} while (errno == ENOBUFS);
|
||||
}
|
||||
|
||||
static void nl_async(struct nlmsghdr *h)
|
||||
void netlink_multicast(void)
|
||||
{
|
||||
unsigned state = 0;
|
||||
nl_multicast_state(state);
|
||||
}
|
||||
|
||||
|
||||
static unsigned nl_async(struct nlmsghdr *h, unsigned state)
|
||||
{
|
||||
if (h->nlmsg_type == NLMSG_ERROR)
|
||||
{
|
||||
@@ -368,7 +376,8 @@ static void nl_async(struct nlmsghdr *h)
|
||||
if (err->error != 0)
|
||||
my_syslog(LOG_ERR, _("netlink returns error: %s"), strerror(-(err->error)));
|
||||
}
|
||||
else if (h->nlmsg_pid == 0 && h->nlmsg_type == RTM_NEWROUTE)
|
||||
else if (h->nlmsg_pid == 0 && h->nlmsg_type == RTM_NEWROUTE &&
|
||||
(state & STATE_NEWROUTE)==0)
|
||||
{
|
||||
/* We arrange to receive netlink multicast messages whenever the network route is added.
|
||||
If this happens and we still have a DNS packet in the buffer, we re-send it.
|
||||
@@ -380,11 +389,17 @@ static void nl_async(struct nlmsghdr *h)
|
||||
if (rtm->rtm_type == RTN_UNICAST && rtm->rtm_scope == RT_SCOPE_LINK &&
|
||||
(rtm->rtm_table == RT_TABLE_MAIN ||
|
||||
rtm->rtm_table == RT_TABLE_LOCAL))
|
||||
queue_event(EVENT_NEWROUTE);
|
||||
{
|
||||
queue_event(EVENT_NEWROUTE);
|
||||
state |= STATE_NEWROUTE;
|
||||
}
|
||||
}
|
||||
else if (h->nlmsg_type == RTM_NEWADDR || h->nlmsg_type == RTM_DELADDR)
|
||||
queue_event(EVENT_NEWADDR);
|
||||
else if ((h->nlmsg_type == RTM_NEWADDR || h->nlmsg_type == RTM_DELADDR) &&
|
||||
(state & STATE_NEWADDR)==0)
|
||||
{
|
||||
queue_event(EVENT_NEWADDR);
|
||||
state |= STATE_NEWADDR;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* HAVE_LINUX_NETWORK */
|
||||
|
||||
655
src/network.c
655
src/network.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -31,7 +31,7 @@ int indextoname(int fd, int index, char *name)
|
||||
|
||||
safe_strncpy(name, ifr.ifr_name, IF_NAMESIZE);
|
||||
|
||||
return 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -232,7 +232,7 @@ static int iface_allowed(struct iface_param *param, int if_index, char *label,
|
||||
union mysockaddr *addr, struct in_addr netmask, int prefixlen, int iface_flags)
|
||||
{
|
||||
struct irec *iface;
|
||||
int mtu = 0, loopback;
|
||||
int loopback;
|
||||
struct ifreq ifr;
|
||||
int tftp_ok = !!option_bool(OPT_TFTP);
|
||||
int dhcp_ok = 1;
|
||||
@@ -253,9 +253,6 @@ static int iface_allowed(struct iface_param *param, int if_index, char *label,
|
||||
if (loopback)
|
||||
dhcp_ok = 0;
|
||||
|
||||
if (ioctl(param->fd, SIOCGIFMTU, &ifr) != -1)
|
||||
mtu = ifr.ifr_mtu;
|
||||
|
||||
if (!label)
|
||||
label = ifr.ifr_name;
|
||||
else
|
||||
@@ -351,36 +348,109 @@ static int iface_allowed(struct iface_param *param, int if_index, char *label,
|
||||
/* Update addresses from interface_names. These are a set independent
|
||||
of the set we're listening on. */
|
||||
for (int_name = daemon->int_names; int_name; int_name = int_name->next)
|
||||
if (strncmp(label, int_name->intr, IF_NAMESIZE) == 0 &&
|
||||
(addr->sa.sa_family == int_name->family || int_name->family == 0))
|
||||
if (strncmp(label, int_name->intr, IF_NAMESIZE) == 0)
|
||||
{
|
||||
if (param->spare)
|
||||
struct addrlist *lp;
|
||||
|
||||
al = NULL;
|
||||
|
||||
if (addr->sa.sa_family == AF_INET && (int_name->flags & (IN4 | INP4)))
|
||||
{
|
||||
al = param->spare;
|
||||
param->spare = al->next;
|
||||
struct in_addr newaddr = addr->in.sin_addr;
|
||||
|
||||
if (int_name->flags & INP4)
|
||||
{
|
||||
if (netmask.s_addr == 0xffff)
|
||||
continue;
|
||||
|
||||
newaddr.s_addr = (addr->in.sin_addr.s_addr & netmask.s_addr) |
|
||||
(int_name->proto4.s_addr & ~netmask.s_addr);
|
||||
}
|
||||
|
||||
/* check for duplicates. */
|
||||
for (lp = int_name->addr; lp; lp = lp->next)
|
||||
if (lp->flags == 0 && lp->addr.addr4.s_addr == newaddr.s_addr)
|
||||
break;
|
||||
|
||||
if (!lp)
|
||||
{
|
||||
if (param->spare)
|
||||
{
|
||||
al = param->spare;
|
||||
param->spare = al->next;
|
||||
}
|
||||
else
|
||||
al = whine_malloc(sizeof(struct addrlist));
|
||||
|
||||
if (al)
|
||||
{
|
||||
al->flags = 0;
|
||||
al->addr.addr4 = newaddr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (addr->sa.sa_family == AF_INET6 && (int_name->flags & (IN6 | INP6)))
|
||||
{
|
||||
struct in6_addr newaddr = addr->in6.sin6_addr;
|
||||
|
||||
if (int_name->flags & INP6)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* No sense in doing /128. */
|
||||
if (prefixlen == 128)
|
||||
continue;
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
int bits = ((i+1)*8) - prefixlen;
|
||||
|
||||
if (bits >= 8)
|
||||
newaddr.s6_addr[i] = int_name->proto6.s6_addr[i];
|
||||
else if (bits >= 0)
|
||||
{
|
||||
unsigned char mask = 0xff << bits;
|
||||
newaddr.s6_addr[i] =
|
||||
(addr->in6.sin6_addr.s6_addr[i] & mask) |
|
||||
(int_name->proto6.s6_addr[i] & ~mask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* check for duplicates. */
|
||||
for (lp = int_name->addr; lp; lp = lp->next)
|
||||
if ((lp->flags & ADDRLIST_IPV6) &&
|
||||
IN6_ARE_ADDR_EQUAL(&lp->addr.addr6, &newaddr))
|
||||
break;
|
||||
|
||||
if (!lp)
|
||||
{
|
||||
if (param->spare)
|
||||
{
|
||||
al = param->spare;
|
||||
param->spare = al->next;
|
||||
}
|
||||
else
|
||||
al = whine_malloc(sizeof(struct addrlist));
|
||||
|
||||
if (al)
|
||||
{
|
||||
al->flags = ADDRLIST_IPV6;
|
||||
al->addr.addr6 = newaddr;
|
||||
|
||||
/* Privacy addresses and addresses still undergoing DAD and deprecated addresses
|
||||
don't appear in forward queries, but will in reverse ones. */
|
||||
if (!(iface_flags & IFACE_PERMANENT) || (iface_flags & (IFACE_DEPRECATED | IFACE_TENTATIVE)))
|
||||
al->flags |= ADDRLIST_REVONLY;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
al = whine_malloc(sizeof(struct addrlist));
|
||||
|
||||
if (al)
|
||||
{
|
||||
al->next = int_name->addr;
|
||||
int_name->addr = al;
|
||||
|
||||
if (addr->sa.sa_family == AF_INET)
|
||||
{
|
||||
al->addr.addr4 = addr->in.sin_addr;
|
||||
al->flags = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
al->addr.addr6 = addr->in6.sin6_addr;
|
||||
al->flags = ADDRLIST_IPV6;
|
||||
/* Privacy addresses and addresses still undergoing DAD and deprecated addresses
|
||||
don't appear in forward queries, but will in reverse ones. */
|
||||
if (!(iface_flags & IFACE_PERMANENT) || (iface_flags & (IFACE_DEPRECATED | IFACE_TENTATIVE)))
|
||||
al->flags |= ADDRLIST_REVONLY;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -458,6 +528,11 @@ static int iface_allowed(struct iface_param *param, int if_index, char *label,
|
||||
/* add to list */
|
||||
if ((iface = whine_malloc(sizeof(struct irec))))
|
||||
{
|
||||
int mtu = 0;
|
||||
|
||||
if (ioctl(param->fd, SIOCGIFMTU, &ifr) != -1)
|
||||
mtu = ifr.ifr_mtu;
|
||||
|
||||
iface->addr = *addr;
|
||||
iface->netmask = netmask;
|
||||
iface->tftp_ok = tftp_ok;
|
||||
@@ -592,7 +667,7 @@ static int release_listener(struct listener *l)
|
||||
int port;
|
||||
|
||||
port = prettyprint_addr(&l->iface->addr, daemon->addrbuff);
|
||||
my_syslog(LOG_DEBUG, _("stopped listening on %s(#%d): %s port %d"),
|
||||
my_syslog(LOG_DEBUG|MS_DEBUG, _("stopped listening on %s(#%d): %s port %d"),
|
||||
l->iface->name, l->iface->index, daemon->addrbuff, port);
|
||||
/* In case it ever returns */
|
||||
l->iface->done = 0;
|
||||
@@ -621,7 +696,8 @@ int enumerate_interfaces(int reset)
|
||||
#ifdef HAVE_AUTH
|
||||
struct auth_zone *zone;
|
||||
#endif
|
||||
|
||||
struct server *serv;
|
||||
|
||||
/* Do this max once per select cycle - also inhibits netlink socket use
|
||||
in TCP child processes. */
|
||||
|
||||
@@ -638,7 +714,26 @@ int enumerate_interfaces(int reset)
|
||||
|
||||
if ((param.fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
|
||||
return 0;
|
||||
|
||||
|
||||
/* iface indexes can change when interfaces are created/destroyed.
|
||||
We use them in the main forwarding control path, when the path
|
||||
to a server is specified by an interface, so cache them.
|
||||
Update the cache here. */
|
||||
for (serv = daemon->servers; serv; serv = serv->next)
|
||||
if (serv->interface[0] != 0)
|
||||
{
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
struct ifreq ifr;
|
||||
|
||||
safe_strncpy(ifr.ifr_name, serv->interface, IF_NAMESIZE);
|
||||
if (ioctl(param.fd, SIOCGIFINDEX, &ifr) != -1)
|
||||
serv->ifindex = ifr.ifr_ifindex;
|
||||
#else
|
||||
serv->ifindex = if_nametoindex(serv->interface);
|
||||
#endif
|
||||
}
|
||||
|
||||
again:
|
||||
/* Mark interfaces for garbage collection */
|
||||
for (iface = daemon->interfaces; iface; iface = iface->next)
|
||||
iface->found = 0;
|
||||
@@ -690,9 +785,14 @@ int enumerate_interfaces(int reset)
|
||||
param.spare = spare;
|
||||
|
||||
ret = iface_enumerate(AF_INET6, ¶m, iface_allowed_v6);
|
||||
|
||||
if (ret)
|
||||
ret = iface_enumerate(AF_INET, ¶m, iface_allowed_v4);
|
||||
if (ret < 0)
|
||||
goto again;
|
||||
else if (ret)
|
||||
{
|
||||
ret = iface_enumerate(AF_INET, ¶m, iface_allowed_v4);
|
||||
if (ret < 0)
|
||||
goto again;
|
||||
}
|
||||
|
||||
errsave = errno;
|
||||
close(param.fd);
|
||||
@@ -727,7 +827,7 @@ int enumerate_interfaces(int reset)
|
||||
|
||||
errno = errsave;
|
||||
spare = param.spare;
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -867,10 +967,10 @@ int tcp_interface(int fd, int af)
|
||||
/* use mshdr so that the CMSDG_* macros are available */
|
||||
msg.msg_control = daemon->packet;
|
||||
msg.msg_controllen = len = daemon->packet_buff_sz;
|
||||
|
||||
|
||||
/* we overwrote the buffer... */
|
||||
daemon->srv_save = NULL;
|
||||
|
||||
daemon->srv_save = NULL;
|
||||
|
||||
if (af == AF_INET)
|
||||
{
|
||||
if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt)) != -1 &&
|
||||
@@ -1045,7 +1145,7 @@ void create_bound_listeners(int dienow)
|
||||
if (!dienow)
|
||||
{
|
||||
int port = prettyprint_addr(&iface->addr, daemon->addrbuff);
|
||||
my_syslog(LOG_DEBUG, _("listening on %s(#%d): %s port %d"),
|
||||
my_syslog(LOG_DEBUG|MS_DEBUG, _("listening on %s(#%d): %s port %d"),
|
||||
iface->name, iface->index, daemon->addrbuff, port);
|
||||
}
|
||||
}
|
||||
@@ -1072,7 +1172,7 @@ void create_bound_listeners(int dienow)
|
||||
if (!dienow)
|
||||
{
|
||||
int port = prettyprint_addr(&if_tmp->addr, daemon->addrbuff);
|
||||
my_syslog(LOG_DEBUG, _("listening on %s port %d"), daemon->addrbuff, port);
|
||||
my_syslog(LOG_DEBUG|MS_DEBUG, _("listening on %s port %d"), daemon->addrbuff, port);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1206,66 +1306,13 @@ void join_multicast(int dienow)
|
||||
}
|
||||
#endif
|
||||
|
||||
/* return a UDP socket bound to a random port, have to cope with straying into
|
||||
occupied port nos and reserved ones. */
|
||||
int random_sock(int family)
|
||||
{
|
||||
int fd;
|
||||
|
||||
if ((fd = socket(family, SOCK_DGRAM, 0)) != -1)
|
||||
{
|
||||
union mysockaddr addr;
|
||||
unsigned int ports_avail = ((unsigned short)daemon->max_port - (unsigned short)daemon->min_port) + 1;
|
||||
int tries = ports_avail < 30 ? 3 * ports_avail : 100;
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sa.sa_family = family;
|
||||
|
||||
/* don't loop forever if all ports in use. */
|
||||
|
||||
if (fix_fd(fd))
|
||||
while(tries--)
|
||||
{
|
||||
unsigned short port = htons(daemon->min_port + (rand16() % ((unsigned short)ports_avail)));
|
||||
|
||||
if (family == AF_INET)
|
||||
{
|
||||
addr.in.sin_addr.s_addr = INADDR_ANY;
|
||||
addr.in.sin_port = port;
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
addr.in.sin_len = sizeof(struct sockaddr_in);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
addr.in6.sin6_addr = in6addr_any;
|
||||
addr.in6.sin6_port = port;
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
addr.in6.sin6_len = sizeof(struct sockaddr_in6);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (bind(fd, (struct sockaddr *)&addr, sa_len(&addr)) == 0)
|
||||
return fd;
|
||||
|
||||
if (errno != EADDRINUSE && errno != EACCES)
|
||||
break;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifindex, int is_tcp)
|
||||
{
|
||||
union mysockaddr addr_copy = *addr;
|
||||
unsigned short port;
|
||||
int tries = 1, done = 0;
|
||||
unsigned int ports_avail = ((unsigned short)daemon->max_port - (unsigned short)daemon->min_port) + 1;
|
||||
|
||||
int tries = 1;
|
||||
unsigned short ports_avail = 1;
|
||||
|
||||
if (addr_copy.sa.sa_family == AF_INET)
|
||||
port = addr_copy.in.sin_port;
|
||||
else
|
||||
@@ -1274,35 +1321,43 @@ int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifind
|
||||
/* cannot set source _port_ for TCP connections. */
|
||||
if (is_tcp)
|
||||
port = 0;
|
||||
|
||||
/* Bind a random port within the range given by min-port and max-port */
|
||||
if (port == 0)
|
||||
else if (port == 0 && daemon->max_port != 0)
|
||||
{
|
||||
/* Bind a random port within the range given by min-port and max-port if either
|
||||
or both are set. Otherwise use the OS's random ephemeral port allocation by
|
||||
leaving port == 0 and tries == 1 */
|
||||
ports_avail = daemon->max_port - daemon->min_port + 1;
|
||||
tries = ports_avail < 30 ? 3 * ports_avail : 100;
|
||||
port = htons(daemon->min_port + (rand16() % ((unsigned short)ports_avail)));
|
||||
port = htons(daemon->min_port + (rand16() % ports_avail));
|
||||
}
|
||||
|
||||
while (tries--)
|
||||
while (1)
|
||||
{
|
||||
/* elide bind() call if it's to port 0, address 0 */
|
||||
if (addr_copy.sa.sa_family == AF_INET)
|
||||
addr_copy.in.sin_port = port;
|
||||
else
|
||||
addr_copy.in6.sin6_port = port;
|
||||
|
||||
if (bind(fd, (struct sockaddr *)&addr_copy, sa_len(&addr_copy)) != -1)
|
||||
{
|
||||
done = 1;
|
||||
break;
|
||||
if (port == 0 && addr_copy.in.sin_addr.s_addr == 0)
|
||||
break;
|
||||
addr_copy.in.sin_port = port;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (port == 0 && IN6_IS_ADDR_UNSPECIFIED(&addr_copy.in6.sin6_addr))
|
||||
break;
|
||||
addr_copy.in6.sin6_port = port;
|
||||
}
|
||||
|
||||
if (errno != EADDRINUSE && errno != EACCES)
|
||||
return 0;
|
||||
if (bind(fd, (struct sockaddr *)&addr_copy, sa_len(&addr_copy)) != -1)
|
||||
break;
|
||||
|
||||
port = htons(daemon->min_port + (rand16() % ((unsigned short)ports_avail)));
|
||||
}
|
||||
if (errno != EADDRINUSE && errno != EACCES)
|
||||
return 0;
|
||||
|
||||
if (!done)
|
||||
return 0;
|
||||
if (--tries == 0)
|
||||
return 0;
|
||||
|
||||
port = htons(daemon->min_port + (rand16() % ports_avail));
|
||||
}
|
||||
|
||||
if (!is_tcp && ifindex > 0)
|
||||
{
|
||||
@@ -1332,38 +1387,33 @@ int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifind
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct serverfd *allocate_sfd(union mysockaddr *addr, char *intname)
|
||||
static struct serverfd *allocate_sfd(union mysockaddr *addr, char *intname, unsigned int ifindex)
|
||||
{
|
||||
struct serverfd *sfd;
|
||||
unsigned int ifindex = 0;
|
||||
int errsave;
|
||||
int opt = 1;
|
||||
|
||||
/* when using random ports, servers which would otherwise use
|
||||
the INADDR_ANY/port0 socket have sfd set to NULL */
|
||||
if (!daemon->osport && intname[0] == 0)
|
||||
the INADDR_ANY/port0 socket have sfd set to NULL, this is
|
||||
anything without an explictly set source port. */
|
||||
if (!daemon->osport)
|
||||
{
|
||||
errno = 0;
|
||||
|
||||
if (addr->sa.sa_family == AF_INET &&
|
||||
addr->in.sin_addr.s_addr == INADDR_ANY &&
|
||||
addr->in.sin_port == htons(0))
|
||||
return NULL;
|
||||
|
||||
if (addr->sa.sa_family == AF_INET6 &&
|
||||
memcmp(&addr->in6.sin6_addr, &in6addr_any, sizeof(in6addr_any)) == 0 &&
|
||||
addr->in6.sin6_port == htons(0))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (intname && strlen(intname) != 0)
|
||||
ifindex = if_nametoindex(intname); /* index == 0 when not binding to an interface */
|
||||
|
||||
/* may have a suitable one already */
|
||||
for (sfd = daemon->sfds; sfd; sfd = sfd->next )
|
||||
if (sockaddr_isequal(&sfd->source_addr, addr) &&
|
||||
strcmp(intname, sfd->interface) == 0 &&
|
||||
ifindex == sfd->ifindex)
|
||||
if (ifindex == sfd->ifindex &&
|
||||
sockaddr_isequal(&sfd->source_addr, addr) &&
|
||||
strcmp(intname, sfd->interface) == 0)
|
||||
return sfd;
|
||||
|
||||
/* need to make a new one. */
|
||||
@@ -1414,7 +1464,7 @@ void pre_allocate_sfds(void)
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
addr.in.sin_len = sizeof(struct sockaddr_in);
|
||||
#endif
|
||||
if ((sfd = allocate_sfd(&addr, "")))
|
||||
if ((sfd = allocate_sfd(&addr, "", 0)))
|
||||
sfd->preallocated = 1;
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
@@ -1424,13 +1474,12 @@ void pre_allocate_sfds(void)
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
addr.in6.sin6_len = sizeof(struct sockaddr_in6);
|
||||
#endif
|
||||
if ((sfd = allocate_sfd(&addr, "")))
|
||||
if ((sfd = allocate_sfd(&addr, "", 0)))
|
||||
sfd->preallocated = 1;
|
||||
}
|
||||
|
||||
for (srv = daemon->servers; srv; srv = srv->next)
|
||||
if (!(srv->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND)) &&
|
||||
!allocate_sfd(&srv->source_addr, srv->interface) &&
|
||||
if (!allocate_sfd(&srv->source_addr, srv->interface, srv->ifindex) &&
|
||||
errno != 0 &&
|
||||
option_bool(OPT_NOWILD))
|
||||
{
|
||||
@@ -1445,136 +1494,23 @@ void pre_allocate_sfds(void)
|
||||
}
|
||||
}
|
||||
|
||||
void mark_servers(int flag)
|
||||
{
|
||||
struct server *serv;
|
||||
|
||||
/* mark everything with argument flag */
|
||||
for (serv = daemon->servers; serv; serv = serv->next)
|
||||
{
|
||||
if (serv->flags & flag)
|
||||
serv->flags |= SERV_MARK;
|
||||
#ifdef HAVE_LOOP
|
||||
/* Give looped servers another chance */
|
||||
serv->flags &= ~SERV_LOOP;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void cleanup_servers(void)
|
||||
{
|
||||
struct server *serv, *tmp, **up;
|
||||
|
||||
/* unlink and free anything still marked. */
|
||||
for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp)
|
||||
{
|
||||
tmp = serv->next;
|
||||
if (serv->flags & SERV_MARK)
|
||||
{
|
||||
server_gone(serv);
|
||||
*up = serv->next;
|
||||
if (serv->domain)
|
||||
free(serv->domain);
|
||||
free(serv);
|
||||
}
|
||||
else
|
||||
up = &serv->next;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LOOP
|
||||
/* Now we have a new set of servers, test for loops. */
|
||||
loop_send_probes();
|
||||
#endif
|
||||
}
|
||||
|
||||
void add_update_server(int flags,
|
||||
union mysockaddr *addr,
|
||||
union mysockaddr *source_addr,
|
||||
const char *interface,
|
||||
const char *domain)
|
||||
{
|
||||
struct server *serv, *next = NULL;
|
||||
char *domain_str = NULL;
|
||||
|
||||
/* See if there is a suitable candidate, and unmark */
|
||||
for (serv = daemon->servers; serv; serv = serv->next)
|
||||
if (serv->flags & SERV_MARK)
|
||||
{
|
||||
if (domain)
|
||||
{
|
||||
if (!(serv->flags & SERV_HAS_DOMAIN) || !hostname_isequal(domain, serv->domain))
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (serv->flags & SERV_HAS_DOMAIN)
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (serv)
|
||||
{
|
||||
domain_str = serv->domain;
|
||||
next = serv->next;
|
||||
}
|
||||
else if ((serv = whine_malloc(sizeof (struct server))))
|
||||
{
|
||||
/* Not found, create a new one. */
|
||||
if (domain && !(domain_str = whine_malloc(strlen(domain)+1)))
|
||||
{
|
||||
free(serv);
|
||||
serv = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct server *s;
|
||||
/* Add to the end of the chain, for order */
|
||||
if (!daemon->servers)
|
||||
daemon->servers = serv;
|
||||
else
|
||||
{
|
||||
for (s = daemon->servers; s->next; s = s->next);
|
||||
s->next = serv;
|
||||
}
|
||||
if (domain)
|
||||
strcpy(domain_str, domain);
|
||||
}
|
||||
}
|
||||
|
||||
if (serv)
|
||||
{
|
||||
memset(serv, 0, sizeof(struct server));
|
||||
serv->flags = flags;
|
||||
serv->domain = domain_str;
|
||||
serv->next = next;
|
||||
serv->queries = serv->failed_queries = 0;
|
||||
#ifdef HAVE_LOOP
|
||||
serv->uid = rand32();
|
||||
#endif
|
||||
|
||||
if (domain)
|
||||
serv->flags |= SERV_HAS_DOMAIN;
|
||||
|
||||
if (interface)
|
||||
safe_strncpy(serv->interface, interface, sizeof(serv->interface));
|
||||
if (addr)
|
||||
serv->addr = *addr;
|
||||
if (source_addr)
|
||||
serv->source_addr = *source_addr;
|
||||
}
|
||||
}
|
||||
|
||||
void check_servers(void)
|
||||
void check_servers(int no_loop_check)
|
||||
{
|
||||
struct irec *iface;
|
||||
struct server *serv;
|
||||
struct serverfd *sfd, *tmp, **up;
|
||||
int port = 0, count;
|
||||
int locals = 0;
|
||||
|
||||
#ifdef HAVE_LOOP
|
||||
if (!no_loop_check)
|
||||
loop_send_probes();
|
||||
#endif
|
||||
|
||||
/* interface may be new since startup */
|
||||
/* clear all marks. */
|
||||
mark_servers(0);
|
||||
|
||||
/* interface may be new since startup */
|
||||
if (!option_bool(OPT_NOWILD))
|
||||
enumerate_interfaces(0);
|
||||
|
||||
@@ -1584,114 +1520,117 @@ void check_servers(void)
|
||||
|
||||
for (count = 0, serv = daemon->servers; serv; serv = serv->next)
|
||||
{
|
||||
if (!(serv->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND)))
|
||||
{
|
||||
/* Init edns_pktsz for newly created server records. */
|
||||
if (serv->edns_pktsz == 0)
|
||||
serv->edns_pktsz = daemon->edns_pktsz;
|
||||
|
||||
/* Init edns_pktsz for newly created server records. */
|
||||
if (serv->edns_pktsz == 0)
|
||||
serv->edns_pktsz = daemon->edns_pktsz;
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID))
|
||||
{
|
||||
if (!(serv->flags & SERV_FOR_NODOTS))
|
||||
serv->flags |= SERV_DO_DNSSEC;
|
||||
if (option_bool(OPT_DNSSEC_VALID))
|
||||
{
|
||||
if (!(serv->flags & SERV_FOR_NODOTS))
|
||||
serv->flags |= SERV_DO_DNSSEC;
|
||||
|
||||
/* Disable DNSSEC validation when using server=/domain/.... servers
|
||||
unless there's a configured trust anchor. */
|
||||
if (strlen(serv->domain) != 0)
|
||||
{
|
||||
struct ds_config *ds;
|
||||
char *domain = serv->domain;
|
||||
|
||||
/* Disable DNSSEC validation when using server=/domain/.... servers
|
||||
unless there's a configured trust anchor. */
|
||||
if (serv->flags & SERV_HAS_DOMAIN)
|
||||
{
|
||||
struct ds_config *ds;
|
||||
char *domain = serv->domain;
|
||||
|
||||
/* .example.com is valid */
|
||||
while (*domain == '.')
|
||||
domain++;
|
||||
|
||||
for (ds = daemon->ds; ds; ds = ds->next)
|
||||
if (ds->name[0] != 0 && hostname_isequal(domain, ds->name))
|
||||
break;
|
||||
|
||||
if (!ds)
|
||||
serv->flags &= ~SERV_DO_DNSSEC;
|
||||
}
|
||||
/* .example.com is valid */
|
||||
while (*domain == '.')
|
||||
domain++;
|
||||
|
||||
for (ds = daemon->ds; ds; ds = ds->next)
|
||||
if (ds->name[0] != 0 && hostname_isequal(domain, ds->name))
|
||||
break;
|
||||
|
||||
if (!ds)
|
||||
serv->flags &= ~SERV_DO_DNSSEC;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
port = prettyprint_addr(&serv->addr, daemon->namebuff);
|
||||
|
||||
/* 0.0.0.0 is nothing, the stack treats it like 127.0.0.1 */
|
||||
if (serv->addr.sa.sa_family == AF_INET &&
|
||||
serv->addr.in.sin_addr.s_addr == 0)
|
||||
{
|
||||
serv->flags |= SERV_MARK;
|
||||
continue;
|
||||
}
|
||||
|
||||
for (iface = daemon->interfaces; iface; iface = iface->next)
|
||||
if (sockaddr_isequal(&serv->addr, &iface->addr))
|
||||
break;
|
||||
if (iface)
|
||||
{
|
||||
my_syslog(LOG_WARNING, _("ignoring nameserver %s - local interface"), daemon->namebuff);
|
||||
serv->flags |= SERV_MARK;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Do we need a socket set? */
|
||||
if (!serv->sfd &&
|
||||
!(serv->sfd = allocate_sfd(&serv->source_addr, serv->interface)) &&
|
||||
errno != 0)
|
||||
{
|
||||
my_syslog(LOG_WARNING,
|
||||
_("ignoring nameserver %s - cannot make/bind socket: %s"),
|
||||
daemon->namebuff, strerror(errno));
|
||||
serv->flags |= SERV_MARK;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (serv->sfd)
|
||||
serv->sfd->used = 1;
|
||||
|
||||
port = prettyprint_addr(&serv->addr, daemon->namebuff);
|
||||
|
||||
/* 0.0.0.0 is nothing, the stack treats it like 127.0.0.1 */
|
||||
if (serv->addr.sa.sa_family == AF_INET &&
|
||||
serv->addr.in.sin_addr.s_addr == 0)
|
||||
{
|
||||
serv->flags |= SERV_MARK;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(serv->flags & SERV_NO_REBIND) && !(serv->flags & SERV_LITERAL_ADDRESS))
|
||||
for (iface = daemon->interfaces; iface; iface = iface->next)
|
||||
if (sockaddr_isequal(&serv->addr, &iface->addr))
|
||||
break;
|
||||
if (iface)
|
||||
{
|
||||
if (++count > SERVERS_LOGGED)
|
||||
continue;
|
||||
|
||||
if (serv->flags & (SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_USE_RESOLV))
|
||||
{
|
||||
char *s1, *s2, *s3 = "";
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID) && !(serv->flags & SERV_DO_DNSSEC))
|
||||
s3 = _("(no DNSSEC)");
|
||||
#endif
|
||||
if (!(serv->flags & SERV_HAS_DOMAIN))
|
||||
s1 = _("unqualified"), s2 = _("names");
|
||||
else if (strlen(serv->domain) == 0)
|
||||
s1 = _("default"), s2 = "";
|
||||
else
|
||||
s1 = _("domain"), s2 = serv->domain;
|
||||
|
||||
if (serv->flags & SERV_NO_ADDR)
|
||||
{
|
||||
count--;
|
||||
if (++locals <= LOCALS_LOGGED)
|
||||
my_syslog(LOG_INFO, _("using only locally-known addresses for %s %s"), s1, s2);
|
||||
}
|
||||
else if (serv->flags & SERV_USE_RESOLV)
|
||||
my_syslog(LOG_INFO, _("using standard nameservers for %s %s"), s1, s2);
|
||||
else
|
||||
my_syslog(LOG_INFO, _("using nameserver %s#%d for %s %s %s"), daemon->namebuff, port, s1, s2, s3);
|
||||
}
|
||||
#ifdef HAVE_LOOP
|
||||
else if (serv->flags & SERV_LOOP)
|
||||
my_syslog(LOG_INFO, _("NOT using nameserver %s#%d - query loop detected"), daemon->namebuff, port);
|
||||
#endif
|
||||
else if (serv->interface[0] != 0)
|
||||
my_syslog(LOG_INFO, _("using nameserver %s#%d(via %s)"), daemon->namebuff, port, serv->interface);
|
||||
else
|
||||
my_syslog(LOG_INFO, _("using nameserver %s#%d"), daemon->namebuff, port);
|
||||
my_syslog(LOG_WARNING, _("ignoring nameserver %s - local interface"), daemon->namebuff);
|
||||
serv->flags |= SERV_MARK;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Do we need a socket set? */
|
||||
if (!serv->sfd &&
|
||||
!(serv->sfd = allocate_sfd(&serv->source_addr, serv->interface, serv->ifindex)) &&
|
||||
errno != 0)
|
||||
{
|
||||
my_syslog(LOG_WARNING,
|
||||
_("ignoring nameserver %s - cannot make/bind socket: %s"),
|
||||
daemon->namebuff, strerror(errno));
|
||||
serv->flags |= SERV_MARK;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (serv->sfd)
|
||||
serv->sfd->used = 1;
|
||||
|
||||
if (++count > SERVERS_LOGGED)
|
||||
continue;
|
||||
|
||||
if (strlen(serv->domain) != 0 || (serv->flags & SERV_FOR_NODOTS))
|
||||
{
|
||||
char *s1, *s2, *s3 = "", *s4 = "";
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID) && !(serv->flags & SERV_DO_DNSSEC))
|
||||
s3 = _("(no DNSSEC)");
|
||||
#endif
|
||||
if (serv->flags & SERV_FOR_NODOTS)
|
||||
s1 = _("unqualified"), s2 = _("names");
|
||||
else if (strlen(serv->domain) == 0)
|
||||
s1 = _("default"), s2 = "";
|
||||
else
|
||||
s1 = _("domain"), s2 = serv->domain, s4 = (serv->flags & SERV_WILDCARD) ? "*" : "";
|
||||
|
||||
my_syslog(LOG_INFO, _("using nameserver %s#%d for %s %s%s %s"), daemon->namebuff, port, s1, s4, s2, s3);
|
||||
}
|
||||
#ifdef HAVE_LOOP
|
||||
else if (serv->flags & SERV_LOOP)
|
||||
my_syslog(LOG_INFO, _("NOT using nameserver %s#%d - query loop detected"), daemon->namebuff, port);
|
||||
#endif
|
||||
else if (serv->interface[0] != 0)
|
||||
my_syslog(LOG_INFO, _("using nameserver %s#%d(via %s)"), daemon->namebuff, port, serv->interface);
|
||||
else
|
||||
my_syslog(LOG_INFO, _("using nameserver %s#%d"), daemon->namebuff, port);
|
||||
|
||||
}
|
||||
|
||||
for (count = 0, serv = daemon->local_domains; serv; serv = serv->next)
|
||||
{
|
||||
if (++count > SERVERS_LOGGED)
|
||||
continue;
|
||||
|
||||
if ((serv->flags & SERV_LITERAL_ADDRESS) &&
|
||||
!(serv->flags & (SERV_6ADDR | SERV_4ADDR | SERV_ALL_ZEROS)))
|
||||
{
|
||||
count--;
|
||||
if (++locals <= LOCALS_LOGGED)
|
||||
my_syslog(LOG_INFO, _("using only locally-known addresses for %s"), serv->domain);
|
||||
}
|
||||
else if (serv->flags & SERV_USE_RESOLV)
|
||||
my_syslog(LOG_INFO, _("using standard nameservers for %s"), serv->domain);
|
||||
}
|
||||
|
||||
if (locals > LOCALS_LOGGED)
|
||||
@@ -1713,7 +1652,8 @@ void check_servers(void)
|
||||
up = &sfd->next;
|
||||
}
|
||||
|
||||
cleanup_servers();
|
||||
cleanup_servers(); /* remove servers we just deleted. */
|
||||
build_server_array();
|
||||
}
|
||||
|
||||
/* Return zero if no servers found, in that case we keep polling.
|
||||
@@ -1786,7 +1726,7 @@ int reload_servers(char *fname)
|
||||
continue;
|
||||
}
|
||||
|
||||
add_update_server(SERV_FROM_RESOLV, &addr, &source_addr, NULL, NULL);
|
||||
add_update_server(SERV_FROM_RESOLV, &addr, &source_addr, NULL, NULL, NULL);
|
||||
gotone = 1;
|
||||
}
|
||||
|
||||
@@ -1819,8 +1759,3 @@ void newaddress(time_t now)
|
||||
lease_find_interfaces(now);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
726
src/option.c
726
src/option.c
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
386
src/pattern.c
Normal file
386
src/pattern.c
Normal file
@@ -0,0 +1,386 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 dated June, 1991, or
|
||||
(at your option) version 3 dated 29 June, 2007.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
#ifdef HAVE_CONNTRACK
|
||||
|
||||
#define LOG(...) \
|
||||
do { \
|
||||
my_syslog(LOG_WARNING, __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define ASSERT(condition) \
|
||||
do { \
|
||||
if (!(condition)) \
|
||||
LOG("[pattern.c:%d] Assertion failure: %s", __LINE__, #condition); \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* Determines whether a given string value matches against a glob pattern
|
||||
* which may contain zero-or-more-character wildcards denoted by '*'.
|
||||
*
|
||||
* Based on "Glob Matching Can Be Simple And Fast Too" by Russ Cox,
|
||||
* See https://research.swtch.com/glob
|
||||
*
|
||||
* @param value A string value.
|
||||
* @param num_value_bytes The number of bytes of the string value.
|
||||
* @param pattern A glob pattern.
|
||||
* @param num_pattern_bytes The number of bytes of the glob pattern.
|
||||
*
|
||||
* @return 1 If the provided value matches against the glob pattern.
|
||||
* @return 0 Otherwise.
|
||||
*/
|
||||
static int is_string_matching_glob_pattern(
|
||||
const char *value,
|
||||
size_t num_value_bytes,
|
||||
const char *pattern,
|
||||
size_t num_pattern_bytes)
|
||||
{
|
||||
ASSERT(value);
|
||||
ASSERT(pattern);
|
||||
|
||||
size_t value_index = 0;
|
||||
size_t next_value_index = 0;
|
||||
size_t pattern_index = 0;
|
||||
size_t next_pattern_index = 0;
|
||||
while (value_index < num_value_bytes || pattern_index < num_pattern_bytes)
|
||||
{
|
||||
if (pattern_index < num_pattern_bytes)
|
||||
{
|
||||
char pattern_character = pattern[pattern_index];
|
||||
if ('a' <= pattern_character && pattern_character <= 'z')
|
||||
pattern_character -= 'a' - 'A';
|
||||
if (pattern_character == '*')
|
||||
{
|
||||
// zero-or-more-character wildcard
|
||||
// Try to match at value_index, otherwise restart at value_index + 1 next.
|
||||
next_pattern_index = pattern_index;
|
||||
pattern_index++;
|
||||
if (value_index < num_value_bytes)
|
||||
next_value_index = value_index + 1;
|
||||
else
|
||||
next_value_index = 0;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// ordinary character
|
||||
if (value_index < num_value_bytes)
|
||||
{
|
||||
char value_character = value[value_index];
|
||||
if ('a' <= value_character && value_character <= 'z')
|
||||
value_character -= 'a' - 'A';
|
||||
if (value_character == pattern_character)
|
||||
{
|
||||
pattern_index++;
|
||||
value_index++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (next_value_index)
|
||||
{
|
||||
pattern_index = next_pattern_index;
|
||||
value_index = next_value_index;
|
||||
continue;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a given string value represents a valid DNS name.
|
||||
*
|
||||
* - DNS names must adhere to RFC 1123: 1 to 253 characters in length, consisting of a sequence of labels
|
||||
* delimited by dots ("."). Each label must be 1 to 63 characters in length, contain only
|
||||
* ASCII letters ("a"-"Z"), digits ("0"-"9"), or hyphens ("-") and must not start or end with a hyphen.
|
||||
*
|
||||
* - A valid name must be fully qualified, i.e., consist of at least two labels.
|
||||
* The final label must not be fully numeric, and must not be the "local" pseudo-TLD.
|
||||
*
|
||||
* - Examples:
|
||||
* Valid: "example.com"
|
||||
* Invalid: "ipcamera", "ipcamera.local", "8.8.8.8"
|
||||
*
|
||||
* @param value A string value.
|
||||
*
|
||||
* @return 1 If the provided string value is a valid DNS name.
|
||||
* @return 0 Otherwise.
|
||||
*/
|
||||
int is_valid_dns_name(const char *value)
|
||||
{
|
||||
ASSERT(value);
|
||||
|
||||
size_t num_bytes = 0;
|
||||
size_t num_labels = 0;
|
||||
const char *label = NULL;
|
||||
int is_label_numeric = 1;
|
||||
for (const char *c = value;; c++)
|
||||
{
|
||||
if (*c &&
|
||||
*c != '-' && *c != '.' &&
|
||||
(*c < '0' || *c > '9') &&
|
||||
(*c < 'A' || *c > 'Z') &&
|
||||
(*c < 'a' || *c > 'z'))
|
||||
{
|
||||
LOG("Invalid DNS name: Invalid character %c.", *c);
|
||||
return 0;
|
||||
}
|
||||
if (*c)
|
||||
num_bytes++;
|
||||
if (!label)
|
||||
{
|
||||
if (!*c || *c == '.')
|
||||
{
|
||||
LOG("Invalid DNS name: Empty label.");
|
||||
return 0;
|
||||
}
|
||||
if (*c == '-')
|
||||
{
|
||||
LOG("Invalid DNS name: Label starts with hyphen.");
|
||||
return 0;
|
||||
}
|
||||
label = c;
|
||||
}
|
||||
if (*c && *c != '.')
|
||||
{
|
||||
if (*c < '0' || *c > '9')
|
||||
is_label_numeric = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (c[-1] == '-')
|
||||
{
|
||||
LOG("Invalid DNS name: Label ends with hyphen.");
|
||||
return 0;
|
||||
}
|
||||
size_t num_label_bytes = (size_t) (c - label);
|
||||
if (num_label_bytes > 63)
|
||||
{
|
||||
LOG("Invalid DNS name: Label is too long (%zu).", num_label_bytes);
|
||||
return 0;
|
||||
}
|
||||
num_labels++;
|
||||
if (!*c)
|
||||
{
|
||||
if (num_labels < 2)
|
||||
{
|
||||
LOG("Invalid DNS name: Not enough labels (%zu).", num_labels);
|
||||
return 0;
|
||||
}
|
||||
if (is_label_numeric)
|
||||
{
|
||||
LOG("Invalid DNS name: Final label is fully numeric.");
|
||||
return 0;
|
||||
}
|
||||
if (num_label_bytes == 5 &&
|
||||
(label[0] == 'l' || label[0] == 'L') &&
|
||||
(label[1] == 'o' || label[1] == 'O') &&
|
||||
(label[2] == 'c' || label[2] == 'C') &&
|
||||
(label[3] == 'a' || label[3] == 'A') &&
|
||||
(label[4] == 'l' || label[4] == 'L'))
|
||||
{
|
||||
LOG("Invalid DNS name: \"local\" pseudo-TLD.");
|
||||
return 0;
|
||||
}
|
||||
if (num_bytes < 1 || num_bytes > 253)
|
||||
{
|
||||
LOG("DNS name has invalid length (%zu).", num_bytes);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
label = NULL;
|
||||
is_label_numeric = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a given string value represents a valid DNS name pattern.
|
||||
*
|
||||
* - DNS names must adhere to RFC 1123: 1 to 253 characters in length, consisting of a sequence of labels
|
||||
* delimited by dots ("."). Each label must be 1 to 63 characters in length, contain only
|
||||
* ASCII letters ("a"-"Z"), digits ("0"-"9"), or hyphens ("-") and must not start or end with a hyphen.
|
||||
*
|
||||
* - Patterns follow the syntax of DNS names, but additionally allow the wildcard character "*" to be used up to
|
||||
* twice per label to match 0 or more characters within that label. Note that the wildcard never matches a dot
|
||||
* (e.g., "*.example.com" matches "api.example.com" but not "api.us.example.com").
|
||||
*
|
||||
* - A valid name or pattern must be fully qualified, i.e., consist of at least two labels.
|
||||
* The final label must not be fully numeric, and must not be the "local" pseudo-TLD.
|
||||
* A pattern must end with at least two literal (non-wildcard) labels.
|
||||
*
|
||||
* - Examples:
|
||||
* Valid: "example.com", "*.example.com", "video*.example.com", "api*.*.example.com", "*-prod-*.example.com"
|
||||
* Invalid: "ipcamera", "ipcamera.local", "*", "*.com", "8.8.8.8"
|
||||
*
|
||||
* @param value A string value.
|
||||
*
|
||||
* @return 1 If the provided string value is a valid DNS name pattern.
|
||||
* @return 0 Otherwise.
|
||||
*/
|
||||
int is_valid_dns_name_pattern(const char *value)
|
||||
{
|
||||
ASSERT(value);
|
||||
|
||||
size_t num_bytes = 0;
|
||||
size_t num_labels = 0;
|
||||
const char *label = NULL;
|
||||
int is_label_numeric = 1;
|
||||
size_t num_wildcards = 0;
|
||||
int previous_label_has_wildcard = 1;
|
||||
for (const char *c = value;; c++)
|
||||
{
|
||||
if (*c &&
|
||||
*c != '*' && // Wildcard.
|
||||
*c != '-' && *c != '.' &&
|
||||
(*c < '0' || *c > '9') &&
|
||||
(*c < 'A' || *c > 'Z') &&
|
||||
(*c < 'a' || *c > 'z'))
|
||||
{
|
||||
LOG("Invalid DNS name pattern: Invalid character %c.", *c);
|
||||
return 0;
|
||||
}
|
||||
if (*c && *c != '*')
|
||||
num_bytes++;
|
||||
if (!label)
|
||||
{
|
||||
if (!*c || *c == '.')
|
||||
{
|
||||
LOG("Invalid DNS name pattern: Empty label.");
|
||||
return 0;
|
||||
}
|
||||
if (*c == '-')
|
||||
{
|
||||
LOG("Invalid DNS name pattern: Label starts with hyphen.");
|
||||
return 0;
|
||||
}
|
||||
label = c;
|
||||
}
|
||||
if (*c && *c != '.')
|
||||
{
|
||||
if (*c < '0' || *c > '9')
|
||||
is_label_numeric = 0;
|
||||
if (*c == '*')
|
||||
{
|
||||
if (num_wildcards >= 2)
|
||||
{
|
||||
LOG("Invalid DNS name pattern: Wildcard character used more than twice per label.");
|
||||
return 0;
|
||||
}
|
||||
num_wildcards++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (c[-1] == '-')
|
||||
{
|
||||
LOG("Invalid DNS name pattern: Label ends with hyphen.");
|
||||
return 0;
|
||||
}
|
||||
size_t num_label_bytes = (size_t) (c - label) - num_wildcards;
|
||||
if (num_label_bytes > 63)
|
||||
{
|
||||
LOG("Invalid DNS name pattern: Label is too long (%zu).", num_label_bytes);
|
||||
return 0;
|
||||
}
|
||||
num_labels++;
|
||||
if (!*c)
|
||||
{
|
||||
if (num_labels < 2)
|
||||
{
|
||||
LOG("Invalid DNS name pattern: Not enough labels (%zu).", num_labels);
|
||||
return 0;
|
||||
}
|
||||
if (num_wildcards != 0 || previous_label_has_wildcard)
|
||||
{
|
||||
LOG("Invalid DNS name pattern: Wildcard within final two labels.");
|
||||
return 0;
|
||||
}
|
||||
if (is_label_numeric)
|
||||
{
|
||||
LOG("Invalid DNS name pattern: Final label is fully numeric.");
|
||||
return 0;
|
||||
}
|
||||
if (num_label_bytes == 5 &&
|
||||
(label[0] == 'l' || label[0] == 'L') &&
|
||||
(label[1] == 'o' || label[1] == 'O') &&
|
||||
(label[2] == 'c' || label[2] == 'C') &&
|
||||
(label[3] == 'a' || label[3] == 'A') &&
|
||||
(label[4] == 'l' || label[4] == 'L'))
|
||||
{
|
||||
LOG("Invalid DNS name pattern: \"local\" pseudo-TLD.");
|
||||
return 0;
|
||||
}
|
||||
if (num_bytes < 1 || num_bytes > 253)
|
||||
{
|
||||
LOG("DNS name pattern has invalid length after removing wildcards (%zu).", num_bytes);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
label = NULL;
|
||||
is_label_numeric = 1;
|
||||
previous_label_has_wildcard = num_wildcards != 0;
|
||||
num_wildcards = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a given DNS name matches against a DNS name pattern.
|
||||
*
|
||||
* @param name A valid DNS name.
|
||||
* @param pattern A valid DNS name pattern.
|
||||
*
|
||||
* @return 1 If the provided DNS name matches against the DNS name pattern.
|
||||
* @return 0 Otherwise.
|
||||
*/
|
||||
int is_dns_name_matching_pattern(const char *name, const char *pattern)
|
||||
{
|
||||
ASSERT(name);
|
||||
ASSERT(is_valid_dns_name(name));
|
||||
ASSERT(pattern);
|
||||
ASSERT(is_valid_dns_name_pattern(pattern));
|
||||
|
||||
const char *n = name;
|
||||
const char *p = pattern;
|
||||
|
||||
do {
|
||||
const char *name_label = n;
|
||||
while (*n && *n != '.')
|
||||
n++;
|
||||
const char *pattern_label = p;
|
||||
while (*p && *p != '.')
|
||||
p++;
|
||||
if (!is_string_matching_glob_pattern(
|
||||
name_label, (size_t) (n - name_label),
|
||||
pattern_label, (size_t) (p - pattern_label)))
|
||||
break;
|
||||
if (*n)
|
||||
n++;
|
||||
if (*p)
|
||||
p++;
|
||||
} while (*n && *p);
|
||||
|
||||
return !*n && !*p;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -53,6 +53,3 @@ struct prefix_opt {
|
||||
#define ICMP6_OPT_RT_INFO 24
|
||||
#define ICMP6_OPT_RDNSS 25
|
||||
#define ICMP6_OPT_DNSSL 31
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
328
src/rfc1035.c
328
src/rfc1035.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -364,7 +364,7 @@ int private_net(struct in_addr addr, int ban_localhost)
|
||||
|
||||
return
|
||||
(((ip_addr & 0xFF000000) == 0x7F000000) && ban_localhost) /* 127.0.0.0/8 (loopback) */ ||
|
||||
((ip_addr & 0xFF000000) == 0x00000000) /* RFC 5735 section 3. "here" network */ ||
|
||||
(((ip_addr & 0xFF000000) == 0x00000000) && ban_localhost) /* RFC 5735 section 3. "here" network */ ||
|
||||
((ip_addr & 0xFF000000) == 0x0A000000) /* 10.0.0.0/8 (private) */ ||
|
||||
((ip_addr & 0xFFF00000) == 0xAC100000) /* 172.16.0.0/12 (private) */ ||
|
||||
((ip_addr & 0xFFFF0000) == 0xC0A80000) /* 192.168.0.0/16 (private) */ ||
|
||||
@@ -375,12 +375,21 @@ int private_net(struct in_addr addr, int ban_localhost)
|
||||
((ip_addr & 0xFFFFFFFF) == 0xFFFFFFFF) /* 255.255.255.255/32 (broadcast)*/ ;
|
||||
}
|
||||
|
||||
static int private_net6(struct in6_addr *a)
|
||||
static int private_net6(struct in6_addr *a, int ban_localhost)
|
||||
{
|
||||
return
|
||||
IN6_IS_ADDR_UNSPECIFIED(a) || /* RFC 6303 4.3 */
|
||||
IN6_IS_ADDR_LOOPBACK(a) || /* RFC 6303 4.3 */
|
||||
/* Block IPv4-mapped IPv6 addresses in private IPv4 address space */
|
||||
if (IN6_IS_ADDR_V4MAPPED(a))
|
||||
{
|
||||
struct in_addr v4;
|
||||
v4.s_addr = ((const uint32_t *) (a))[3];
|
||||
return private_net(v4, ban_localhost);
|
||||
}
|
||||
|
||||
return
|
||||
(IN6_IS_ADDR_UNSPECIFIED(a) && ban_localhost) || /* RFC 6303 4.3 */
|
||||
(IN6_IS_ADDR_LOOPBACK(a) && ban_localhost) || /* RFC 6303 4.3 */
|
||||
IN6_IS_ADDR_LINKLOCAL(a) || /* RFC 6303 4.5 */
|
||||
IN6_IS_ADDR_SITELOCAL(a) ||
|
||||
((unsigned char *)a)[0] == 0xfd || /* RFC 6303 4.4 */
|
||||
((u32 *)a)[0] == htonl(0x20010db8); /* RFC 6303 4.6 */
|
||||
}
|
||||
@@ -795,28 +804,9 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
private_net(addr.addr4, !option_bool(OPT_LOCAL_REBIND)))
|
||||
return 1;
|
||||
|
||||
/* Block IPv4-mapped IPv6 addresses in private IPv4 address space */
|
||||
if (flags & F_IPV6)
|
||||
{
|
||||
if (IN6_IS_ADDR_V4MAPPED(&addr.addr6))
|
||||
{
|
||||
struct in_addr v4;
|
||||
v4.s_addr = ((const uint32_t *) (&addr.addr6))[3];
|
||||
if (private_net(v4, !option_bool(OPT_LOCAL_REBIND)))
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Check for link-local (LL) and site-local (ULA) IPv6 addresses */
|
||||
if (IN6_IS_ADDR_LINKLOCAL(&addr.addr6) ||
|
||||
IN6_IS_ADDR_SITELOCAL(&addr.addr6))
|
||||
return 1;
|
||||
|
||||
/* Check for the IPv6 loopback address (::1) when
|
||||
option rebind-localhost-ok is NOT set */
|
||||
if (!option_bool(OPT_LOCAL_REBIND) &&
|
||||
IN6_IS_ADDR_LOOPBACK(&addr.addr6))
|
||||
return 1;
|
||||
}
|
||||
if ((flags & F_IPV6) &&
|
||||
private_net6(&addr.addr6, !option_bool(OPT_LOCAL_REBIND)))
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef HAVE_IPSET
|
||||
@@ -884,6 +874,92 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS)
|
||||
/* Don't pass control chars and weird escapes to UBus. */
|
||||
static int safe_name(char *name)
|
||||
{
|
||||
unsigned char *r;
|
||||
|
||||
for (r = (unsigned char *)name; *r; r++)
|
||||
if (!isprint((int)*r))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void report_addresses(struct dns_header *header, size_t len, u32 mark)
|
||||
{
|
||||
unsigned char *p, *endrr;
|
||||
int i;
|
||||
unsigned long attl;
|
||||
struct allowlist *allowlists;
|
||||
char **pattern_pos;
|
||||
|
||||
if (RCODE(header) != NOERROR)
|
||||
return;
|
||||
|
||||
for (allowlists = daemon->allowlists; allowlists; allowlists = allowlists->next)
|
||||
if (allowlists->mark == (mark & daemon->allowlist_mask & allowlists->mask))
|
||||
for (pattern_pos = allowlists->patterns; *pattern_pos; pattern_pos++)
|
||||
if (!strcmp(*pattern_pos, "*"))
|
||||
return;
|
||||
|
||||
if (!(p = skip_questions(header, len)))
|
||||
return;
|
||||
for (i = ntohs(header->ancount); i != 0; i--)
|
||||
{
|
||||
int aqtype, aqclass, ardlen;
|
||||
|
||||
if (!extract_name(header, len, &p, daemon->namebuff, 1, 10))
|
||||
return;
|
||||
|
||||
if (!CHECK_LEN(header, p, len, 10))
|
||||
return;
|
||||
GETSHORT(aqtype, p);
|
||||
GETSHORT(aqclass, p);
|
||||
GETLONG(attl, p);
|
||||
GETSHORT(ardlen, p);
|
||||
|
||||
if (!CHECK_LEN(header, p, len, ardlen))
|
||||
return;
|
||||
endrr = p+ardlen;
|
||||
|
||||
if (aqclass == C_IN)
|
||||
{
|
||||
if (aqtype == T_CNAME)
|
||||
{
|
||||
if (!extract_name(header, len, &p, daemon->workspacename, 1, 0))
|
||||
return;
|
||||
if (safe_name(daemon->namebuff) && safe_name(daemon->workspacename))
|
||||
ubus_event_bcast_connmark_allowlist_resolved(mark, daemon->namebuff, daemon->workspacename, attl);
|
||||
}
|
||||
if (aqtype == T_A)
|
||||
{
|
||||
struct in_addr addr;
|
||||
char ip[INET_ADDRSTRLEN];
|
||||
if (ardlen != INADDRSZ)
|
||||
return;
|
||||
memcpy(&addr, p, ardlen);
|
||||
if (inet_ntop(AF_INET, &addr, ip, sizeof ip) && safe_name(daemon->namebuff))
|
||||
ubus_event_bcast_connmark_allowlist_resolved(mark, daemon->namebuff, ip, attl);
|
||||
}
|
||||
else if (aqtype == T_AAAA)
|
||||
{
|
||||
struct in6_addr addr;
|
||||
char ip[INET6_ADDRSTRLEN];
|
||||
if (ardlen != IN6ADDRSZ)
|
||||
return;
|
||||
memcpy(&addr, p, ardlen);
|
||||
if (inet_ntop(AF_INET6, &addr, ip, sizeof ip) && safe_name(daemon->namebuff))
|
||||
ubus_event_bcast_connmark_allowlist_resolved(mark, daemon->namebuff, ip, attl);
|
||||
}
|
||||
}
|
||||
|
||||
p = endrr;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* If the packet holds exactly one query
|
||||
return F_IPV4 or F_IPV6 and leave the name from the query in name */
|
||||
unsigned int extract_request(struct dns_header *header, size_t qlen, char *name, unsigned short *typep)
|
||||
@@ -894,9 +970,14 @@ unsigned int extract_request(struct dns_header *header, size_t qlen, char *name,
|
||||
if (typep)
|
||||
*typep = 0;
|
||||
|
||||
*name = 0; /* return empty name if no query found. */
|
||||
|
||||
if (ntohs(header->qdcount) != 1 || OPCODE(header) != QUERY)
|
||||
return 0; /* must be exactly one query. */
|
||||
|
||||
if (!(header->hb3 & HB3_QR) && (ntohs(header->ancount) != 0 || ntohs(header->nscount) != 0))
|
||||
return 0; /* non-standard query. */
|
||||
|
||||
if (!extract_name(header, qlen, &p, name, 1, 4))
|
||||
return 0; /* bad packet */
|
||||
|
||||
@@ -916,24 +997,20 @@ unsigned int extract_request(struct dns_header *header, size_t qlen, char *name,
|
||||
return F_IPV4 | F_IPV6;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
/* F_DNSSECOK as agument to search_servers() inhibits forwarding
|
||||
to servers for domains without a trust anchor. This make the
|
||||
behaviour for DS and DNSKEY queries we forward the same
|
||||
as for DS and DNSKEY queries we originate. */
|
||||
if (qtype == T_DS || qtype == T_DNSKEY)
|
||||
if (option_bool(OPT_DNSSEC_VALID) && (qtype == T_DS || qtype == T_DNSKEY))
|
||||
return F_DNSSECOK;
|
||||
#endif
|
||||
|
||||
return F_QUERY;
|
||||
}
|
||||
|
||||
size_t setup_reply(struct dns_header *header, size_t qlen,
|
||||
union all_addr *addrp, unsigned int flags, unsigned long ttl)
|
||||
void setup_reply(struct dns_header *header, unsigned int flags, int ede)
|
||||
{
|
||||
unsigned char *p;
|
||||
|
||||
if (!(p = skip_questions(header, qlen)))
|
||||
return 0;
|
||||
|
||||
/* clear authoritative and truncated flags, set QR flag */
|
||||
header->hb3 = (header->hb3 & ~(HB3_AA | HB3_TC )) | HB3_QR;
|
||||
/* clear AD flag, set RA flag */
|
||||
@@ -946,40 +1023,19 @@ size_t setup_reply(struct dns_header *header, size_t qlen,
|
||||
SET_RCODE(header, NOERROR); /* empty domain */
|
||||
else if (flags == F_NXDOMAIN)
|
||||
SET_RCODE(header, NXDOMAIN);
|
||||
else if (flags == F_SERVFAIL)
|
||||
{
|
||||
union all_addr a;
|
||||
a.log.rcode = SERVFAIL;
|
||||
log_query(F_CONFIG | F_RCODE, "error", &a, NULL);
|
||||
SET_RCODE(header, SERVFAIL);
|
||||
}
|
||||
else if (flags & ( F_IPV4 | F_IPV6))
|
||||
{
|
||||
if (flags & F_IPV4)
|
||||
{ /* we know the address */
|
||||
SET_RCODE(header, NOERROR);
|
||||
header->ancount = htons(1);
|
||||
header->hb3 |= HB3_AA;
|
||||
add_resource_record(header, NULL, NULL, sizeof(struct dns_header), &p, ttl, NULL, T_A, C_IN, "4", addrp);
|
||||
}
|
||||
|
||||
if (flags & F_IPV6)
|
||||
{
|
||||
SET_RCODE(header, NOERROR);
|
||||
header->ancount = htons(ntohs(header->ancount) + 1);
|
||||
header->hb3 |= HB3_AA;
|
||||
add_resource_record(header, NULL, NULL, sizeof(struct dns_header), &p, ttl, NULL, T_AAAA, C_IN, "6", addrp);
|
||||
}
|
||||
SET_RCODE(header, NOERROR);
|
||||
header->hb3 |= HB3_AA;
|
||||
}
|
||||
else /* nowhere to forward to */
|
||||
{
|
||||
union all_addr a;
|
||||
a.log.rcode = REFUSED;
|
||||
a.log.ede = ede;
|
||||
log_query(F_CONFIG | F_RCODE, "error", &a, NULL);
|
||||
SET_RCODE(header, REFUSED);
|
||||
}
|
||||
|
||||
return p - (unsigned char *)header;
|
||||
}
|
||||
|
||||
/* check if name matches local names ie from /etc/hosts or DHCP or local mx names. */
|
||||
@@ -1017,47 +1073,61 @@ int check_for_local_domain(char *name, time_t now)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Is the packet a reply with the answer address equal to addr?
|
||||
If so mung is into an NXDOMAIN reply and also put that information
|
||||
in the cache. */
|
||||
int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
|
||||
struct bogus_addr *baddr, time_t now)
|
||||
static int check_bad_address(struct dns_header *header, size_t qlen, struct bogus_addr *baddr, char *name, unsigned long *ttlp)
|
||||
{
|
||||
unsigned char *p;
|
||||
int i, qtype, qclass, rdlen;
|
||||
unsigned long ttl;
|
||||
struct bogus_addr *baddrp;
|
||||
|
||||
|
||||
/* skip over questions */
|
||||
if (!(p = skip_questions(header, qlen)))
|
||||
return 0; /* bad packet */
|
||||
|
||||
for (i = ntohs(header->ancount); i != 0; i--)
|
||||
{
|
||||
if (!extract_name(header, qlen, &p, name, 1, 10))
|
||||
if (name && !extract_name(header, qlen, &p, name, 1, 10))
|
||||
return 0; /* bad packet */
|
||||
|
||||
|
||||
if (!name && !(p = skip_name(p, header, qlen, 10)))
|
||||
return 0;
|
||||
|
||||
GETSHORT(qtype, p);
|
||||
GETSHORT(qclass, p);
|
||||
GETLONG(ttl, p);
|
||||
GETSHORT(rdlen, p);
|
||||
|
||||
if (ttlp)
|
||||
*ttlp = ttl;
|
||||
|
||||
if (qclass == C_IN && qtype == T_A)
|
||||
if (qclass == C_IN)
|
||||
{
|
||||
if (!CHECK_LEN(header, p, qlen, INADDRSZ))
|
||||
return 0;
|
||||
|
||||
for (baddrp = baddr; baddrp; baddrp = baddrp->next)
|
||||
if (memcmp(&baddrp->addr, p, INADDRSZ) == 0)
|
||||
{
|
||||
/* Found a bogus address. Insert that info here, since there no SOA record
|
||||
to get the ttl from in the normal processing */
|
||||
cache_start_insert();
|
||||
cache_insert(name, NULL, C_IN, now, ttl, F_IPV4 | F_FORWARD | F_NEG | F_NXDOMAIN);
|
||||
cache_end_insert();
|
||||
|
||||
return 1;
|
||||
}
|
||||
if (qtype == T_A)
|
||||
{
|
||||
struct in_addr addr;
|
||||
|
||||
if (!CHECK_LEN(header, p, qlen, INADDRSZ))
|
||||
return 0;
|
||||
|
||||
memcpy(&addr, p, INADDRSZ);
|
||||
|
||||
for (baddrp = baddr; baddrp; baddrp = baddrp->next)
|
||||
if (!baddrp->is6 && is_same_net_prefix(addr, baddrp->addr.addr4, baddrp->prefix))
|
||||
return 1;
|
||||
}
|
||||
else if (qtype == T_AAAA)
|
||||
{
|
||||
struct in6_addr addr;
|
||||
|
||||
if (!CHECK_LEN(header, p, qlen, IN6ADDRSZ))
|
||||
return 0;
|
||||
|
||||
memcpy(&addr, p, IN6ADDRSZ);
|
||||
|
||||
for (baddrp = baddr; baddrp; baddrp = baddrp->next)
|
||||
if (baddrp->is6 && is_same_net6(&addr, &baddrp->addr.addr6, baddrp->prefix))
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ADD_RDLEN(header, p, qlen, rdlen))
|
||||
@@ -1067,43 +1137,31 @@ int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int check_for_ignored_address(struct dns_header *header, size_t qlen, struct bogus_addr *baddr)
|
||||
/* Is the packet a reply with the answer address equal to addr?
|
||||
If so mung is into an NXDOMAIN reply and also put that information
|
||||
in the cache. */
|
||||
int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name, time_t now)
|
||||
{
|
||||
unsigned char *p;
|
||||
int i, qtype, qclass, rdlen;
|
||||
struct bogus_addr *baddrp;
|
||||
unsigned long ttl;
|
||||
|
||||
/* skip over questions */
|
||||
if (!(p = skip_questions(header, qlen)))
|
||||
return 0; /* bad packet */
|
||||
|
||||
for (i = ntohs(header->ancount); i != 0; i--)
|
||||
if (check_bad_address(header, qlen, daemon->bogus_addr, name, &ttl))
|
||||
{
|
||||
if (!(p = skip_name(p, header, qlen, 10)))
|
||||
return 0; /* bad packet */
|
||||
|
||||
GETSHORT(qtype, p);
|
||||
GETSHORT(qclass, p);
|
||||
p += 4; /* TTL */
|
||||
GETSHORT(rdlen, p);
|
||||
|
||||
if (qclass == C_IN && qtype == T_A)
|
||||
{
|
||||
if (!CHECK_LEN(header, p, qlen, INADDRSZ))
|
||||
return 0;
|
||||
|
||||
for (baddrp = baddr; baddrp; baddrp = baddrp->next)
|
||||
if (memcmp(&baddrp->addr, p, INADDRSZ) == 0)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!ADD_RDLEN(header, p, qlen, rdlen))
|
||||
return 0;
|
||||
/* Found a bogus address. Insert that info here, since there no SOA record
|
||||
to get the ttl from in the normal processing */
|
||||
cache_start_insert();
|
||||
cache_insert(name, NULL, C_IN, now, ttl, F_IPV4 | F_FORWARD | F_NEG | F_NXDOMAIN);
|
||||
cache_end_insert();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int check_for_ignored_address(struct dns_header *header, size_t qlen)
|
||||
{
|
||||
return check_bad_address(header, qlen, daemon->ignore_addr, NULL, NULL);
|
||||
}
|
||||
|
||||
int add_resource_record(struct dns_header *header, char *limit, int *truncp, int nameoffset, unsigned char **pp,
|
||||
unsigned long ttl, int *offset, unsigned short type, unsigned short class, char *format, ...)
|
||||
@@ -1566,43 +1624,17 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
anscount++;
|
||||
}
|
||||
}
|
||||
else if (option_bool(OPT_BOGUSPRIV) && (
|
||||
(is_arpa == F_IPV6 && private_net6(&addr.addr6)) ||
|
||||
(is_arpa == F_IPV4 && private_net(addr.addr4, 1))))
|
||||
else if (option_bool(OPT_BOGUSPRIV) &&
|
||||
((is_arpa == F_IPV6 && private_net6(&addr.addr6, 1)) || (is_arpa == F_IPV4 && private_net(addr.addr4, 1))) &&
|
||||
!lookup_domain(name, F_DOMAINSRV, NULL, NULL))
|
||||
{
|
||||
struct server *serv;
|
||||
unsigned int namelen = strlen(name);
|
||||
char *nameend = name + namelen;
|
||||
|
||||
/* see if have rev-server set */
|
||||
for (serv = daemon->servers; serv; serv = serv->next)
|
||||
{
|
||||
unsigned int domainlen;
|
||||
char *matchstart;
|
||||
|
||||
if ((serv->flags & (SERV_HAS_DOMAIN | SERV_NO_ADDR)) != SERV_HAS_DOMAIN)
|
||||
continue;
|
||||
|
||||
domainlen = strlen(serv->domain);
|
||||
if (domainlen == 0 || domainlen > namelen)
|
||||
continue;
|
||||
|
||||
matchstart = nameend - domainlen;
|
||||
if (hostname_isequal(matchstart, serv->domain) &&
|
||||
(namelen == domainlen || *(matchstart-1) == '.' ))
|
||||
break;
|
||||
}
|
||||
|
||||
/* if no configured server, not in cache, enabled and private IPV4 address, return NXDOMAIN */
|
||||
if (!serv)
|
||||
{
|
||||
ans = 1;
|
||||
sec_data = 0;
|
||||
nxdomain = 1;
|
||||
if (!dryrun)
|
||||
log_query(F_CONFIG | F_REVERSE | is_arpa | F_NEG | F_NXDOMAIN,
|
||||
name, &addr, NULL);
|
||||
}
|
||||
ans = 1;
|
||||
sec_data = 0;
|
||||
nxdomain = 1;
|
||||
if (!dryrun)
|
||||
log_query(F_CONFIG | F_REVERSE | is_arpa | F_NEG | F_NXDOMAIN,
|
||||
name, &addr, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -2785,11 +2785,4 @@ static void apply_delay(u32 xid, time_t recvtime, struct dhcp_netid *netid)
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif /* HAVE_DHCP */
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -919,11 +919,14 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
|
||||
|
||||
|
||||
case DHCP6RENEW:
|
||||
case DHCP6REBIND:
|
||||
{
|
||||
int address_assigned = 0;
|
||||
|
||||
/* set reply message type */
|
||||
*outmsgtypep = DHCP6REPLY;
|
||||
|
||||
log6_quiet(state, "DHCPRENEW", NULL, NULL);
|
||||
log6_quiet(state, msg_type == DHCP6RENEW ? "DHCPRENEW" : "DHCPREBIND", NULL, NULL);
|
||||
|
||||
for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end))
|
||||
{
|
||||
@@ -952,24 +955,35 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
|
||||
state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA,
|
||||
state->iaid, &req_addr)))
|
||||
{
|
||||
/* If the server cannot find a client entry for the IA the server
|
||||
returns the IA containing no addresses with a Status Code option set
|
||||
to NoBinding in the Reply message. */
|
||||
save_counter(iacntr);
|
||||
t1cntr = 0;
|
||||
|
||||
log6_packet(state, "DHCPREPLY", &req_addr, _("lease not found"));
|
||||
|
||||
o1 = new_opt6(OPTION6_STATUS_CODE);
|
||||
put_opt6_short(DHCP6NOBINDING);
|
||||
put_opt6_string(_("no binding found"));
|
||||
end_opt6(o1);
|
||||
|
||||
preferred_time = valid_time = 0;
|
||||
break;
|
||||
if (msg_type == DHCP6REBIND)
|
||||
{
|
||||
/* When rebinding, we can create a lease if it doesn't exist. */
|
||||
lease = lease6_allocate(&req_addr, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA);
|
||||
if (lease)
|
||||
lease_set_iaid(lease, state->iaid);
|
||||
else
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If the server cannot find a client entry for the IA the server
|
||||
returns the IA containing no addresses with a Status Code option set
|
||||
to NoBinding in the Reply message. */
|
||||
save_counter(iacntr);
|
||||
t1cntr = 0;
|
||||
|
||||
log6_packet(state, "DHCPREPLY", &req_addr, _("lease not found"));
|
||||
|
||||
o1 = new_opt6(OPTION6_STATUS_CODE);
|
||||
put_opt6_short(DHCP6NOBINDING);
|
||||
put_opt6_string(_("no binding found"));
|
||||
end_opt6(o1);
|
||||
|
||||
preferred_time = valid_time = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ((this_context = address6_available(state->context, &req_addr, tagif, 1)) ||
|
||||
(this_context = address6_valid(state->context, &req_addr, tagif, 1)))
|
||||
{
|
||||
@@ -1000,6 +1014,8 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
|
||||
|
||||
if (preferred_time == 0)
|
||||
message = _("deprecated");
|
||||
|
||||
address_assigned = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1022,10 +1038,18 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
|
||||
end_ia(t1cntr, min_time, 1);
|
||||
end_opt6(o);
|
||||
}
|
||||
|
||||
if (!address_assigned && msg_type == DHCP6REBIND)
|
||||
{
|
||||
/* can't create lease for any address, return error */
|
||||
o1 = new_opt6(OPTION6_STATUS_CODE);
|
||||
put_opt6_short(DHCP6NOADDRS);
|
||||
put_opt6_string(_("no addresses available"));
|
||||
end_opt6(o1);
|
||||
}
|
||||
|
||||
tagif = add_options(state, 0);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case DHCP6CONFIRM:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -98,7 +98,7 @@ int add_to_ipset(const char *setname, const union all_addr *ipaddr,
|
||||
io.pfrio_size = 1;
|
||||
if (ioctl(dev, DIOCRADDTABLES, &io))
|
||||
{
|
||||
my_syslog(LOG_WARNING, _("IPset: error:%s"), pfr_strerror(errno));
|
||||
my_syslog(LOG_WARNING, _("IPset: error: %s"), pfr_strerror(errno));
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
40
src/tftp.c
40
src/tftp.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -22,7 +22,7 @@ static void handle_tftp(time_t now, struct tftp_transfer *transfer, ssize_t len)
|
||||
static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix);
|
||||
static void free_transfer(struct tftp_transfer *transfer);
|
||||
static ssize_t tftp_err(int err, char *packet, char *message, char *file);
|
||||
static ssize_t tftp_err_oops(char *packet, char *file);
|
||||
static ssize_t tftp_err_oops(char *packet, const char *file);
|
||||
static ssize_t get_block(char *packet, struct tftp_transfer *transfer);
|
||||
static char *next(char **p, char *end);
|
||||
static void sanitise(char *buf);
|
||||
@@ -39,6 +39,7 @@ static void sanitise(char *buf);
|
||||
#define ERR_PERM 2
|
||||
#define ERR_FULL 3
|
||||
#define ERR_ILL 4
|
||||
#define ERR_TID 5
|
||||
|
||||
void tftp_request(struct listener *listen, time_t now)
|
||||
{
|
||||
@@ -94,7 +95,7 @@ void tftp_request(struct listener *listen, time_t now)
|
||||
|
||||
if ((len = recvmsg(listen->tftpfd, &msg, 0)) < 2)
|
||||
return;
|
||||
|
||||
|
||||
/* Can always get recvd interface for IPv6 */
|
||||
if (!check_dest)
|
||||
{
|
||||
@@ -583,11 +584,27 @@ void check_tftp_listeners(time_t now)
|
||||
for (transfer = daemon->tftp_trans; transfer; transfer = transfer->next)
|
||||
if (poll_check(transfer->sockfd, POLLIN))
|
||||
{
|
||||
union mysockaddr peer;
|
||||
socklen_t addr_len = sizeof(union mysockaddr);
|
||||
ssize_t len;
|
||||
|
||||
/* we overwrote the buffer... */
|
||||
daemon->srv_save = NULL;
|
||||
handle_tftp(now, transfer, recv(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0));
|
||||
}
|
||||
|
||||
if ((len = recvfrom(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0, &peer.sa, &addr_len)) > 0)
|
||||
{
|
||||
if (sockaddr_isequal(&peer, &transfer->peer))
|
||||
handle_tftp(now, transfer, len);
|
||||
else
|
||||
{
|
||||
/* Wrong source address. See rfc1350 para 4. */
|
||||
prettyprint_addr(&peer, daemon->addrbuff);
|
||||
len = tftp_err(ERR_TID, daemon->packet, _("ignoring packet from %s (TID mismatch)"), daemon->addrbuff);
|
||||
sendto(transfer->sockfd, daemon->packet, len, 0, &peer.sa, sa_len(&peer));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; transfer = tmp)
|
||||
{
|
||||
tmp = transfer->next;
|
||||
@@ -602,7 +619,7 @@ void check_tftp_listeners(time_t now)
|
||||
|
||||
/* we overwrote the buffer... */
|
||||
daemon->srv_save = NULL;
|
||||
|
||||
|
||||
if ((len = get_block(daemon->packet, transfer)) == -1)
|
||||
{
|
||||
len = tftp_err_oops(daemon->packet, transfer->file->filename);
|
||||
@@ -736,22 +753,25 @@ static ssize_t tftp_err(int err, char *packet, char *message, char *file)
|
||||
char *errstr = strerror(errno);
|
||||
|
||||
memset(packet, 0, daemon->packet_buff_sz);
|
||||
sanitise(file);
|
||||
if (file)
|
||||
sanitise(file);
|
||||
|
||||
mess->op = htons(OP_ERR);
|
||||
mess->err = htons(err);
|
||||
len = snprintf(mess->message, MAXMESSAGE, message, file, errstr);
|
||||
ret += (len < MAXMESSAGE) ? len + 1 : MAXMESSAGE; /* include terminating zero */
|
||||
|
||||
my_syslog(MS_TFTP | LOG_ERR, "%s", mess->message);
|
||||
if (err != ERR_FNF || !option_bool(OPT_QUIET_TFTP))
|
||||
my_syslog(MS_TFTP | LOG_ERR, "%s", mess->message);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t tftp_err_oops(char *packet, char *file)
|
||||
static ssize_t tftp_err_oops(char *packet, const char *file)
|
||||
{
|
||||
/* May have >1 refs to file, so potentially mangle a copy of the name */
|
||||
strcpy(daemon->namebuff, file);
|
||||
if (file != daemon->namebuff)
|
||||
strcpy(daemon->namebuff, file);
|
||||
return tftp_err(ERR_NOTDEF, packet, _("cannot read %s: %s"), daemon->namebuff);
|
||||
}
|
||||
|
||||
|
||||
217
src/ubus.c
217
src/ubus.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -28,10 +28,38 @@ static int ubus_handle_metrics(struct ubus_context *ctx, struct ubus_object *obj
|
||||
struct ubus_request_data *req, const char *method,
|
||||
struct blob_attr *msg);
|
||||
|
||||
#ifdef HAVE_CONNTRACK
|
||||
enum {
|
||||
SET_CONNMARK_ALLOWLIST_MARK,
|
||||
SET_CONNMARK_ALLOWLIST_MASK,
|
||||
SET_CONNMARK_ALLOWLIST_PATTERNS
|
||||
};
|
||||
static const struct blobmsg_policy set_connmark_allowlist_policy[] = {
|
||||
[SET_CONNMARK_ALLOWLIST_MARK] = {
|
||||
.name = "mark",
|
||||
.type = BLOBMSG_TYPE_INT32
|
||||
},
|
||||
[SET_CONNMARK_ALLOWLIST_MASK] = {
|
||||
.name = "mask",
|
||||
.type = BLOBMSG_TYPE_INT32
|
||||
},
|
||||
[SET_CONNMARK_ALLOWLIST_PATTERNS] = {
|
||||
.name = "patterns",
|
||||
.type = BLOBMSG_TYPE_ARRAY
|
||||
}
|
||||
};
|
||||
static int ubus_handle_set_connmark_allowlist(struct ubus_context *ctx, struct ubus_object *obj,
|
||||
struct ubus_request_data *req, const char *method,
|
||||
struct blob_attr *msg);
|
||||
#endif
|
||||
|
||||
static void ubus_subscribe_cb(struct ubus_context *ctx, struct ubus_object *obj);
|
||||
|
||||
static const struct ubus_method ubus_object_methods[] = {
|
||||
UBUS_METHOD_NOARG("metrics", ubus_handle_metrics),
|
||||
#ifdef HAVE_CONNTRACK
|
||||
UBUS_METHOD("set_connmark_allowlist", ubus_handle_set_connmark_allowlist, set_connmark_allowlist_policy),
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct ubus_object_type ubus_object_type =
|
||||
@@ -76,42 +104,27 @@ static void ubus_disconnect_cb(struct ubus_context *ubus)
|
||||
}
|
||||
}
|
||||
|
||||
void ubus_init()
|
||||
char *ubus_init()
|
||||
{
|
||||
struct ubus_context *ubus = NULL;
|
||||
int ret = 0;
|
||||
|
||||
ubus = ubus_connect(NULL);
|
||||
if (!ubus)
|
||||
{
|
||||
if (!error_logged)
|
||||
{
|
||||
my_syslog(LOG_ERR, _("Cannot initialize UBus: connection failed"));
|
||||
error_logged = 1;
|
||||
}
|
||||
|
||||
ubus_destroy(ubus);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(ubus = ubus_connect(NULL)))
|
||||
return NULL;
|
||||
|
||||
ubus_object.name = daemon->ubus_name;
|
||||
ret = ubus_add_object(ubus, &ubus_object);
|
||||
if (ret)
|
||||
{
|
||||
if (!error_logged)
|
||||
{
|
||||
my_syslog(LOG_ERR, _("Cannot add object to UBus: %s"), ubus_strerror(ret));
|
||||
error_logged = 1;
|
||||
}
|
||||
ubus_destroy(ubus);
|
||||
return;
|
||||
}
|
||||
|
||||
return (char *)ubus_strerror(ret);
|
||||
}
|
||||
|
||||
ubus->connection_lost = ubus_disconnect_cb;
|
||||
daemon->ubus = ubus;
|
||||
error_logged = 0;
|
||||
|
||||
my_syslog(LOG_INFO, _("Connected to system UBus"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void set_ubus_listeners()
|
||||
@@ -178,6 +191,122 @@ static int ubus_handle_metrics(struct ubus_context *ctx, struct ubus_object *obj
|
||||
return ubus_send_reply(ctx, req, b.head);
|
||||
}
|
||||
|
||||
#ifdef HAVE_CONNTRACK
|
||||
static int ubus_handle_set_connmark_allowlist(struct ubus_context *ctx, struct ubus_object *obj,
|
||||
struct ubus_request_data *req, const char *method,
|
||||
struct blob_attr *msg)
|
||||
{
|
||||
const struct blobmsg_policy *policy = set_connmark_allowlist_policy;
|
||||
size_t policy_len = countof(set_connmark_allowlist_policy);
|
||||
struct allowlist *allowlists = NULL, **allowlists_pos;
|
||||
char **patterns = NULL, **patterns_pos;
|
||||
u32 mark, mask = UINT32_MAX;
|
||||
size_t num_patterns = 0;
|
||||
struct blob_attr *tb[policy_len];
|
||||
struct blob_attr *attr;
|
||||
|
||||
if (blobmsg_parse(policy, policy_len, tb, blob_data(msg), blob_len(msg)))
|
||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
if (!tb[SET_CONNMARK_ALLOWLIST_MARK])
|
||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||
mark = blobmsg_get_u32(tb[SET_CONNMARK_ALLOWLIST_MARK]);
|
||||
if (!mark)
|
||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
if (tb[SET_CONNMARK_ALLOWLIST_MASK])
|
||||
{
|
||||
mask = blobmsg_get_u32(tb[SET_CONNMARK_ALLOWLIST_MASK]);
|
||||
if (!mask || (mark & ~mask))
|
||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (tb[SET_CONNMARK_ALLOWLIST_PATTERNS])
|
||||
{
|
||||
struct blob_attr *head = blobmsg_data(tb[SET_CONNMARK_ALLOWLIST_PATTERNS]);
|
||||
size_t len = blobmsg_data_len(tb[SET_CONNMARK_ALLOWLIST_PATTERNS]);
|
||||
__blob_for_each_attr(attr, head, len)
|
||||
{
|
||||
char *pattern;
|
||||
if (blob_id(attr) != BLOBMSG_TYPE_STRING)
|
||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||
if (!(pattern = blobmsg_get_string(attr)))
|
||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||
if (strcmp(pattern, "*") && !is_valid_dns_name_pattern(pattern))
|
||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||
num_patterns++;
|
||||
}
|
||||
}
|
||||
|
||||
for (allowlists_pos = &daemon->allowlists; *allowlists_pos; allowlists_pos = &(*allowlists_pos)->next)
|
||||
if ((*allowlists_pos)->mark == mark && (*allowlists_pos)->mask == mask)
|
||||
{
|
||||
struct allowlist *allowlists_next = (*allowlists_pos)->next;
|
||||
for (patterns_pos = (*allowlists_pos)->patterns; *patterns_pos; patterns_pos++)
|
||||
{
|
||||
free(*patterns_pos);
|
||||
*patterns_pos = NULL;
|
||||
}
|
||||
free((*allowlists_pos)->patterns);
|
||||
(*allowlists_pos)->patterns = NULL;
|
||||
free(*allowlists_pos);
|
||||
*allowlists_pos = allowlists_next;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!num_patterns)
|
||||
return UBUS_STATUS_OK;
|
||||
|
||||
patterns = whine_malloc((num_patterns + 1) * sizeof(char *));
|
||||
if (!patterns)
|
||||
goto fail;
|
||||
patterns_pos = patterns;
|
||||
if (tb[SET_CONNMARK_ALLOWLIST_PATTERNS])
|
||||
{
|
||||
struct blob_attr *head = blobmsg_data(tb[SET_CONNMARK_ALLOWLIST_PATTERNS]);
|
||||
size_t len = blobmsg_data_len(tb[SET_CONNMARK_ALLOWLIST_PATTERNS]);
|
||||
__blob_for_each_attr(attr, head, len)
|
||||
{
|
||||
char *pattern;
|
||||
if (!(pattern = blobmsg_get_string(attr)))
|
||||
goto fail;
|
||||
if (!(*patterns_pos = whine_malloc(strlen(pattern) + 1)))
|
||||
goto fail;
|
||||
strcpy(*patterns_pos++, pattern);
|
||||
}
|
||||
}
|
||||
|
||||
allowlists = whine_malloc(sizeof(struct allowlist));
|
||||
if (!allowlists)
|
||||
goto fail;
|
||||
memset(allowlists, 0, sizeof(struct allowlist));
|
||||
allowlists->mark = mark;
|
||||
allowlists->mask = mask;
|
||||
allowlists->patterns = patterns;
|
||||
allowlists->next = daemon->allowlists;
|
||||
daemon->allowlists = allowlists;
|
||||
return UBUS_STATUS_OK;
|
||||
|
||||
fail:
|
||||
if (patterns)
|
||||
{
|
||||
for (patterns_pos = patterns; *patterns_pos; patterns_pos++)
|
||||
{
|
||||
free(*patterns_pos);
|
||||
*patterns_pos = NULL;
|
||||
}
|
||||
free(patterns);
|
||||
patterns = NULL;
|
||||
}
|
||||
if (allowlists)
|
||||
{
|
||||
free(allowlists);
|
||||
allowlists = NULL;
|
||||
}
|
||||
return UBUS_STATUS_UNKNOWN_ERROR;
|
||||
}
|
||||
#endif
|
||||
|
||||
void ubus_event_bcast(const char *type, const char *mac, const char *ip, const char *name, const char *interface)
|
||||
{
|
||||
struct ubus_context *ubus = (struct ubus_context *)daemon->ubus;
|
||||
@@ -197,9 +326,47 @@ void ubus_event_bcast(const char *type, const char *mac, const char *ip, const c
|
||||
blobmsg_add_string(&b, "interface", interface);
|
||||
|
||||
ret = ubus_notify(ubus, &ubus_object, type, b.head, -1);
|
||||
if (!ret)
|
||||
if (ret)
|
||||
my_syslog(LOG_ERR, _("Failed to send UBus event: %s"), ubus_strerror(ret));
|
||||
}
|
||||
|
||||
#ifdef HAVE_CONNTRACK
|
||||
void ubus_event_bcast_connmark_allowlist_refused(u32 mark, const char *name)
|
||||
{
|
||||
struct ubus_context *ubus = (struct ubus_context *)daemon->ubus;
|
||||
int ret;
|
||||
|
||||
if (!ubus || !notify)
|
||||
return;
|
||||
|
||||
blob_buf_init(&b, 0);
|
||||
blobmsg_add_u32(&b, "mark", mark);
|
||||
blobmsg_add_string(&b, "name", name);
|
||||
|
||||
ret = ubus_notify(ubus, &ubus_object, "connmark-allowlist.refused", b.head, -1);
|
||||
if (ret)
|
||||
my_syslog(LOG_ERR, _("Failed to send UBus event: %s"), ubus_strerror(ret));
|
||||
}
|
||||
|
||||
void ubus_event_bcast_connmark_allowlist_resolved(u32 mark, const char *name, const char *value, u32 ttl)
|
||||
{
|
||||
struct ubus_context *ubus = (struct ubus_context *)daemon->ubus;
|
||||
int ret;
|
||||
|
||||
if (!ubus || !notify)
|
||||
return;
|
||||
|
||||
blob_buf_init(&b, 0);
|
||||
blobmsg_add_u32(&b, "mark", mark);
|
||||
blobmsg_add_string(&b, "name", name);
|
||||
blobmsg_add_string(&b, "value", value);
|
||||
blobmsg_add_u32(&b, "ttl", ttl);
|
||||
|
||||
ret = ubus_notify(ubus, &ubus_object, "connmark-allowlist.resolved", b.head, /* timeout: */ 1000);
|
||||
if (ret)
|
||||
my_syslog(LOG_ERR, _("Failed to send UBus event: %s"), ubus_strerror(ret));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* HAVE_UBUS */
|
||||
|
||||
16
src/util.c
16
src/util.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -316,7 +316,7 @@ void *whine_malloc(size_t size)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2)
|
||||
int sockaddr_isequal(const union mysockaddr *s1, const union mysockaddr *s2)
|
||||
{
|
||||
if (s1->sa.sa_family == s2->sa.sa_family)
|
||||
{
|
||||
@@ -436,7 +436,17 @@ int netmask_length(struct in_addr mask)
|
||||
int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask)
|
||||
{
|
||||
return (a.s_addr & mask.s_addr) == (b.s_addr & mask.s_addr);
|
||||
}
|
||||
}
|
||||
|
||||
int is_same_net_prefix(struct in_addr a, struct in_addr b, int prefix)
|
||||
{
|
||||
struct in_addr mask;
|
||||
|
||||
mask.s_addr = htonl(~((1 << (32 - prefix)) - 1));
|
||||
|
||||
return is_same_net(a, b, mask);
|
||||
}
|
||||
|
||||
|
||||
int is_same_net6(struct in6_addr *a, struct in6_addr *b, int prefixlen)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user