Compare commits
262 Commits
v2.83
...
v2.87test8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e426c2d3bc | ||
|
|
6279d9eaf3 | ||
|
|
12949aa0c0 | ||
|
|
84f3357dd9 | ||
|
|
4333d5d93a | ||
|
|
fa580ad3eb | ||
|
|
292dfa653e | ||
|
|
7fbf1cce7b | ||
|
|
dbceeb4178 | ||
|
|
ed200fa001 | ||
|
|
b5dafc0b7e | ||
|
|
fc664d114d | ||
|
|
c6d4c33d61 | ||
|
|
bf1fc6c6fd | ||
|
|
b18e9c8c61 | ||
|
|
a3293bb242 | ||
|
|
4e2a4b8788 | ||
|
|
2362784bc0 | ||
|
|
b2cec1b881 | ||
|
|
a946857133 | ||
|
|
10cd342f5c | ||
|
|
27ce754b3d | ||
|
|
3ab0ad8748 | ||
|
|
4308236262 | ||
|
|
ebd8350300 | ||
|
|
8285d335f4 | ||
|
|
9db275ebea | ||
|
|
1f8f78a49b | ||
|
|
c2f129ba3d | ||
|
|
07c47416a9 | ||
|
|
8f2a62b386 | ||
|
|
a6c0edd4f4 | ||
|
|
ff43d35aee | ||
|
|
70fca205be | ||
|
|
1033130b6c | ||
|
|
8cfcd9ff63 | ||
|
|
80a6c16dcc | ||
|
|
553c4c99cc | ||
|
|
4165c1331b | ||
|
|
b2690415bf | ||
|
|
011f8cf1d0 | ||
|
|
2748fb81e2 | ||
|
|
46312909d9 | ||
|
|
41adecad14 | ||
|
|
ea5d8c56a0 | ||
|
|
d242cbffa4 | ||
|
|
1c8855ed10 | ||
|
|
ea33a01303 | ||
|
|
18b1d1424e | ||
|
|
1176cd58c9 | ||
|
|
44a4643b62 | ||
|
|
ed96efd865 | ||
|
|
e3093b532c | ||
|
|
9560658c5b | ||
|
|
37a70d39e0 | ||
|
|
72fac0810c | ||
|
|
c166c07a93 | ||
|
|
39a625ff72 | ||
|
|
ad32ca18a7 | ||
|
|
efea282396 | ||
|
|
33d6a01cd3 | ||
|
|
d290630d31 | ||
|
|
d2ad5dc073 | ||
|
|
68ab5127af | ||
|
|
089a11f340 | ||
|
|
de1d04eb66 | ||
|
|
ed4e7defd7 | ||
|
|
267ab619c4 | ||
|
|
0140454ba2 | ||
|
|
2c60441239 | ||
|
|
cbbd56c965 | ||
|
|
2561f9fe0e | ||
|
|
47aefca5e4 | ||
|
|
981fb03710 | ||
|
|
ef2f8d70d2 | ||
|
|
d9995a1add | ||
|
|
ea7a05ad43 | ||
|
|
26bbf5a314 | ||
|
|
c147329823 | ||
|
|
eb88eed1fc | ||
|
|
8312a3ba4f | ||
|
|
35f93081dc | ||
|
|
de372d6914 | ||
|
|
4ac517e4ac | ||
|
|
e3651367b3 | ||
|
|
02ea41ddd1 | ||
|
|
51ffae4eab | ||
|
|
afe84f37f8 | ||
|
|
0afeef0e00 | ||
|
|
94a17fd97f | ||
|
|
5b5ec55445 | ||
|
|
1e6565c1a5 | ||
|
|
fc522515b9 | ||
|
|
9881b0736d | ||
|
|
e52b4b1466 | ||
|
|
2f45670951 | ||
|
|
50d75ae514 | ||
|
|
dea69a12aa | ||
|
|
e0ce3c12f2 | ||
|
|
93cf516bf1 | ||
|
|
6f4de018af | ||
|
|
6e91cf3172 | ||
|
|
9cb7f8a655 | ||
|
|
5d8d1ad14b | ||
|
|
cac9ca38f6 | ||
|
|
c4523639d5 | ||
|
|
1ce1c6beae | ||
|
|
51d56df7a3 | ||
|
|
860a9a57d6 | ||
|
|
c83e33d608 | ||
|
|
7b80c75d9d | ||
|
|
79337f99ae | ||
|
|
a42ee397f3 | ||
|
|
e58f8bb8c1 | ||
|
|
58cf958e41 | ||
|
|
06d01f7ae4 | ||
|
|
1a33eec0ba | ||
|
|
82de7a1e96 | ||
|
|
8e9bde57c5 | ||
|
|
1bb70e08be | ||
|
|
9afcb7ae61 | ||
|
|
545c4955e6 | ||
|
|
2f2d59b35c | ||
|
|
a1729deed3 | ||
|
|
fc64b97cd5 | ||
|
|
daddc8cb80 | ||
|
|
527c3c7d0d | ||
|
|
fcb4dcaf7c | ||
|
|
3ca4995d34 | ||
|
|
d387f8f06c | ||
|
|
ea43234c86 | ||
|
|
867e56a45e | ||
|
|
a163c63787 | ||
|
|
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 |
239
CHANGELOG
239
CHANGELOG
@@ -1,3 +1,238 @@
|
||||
version 2.87
|
||||
Allow arbitrary prefix lengths in --rev-server and
|
||||
--domain=....,local
|
||||
|
||||
Replace --address=/#/..... functionality which got
|
||||
missed in the 2.86 domain search rewrite.
|
||||
|
||||
Add --nftset option, like --ipset but for the newer nftables.
|
||||
Thanks to Chen Zhenge for the patch.
|
||||
|
||||
Add --filter-A and --filter-AAAA options, to remove IPv4 or IPv6
|
||||
addresses from DNS answers.
|
||||
|
||||
Fix crash doing netbooting when --port is set to zero
|
||||
to disable the DNS server. Thanks to Drexl Johannes
|
||||
for the bug report.
|
||||
|
||||
Generalise --dhcp-relay. Sending via broadcast/multicast is
|
||||
now supported for both IPv4 and IPv6 and the configuration
|
||||
syntax made easier (but backwards compatible).
|
||||
|
||||
Add snooping of IPv6 prefix-delegations to the DHCP-relay system.
|
||||
|
||||
Finesse parsing of --dhcp-remoteid and --dhcp-subscrid. To be treated
|
||||
as hex, the pattern must consist of only hex digits AND contain
|
||||
at least one ':'. Thanks to Bengt-Erik Sandstrom who tripped
|
||||
over a pattern consisting of a decimal number which was interpreted
|
||||
surprisingly.
|
||||
|
||||
Include client address in TFTP file-not-found error reports.
|
||||
Thanks to Stefan Rink for the initial patch, which has been
|
||||
re-worked by me (srk). All bugs mine.
|
||||
|
||||
Note in manpage the change in behaviour of -address. This behaviour
|
||||
actually changed in v2.86, but was undocumented there. From 2.86 on,
|
||||
(eg) --address=/example.com/1.2.3.4 ONLY applies to A queries. All other
|
||||
types of query will be sent upstream. Pre 2.86, that would catch the
|
||||
whole example.com domain and queries for other types would get
|
||||
a local NODATA answer. The pre-2.86 behaviour is still available,
|
||||
by configuring --address=/example.com/1.2.3.4 --local=/example.com/
|
||||
|
||||
Fix problem with binding DHCP sockets to an individual interface.
|
||||
Despite the fact that the system call tales the interface _name_ as
|
||||
a parameter, it actually, binds the socket to interface _index_.
|
||||
Deleting the interface and creating a new one with the same name
|
||||
leaves the socket bound to the old index. (Creating new sockets
|
||||
always allocates a fresh index, they are not reused). We now
|
||||
take this behaviour into account and keep up with changing indexes.
|
||||
|
||||
Add --conf-script configuration option.
|
||||
|
||||
|
||||
version 2.86
|
||||
Handle DHCPREBIND requests in the DHCPv6 server code.
|
||||
Thanks to Aichun Li for spotting this omission, 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 algorithms 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 than 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.
|
||||
|
||||
Ensure CAP_NET_ADMIN capability is available when
|
||||
conntrack is configured. Thanks to Yick Xie for spotting
|
||||
the lack of this.
|
||||
|
||||
When --dhcp-hostsfile --dhcp-optsfile and --addn-hosts are
|
||||
given a directory as argument, define the order in which
|
||||
files within that directory are read (alphabetical order
|
||||
of filename). Thanks to Ed Wildgoose for the initial patch
|
||||
and motivation for this.
|
||||
|
||||
Allow adding IP address to nftables set in addition to
|
||||
ipset.
|
||||
|
||||
|
||||
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.
|
||||
|
||||
Support some wildcard matching of input tags to --tag-if.
|
||||
Thanks to Geoff Back for the idea and the patch.
|
||||
|
||||
|
||||
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 +254,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
|
||||
|
||||
20
Makefile
20
Makefile
@@ -1,4 +1,4 @@
|
||||
# dnsmasq is Copyright (c) 2000-2016 Simon Kelley
|
||||
# dnsmasq is Copyright (c) 2000-2022 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,12 +63,16 @@ 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`
|
||||
version = -DVERSION='\"`$(top)/bld/get-version $(top)`\"'
|
||||
nft_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_NFTSET $(PKG_CONFIG) --cflags libnftables`
|
||||
nft_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_NFTSET $(PKG_CONFIG) --libs libnftables`
|
||||
version = -DVERSION='\"`$(top)/bld/get-version $(top)`\"'
|
||||
|
||||
sum?=$(shell $(CC) -DDNSMASQ_COMPILE_OPTS $(COPTS) -E $(top)/$(SRC)/dnsmasq.h | ( md5sum 2>/dev/null || md5 ) | cut -f 1 -d ' ')
|
||||
sum!=$(CC) -DDNSMASQ_COMPILE_OPTS $(COPTS) -E $(top)/$(SRC)/dnsmasq.h | ( md5sum 2>/dev/null || md5 ) | cut -f 1 -d ' '
|
||||
@@ -77,10 +81,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 nftset.o
|
||||
|
||||
hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \
|
||||
dns-protocol.h radv-protocol.h ip6addr.h metrics.h
|
||||
@@ -88,8 +92,8 @@ hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \
|
||||
all : $(BUILDDIR)
|
||||
@cd $(BUILDDIR) && $(MAKE) \
|
||||
top="$(top)" \
|
||||
build_cflags="$(version) $(dbus_cflags) $(idn2_cflags) $(idn_cflags) $(ct_cflags) $(lua_cflags) $(nettle_cflags)" \
|
||||
build_libs="$(dbus_libs) $(idn2_libs) $(idn_libs) $(ct_libs) $(lua_libs) $(sunos_libs) $(nettle_libs) $(gmp_libs) $(ubus_libs)" \
|
||||
build_cflags="$(version) $(dbus_cflags) $(idn2_cflags) $(idn_cflags) $(ct_cflags) $(lua_cflags) $(nettle_cflags) $(nft_cflags)" \
|
||||
build_libs="$(dbus_libs) $(idn2_libs) $(idn_libs) $(ct_libs) $(lua_libs) $(sunos_libs) $(nettle_libs) $(gmp_libs) $(ubus_libs) $(nft_libs)" \
|
||||
-f $(top)/Makefile dnsmasq
|
||||
|
||||
mostly_clean :
|
||||
@@ -113,8 +117,8 @@ all-i18n : $(BUILDDIR)
|
||||
@cd $(BUILDDIR) && $(MAKE) \
|
||||
top="$(top)" \
|
||||
i18n=-DLOCALEDIR=\'\"$(LOCALEDIR)\"\' \
|
||||
build_cflags="$(version) $(dbus_cflags) $(idn2_cflags) $(idn_cflags) $(ct_cflags) $(lua_cflags) $(nettle_cflags)" \
|
||||
build_libs="$(dbus_libs) $(idn2_libs) $(idn_libs) $(ct_libs) $(lua_libs) $(sunos_libs) $(nettle_libs) $(gmp_libs) $(ubus_libs)" \
|
||||
build_cflags="$(version) $(dbus_cflags) $(idn2_cflags) $(idn_cflags) $(ct_cflags) $(lua_cflags) $(nettle_cflags) $(nft_cflags)" \
|
||||
build_libs="$(dbus_libs) $(idn2_libs) $(idn_libs) $(ct_libs) $(lua_libs) $(sunos_libs) $(nettle_libs) $(gmp_libs) $(ubus_libs) $(nft_libs)" \
|
||||
-f $(top)/Makefile dnsmasq
|
||||
for f in `cd $(PO); echo *.po`; do \
|
||||
cd $(top) && cd $(BUILDDIR) && $(MAKE) top="$(top)" -f $(top)/Makefile $${f%.po}.mo; \
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
This packaging is now unmaintained in the dnsmasq source: dnsmasq is
|
||||
included in Suse proper, and up-to-date packages are now available
|
||||
from
|
||||
|
||||
ftp://ftp.suse.com/pub/people/ug/
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
This is a patch against SuSEfirewall2-3.1-206 (SuSE 9.x and older)
|
||||
It fixes the dependency from the dns daemon name 'named'
|
||||
After appending the patch, the SuSEfirewall is again able to autodetect
|
||||
the dnsmasq named service.
|
||||
This is a very old bug in the SuSEfirewall script.
|
||||
The SuSE people think the name of the dns server will always 'named'
|
||||
|
||||
|
||||
--- /sbin/SuSEfirewall2.orig 2004-01-23 13:30:09.000000000 +0100
|
||||
+++ /sbin/SuSEfirewall2 2004-01-23 13:31:56.000000000 +0100
|
||||
@@ -764,7 +764,7 @@
|
||||
echo 'FW_ALLOW_INCOMING_HIGHPORTS_UDP should be set to yes, if you are running a DNS server!'
|
||||
|
||||
test "$FW_SERVICE_AUTODETECT" = yes -o "$FW_SERVICE_AUTODETECT" = dmz -o "$FW_SERVICE_AUTODETECT" = ext && {
|
||||
- test "$FW_SERVICE_DNS" = no -a '!' "$START_NAMED" = no && check_srv named && {
|
||||
+ test "$FW_SERVICE_DNS" = no -a '!' "$START_NAMED" = no && check_srv dnsmasq && {
|
||||
echo -e 'Warning: detected activated named, enabling FW_SERVICE_DNS!
|
||||
You still have to allow tcp/udp port 53 on internal, dmz and/or external.'
|
||||
FW_SERVICE_DNS=$FW_SERVICE_AUTODETECT
|
||||
@@ -878,7 +878,7 @@
|
||||
test -e /etc/resolv.conf || echo "Warning: /etc/resolv.conf not found"
|
||||
# Get ports/IP bindings of NAMED/SQUID
|
||||
test "$FW_SERVICE_DNS" = yes -o "$FW_SERVICE_DNS" = dmz -o "$FW_SERVICE_DNS" = ext -o "$START_NAMED" = yes && DNS_PORT=`$LSOF -i -n -P | \
|
||||
- $AWK -F: '/^named .* UDP / {print $2}'| $GREP -vw 53 | $SORT -un`
|
||||
+ $AWK -F: '/^dnsmasq .* UDP / {print $2}'| $GREP -vw 53 | $SORT -un`
|
||||
test "$FW_SERVICE_SQUID" = yes -o "$FW_SERVICE_SQUID" = dmz -o "$FW_SERVICE_SQUID" = ext -o "$START_SQUID" = yes && SQUID_PORT=`$LSOF -i -n -P | \
|
||||
$AWK -F: '/^squid .* UDP/ {print $2}'| $SORT -un`
|
||||
@@ -1,23 +0,0 @@
|
||||
--- man/dnsmasq.8 2004-08-08 20:57:56.000000000 +0200
|
||||
+++ man/dnsmasq.8 2004-08-12 00:40:01.000000000 +0200
|
||||
@@ -69,7 +69,7 @@
|
||||
.TP
|
||||
.B \-g, --group=<groupname>
|
||||
Specify the group which dnsmasq will run
|
||||
-as. The defaults to "dip", if available, to facilitate access to
|
||||
+as. The defaults to "dialout", if available, to facilitate access to
|
||||
/etc/ppp/resolv.conf which is not normally world readable.
|
||||
.TP
|
||||
.B \-v, --version
|
||||
--- src/config.h 2004-08-11 11:39:18.000000000 +0200
|
||||
+++ src/config.h 2004-08-12 00:40:01.000000000 +0200
|
||||
@@ -44,7 +44,7 @@
|
||||
#endif
|
||||
#define DEFLEASE 3600 /* default lease time, 1 hour */
|
||||
#define CHUSER "nobody"
|
||||
-#define CHGRP "dip"
|
||||
+#define CHGRP "dialout"
|
||||
#define DHCP_SERVER_PORT 67
|
||||
#define DHCP_CLIENT_PORT 68
|
||||
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
###############################################################################
|
||||
#
|
||||
# General
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
Name: dnsmasq
|
||||
Version: 2.33
|
||||
Release: 1
|
||||
Copyright: GPL
|
||||
Group: Productivity/Networking/DNS/Servers
|
||||
Vendor: Simon Kelley
|
||||
Packager: Simon Kelley
|
||||
URL: http://www.thekelleys.org.uk/dnsmasq
|
||||
Provides: dns_daemon
|
||||
Conflicts: bind bind8 bind9
|
||||
PreReq: %fillup_prereq %insserv_prereq
|
||||
Autoreqprov: on
|
||||
Source0: %{name}-%{version}.tar.bz2
|
||||
BuildRoot: /var/tmp/%{name}-%{version}
|
||||
Summary: A lightweight caching nameserver
|
||||
|
||||
%description
|
||||
Dnsmasq is lightweight, easy to configure DNS forwarder and DHCP server. It
|
||||
is designed to provide DNS and, optionally, DHCP, to a small network. It can
|
||||
serve the names of local machines which are not in the global DNS. The DHCP
|
||||
server integrates with the DNS server and allows machines with DHCP-allocated
|
||||
addresses to appear in the DNS with names configured either in each host or
|
||||
in a central configuration file. Dnsmasq supports static and dynamic DHCP
|
||||
leases and BOOTP for network booting of diskless machines.
|
||||
|
||||
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# Build
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
patch -p0 <rpm/%{name}-SuSE.patch
|
||||
|
||||
%build
|
||||
%{?suse_update_config:%{suse_update_config -f}}
|
||||
make all-i18n DESTDIR=$RPM_BUILD_ROOT PREFIX=/usr
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# Install
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
%install
|
||||
rm -rf $RPM_BUILD_ROOT
|
||||
mkdir -p ${RPM_BUILD_ROOT}/etc/init.d
|
||||
make install-i18n DESTDIR=$RPM_BUILD_ROOT PREFIX=/usr
|
||||
install -o root -g root -m 755 rpm/rc.dnsmasq-suse $RPM_BUILD_ROOT/etc/init.d/dnsmasq
|
||||
install -o root -g root -m 644 dnsmasq.conf.example $RPM_BUILD_ROOT/etc/dnsmasq.conf
|
||||
strip $RPM_BUILD_ROOT/usr/sbin/dnsmasq
|
||||
ln -sf ../../etc/init.d/dnsmasq $RPM_BUILD_ROOT/usr/sbin/rcdnsmasq
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# Clean up
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
%clean
|
||||
rm -rf $RPM_BUILD_ROOT
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# Post-install scriptlet
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
%post
|
||||
%{fillup_and_insserv dnsmasq}
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# Post-uninstall scriptlet
|
||||
#
|
||||
# The %postun script executes after the package has been removed. It is the
|
||||
# last chance for a package to clean up after itself.
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
%postun
|
||||
%{insserv_cleanup}
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# File list
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
%files
|
||||
%defattr(-,root,root)
|
||||
%doc CHANGELOG COPYING FAQ doc.html setup.html UPGRADING_to_2.0 rpm/README.susefirewall
|
||||
%doc contrib
|
||||
%config /etc/init.d/dnsmasq
|
||||
%config /etc/dnsmasq.conf
|
||||
/usr/sbin/rcdnsmasq
|
||||
/usr/sbin/dnsmasq
|
||||
/usr/share/locale/*/LC_MESSAGES/*
|
||||
%doc %{_mandir}/man8/dnsmasq.8.gz
|
||||
%doc %{_mandir}/*/man8/dnsmasq.8.gz
|
||||
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
#! /bin/sh
|
||||
#
|
||||
# init.d/dnsmasq
|
||||
#
|
||||
### BEGIN INIT INFO
|
||||
# Provides: dnsmasq
|
||||
# Required-Start: $network $remote_fs $syslog
|
||||
# Required-Stop:
|
||||
# Default-Start: 3 5
|
||||
# Default-Stop:
|
||||
# Description: Starts internet name service masq caching server (DNS)
|
||||
### END INIT INFO
|
||||
|
||||
NAMED_BIN=/usr/sbin/dnsmasq
|
||||
NAMED_PID=/var/run/dnsmasq.pid
|
||||
NAMED_CONF=/etc/dnsmasq.conf
|
||||
|
||||
if [ ! -x $NAMED_BIN ] ; then
|
||||
echo -n "dnsmasq not installed ! "
|
||||
exit 5
|
||||
fi
|
||||
|
||||
. /etc/rc.status
|
||||
rc_reset
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
echo -n "Starting name service masq caching server "
|
||||
checkproc -p $NAMED_PID $NAMED_BIN
|
||||
if [ $? -eq 0 ] ; then
|
||||
echo -n "- Warning: dnsmasq already running ! "
|
||||
else
|
||||
[ -e $NAMED_PID ] && echo -n "- Warning: $NAMED_PID exists ! "
|
||||
fi
|
||||
startproc -p $NAMED_PID $NAMED_BIN -u nobody
|
||||
rc_status -v
|
||||
;;
|
||||
stop)
|
||||
echo -n "Shutting name service masq caching server "
|
||||
checkproc -p $NAMED_PID $NAMED_BIN
|
||||
[ $? -ne 0 ] && echo -n "- Warning: dnsmasq not running ! "
|
||||
killproc -p $NAMED_PID -TERM $NAMED_BIN
|
||||
rc_status -v
|
||||
;;
|
||||
try-restart)
|
||||
$0 stop && $0 start
|
||||
rc_status
|
||||
;;
|
||||
restart)
|
||||
$0 stop
|
||||
$0 start
|
||||
rc_status
|
||||
;;
|
||||
force-reload)
|
||||
$0 reload
|
||||
rc_status
|
||||
;;
|
||||
reload)
|
||||
echo -n "Reloading name service masq caching server "
|
||||
checkproc -p $NAMED_PID $NAMED_BIN
|
||||
[ $? -ne 0 ] && echo -n "- Warning: dnsmasq not running ! "
|
||||
killproc -p $NAMED_PID -HUP $NAMED_BIN
|
||||
rc_status -v
|
||||
;;
|
||||
status)
|
||||
echo -n "Checking for name service masq caching server "
|
||||
checkproc -p $NAMED_PID $NAMED_BIN
|
||||
rc_status -v
|
||||
;;
|
||||
probe)
|
||||
test $NAMED_CONF -nt $NAMED_PID && echo reload
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {start|stop|status|try-restart|restart|force-reload|reload|probe}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
rc_exit
|
||||
|
||||
@@ -153,7 +153,11 @@ int main(int argc, char **argv)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
lease.s_addr = inet_addr(argv[1]);
|
||||
if (inet_pton(AF_INET, argv[1], &lease) < 1)
|
||||
{
|
||||
fprintf(stderr, "invalid address: %s\n", argv[1]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
memset(&packet, 0, sizeof(packet));
|
||||
|
||||
@@ -176,8 +180,8 @@ int main(int argc, char **argv)
|
||||
|
||||
*(p++) = OPTION_END;
|
||||
|
||||
dest.sin_family = AF_INET;
|
||||
dest.sin_addr.s_addr = inet_addr("127.0.0.1");
|
||||
dest.sin_family = AF_INET;
|
||||
(void)inet_pton(AF_INET, "127.0.0.1", &dest.sin_addr);
|
||||
dest.sin_port = ntohs(DHCP_SERVER_PORT);
|
||||
|
||||
if (sendto(fd, &packet, sizeof(packet), 0,
|
||||
|
||||
@@ -280,6 +280,7 @@ int main(int argc, char **argv)
|
||||
|
||||
/* This voodoo fakes up a packet coming from the correct interface, which really matters for
|
||||
a DHCP server */
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
strncpy(ifr.ifr_name, argv[1], sizeof(ifr.ifr_name)-1);
|
||||
ifr.ifr_name[sizeof(ifr.ifr_name)-1] = '\0';
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) == -1)
|
||||
@@ -288,13 +289,12 @@ int main(int argc, char **argv)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (inet_addr(argv[2]) == INADDR_NONE)
|
||||
if (inet_pton(AF_INET, argv[2], &lease.s_addr) < 1)
|
||||
{
|
||||
perror("invalid ip address");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
lease.s_addr = inet_addr(argv[2]);
|
||||
server = find_interface(lease, nl, if_nametoindex(argv[1]), fd, &ifr);
|
||||
|
||||
memset(&packet, 0, sizeof(packet));
|
||||
|
||||
@@ -318,6 +318,12 @@ void usage(const char* arg, FILE* stream)
|
||||
fprintf (stream, "Usage: %s %s\n", arg, usage_string);
|
||||
}
|
||||
|
||||
static void fail_fatal(const char *errstr, int exitcode)
|
||||
{
|
||||
perror(errstr);
|
||||
exit(exitcode);
|
||||
}
|
||||
|
||||
int send_release_packet(const char* iface, struct dhcp6_packet* packet)
|
||||
{
|
||||
struct sockaddr_in6 server_addr, client_addr;
|
||||
@@ -343,18 +349,19 @@ int send_release_packet(const char* iface, struct dhcp6_packet* packet)
|
||||
client_addr.sin6_port = htons(DHCP6_CLIENT_PORT);
|
||||
client_addr.sin6_flowinfo = 0;
|
||||
client_addr.sin6_scope_id =0;
|
||||
inet_pton(AF_INET6, "::", &client_addr.sin6_addr);
|
||||
bind(sock, (struct sockaddr*)&client_addr, sizeof(struct sockaddr_in6));
|
||||
inet_pton(AF_INET6, DHCP6_MULTICAST_ADDRESS, &server_addr.sin6_addr);
|
||||
if (inet_pton(AF_INET6, "::", &client_addr.sin6_addr) <= 0)
|
||||
fail_fatal("inet_pton", 5);
|
||||
if (bind(sock, (struct sockaddr*)&client_addr, sizeof(struct sockaddr_in6)) != 0)
|
||||
perror("bind"); /* continue on bind error */
|
||||
if (inet_pton(AF_INET6, DHCP6_MULTICAST_ADDRESS, &server_addr.sin6_addr) <= 0)
|
||||
fail_fatal("inet_pton", 5);
|
||||
server_addr.sin6_port = htons(DHCP6_SERVER_PORT);
|
||||
int16_t recv_size = 0;
|
||||
ssize_t recv_size = 0;
|
||||
int result;
|
||||
for (i = 0; i < 5; i++)
|
||||
{
|
||||
if (sendto(sock, packet->buf, packet->len, 0, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
|
||||
{
|
||||
perror("sendto failed");
|
||||
exit(4);
|
||||
}
|
||||
fail_fatal("sendto failed", 4);
|
||||
|
||||
recv_size = recvfrom(sock, response, sizeof(response), MSG_DONTWAIT, NULL, 0);
|
||||
if (recv_size == -1)
|
||||
@@ -367,16 +374,18 @@ int send_release_packet(const char* iface, struct dhcp6_packet* packet)
|
||||
else
|
||||
{
|
||||
perror("recvfrom");
|
||||
result = UNSPEC_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
int16_t result = parse_packet(response, recv_size);
|
||||
if (result == NOT_REPLY_CODE)
|
||||
else
|
||||
{
|
||||
sleep(1);
|
||||
continue;
|
||||
result = parse_packet(response, recv_size);
|
||||
if (result == NOT_REPLY_CODE)
|
||||
{
|
||||
sleep(1);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
close(sock);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -48,6 +48,10 @@ SetBogusPrivOption
|
||||
------------------
|
||||
Takes boolean, sets or resets the --bogus-priv option.
|
||||
|
||||
SetLocaliseQueriesOption
|
||||
------------------------
|
||||
Takes boolean, sets or resets the --localise-queries option.
|
||||
|
||||
SetServers
|
||||
----------
|
||||
Returns nothing. Takes a set of arguments representing the new
|
||||
|
||||
57
debian/changelog
vendored
57
debian/changelog
vendored
@@ -1,3 +1,58 @@
|
||||
dnsmasq (2.87-1) unstable; urgency=low
|
||||
|
||||
* New upstream. (closes: #1001209, #1003156)
|
||||
* Include new NFTset support in the build.
|
||||
* Fix crash on netboot with DNS server disabled. (closes: #996332)
|
||||
* Fix rare lockup in DNSSEC. (closes: #1001576)
|
||||
* Close old bug. (closes: #902963)
|
||||
|
||||
-- Simon Kelley <simon@thekelleys.org.uk> Wed, 08 Sep 2021 23:11:25 +0000
|
||||
|
||||
dnsmasq (2.86-1.1) unstable; urgency=medium
|
||||
|
||||
* Non-maintainer upload.
|
||||
* Fix --address=/#/...... which was lost in 2.86. (closes: #995655)
|
||||
|
||||
-- Michael Biebl <biebl@debian.org> Wed, 10 Nov 2021 22:05:45 +0100
|
||||
|
||||
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 +1098,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/control
vendored
2
debian/control
vendored
@@ -5,7 +5,7 @@ Build-depends: gettext, libnetfilter-conntrack-dev [linux-any],
|
||||
libidn2-dev, libdbus-1-dev (>=0.61), libgmp-dev,
|
||||
nettle-dev (>=2.4-3), libbsd-dev [kfreebsd-any],
|
||||
liblua5.2-dev, dh-runit, debhelper-compat (= 10),
|
||||
pkg-config
|
||||
pkg-config, libnftables-dev
|
||||
Maintainer: Simon Kelley <simon@thekelleys.org.uk>
|
||||
Homepage: http://www.thekelleys.org.uk/dnsmasq/doc.html
|
||||
Vcs-Git: http://thekelleys.org.uk/git/dnsmasq.git
|
||||
|
||||
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 -- "$@"
|
||||
1
debian/readme
vendored
1
debian/readme
vendored
@@ -60,6 +60,7 @@ Notes on configuring dnsmasq as packaged for Debian.
|
||||
nodbus : omit DBus support.
|
||||
noconntrack : omit connection tracking support.
|
||||
noipset : omit IPset support.
|
||||
nonftset : omit nftset support.
|
||||
nortc : compile alternate mode suitable for systems without an RTC.
|
||||
noi18n : omit translations and internationalisation support.
|
||||
noidn : omit international domain name support, must be
|
||||
|
||||
6
debian/rules
vendored
6
debian/rules
vendored
@@ -52,6 +52,10 @@ ifeq (,$(filter noidn, $(DEB_BUILD_OPTIONS)))
|
||||
DEB_COPTS += -DHAVE_LIBIDN2
|
||||
endif
|
||||
|
||||
ifeq (,$(filter nonftset, $(DEB_BUILD_OPTIONS)))
|
||||
DEB_COPTS += -DHAVE_NFTSET
|
||||
endif
|
||||
|
||||
ifeq (,$(filter noconntrack,$(DEB_BUILD_OPTIONS)))
|
||||
ifeq ($(DEB_HOST_ARCH_OS),linux)
|
||||
DEB_COPTS += -DHAVE_CONNTRACK
|
||||
@@ -176,7 +180,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; \
|
||||
|
||||
@@ -85,6 +85,16 @@
|
||||
# subdomains to the vpn and search ipsets:
|
||||
#ipset=/yahoo.com/google.com/vpn,search
|
||||
|
||||
# Add the IPs of all queries to yahoo.com, google.com, and their
|
||||
# subdomains to netfilters sets, which is equivalent to
|
||||
# 'nft add element ip test vpn { ... }; nft add element ip test search { ... }'
|
||||
#nftset=/yahoo.com/google.com/ip#test#vpn,ip#test#search
|
||||
|
||||
# Use netfilters sets for both IPv4 and IPv6:
|
||||
# This adds all addresses in *.yahoo.com to vpn4 and vpn6 for IPv4 and IPv6 addresses.
|
||||
#nftset=/yahoo.com/4#ip#test#vpn4
|
||||
#nftset=/yahoo.com/6#ip#test#vpn6
|
||||
|
||||
# You can control how dnsmasq talks to a server: this forces
|
||||
# queries to 10.1.2.3 to be routed via eth1
|
||||
# server=10.1.2.3@eth1
|
||||
|
||||
242
man/dnsmasq.8
242
man/dnsmasq.8
@@ -1,4 +1,4 @@
|
||||
.TH DNSMASQ 8 2020-04-05
|
||||
.TH DNSMASQ 8 2021-08-16
|
||||
.SH NAME
|
||||
dnsmasq \- A lightweight DHCP and caching DNS server.
|
||||
.SH SYNOPSIS
|
||||
@@ -55,7 +55,8 @@ Don't read the hostnames in /etc/hosts.
|
||||
.B \-H, --addn-hosts=<file>
|
||||
Additional hosts file. Read the specified file as well as /etc/hosts. If \fB--no-hosts\fP is given, read
|
||||
only the specified file. This option may be repeated for more than one
|
||||
additional hosts file. If a directory is given, then read all the files contained in that directory.
|
||||
additional hosts file. If a directory is given, then read all the files contained in that directory
|
||||
in alphabetical order.
|
||||
.TP
|
||||
.B --hostsdir=<path>
|
||||
Read all the hosts files contained in the directory. New or changed files
|
||||
@@ -135,6 +136,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 +185,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 +300,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 +327,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.
|
||||
@@ -341,8 +345,15 @@ forged answer to a DNS request for certain domain, before the correct answer can
|
||||
.B \-f, --filterwin2k
|
||||
Later versions of windows make periodic DNS requests which don't get sensible answers from
|
||||
the public DNS and can cause problems by triggering dial-on-demand links. This flag turns on an option
|
||||
to filter such requests. The requests blocked are for records of types SOA and SRV, and type ANY where the
|
||||
requested name has underscores, to catch LDAP requests.
|
||||
to filter such requests. The requests blocked are for records of type ANY
|
||||
where the requested name has underscores, to catch LDAP requests, and for
|
||||
\fBall\fP records of types SOA and SRV.
|
||||
.TP
|
||||
.B --filter-A
|
||||
Remove A records from answers. No IPv4 addresses will be returned.
|
||||
.TP
|
||||
.B --filter-AAAA
|
||||
Remove AAAA records from answers. No IPv6 addresses will be returned.
|
||||
.TP
|
||||
.B \-r, --resolv-file=<file>
|
||||
Read the IP addresses of the upstream nameservers from <file>, instead of
|
||||
@@ -368,7 +379,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 +442,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 +468,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,25 +512,24 @@ 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
|
||||
.B --rev-server=1.2.3.0/24,192.168.0.1
|
||||
is exactly equivalent to
|
||||
.B --server=/3.2.1.in-addr.arpa/192.168.0.1
|
||||
Allowed prefix lengths are 1-32 (IPv4) and 1-128 (IPv6). If the prefix length is omitted, dnsmasq substitutes either 32 (IPv4) or 128 (IPv6).
|
||||
.TP
|
||||
.B \-A, --address=/<domain>[/<domain>...]/[<ipaddr>]
|
||||
Specify an IP address to return for any host in the given domains.
|
||||
Queries in the domains are never forwarded and always replied to
|
||||
A (or AAAA) queries in the domains are never forwarded and always replied to
|
||||
with the specified IP address which may be IPv4 or IPv6. To give
|
||||
both IPv4 and IPv6 addresses for a domain, use repeated \fB--address\fP flags.
|
||||
To include multiple IP addresses for a single query, use
|
||||
\fB--addn-hosts=<path>\fP instead.
|
||||
multiple addresses or both IPv4 and IPv6 addresses for a domain, use repeated \fB--address\fP flags.
|
||||
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
|
||||
@@ -521,6 +543,11 @@ address of 0.0.0.0 and its IPv6 equivalent of :: so
|
||||
its subdomains. This is partly syntactic sugar for \fB--address=/example.com/0.0.0.0\fP
|
||||
and \fB--address=/example.com/::\fP but is also more efficient than including both
|
||||
as separate configuration lines. Note that NULL addresses normally work in the same way as localhost, so beware that clients looking up these names are likely to end up talking to themselves.
|
||||
|
||||
Note that the behaviour for queries which don't match the specified address literal changed in version 2.86.
|
||||
Previous versions, configured with (eg) --address=/example.com/1.2.3.4 and then queried for a RR type other than
|
||||
A would return a NoData answer. From 2.86, the query is sent upstream. To restore the pre-2.86 behaviour,
|
||||
use the configuration --address=/example.com/1.2.3.4 --local=/example.com/
|
||||
.TP
|
||||
.B --ipset=/<domain>[/<domain>...]/<ipset>[,<ipset>...]
|
||||
Places the resolved IP addresses of queries for one or more domains in
|
||||
@@ -533,6 +560,41 @@ These IP sets must already exist. See
|
||||
.BR ipset (8)
|
||||
for more details.
|
||||
.TP
|
||||
.B --nftset=/<domain>[/<domain>...]/[(6|4)#[<family>#]<table>#<set>[,[(6|4)#[<family>#]<table>#<set>]...]
|
||||
Similar to the \fB--ipset\fP option, but accepts one or more nftables
|
||||
sets to add IP addresses into.
|
||||
These sets must already exist. See
|
||||
.BR nft (8)
|
||||
for more details. The family, table and set are passed directly to the nft. If the spec starts with 4# or 6# then
|
||||
only A or AAAA records respectively are added to the set. Since an nftset can hold only IPv4 or IPv6 addresses, this
|
||||
avoids errors being logged for addresses of the wrong type.
|
||||
.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 +653,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,14 +726,15 @@ 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.
|
||||
.TP
|
||||
.B --dumpmask=<mask>
|
||||
Specify which types of packets should be added to the dumpfile. The argument should be the OR of the bitmasks for each type of packet to be dumped: it can be specified in hex by preceding the number with 0x in the normal way. Each time a packet is written to the dumpfile, dnsmasq logs the packet sequence and the mask
|
||||
representing its type. The current types are: 0x0001 - DNS queries from clients 0x0002 DNS replies to clients 0x0004 - DNS queries to upstream 0x0008 - DNS replies from upstream 0x0010 - queries send upstream for DNSSEC validation 0x0020 - replies to queries for DNSSEC validation 0x0040 - replies to client queries which fail DNSSEC validation 0x0080 replies to queries for DNSSEC validation which fail validation.
|
||||
representing its type. The current types are: 0x0001 - DNS queries from clients, 0x0002 DNS replies to clients, 0x0004 - DNS queries to upstream, 0x0008 - DNS replies from upstream, 0x0010 - queries send upstream for DNSSEC validation, 0x0020 - replies to queries for DNSSEC validation, 0x0040 - replies to client queries which fail DNSSEC validation, 0x0080 replies to queries for DNSSEC validation which fail validation, 0x1000 - DHCPv4, 0x2000 - DHCPv6, 0x4000 - Router advertisement, 0x8000 - TFTP.
|
||||
.TP
|
||||
.B --add-mac[=base64|text]
|
||||
Add the MAC address of the requestor to DNS queries which are
|
||||
@@ -678,6 +747,9 @@ have security and privacy implications. The warning about caching
|
||||
given for \fB--add-subnet\fP applies to \fB--add-mac\fP too. An alternative encoding of the
|
||||
MAC, as base64, is enabled by adding the "base64" parameter and a human-readable encoding of hex-and-colons is enabled by added the "text" parameter.
|
||||
.TP
|
||||
.B --strip-mac
|
||||
Remove any MAC address information already in downstream queries before forwarding upstream.
|
||||
.TP
|
||||
.B --add-cpe-id=<string>
|
||||
Add an arbitrary identifying string to DNS queries which are
|
||||
forwarded upstream.
|
||||
@@ -702,7 +774,19 @@ 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 --strip-subnet
|
||||
Remove any subnet address already present in a downstream query before forwarding it upstream. If --add-subnet is set this also
|
||||
ensures that any downstream-provided subnet is replaced by the one added by dnsmasq. Otherwise, dnsmasq will NOT replace an
|
||||
existing subnet in the query.
|
||||
.TP
|
||||
.B --umbrella[=[deviceid:<deviceid>][,orgid:<orgid>][,assetid:<id>]]
|
||||
Embeds the requestor's IP address in DNS queries forwarded upstream.
|
||||
If device id or, asset 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 id
|
||||
attributes is irrelevant, but they must be separated by a comma. Deviceid is
|
||||
a sixteen digit hexadecimal number, org and asset ids are decimal numbers.
|
||||
.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 +800,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
|
||||
@@ -850,7 +935,7 @@ compiled in and the kernel must have conntrack support
|
||||
included and configured. This option cannot be combined with
|
||||
.B --query-port.
|
||||
.TP
|
||||
.B \-F, --dhcp-range=[tag:<tag>[,tag:<tag>],][set:<tag>,]<start-addr>[,<end-addr>|<mode>][,<netmask>[,<broadcast>]][,<lease time>]
|
||||
.B \-F, --dhcp-range=[tag:<tag>[,tag:<tag>],][set:<tag>,]<start-addr>[,<end-addr>|<mode>[,<netmask>[,<broadcast>]]][,<lease time>]
|
||||
.TP
|
||||
.B \-F, --dhcp-range=[tag:<tag>[,tag:<tag>],][set:<tag>,]<start-IPv6addr>[,<end-IPv6addr>|constructor:<interface>][,<mode>][,<prefix-len>][,<lease time>]
|
||||
|
||||
@@ -860,7 +945,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
|
||||
@@ -980,7 +1065,7 @@ is also included, as described in RFC-3775 section 7.3.
|
||||
tells dnsmasq to advertise the prefix without the on-link (aka L) bit set.
|
||||
|
||||
.TP
|
||||
.B \-G, --dhcp-host=[<hwaddr>][,id:<client_id>|*][,set:<tag>][tag:<tag>][,<ipaddr>][,<hostname>][,<lease_time>][,ignore]
|
||||
.B \-G, --dhcp-host=[<hwaddr>][,id:<client_id>|*][,set:<tag>][,tag:<tag>][,<ipaddr>][,<hostname>][,<lease_time>][,ignore]
|
||||
Specify per host parameters for the DHCP server. This allows a machine
|
||||
with a particular hardware address to be always allocated the same
|
||||
hostname, IP address and lease time. A hostname specified like this
|
||||
@@ -1045,6 +1130,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
|
||||
@@ -1066,7 +1164,10 @@ ignore requests from unknown machines using
|
||||
If the host matches only a \fB--dhcp-host\fP directive which cannot
|
||||
be used because it specifies an address on different subnet, the tag "known-othernet" is set.
|
||||
|
||||
The tag:<tag> construct filters which dhcp-host directives are used. Tagged directives are used in preference to untagged ones.
|
||||
The tag:<tag> construct filters which dhcp-host directives are used; more than
|
||||
one can be provided, in this case the request must match all of them. Tagged
|
||||
directives are used in preference to untagged ones. Note that one of <hwaddr>,
|
||||
<client_id> or <hostname> still needs to be specified (can be a wildcard).
|
||||
|
||||
Ethernet addresses (but not client-ids) may have
|
||||
wildcard bytes, so for example
|
||||
@@ -1097,7 +1198,7 @@ has both wired and wireless interfaces.
|
||||
.TP
|
||||
.B --dhcp-hostsfile=<path>
|
||||
Read DHCP host information from the specified file. If a directory
|
||||
is given, then read all the files contained in that directory. The file contains
|
||||
is given, then read all the files contained in that directory in alphabetical order. The file contains
|
||||
information about one host per line. The format of a line is the same
|
||||
as text to the right of '=' in \fB--dhcp-host\fP. The advantage of storing DHCP host information
|
||||
in this file is that it can be changed without re-starting dnsmasq:
|
||||
@@ -1105,7 +1206,7 @@ the file will be re-read when dnsmasq receives SIGHUP.
|
||||
.TP
|
||||
.B --dhcp-optsfile=<path>
|
||||
Read DHCP option information from the specified file. If a directory
|
||||
is given, then read all the files contained in that directory. The advantage of
|
||||
is given, then read all the files contained in that directory in alphabetical order. The advantage of
|
||||
using this option is the same as for \fB--dhcp-hostsfile\fP: the
|
||||
\fB--dhcp-optsfile\fP will be re-read when dnsmasq receives SIGHUP. Note that
|
||||
it is possible to encode the information in a
|
||||
@@ -1120,7 +1221,8 @@ directory, and not an individual file. Changed or new files within
|
||||
the directory are read automatically, without the need to send SIGHUP.
|
||||
If a file is deleted or changed after it has been read by dnsmasq, then the
|
||||
host record it contained will remain until dnsmasq receives a SIGHUP, or
|
||||
is restarted; ie host records are only added dynamically.
|
||||
is restarted; ie host records are only added dynamically. The order in which the
|
||||
files in a directory are read is not defined.
|
||||
.TP
|
||||
.B --dhcp-optsdir=<path>
|
||||
This is equivalent to \fB--dhcp-optsfile\fP, with the differences noted for \fB--dhcp-hostsdir\fP.
|
||||
@@ -1239,7 +1341,7 @@ DHCP options. This make extra space available in the DHCP packet for
|
||||
options but can, rarely, confuse old or broken clients. This flag
|
||||
forces "simple and safe" behaviour to avoid problems in such a case.
|
||||
.TP
|
||||
.B --dhcp-relay=<local address>,<server address>[,<interface]
|
||||
.B --dhcp-relay=<local address>[,<server address>][,<interface]
|
||||
Configure dnsmasq to do DHCP relay. The local address is an address
|
||||
allocated to an interface on the host running dnsmasq. All DHCP
|
||||
requests arriving on that interface will we relayed to a remote DHCP
|
||||
@@ -1247,10 +1349,9 @@ server at the server address. It is possible to relay from a single local
|
||||
address to multiple remote servers by using multiple \fB--dhcp-relay\fP
|
||||
configs with the same local address and different server
|
||||
addresses. A server address must be an IP literal address, not a
|
||||
domain name. In the case of DHCPv6, the server address may be the
|
||||
ALL_SERVERS multicast address, ff05::1:3. In this case the interface
|
||||
must be given, not be wildcard, and is used to direct the multicast to the
|
||||
correct interface to reach the DHCP server.
|
||||
domain name. If the server address is ommitted, the request will be
|
||||
forwarded by broadcast (IPv4) or multicast (IPv6). In this case the interface
|
||||
must be given and not be wildcard.
|
||||
|
||||
Access control for DHCP clients has the same rules as for the DHCP
|
||||
server, see \fB--interface\fP, \fB--except-interface\fP, etc. The optional
|
||||
@@ -1270,6 +1371,11 @@ supported: the relay function will take precedence.
|
||||
|
||||
Both DHCPv4 and DHCPv6 relay is supported. It's not possible to relay
|
||||
DHCPv4 to a DHCPv6 server or vice-versa.
|
||||
|
||||
The DHCP relay function for IPv6 includes the ability to snoop
|
||||
prefix-delegation from relayed DHCP transactions. See
|
||||
.B --dhcp-script
|
||||
for details.
|
||||
.TP
|
||||
.B \-U, --dhcp-vendorclass=set:<tag>,[enterprise:<IANA-enterprise number>,]<vendor-class>
|
||||
Map from a vendor-class string to a tag. Most DHCP clients provide a
|
||||
@@ -1365,6 +1471,12 @@ Any number of set: and tag: forms may appear, in any order.
|
||||
tag set by another
|
||||
.B --tag-if,
|
||||
the line which sets the tag must precede the one which tests it.
|
||||
|
||||
As an extension, the tag:<tag> clauses support limited wildcard matching,
|
||||
similar to the matching in the \fB--interface\fP directive. This allows, for
|
||||
example, using \fB--tag-if=set:ppp,tag:ppp*\fP to set the tag 'ppp' for all requests
|
||||
received on any matching interface (ppp0, ppp1, etc). This can be used in conjunction
|
||||
with the tag:!<tag> format meaning that no tag matching the wildcard may be set.
|
||||
.TP
|
||||
.B \-J, --dhcp-ignore=tag:<tag>[,tag:<tag>]
|
||||
When all the given tags appear in the tag set ignore the host and do
|
||||
@@ -1436,7 +1548,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 +1607,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 +1657,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.
|
||||
@@ -1674,15 +1786,25 @@ receives a HUP signal, the script will be invoked for existing leases
|
||||
with an "old" event.
|
||||
|
||||
|
||||
There are four further actions which may appear as the first argument
|
||||
to the script, "init", "arp-add", "arp-del" and "tftp". More may be added in the future, so
|
||||
There are five further actions which may appear as the first argument
|
||||
to the script, "init", "arp-add", "arp-del", "relay-snoop" and "tftp".
|
||||
More may be added in the future, so
|
||||
scripts should be written to ignore unknown actions. "init" is
|
||||
described below in
|
||||
.B --leasefile-ro
|
||||
|
||||
The "tftp" action is invoked when a TFTP file transfer completes: the
|
||||
arguments are the file size in bytes, the address to which the file
|
||||
was sent, and the complete pathname of the file.
|
||||
|
||||
|
||||
The "relay-snoop" action is invoked when dnsmasq is configured as a DHCP
|
||||
relay for DHCPv6 and it relays a prefx delegation to a client. The arguments
|
||||
are the name of the interface where the client is conected, its (link-local)
|
||||
address on that interface and the delegated prefix. This information is
|
||||
sufficient to install routes to the delegated prefix of a router. See
|
||||
.B --dhcp-relay
|
||||
for more details on configuring DHCP relay.
|
||||
|
||||
The "arp-add" and "arp-del" actions are only called if enabled with
|
||||
.B --script-arp
|
||||
They are are supplied with a MAC address and IP address as arguments. "arp-add" indicates
|
||||
@@ -1847,7 +1969,6 @@ additional flag "local" may be supplied which has the effect of adding
|
||||
is identical to
|
||||
.B --domain=thekelleys.org.uk,192.168.0.0/24
|
||||
.B --local=/thekelleys.org.uk/ --local=/0.168.192.in-addr.arpa/
|
||||
The network size must be 8, 16 or 24 for this to be legal.
|
||||
.TP
|
||||
.B --dhcp-fqdn
|
||||
In the default mode, dnsmasq inserts the unqualified names of
|
||||
@@ -2020,6 +2141,23 @@ A special case of
|
||||
which differs in two respects. Firstly, only \fB--server\fP and \fB--rev-server\fP are allowed
|
||||
in the configuration file included. Secondly, the file is re-read and the configuration
|
||||
therein is updated when dnsmasq receives SIGHUP.
|
||||
.TP
|
||||
.B \--conf-script=<file>[ <arg]
|
||||
Execute <file>, and treat what it emits to stdout as the contents of a configuration file.
|
||||
If the script exits with a non-zero exit code, dnsmasq treats this as a fatal error.
|
||||
The script can be passed arguments, space seperated from the filename and each other so, for instance
|
||||
.B --conf-dir="/etc/dnsmasq-uncompress-ads /share/ads-domains.gz"
|
||||
|
||||
with /etc/dnsmasq-uncompress-ads containing
|
||||
|
||||
set -e
|
||||
|
||||
zcat ${1} | sed -e "s:^:address=/:" -e "s:$:/:"
|
||||
|
||||
exit 0
|
||||
|
||||
and /share/ads-domains.gz containing a compressed
|
||||
list of ad server domains will save disk space with large ad-server blocklists.
|
||||
.SH CONFIG FILE
|
||||
At startup, dnsmasq reads
|
||||
.I /etc/dnsmasq.conf,
|
||||
@@ -2351,6 +2489,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 +2509,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
|
||||
@@ -2410,7 +2554,9 @@ file/directory, permissions).
|
||||
5 - Other miscellaneous problem.
|
||||
.PP
|
||||
11 or greater - a non zero return code was received from the
|
||||
lease-script process "init" call. The exit code from dnsmasq is the
|
||||
lease-script process "init" call or a
|
||||
.B \--conf-script
|
||||
file. The exit code from dnsmasq is the
|
||||
script's exit code with 10 added.
|
||||
|
||||
.SH LIMITS
|
||||
|
||||
1433
po/pt_BR.po
1433
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-2022 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
86
src/auth.c
86
src/auth.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2022 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;
|
||||
}
|
||||
@@ -208,7 +210,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
if (local_query || in_zone(zone, intr->name, NULL))
|
||||
{
|
||||
found = 1;
|
||||
log_query(flag | F_REVERSE | F_CONFIG, intr->name, &addr, NULL);
|
||||
log_query(flag | F_REVERSE | F_CONFIG, intr->name, &addr, NULL, 0);
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
daemon->auth_ttl, NULL,
|
||||
T_PTR, C_IN, "d", intr->name))
|
||||
@@ -232,7 +234,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
strcat(name, ".");
|
||||
strcat(name, zone->domain);
|
||||
}
|
||||
log_query(flag | F_DHCP | F_REVERSE, name, &addr, record_source(crecp->uid));
|
||||
log_query(flag | F_DHCP | F_REVERSE, name, &addr, record_source(crecp->uid), 0);
|
||||
found = 1;
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
daemon->auth_ttl, NULL,
|
||||
@@ -241,7 +243,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
}
|
||||
else if (crecp->flags & (F_DHCP | F_HOSTS) && (local_query || in_zone(zone, name, NULL)))
|
||||
{
|
||||
log_query(crecp->flags & ~F_FORWARD, name, &addr, record_source(crecp->uid));
|
||||
log_query(crecp->flags & ~F_FORWARD, name, &addr, record_source(crecp->uid), 0);
|
||||
found = 1;
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
daemon->auth_ttl, NULL,
|
||||
@@ -253,10 +255,21 @@ 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, 0);
|
||||
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
|
||||
log_query(flag | F_NEG | F_NXDOMAIN | F_REVERSE | (auth ? F_AUTH : 0), NULL, &addr, NULL);
|
||||
log_query(flag | F_NEG | F_NXDOMAIN | F_REVERSE | (auth ? F_AUTH : 0), NULL, &addr, NULL, 0);
|
||||
|
||||
continue;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -286,7 +300,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
if (rc == 2 && qtype == T_MX)
|
||||
{
|
||||
found = 1;
|
||||
log_query(F_CONFIG | F_RRNAME, name, NULL, "<MX>");
|
||||
log_query(F_CONFIG | F_RRNAME, name, NULL, "<MX>", 0);
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
|
||||
NULL, T_MX, C_IN, "sd", rec->weight, rec->target))
|
||||
anscount++;
|
||||
@@ -301,7 +315,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
if (rc == 2 && qtype == T_SRV)
|
||||
{
|
||||
found = 1;
|
||||
log_query(F_CONFIG | F_RRNAME, name, NULL, "<SRV>");
|
||||
log_query(F_CONFIG | F_RRNAME, name, NULL, "<SRV>", 0);
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
|
||||
NULL, T_SRV, C_IN, "sssd",
|
||||
rec->priority, rec->weight, rec->srvport, rec->target))
|
||||
@@ -335,7 +349,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
if (rc == 2 && txt->class == qtype)
|
||||
{
|
||||
found = 1;
|
||||
log_query(F_CONFIG | F_RRNAME, name, NULL, querystr(NULL, txt->class));
|
||||
log_query(F_CONFIG | F_RRNAME, name, NULL, NULL, txt->class);
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
|
||||
NULL, txt->class, C_IN, "t", txt->len, txt->txt))
|
||||
anscount++;
|
||||
@@ -349,7 +363,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
if (rc == 2 && qtype == T_TXT)
|
||||
{
|
||||
found = 1;
|
||||
log_query(F_CONFIG | F_RRNAME, name, NULL, "<TXT>");
|
||||
log_query(F_CONFIG | F_RRNAME, name, NULL, "<TXT>", 0);
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
|
||||
NULL, T_TXT, C_IN, "t", txt->len, txt->txt))
|
||||
anscount++;
|
||||
@@ -363,7 +377,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
if (rc == 2 && qtype == T_NAPTR)
|
||||
{
|
||||
found = 1;
|
||||
log_query(F_CONFIG | F_RRNAME, name, NULL, "<NAPTR>");
|
||||
log_query(F_CONFIG | F_RRNAME, name, NULL, "<NAPTR>", 0);
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
|
||||
NULL, T_NAPTR, C_IN, "sszzzd",
|
||||
na->order, na->pref, na->flags, na->services, na->regexp, na->replace))
|
||||
@@ -393,13 +407,23 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
continue;
|
||||
|
||||
found = 1;
|
||||
log_query(F_FORWARD | F_CONFIG | flag, name, &addrlist->addr, NULL);
|
||||
log_query(F_FORWARD | F_CONFIG | flag, name, &addrlist->addr, NULL, 0);
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
daemon->auth_ttl, NULL, qtype, C_IN,
|
||||
qtype == T_A ? "4" : "6", &addrlist->addr))
|
||||
anscount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found && is_name_synthetic(flag, name, &addr) )
|
||||
{
|
||||
nxdomain = 0;
|
||||
|
||||
log_query(F_FORWARD | F_CONFIG | flag, name, &addr, NULL, 0);
|
||||
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)
|
||||
{
|
||||
@@ -408,8 +432,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
if (qtype == T_SOA)
|
||||
{
|
||||
auth = soa = 1; /* inhibits auth section */
|
||||
found = 1;
|
||||
log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<SOA>");
|
||||
log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<SOA>", 0);
|
||||
}
|
||||
else if (qtype == T_AXFR)
|
||||
{
|
||||
@@ -444,16 +467,14 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
soa = 1; /* inhibits auth section */
|
||||
ns = 1; /* ensure we include NS records! */
|
||||
axfr = 1;
|
||||
found = 1;
|
||||
axfroffset = nameoffset;
|
||||
log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<AXFR>");
|
||||
log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<AXFR>", 0);
|
||||
}
|
||||
else if (qtype == T_NS)
|
||||
{
|
||||
auth = 1;
|
||||
ns = 1; /* inhibits auth section */
|
||||
found = 1;
|
||||
log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<NS>");
|
||||
log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<NS>", 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -471,9 +492,8 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
(local_query || filter_zone(zone, flag, &(crecp->addr))))
|
||||
{
|
||||
*cut = '.'; /* restore domain part */
|
||||
log_query(crecp->flags, name, &crecp->addr, record_source(crecp->uid));
|
||||
log_query(crecp->flags, name, &crecp->addr, record_source(crecp->uid), 0);
|
||||
*cut = 0; /* remove domain part */
|
||||
found = 1;
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
daemon->auth_ttl, NULL, qtype, C_IN,
|
||||
qtype == T_A ? "4" : "6", &crecp->addr))
|
||||
@@ -493,8 +513,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
nxdomain = 0;
|
||||
if ((crecp->flags & flag) && (local_query || filter_zone(zone, flag, &(crecp->addr))))
|
||||
{
|
||||
log_query(crecp->flags, name, &crecp->addr, record_source(crecp->uid));
|
||||
found = 1;
|
||||
log_query(crecp->flags, name, &crecp->addr, record_source(crecp->uid), 0);
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
daemon->auth_ttl, NULL, qtype, C_IN,
|
||||
qtype == T_A ? "4" : "6", &crecp->addr))
|
||||
@@ -541,7 +560,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
|
||||
if (candidate)
|
||||
{
|
||||
log_query(F_CONFIG | F_CNAME, name, NULL, NULL);
|
||||
log_query(F_CONFIG | F_CNAME, name, NULL, NULL, 0);
|
||||
strcpy(name, candidate->target);
|
||||
if (!strchr(name, '.'))
|
||||
{
|
||||
@@ -559,7 +578,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
else if (cache_find_non_terminal(name, now))
|
||||
nxdomain = 0;
|
||||
|
||||
log_query(flag | F_NEG | (nxdomain ? F_NXDOMAIN : 0) | F_FORWARD | F_AUTH, name, NULL, NULL);
|
||||
log_query(flag | F_NEG | (nxdomain ? F_NXDOMAIN : 0) | F_FORWARD | F_AUTH, name, NULL, NULL, 0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -589,7 +608,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
if (subnet->prefixlen >= 16 )
|
||||
p += sprintf(p, "%u.", a & 0xff);
|
||||
a = a >> 8;
|
||||
p += sprintf(p, "%u.in-addr.arpa", a & 0xff);
|
||||
sprintf(p, "%u.in-addr.arpa", a & 0xff);
|
||||
|
||||
}
|
||||
else
|
||||
@@ -602,7 +621,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
int dig = ((unsigned char *)&subnet->addr.addr6)[i>>3];
|
||||
p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
|
||||
}
|
||||
p += sprintf(p, "ip6.arpa");
|
||||
sprintf(p, "ip6.arpa");
|
||||
|
||||
}
|
||||
}
|
||||
@@ -855,10 +874,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, 0);
|
||||
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 +898,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-2022 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
|
||||
@@ -52,7 +52,7 @@ void blockdata_init(void)
|
||||
|
||||
void blockdata_report(void)
|
||||
{
|
||||
my_syslog(LOG_INFO, _("pool memory in use %u, max %u, allocated %u"),
|
||||
my_syslog(LOG_INFO, _("pool memory in use %zu, max %zu, allocated %zu"),
|
||||
blockdata_count * sizeof(struct blockdata),
|
||||
blockdata_hwm * sizeof(struct blockdata),
|
||||
blockdata_alloced * sizeof(struct blockdata));
|
||||
@@ -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-2022 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 */
|
||||
|
||||
|
||||
|
||||
324
src/cache.c
324
src/cache.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2022 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
|
||||
@@ -30,50 +30,100 @@ static struct crec *really_insert(char *name, union all_addr *addr, unsigned sho
|
||||
time_t now, unsigned long ttl, unsigned int flags);
|
||||
|
||||
/* type->string mapping: this is also used by the name-hash function as a mixing table. */
|
||||
/* taken from https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml */
|
||||
static const struct {
|
||||
unsigned int type;
|
||||
const char * const name;
|
||||
} typestr[] = {
|
||||
{ 1, "A" },
|
||||
{ 2, "NS" },
|
||||
{ 5, "CNAME" },
|
||||
{ 6, "SOA" },
|
||||
{ 10, "NULL" },
|
||||
{ 11, "WKS" },
|
||||
{ 12, "PTR" },
|
||||
{ 13, "HINFO" },
|
||||
{ 15, "MX" },
|
||||
{ 16, "TXT" },
|
||||
{ 22, "NSAP" },
|
||||
{ 23, "NSAP_PTR" },
|
||||
{ 24, "SIG" },
|
||||
{ 25, "KEY" },
|
||||
{ 28, "AAAA" },
|
||||
{ 29, "LOC" },
|
||||
{ 33, "SRV" },
|
||||
{ 35, "NAPTR" },
|
||||
{ 36, "KX" },
|
||||
{ 37, "CERT" },
|
||||
{ 38, "A6" },
|
||||
{ 39, "DNAME" },
|
||||
{ 41, "OPT" },
|
||||
{ 43, "DS" },
|
||||
{ 46, "RRSIG" },
|
||||
{ 47, "NSEC" },
|
||||
{ 48, "DNSKEY" },
|
||||
{ 50, "NSEC3" },
|
||||
{ 51, "NSEC3PARAM" },
|
||||
{ 52, "TLSA" },
|
||||
{ 53, "SMIMEA" },
|
||||
{ 55, "HIP" },
|
||||
{ 249, "TKEY" },
|
||||
{ 250, "TSIG" },
|
||||
{ 251, "IXFR" },
|
||||
{ 252, "AXFR" },
|
||||
{ 253, "MAILB" },
|
||||
{ 254, "MAILA" },
|
||||
{ 255, "ANY" },
|
||||
{ 257, "CAA" }
|
||||
{ 1, "A" }, /* a host address [RFC1035] */
|
||||
{ 2, "NS" }, /* an authoritative name server [RFC1035] */
|
||||
{ 3, "MD" }, /* a mail destination (OBSOLETE - use MX) [RFC1035] */
|
||||
{ 4, "MF" }, /* a mail forwarder (OBSOLETE - use MX) [RFC1035] */
|
||||
{ 5, "CNAME" }, /* the canonical name for an alias [RFC1035] */
|
||||
{ 6, "SOA" }, /* marks the start of a zone of authority [RFC1035] */
|
||||
{ 7, "MB" }, /* a mailbox domain name (EXPERIMENTAL) [RFC1035] */
|
||||
{ 8, "MG" }, /* a mail group member (EXPERIMENTAL) [RFC1035] */
|
||||
{ 9, "MR" }, /* a mail rename domain name (EXPERIMENTAL) [RFC1035] */
|
||||
{ 10, "NULL" }, /* a null RR (EXPERIMENTAL) [RFC1035] */
|
||||
{ 11, "WKS" }, /* a well known service description [RFC1035] */
|
||||
{ 12, "PTR" }, /* a domain name pointer [RFC1035] */
|
||||
{ 13, "HINFO" }, /* host information [RFC1035] */
|
||||
{ 14, "MINFO" }, /* mailbox or mail list information [RFC1035] */
|
||||
{ 15, "MX" }, /* mail exchange [RFC1035] */
|
||||
{ 16, "TXT" }, /* text strings [RFC1035] */
|
||||
{ 17, "RP" }, /* for Responsible Person [RFC1183] */
|
||||
{ 18, "AFSDB" }, /* for AFS Data Base location [RFC1183][RFC5864] */
|
||||
{ 19, "X25" }, /* for X.25 PSDN address [RFC1183] */
|
||||
{ 20, "ISDN" }, /* for ISDN address [RFC1183] */
|
||||
{ 21, "RT" }, /* for Route Through [RFC1183] */
|
||||
{ 22, "NSAP" }, /* for NSAP address, NSAP style A record [RFC1706] */
|
||||
{ 23, "NSAP_PTR" }, /* for domain name pointer, NSAP style [RFC1348][RFC1637][RFC1706] */
|
||||
{ 24, "SIG" }, /* for security signature [RFC2535][RFC2536][RFC2537][RFC2931][RFC3008][RFC3110][RFC3755][RFC4034] */
|
||||
{ 25, "KEY" }, /* for security key [RFC2535][RFC2536][RFC2537][RFC2539][RFC3008][RFC3110][RFC3755][RFC4034] */
|
||||
{ 26, "PX" }, /* X.400 mail mapping information [RFC2163] */
|
||||
{ 27, "GPOS" }, /* Geographical Position [RFC1712] */
|
||||
{ 28, "AAAA" }, /* IP6 Address [RFC3596] */
|
||||
{ 29, "LOC" }, /* Location Information [RFC1876] */
|
||||
{ 30, "NXT" }, /* Next Domain (OBSOLETE) [RFC2535][RFC3755] */
|
||||
{ 31, "EID" }, /* Endpoint Identifier [Michael_Patton][http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt] 1995-06*/
|
||||
{ 32, "NIMLOC" }, /* Nimrod Locator [1][Michael_Patton][http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt] 1995-06*/
|
||||
{ 33, "SRV" }, /* Server Selection [1][RFC2782] */
|
||||
{ 34, "ATMA" }, /* ATM Address [ ATM Forum Technical Committee, "ATM Name System, V2.0", Doc ID: AF-DANS-0152.000, July 2000. Available from and held in escrow by IANA.] */
|
||||
{ 35, "NAPTR" }, /* Naming Authority Pointer [RFC2168][RFC2915][RFC3403] */
|
||||
{ 36, "KX" }, /* Key Exchanger [RFC2230] */
|
||||
{ 37, "CERT" }, /* CERT [RFC4398] */
|
||||
{ 38, "A6" }, /* A6 (OBSOLETE - use AAAA) [RFC2874][RFC3226][RFC6563] */
|
||||
{ 39, "DNAME" }, /* DNAME [RFC6672] */
|
||||
{ 40, "SINK" }, /* SINK [Donald_E_Eastlake][http://tools.ietf.org/html/draft-eastlake-kitchen-sink] 1997-11*/
|
||||
{ 41, "OPT" }, /* OPT [RFC3225][RFC6891] */
|
||||
{ 42, "APL" }, /* APL [RFC3123] */
|
||||
{ 43, "DS" }, /* Delegation Signer [RFC3658][RFC4034] */
|
||||
{ 44, "SSHFP" }, /* SSH Key Fingerprint [RFC4255] */
|
||||
{ 45, "IPSECKEY" }, /* IPSECKEY [RFC4025] */
|
||||
{ 46, "RRSIG" }, /* RRSIG [RFC3755][RFC4034] */
|
||||
{ 47, "NSEC" }, /* NSEC [RFC3755][RFC4034][RFC9077] */
|
||||
{ 48, "DNSKEY" }, /* DNSKEY [RFC3755][RFC4034] */
|
||||
{ 49, "DHCID" }, /* DHCID [RFC4701] */
|
||||
{ 50, "NSEC3" }, /* NSEC3 [RFC5155][RFC9077] */
|
||||
{ 51, "NSEC3PARAM" }, /* NSEC3PARAM [RFC5155] */
|
||||
{ 52, "TLSA" }, /* TLSA [RFC6698] */
|
||||
{ 53, "SMIMEA" }, /* S/MIME cert association [RFC8162] SMIMEA/smimea-completed-template 2015-12-01*/
|
||||
{ 55, "HIP" }, /* Host Identity Protocol [RFC8005] */
|
||||
{ 56, "NINFO" }, /* NINFO [Jim_Reid] NINFO/ninfo-completed-template 2008-01-21*/
|
||||
{ 57, "RKEY" }, /* RKEY [Jim_Reid] RKEY/rkey-completed-template 2008-01-21*/
|
||||
{ 58, "TALINK" }, /* Trust Anchor LINK [Wouter_Wijngaards] TALINK/talink-completed-template 2010-02-17*/
|
||||
{ 59, "CDS" }, /* Child DS [RFC7344] CDS/cds-completed-template 2011-06-06*/
|
||||
{ 60, "CDNSKEY" }, /* DNSKEY(s) the Child wants reflected in DS [RFC7344] 2014-06-16*/
|
||||
{ 61, "OPENPGPKEY" }, /* OpenPGP Key [RFC7929] OPENPGPKEY/openpgpkey-completed-template 2014-08-12*/
|
||||
{ 62, "CSYNC" }, /* Child-To-Parent Synchronization [RFC7477] 2015-01-27*/
|
||||
{ 63, "ZONEMD" }, /* Message Digest Over Zone Data [RFC8976] ZONEMD/zonemd-completed-template 2018-12-12*/
|
||||
{ 64, "SVCB" }, /* Service Binding [draft-ietf-dnsop-svcb-https-00] SVCB/svcb-completed-template 2020-06-30*/
|
||||
{ 65, "HTTPS" }, /* HTTPS Binding [draft-ietf-dnsop-svcb-https-00] HTTPS/https-completed-template 2020-06-30*/
|
||||
{ 99, "SPF" }, /* [RFC7208] */
|
||||
{ 100, "UINFO" }, /* [IANA-Reserved] */
|
||||
{ 101, "UID" }, /* [IANA-Reserved] */
|
||||
{ 102, "GID" }, /* [IANA-Reserved] */
|
||||
{ 103, "UNSPEC" }, /* [IANA-Reserved] */
|
||||
{ 104, "NID" }, /* [RFC6742] ILNP/nid-completed-template */
|
||||
{ 105, "L32" }, /* [RFC6742] ILNP/l32-completed-template */
|
||||
{ 106, "L64" }, /* [RFC6742] ILNP/l64-completed-template */
|
||||
{ 107, "LP" }, /* [RFC6742] ILNP/lp-completed-template */
|
||||
{ 108, "EUI48" }, /* an EUI-48 address [RFC7043] EUI48/eui48-completed-template 2013-03-27*/
|
||||
{ 109, "EUI64" }, /* an EUI-64 address [RFC7043] EUI64/eui64-completed-template 2013-03-27*/
|
||||
{ 249, "TKEY" }, /* Transaction Key [RFC2930] */
|
||||
{ 250, "TSIG" }, /* Transaction Signature [RFC8945] */
|
||||
{ 251, "IXFR" }, /* incremental transfer [RFC1995] */
|
||||
{ 252, "AXFR" }, /* transfer of an entire zone [RFC1035][RFC5936] */
|
||||
{ 253, "MAILB" }, /* mailbox-related RRs (MB, MG or MR) [RFC1035] */
|
||||
{ 254, "MAILA" }, /* mail agent RRs (OBSOLETE - see MX) [RFC1035] */
|
||||
{ 255, "ANY" }, /* A request for some or all records the server has available [RFC1035][RFC6895][RFC8482] */
|
||||
{ 256, "URI" }, /* URI [RFC7553] URI/uri-completed-template 2011-02-22*/
|
||||
{ 257, "CAA" }, /* Certification Authority Restriction [RFC8659] CAA/caa-completed-template 2011-04-07*/
|
||||
{ 258, "AVC" }, /* Application Visibility and Control [Wolfgang_Riedel] AVC/avc-completed-template 2016-02-26*/
|
||||
{ 259, "DOA" }, /* Digital Object Architecture [draft-durand-doa-over-dns] DOA/doa-completed-template 2017-08-30*/
|
||||
{ 260, "AMTRELAY" }, /* Automatic Multicast Tunneling Relay [RFC8777] AMTRELAY/amtrelay-completed-template 2019-02-06*/
|
||||
{ 32768, "TA" }, /* DNSSEC Trust Authorities [Sam_Weiler][http://cameo.library.cmu.edu/][ Deploying DNSSEC Without a Signed Root. Technical Report 1999-19, Information Networking Institute, Carnegie Mellon University, April 2004.] 2005-12-13*/
|
||||
{ 32769, "DLV" }, /* DNSSEC Lookaside Validation (OBSOLETE) [RFC8749][RFC4431] */
|
||||
};
|
||||
|
||||
static void cache_free(struct crec *crecp);
|
||||
@@ -363,7 +413,7 @@ static struct crec *cache_scan_free(char *name, union all_addr *addr, unsigned s
|
||||
if ((crecp->flags & F_FORWARD) && hostname_isequal(cache_get_name(crecp), name))
|
||||
{
|
||||
/* Don't delete DNSSEC in favour of a CNAME, they can co-exist */
|
||||
if ((flags & crecp->flags & (F_IPV4 | F_IPV6 | F_SRV)) ||
|
||||
if ((flags & crecp->flags & (F_IPV4 | F_IPV6 | F_SRV | F_NXDOMAIN)) ||
|
||||
(((crecp->flags | flags) & F_CNAME) && !(crecp->flags & (F_DNSKEY | F_DS))))
|
||||
{
|
||||
if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))
|
||||
@@ -433,7 +483,7 @@ static struct crec *cache_scan_free(char *name, union all_addr *addr, unsigned s
|
||||
else if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) &&
|
||||
(flags & crecp->flags & F_REVERSE) &&
|
||||
(flags & crecp->flags & (F_IPV4 | F_IPV6)) &&
|
||||
memcmp(&crecp->addr, addr, addrlen) == 0)
|
||||
addr && memcmp(&crecp->addr, addr, addrlen) == 0)
|
||||
{
|
||||
*up = crecp->hash_next;
|
||||
cache_unlink(crecp);
|
||||
@@ -488,8 +538,6 @@ struct crec *cache_insert(char *name, union all_addr *addr, unsigned short class
|
||||
else
|
||||
#endif
|
||||
{
|
||||
/* Don't log DNSSEC records here, done elsewhere */
|
||||
log_query(flags | F_UPSTREAM, name, addr, NULL);
|
||||
if (daemon->max_cache_ttl != 0 && daemon->max_cache_ttl < ttl)
|
||||
ttl = daemon->max_cache_ttl;
|
||||
if (daemon->min_cache_ttl != 0 && daemon->min_cache_ttl > ttl)
|
||||
@@ -1351,10 +1399,12 @@ struct in_addr a_record_from_hosts(char *name, time_t now)
|
||||
struct crec *crecp = NULL;
|
||||
struct in_addr ret;
|
||||
|
||||
while ((crecp = cache_find_by_name(crecp, name, now, F_IPV4)))
|
||||
if (crecp->flags & F_HOSTS)
|
||||
return crecp->addr.addr4;
|
||||
|
||||
/* If no DNS service, cache not initialised. */
|
||||
if (daemon->port != 0)
|
||||
while ((crecp = cache_find_by_name(crecp, name, now, F_IPV4)))
|
||||
if (crecp->flags & F_HOSTS)
|
||||
return crecp->addr.addr4;
|
||||
|
||||
my_syslog(MS_DHCP | LOG_WARNING, _("No IPv4 address found for %s"), name);
|
||||
|
||||
ret.s_addr = 0;
|
||||
@@ -1549,7 +1599,8 @@ static void make_non_terminals(struct crec *source)
|
||||
if (crecp)
|
||||
{
|
||||
crecp->flags = (source->flags | F_NAMEP) & ~(F_IPV4 | F_IPV6 | F_CNAME | F_SRV | F_DNSKEY | F_DS | F_REVERSE);
|
||||
crecp->ttd = source->ttd;
|
||||
if (!(crecp->flags & F_IMMORTAL))
|
||||
crecp->ttd = source->ttd;
|
||||
crecp->name.namep = name;
|
||||
|
||||
cache_hash(crecp);
|
||||
@@ -1602,21 +1653,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 +1692,7 @@ int cache_make_stat(struct txt_record *t)
|
||||
}
|
||||
t->txt = (unsigned char *)buff;
|
||||
t->len = p - buff;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1686,32 +1735,30 @@ 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 ;
|
||||
int i;
|
||||
my_syslog(LOG_INFO, "Host Address Flags Expires");
|
||||
my_syslog(LOG_INFO, "Host Address Flags Expires Source");
|
||||
my_syslog(LOG_INFO, "------------------------------ ---------------------------------------- ---------- ------------------------ ------------");
|
||||
|
||||
for (i=0; i<hash_size; i++)
|
||||
for (cache = hash_table[i]; cache; cache = cache->hash_next)
|
||||
@@ -1769,7 +1816,10 @@ void dump_cache(time_t now)
|
||||
else if (cache->flags & F_DNSKEY)
|
||||
t = "K";
|
||||
#endif
|
||||
p += sprintf(p, "%-40.40s %s%s%s%s%s%s%s%s%s ", a, t,
|
||||
else /* non-terminal */
|
||||
t = "!";
|
||||
|
||||
p += sprintf(p, "%-40.40s %s%s%s%s%s%s%s%s%s%s ", a, t,
|
||||
cache->flags & F_FORWARD ? "F" : " ",
|
||||
cache->flags & F_REVERSE ? "R" : " ",
|
||||
cache->flags & F_IMMORTAL ? "I" : " ",
|
||||
@@ -1777,14 +1827,16 @@ void dump_cache(time_t now)
|
||||
cache->flags & F_NEG ? "N" : " ",
|
||||
cache->flags & F_NXDOMAIN ? "X" : " ",
|
||||
cache->flags & F_HOSTS ? "H" : " ",
|
||||
cache->flags & F_CONFIG ? "C" : " ",
|
||||
cache->flags & F_DNSSECOK ? "V" : " ");
|
||||
#ifdef HAVE_BROKEN_RTC
|
||||
p += sprintf(p, "%lu", cache->flags & F_IMMORTAL ? 0: (unsigned long)(cache->ttd - now));
|
||||
p += sprintf(p, "%-24lu", cache->flags & F_IMMORTAL ? 0: (unsigned long)(cache->ttd - now));
|
||||
#else
|
||||
p += sprintf(p, "%s", cache->flags & F_IMMORTAL ? "\n" : ctime(&(cache->ttd)));
|
||||
/* ctime includes trailing \n - eat it */
|
||||
*(p-1) = 0;
|
||||
p += sprintf(p, "%-24.24s", cache->flags & F_IMMORTAL ? "" : ctime(&(cache->ttd)));
|
||||
#endif
|
||||
if(cache->flags & (F_HOSTS | F_CONFIG) && cache->uid > 0)
|
||||
p += sprintf(p, " %s", record_source(cache->uid));
|
||||
|
||||
my_syslog(LOG_INFO, "%s", daemon->namebuff);
|
||||
}
|
||||
}
|
||||
@@ -1812,7 +1864,7 @@ char *record_source(unsigned int index)
|
||||
return "<unknown>";
|
||||
}
|
||||
|
||||
char *querystr(char *desc, unsigned short type)
|
||||
static char *querystr(char *desc, unsigned short type)
|
||||
{
|
||||
unsigned int i;
|
||||
int len = 10; /* strlen("type=xxxxx") */
|
||||
@@ -1860,47 +1912,105 @@ char *querystr(char *desc, unsigned short type)
|
||||
if (types)
|
||||
sprintf(buff, "<%s>", types);
|
||||
else
|
||||
sprintf(buff, "type=%d", type);
|
||||
sprintf(buff, "<type=%d>", type);
|
||||
}
|
||||
}
|
||||
|
||||
return buff ? buff : "";
|
||||
}
|
||||
|
||||
void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg)
|
||||
static char *edestr(int ede)
|
||||
{
|
||||
char *source, *dest = daemon->addrbuff;
|
||||
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, unsigned short type)
|
||||
{
|
||||
char *source, *dest = arg;
|
||||
char *verb = "is";
|
||||
char *extra = "";
|
||||
char portstring[7]; /* space for #<portnum> */
|
||||
|
||||
if (!option_bool(OPT_LOG))
|
||||
return;
|
||||
|
||||
/* build query type string if requested */
|
||||
if (!(flags & (F_SERVER | F_IPSET)) && type > 0)
|
||||
arg = querystr(arg, type);
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
if ((flags & F_DNSSECOK) && option_bool(OPT_EXTRALOG))
|
||||
extra = " (DNSSEC signed)";
|
||||
#endif
|
||||
|
||||
name = sanitise(name);
|
||||
|
||||
if (addr)
|
||||
{
|
||||
dest = daemon->addrbuff;
|
||||
|
||||
if (flags & F_KEYTAG)
|
||||
sprintf(daemon->addrbuff, arg, addr->log.keytag, addr->log.algo, addr->log.digest);
|
||||
else if (flags & F_RCODE)
|
||||
{
|
||||
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 if (flags & (F_IPV4 | F_IPV6))
|
||||
{
|
||||
inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
|
||||
addr, daemon->addrbuff, ADDRSTRLEN);
|
||||
if ((flags & F_SERVER) && type != NAMESERVER_PORT)
|
||||
{
|
||||
extra = portstring;
|
||||
sprintf(portstring, "#%u", type);
|
||||
}
|
||||
}
|
||||
else
|
||||
inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
|
||||
addr, daemon->addrbuff, ADDRSTRLEN);
|
||||
|
||||
dest = arg;
|
||||
}
|
||||
else
|
||||
dest = arg;
|
||||
|
||||
if (flags & F_REVERSE)
|
||||
{
|
||||
@@ -1938,10 +2048,23 @@ 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)
|
||||
else if (flags & F_DNSSEC)
|
||||
{
|
||||
source = arg;
|
||||
verb = "to";
|
||||
}
|
||||
else if (flags & F_SERVER)
|
||||
{
|
||||
source = "forwarded";
|
||||
verb = "to";
|
||||
@@ -1951,14 +2074,9 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg)
|
||||
source = arg;
|
||||
verb = "from";
|
||||
}
|
||||
else if (flags & F_DNSSEC)
|
||||
{
|
||||
source = arg;
|
||||
verb = "to";
|
||||
}
|
||||
else if (flags & F_IPSET)
|
||||
{
|
||||
source = "ipset add";
|
||||
source = type ? "ipset add" : "nftset add";
|
||||
dest = name;
|
||||
name = arg;
|
||||
verb = daemon->addrbuff;
|
||||
@@ -1966,19 +2084,19 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg)
|
||||
else
|
||||
source = "cached";
|
||||
|
||||
if (strlen(name) == 0)
|
||||
if (name && !name[0])
|
||||
name = ".";
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
33
src/config.h
33
src/config.h
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2022 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
|
||||
@@ -20,16 +20,15 @@
|
||||
#define TCP_MAX_QUERIES 100 /* Maximum number of queries per incoming TCP connection */
|
||||
#define TCP_BACKLOG 32 /* kernel backlog limit for TCP connections */
|
||||
#define EDNS_PKTSZ 4096 /* default max EDNS.0 UDP packet from RFC5625 */
|
||||
#define SAFE_PKTSZ 1280 /* "go anywhere" UDP packet size */
|
||||
#define SAFE_PKTSZ 1232 /* "go anywhere" UDP packet size, see https://dnsflagday.net/2020/ */
|
||||
#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 */
|
||||
@@ -116,12 +115,16 @@ HAVE_IPSET
|
||||
define this to include the ability to selectively add resolved ip addresses
|
||||
to given ipsets.
|
||||
|
||||
HAVE_NFTSET
|
||||
define this to include the ability to selectively add resolved ip addresses
|
||||
to given nftables sets.
|
||||
|
||||
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 +147,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,9 +194,9 @@ RESOLVFILE
|
||||
/* #define HAVE_IDN */
|
||||
/* #define HAVE_LIBIDN2 */
|
||||
/* #define HAVE_CONNTRACK */
|
||||
/* #define HAVE_NETTLEHASH */
|
||||
/* #define HAVE_CRYPTOHASH */
|
||||
/* #define HAVE_DNSSEC */
|
||||
|
||||
/* #define HAVE_NFTSET */
|
||||
|
||||
/* Default locations for important system files. */
|
||||
|
||||
@@ -420,14 +424,18 @@ static char *compile_opts =
|
||||
"no-"
|
||||
#endif
|
||||
"ipset "
|
||||
#ifndef HAVE_NFTSET
|
||||
"no-"
|
||||
#endif
|
||||
"nftset "
|
||||
#ifndef HAVE_AUTH
|
||||
"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 +456,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-2022 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-2022 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) */
|
||||
|
||||
48
src/dbus.c
48
src/dbus.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2022 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
|
||||
@@ -52,6 +52,9 @@ const char* introspection_xml_template =
|
||||
" <method name=\"SetFilterWin2KOption\">\n"
|
||||
" <arg name=\"filterwin2k\" direction=\"in\" type=\"b\"/>\n"
|
||||
" </method>\n"
|
||||
" <method name=\"SetLocaliseQueriesOption\">\n"
|
||||
" <arg name=\"localise-queries\" direction=\"in\" type=\"b\"/>\n"
|
||||
" </method>\n"
|
||||
" <method name=\"SetBogusPrivOption\">\n"
|
||||
" <arg name=\"boguspriv\" direction=\"in\" type=\"b\"/>\n"
|
||||
" </method>\n"
|
||||
@@ -114,7 +117,7 @@ static dbus_bool_t add_watch(DBusWatch *watch, void *data)
|
||||
w->next = daemon->watches;
|
||||
daemon->watches = w;
|
||||
|
||||
w = data; /* no warning */
|
||||
(void)data; /* no warning */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -134,16 +137,20 @@ static void remove_watch(DBusWatch *watch, void *data)
|
||||
up = &(w->next);
|
||||
}
|
||||
|
||||
w = data; /* no warning */
|
||||
(void)data; /* no warning */
|
||||
}
|
||||
|
||||
static void dbus_read_servers(DBusMessage *message)
|
||||
static DBusMessage* dbus_read_servers(DBusMessage *message)
|
||||
{
|
||||
DBusMessageIter iter;
|
||||
union mysockaddr addr, source_addr;
|
||||
char *domain;
|
||||
|
||||
dbus_message_iter_init(message, &iter);
|
||||
if (!dbus_message_iter_init(message, &iter))
|
||||
{
|
||||
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
|
||||
"Failed to initialize dbus message iter");
|
||||
}
|
||||
|
||||
mark_servers(SERV_FROM_DBUS);
|
||||
|
||||
@@ -215,13 +222,14 @@ 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);
|
||||
}
|
||||
|
||||
/* unlink and free anything still marked. */
|
||||
cleanup_servers();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LOOP
|
||||
@@ -276,7 +284,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 +369,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 +381,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 +396,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 +411,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 +420,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
|
||||
}
|
||||
|
||||
cleanup_servers();
|
||||
|
||||
|
||||
if (dup)
|
||||
free(dup);
|
||||
|
||||
@@ -549,6 +553,10 @@ static DBusMessage *dbus_add_lease(DBusMessage* message)
|
||||
"Invalid IP address '%s'", ipaddr);
|
||||
|
||||
hw_len = parse_hex((char*)hwaddr, dhcp_chaddr, DHCP_CHADDR_MAX, NULL, &hw_type);
|
||||
if (hw_len < 0)
|
||||
return dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
|
||||
"Invalid HW address '%s'", hwaddr);
|
||||
|
||||
if (hw_type == 0 && hw_len != 0)
|
||||
hw_type = ARPHRD_ETHER;
|
||||
|
||||
@@ -672,7 +680,7 @@ DBusHandlerResult message_handler(DBusConnection *connection,
|
||||
#endif
|
||||
else if (strcmp(method, "SetServers") == 0)
|
||||
{
|
||||
dbus_read_servers(message);
|
||||
reply = dbus_read_servers(message);
|
||||
new_servers = 1;
|
||||
}
|
||||
else if (strcmp(method, "SetServersEx") == 0)
|
||||
@@ -689,6 +697,10 @@ DBusHandlerResult message_handler(DBusConnection *connection,
|
||||
{
|
||||
reply = dbus_set_bool(message, OPT_FILTER, "filterwin2k");
|
||||
}
|
||||
else if (strcmp(method, "SetLocaliseQueriesOption") == 0)
|
||||
{
|
||||
reply = dbus_set_bool(message, OPT_LOCALISE, "localise-queries");
|
||||
}
|
||||
else if (strcmp(method, "SetBogusPrivOption") == 0)
|
||||
{
|
||||
reply = dbus_set_bool(message, OPT_BOGUSPRIV, "bogus-priv");
|
||||
@@ -715,7 +727,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;
|
||||
}
|
||||
@@ -723,7 +735,7 @@ DBusHandlerResult message_handler(DBusConnection *connection,
|
||||
if (clear_cache)
|
||||
clear_cache_and_reload(dnsmasq_time());
|
||||
|
||||
method = user_data; /* no warning */
|
||||
(void)user_data; /* no warning */
|
||||
|
||||
/* If no reply or no error, return nothing */
|
||||
if (!reply)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2022 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
|
||||
@@ -79,13 +79,46 @@ ssize_t recv_dhcp_packet(int fd, struct msghdr *msg)
|
||||
return (msg->msg_flags & MSG_TRUNC) ? -1 : new_sz;
|
||||
}
|
||||
|
||||
/* like match_netid() except that the check can have a trailing * for wildcard */
|
||||
/* started as a direct copy of match_netid() */
|
||||
int match_netid_wild(struct dhcp_netid *check, struct dhcp_netid *pool)
|
||||
{
|
||||
struct dhcp_netid *tmp1;
|
||||
|
||||
for (; check; check = check->next)
|
||||
{
|
||||
const int check_len = strlen(check->net);
|
||||
const int is_wc = (check_len > 0 && check->net[check_len - 1] == '*');
|
||||
|
||||
/* '#' for not is for backwards compat. */
|
||||
if (check->net[0] != '!' && check->net[0] != '#')
|
||||
{
|
||||
for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
|
||||
if (is_wc ? (strncmp(check->net, tmp1->net, check_len-1) == 0) :
|
||||
(strcmp(check->net, tmp1->net) == 0))
|
||||
break;
|
||||
if (!tmp1)
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
|
||||
if (is_wc ? (strncmp((check->net)+1, tmp1->net, check_len-2) == 0) :
|
||||
(strcmp((check->net)+1, tmp1->net) == 0))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct dhcp_netid *run_tag_if(struct dhcp_netid *tags)
|
||||
{
|
||||
struct tag_if *exprs;
|
||||
struct dhcp_netid_list *list;
|
||||
|
||||
/* this now uses match_netid_wild() above so that tag_if can
|
||||
* be used to set a 'group of interfaces' tag.
|
||||
*/
|
||||
for (exprs = daemon->tag_if; exprs; exprs = exprs->next)
|
||||
if (match_netid(exprs->tag, tags, 1))
|
||||
if (match_netid_wild(exprs->tag, tags))
|
||||
for (list = exprs->set; list; list = list->next)
|
||||
{
|
||||
list->list->next = tags;
|
||||
@@ -280,31 +313,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;
|
||||
@@ -535,12 +566,16 @@ char *whichdevice(void)
|
||||
}
|
||||
|
||||
if (found)
|
||||
return found->name;
|
||||
|
||||
{
|
||||
char *ret = safe_malloc(strlen(found->name)+1);
|
||||
strcpy(ret, found->name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void bindtodevice(char *device, int fd)
|
||||
static int bindtodevice(char *device, int fd)
|
||||
{
|
||||
size_t len = strlen(device)+1;
|
||||
if (len > IFNAMSIZ)
|
||||
@@ -548,7 +583,33 @@ void bindtodevice(char *device, int fd)
|
||||
/* only allowed by root. */
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, device, len) == -1 &&
|
||||
errno != EPERM)
|
||||
die(_("failed to set SO_BINDTODEVICE on DHCP socket: %s"), NULL, EC_BADNET);
|
||||
return 2;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int bind_dhcp_devices(char *bound_device)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (bound_device)
|
||||
{
|
||||
if (daemon->dhcp)
|
||||
{
|
||||
if (!daemon->relay4)
|
||||
ret |= bindtodevice(bound_device, daemon->dhcpfd);
|
||||
|
||||
if (daemon->enable_pxe && daemon->pxefd != -1)
|
||||
ret |= bindtodevice(bound_device, daemon->pxefd);
|
||||
}
|
||||
|
||||
#if defined(HAVE_DHCP6)
|
||||
if (daemon->doing_dhcp6 && !daemon->relay6)
|
||||
ret |= bindtodevice(bound_device, daemon->dhcp6fd);
|
||||
#endif
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -622,6 +683,8 @@ static const struct opttab_t {
|
||||
{ "client-arch", 93, 2 | OT_DEC },
|
||||
{ "client-interface-id", 94, 0 },
|
||||
{ "client-machine-id", 97, 0 },
|
||||
{ "posix-timezone", 100, OT_NAME }, /* RFC 4833, Sec. 2 */
|
||||
{ "tzdb-timezone", 101, OT_NAME }, /* RFC 4833, Sec. 2 */
|
||||
{ "subnet-select", 118, OT_INTERNAL },
|
||||
{ "domain-search", 119, OT_RFC1035_NAME },
|
||||
{ "sip-server", 120, 0 },
|
||||
@@ -952,11 +1015,27 @@ void log_context(int family, struct dhcp_context *context)
|
||||
|
||||
void log_relay(int family, struct dhcp_relay *relay)
|
||||
{
|
||||
int broadcast = relay->server.addr4.s_addr == 0;
|
||||
inet_ntop(family, &relay->local, daemon->addrbuff, ADDRSTRLEN);
|
||||
inet_ntop(family, &relay->server, daemon->namebuff, ADDRSTRLEN);
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
struct in6_addr multicast;
|
||||
|
||||
inet_pton(AF_INET6, ALL_SERVERS, &multicast);
|
||||
|
||||
if (family == AF_INET6)
|
||||
broadcast = IN6_ARE_ADDR_EQUAL(&relay->server.addr6, &multicast);
|
||||
#endif
|
||||
|
||||
|
||||
if (relay->interface)
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay from %s to %s via %s"), daemon->addrbuff, daemon->namebuff, relay->interface);
|
||||
{
|
||||
if (broadcast)
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay from %s via %s"), daemon->addrbuff, relay->interface);
|
||||
else
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay from %s to %s via %s"), daemon->addrbuff, daemon->namebuff, relay->interface);
|
||||
}
|
||||
else
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay from %s to %s"), daemon->addrbuff, daemon->namebuff);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2022 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
|
||||
|
||||
195
src/dhcp.c
195
src/dhcp.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2022 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
|
||||
@@ -20,8 +20,6 @@
|
||||
|
||||
struct iface_param {
|
||||
struct dhcp_context *current;
|
||||
struct dhcp_relay *relay;
|
||||
struct in_addr relay_local;
|
||||
int ind;
|
||||
};
|
||||
|
||||
@@ -34,7 +32,7 @@ static int complete_context(struct in_addr local, int if_index, char *label,
|
||||
struct in_addr netmask, struct in_addr broadcast, void *vparam);
|
||||
static int check_listen_addrs(struct in_addr local, int if_index, char *label,
|
||||
struct in_addr netmask, struct in_addr broadcast, void *vparam);
|
||||
static int relay_upstream4(struct dhcp_relay *relay, struct dhcp_packet *mess, size_t sz, int iface_index);
|
||||
static int relay_upstream4(int iface_index, struct dhcp_packet *mess, size_t sz);
|
||||
static struct dhcp_relay *relay_reply4(struct dhcp_packet *mess, char *arrival_interface);
|
||||
|
||||
static int make_fd(int port)
|
||||
@@ -177,11 +175,16 @@ void dhcp_packet(time_t now, int pxe_fd)
|
||||
if ((sz = recv_dhcp_packet(fd, &msg)) == -1 ||
|
||||
(sz < (ssize_t)(sizeof(*mess) - sizeof(mess->options))))
|
||||
return;
|
||||
|
||||
#if defined (HAVE_LINUX_NETWORK)
|
||||
|
||||
#ifdef HAVE_DUMPFILE
|
||||
dump_packet(DUMP_DHCP, (void *)daemon->dhcp_packet.iov_base, sz, (union mysockaddr *)&dest, NULL,
|
||||
pxe_fd ? PXE_PORT : daemon->dhcp_server_port);
|
||||
#endif
|
||||
|
||||
#if defined (HAVE_LINUX_NETWORK)
|
||||
if (ioctl(fd, SIOCGSTAMP, &tv) == 0)
|
||||
recvtime = tv.tv_sec;
|
||||
|
||||
|
||||
if (msg.msg_controllen >= sizeof(struct cmsghdr))
|
||||
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
|
||||
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO)
|
||||
@@ -302,12 +305,7 @@ void dhcp_packet(time_t now, int pxe_fd)
|
||||
for (context = daemon->dhcp; context; context = context->next)
|
||||
context->current = context;
|
||||
|
||||
for (relay = daemon->relay4; relay; relay = relay->next)
|
||||
relay->current = relay;
|
||||
|
||||
parm.current = NULL;
|
||||
parm.relay = NULL;
|
||||
parm.relay_local.s_addr = 0;
|
||||
parm.ind = iface_index;
|
||||
|
||||
if (!iface_check(AF_INET, (union all_addr *)&iface_addr, ifr.ifr_name, NULL))
|
||||
@@ -329,15 +327,19 @@ void dhcp_packet(time_t now, int pxe_fd)
|
||||
there is more than one address on the interface in the same subnet */
|
||||
complete_context(match.addr, iface_index, NULL, match.netmask, match.broadcast, &parm);
|
||||
}
|
||||
|
||||
if (relay_upstream4(iface_index, mess, (size_t)sz))
|
||||
return;
|
||||
|
||||
if (!iface_enumerate(AF_INET, &parm, complete_context))
|
||||
return;
|
||||
|
||||
/* We're relaying this request */
|
||||
if (parm.relay_local.s_addr != 0 &&
|
||||
relay_upstream4(parm.relay, mess, (size_t)sz, iface_index))
|
||||
/* Check for a relay again after iface_enumerate/complete_context has had
|
||||
chance to fill in relay->iface_index fields. This handles first time through
|
||||
and any changes in interface config. */
|
||||
if (relay_upstream4(iface_index, mess, (size_t)sz))
|
||||
return;
|
||||
|
||||
|
||||
/* May have configured relay, but not DHCP server */
|
||||
if (!daemon->dhcp)
|
||||
return;
|
||||
@@ -455,6 +457,17 @@ void dhcp_packet(time_t now, int pxe_fd)
|
||||
#elif defined(HAVE_BSD_NETWORK)
|
||||
else
|
||||
{
|
||||
#ifdef HAVE_DUMPFILE
|
||||
if (ntohs(mess->flags) & 0x8000)
|
||||
dest.sin_addr.s_addr = INADDR_BROADCAST;
|
||||
else
|
||||
dest.sin_addr = mess->yiaddr;
|
||||
dest.sin_port = htons(daemon->dhcp_client_port);
|
||||
|
||||
dump_packet(DUMP_DHCP, (void *)iov.iov_base, iov.iov_len, NULL,
|
||||
(union mysockaddr *)&dest, daemon->dhcp_server_port);
|
||||
#endif
|
||||
|
||||
send_via_bpf(mess, iov.iov_len, iface_addr, &ifr);
|
||||
return;
|
||||
}
|
||||
@@ -463,13 +476,21 @@ void dhcp_packet(time_t now, int pxe_fd)
|
||||
#ifdef HAVE_SOLARIS_NETWORK
|
||||
setsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &iface_index, sizeof(iface_index));
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DUMPFILE
|
||||
dump_packet(DUMP_DHCP, (void *)iov.iov_base, iov.iov_len, NULL,
|
||||
(union mysockaddr *)&dest, daemon->dhcp_server_port);
|
||||
#endif
|
||||
|
||||
while(retry_send(sendmsg(fd, &msg, 0)));
|
||||
|
||||
/* This can fail when, eg, iptables DROPS destination 255.255.255.255 */
|
||||
if (errno != 0)
|
||||
my_syslog(MS_DHCP | LOG_WARNING, _("Error sending DHCP packet to %s: %s"),
|
||||
inet_ntoa(dest.sin_addr), strerror(errno));
|
||||
{
|
||||
inet_ntop(AF_INET, &dest.sin_addr, daemon->addrbuff, ADDRSTRLEN);
|
||||
my_syslog(MS_DHCP | LOG_WARNING, _("Error sending DHCP packet to %s: %s"),
|
||||
daemon->addrbuff, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
/* check against secondary interface addresses */
|
||||
@@ -521,10 +542,11 @@ static void guess_range_netmask(struct in_addr addr, struct in_addr netmask)
|
||||
!(is_same_net(addr, context->start, netmask) &&
|
||||
is_same_net(addr, context->end, netmask)))
|
||||
{
|
||||
strcpy(daemon->dhcp_buff, inet_ntoa(context->start));
|
||||
strcpy(daemon->dhcp_buff2, inet_ntoa(context->end));
|
||||
inet_ntop(AF_INET, &context->start, daemon->dhcp_buff, DHCP_BUFF_SZ);
|
||||
inet_ntop(AF_INET, &context->end, daemon->dhcp_buff2, DHCP_BUFF_SZ);
|
||||
inet_ntop(AF_INET, &netmask, daemon->addrbuff, ADDRSTRLEN);
|
||||
my_syslog(MS_DHCP | LOG_WARNING, _("DHCP range %s -- %s is not consistent with netmask %s"),
|
||||
daemon->dhcp_buff, daemon->dhcp_buff2, inet_ntoa(netmask));
|
||||
daemon->dhcp_buff, daemon->dhcp_buff2, daemon->addrbuff);
|
||||
}
|
||||
context->netmask = netmask;
|
||||
}
|
||||
@@ -609,14 +631,9 @@ static int complete_context(struct in_addr local, int if_index, char *label,
|
||||
}
|
||||
|
||||
for (relay = daemon->relay4; relay; relay = relay->next)
|
||||
if (if_index == param->ind && relay->local.addr4.s_addr == local.s_addr && relay->current == relay &&
|
||||
(param->relay_local.s_addr == 0 || param->relay_local.s_addr == local.s_addr))
|
||||
{
|
||||
relay->current = param->relay;
|
||||
param->relay = relay;
|
||||
param->relay_local = local;
|
||||
}
|
||||
|
||||
if (relay->local.addr4.s_addr == local.s_addr)
|
||||
relay->iface_index = if_index;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -922,7 +939,7 @@ void dhcp_read_ethers(void)
|
||||
|
||||
if (!*cp)
|
||||
{
|
||||
if ((addr.s_addr = inet_addr(ip)) == (in_addr_t)-1)
|
||||
if (inet_pton(AF_INET, ip, &addr.s_addr) < 1)
|
||||
{
|
||||
my_syslog(MS_DHCP | LOG_ERR, _("bad address at %s line %d"), ETHERSFILE, lineno);
|
||||
continue;
|
||||
@@ -1057,52 +1074,96 @@ char *host_from_dns(struct in_addr addr)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int relay_upstream4(struct dhcp_relay *relay, struct dhcp_packet *mess, size_t sz, int iface_index)
|
||||
static int relay_upstream4(int iface_index, struct dhcp_packet *mess, size_t sz)
|
||||
{
|
||||
/* ->local is same value for all relays on ->current chain */
|
||||
union all_addr from;
|
||||
|
||||
struct in_addr giaddr = mess->giaddr;
|
||||
u8 hops = mess->hops;
|
||||
struct dhcp_relay *relay;
|
||||
|
||||
if (mess->op != BOOTREQUEST)
|
||||
return 0;
|
||||
|
||||
/* source address == relay address */
|
||||
from.addr4 = relay->local.addr4;
|
||||
for (relay = daemon->relay4; relay; relay = relay->next)
|
||||
if (relay->iface_index != 0 && relay->iface_index == iface_index)
|
||||
break;
|
||||
|
||||
/* No relay config. */
|
||||
if (!relay)
|
||||
return 0;
|
||||
|
||||
/* already gatewayed ? */
|
||||
if (mess->giaddr.s_addr)
|
||||
{
|
||||
/* if so check if by us, to stomp on loops. */
|
||||
if (mess->giaddr.s_addr == relay->local.addr4.s_addr)
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* plug in our address */
|
||||
mess->giaddr.s_addr = relay->local.addr4.s_addr;
|
||||
}
|
||||
for (; relay; relay = relay->next)
|
||||
if (relay->iface_index != 0 && relay->iface_index == iface_index)
|
||||
{
|
||||
union mysockaddr to;
|
||||
union all_addr from;
|
||||
|
||||
if ((mess->hops++) > 20)
|
||||
return 1;
|
||||
mess->hops = hops;
|
||||
mess->giaddr = giaddr;
|
||||
|
||||
if ((mess->hops++) > 20)
|
||||
continue;
|
||||
|
||||
/* source address == relay address */
|
||||
from.addr4 = relay->local.addr4;
|
||||
|
||||
for (; relay; relay = relay->current)
|
||||
{
|
||||
union mysockaddr to;
|
||||
|
||||
to.sa.sa_family = AF_INET;
|
||||
to.in.sin_addr = relay->server.addr4;
|
||||
to.in.sin_port = htons(daemon->dhcp_server_port);
|
||||
|
||||
send_from(daemon->dhcpfd, 0, (char *)mess, sz, &to, &from, 0);
|
||||
|
||||
if (option_bool(OPT_LOG_OPTS))
|
||||
/* already gatewayed ? */
|
||||
if (giaddr.s_addr)
|
||||
{
|
||||
/* if so check if by us, to stomp on loops. */
|
||||
if (giaddr.s_addr == relay->local.addr4.s_addr)
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* plug in our address */
|
||||
mess->giaddr.s_addr = relay->local.addr4.s_addr;
|
||||
}
|
||||
|
||||
to.sa.sa_family = AF_INET;
|
||||
to.in.sin_addr = relay->server.addr4;
|
||||
to.in.sin_port = htons(daemon->dhcp_server_port);
|
||||
|
||||
/* Broadcasting to server. */
|
||||
if (relay->server.addr4.s_addr == 0)
|
||||
{
|
||||
struct ifreq ifr;
|
||||
|
||||
if (relay->interface)
|
||||
safe_strncpy(ifr.ifr_name, relay->interface, IF_NAMESIZE);
|
||||
|
||||
if (!relay->interface || strchr(relay->interface, '*') ||
|
||||
ioctl(daemon->dhcpfd, SIOCGIFBRDADDR, &ifr) == -1)
|
||||
{
|
||||
my_syslog(MS_DHCP | LOG_ERR, _("Cannot broadcast DHCP relay via interface %s"), relay->interface);
|
||||
continue;
|
||||
}
|
||||
|
||||
to.in.sin_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DUMPFILE
|
||||
{
|
||||
inet_ntop(AF_INET, &relay->local, daemon->addrbuff, ADDRSTRLEN);
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay %s -> %s"), daemon->addrbuff, inet_ntoa(relay->server.addr4));
|
||||
union mysockaddr fromsock;
|
||||
fromsock.in.sin_port = htons(daemon->dhcp_server_port);
|
||||
fromsock.in.sin_addr = from.addr4;
|
||||
fromsock.sa.sa_family = AF_INET;
|
||||
|
||||
dump_packet(DUMP_DHCP, (void *)mess, sz, &fromsock, &to, 0);
|
||||
}
|
||||
|
||||
/* Save this for replies */
|
||||
relay->iface_index = iface_index;
|
||||
}
|
||||
#endif
|
||||
|
||||
send_from(daemon->dhcpfd, 0, (char *)mess, sz, &to, &from, 0);
|
||||
|
||||
if (option_bool(OPT_LOG_OPTS))
|
||||
{
|
||||
inet_ntop(AF_INET, &relay->local, daemon->addrbuff, ADDRSTRLEN);
|
||||
if (relay->server.addr4.s_addr == 0)
|
||||
snprintf(daemon->dhcp_buff2, DHCP_BUFF_SZ, _("broadcast via %s"), relay->interface);
|
||||
else
|
||||
inet_ntop(AF_INET, &relay->server.addr4, daemon->dhcp_buff2, DHCP_BUFF_SZ);
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay at %s -> %s"), daemon->addrbuff, daemon->dhcp_buff2);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2022 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
|
||||
@@ -55,6 +55,8 @@
|
||||
#define OPTION6_RECONF_ACCEPT 20
|
||||
#define OPTION6_DNS_SERVER 23
|
||||
#define OPTION6_DOMAIN_SEARCH 24
|
||||
#define OPTION6_IA_PD 25
|
||||
#define OPTION6_IAPREFIX 26
|
||||
#define OPTION6_REFRESH_TIME 32
|
||||
#define OPTION6_REMOTE_ID 37
|
||||
#define OPTION6_SUBSCRIBER_ID 38
|
||||
@@ -72,4 +74,3 @@
|
||||
#define DHCP6NOBINDING 3
|
||||
#define DHCP6NOTONLINK 4
|
||||
#define DHCP6USEMULTI 5
|
||||
|
||||
|
||||
101
src/dhcp6.c
101
src/dhcp6.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2022 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,8 +22,7 @@
|
||||
|
||||
struct iface_param {
|
||||
struct dhcp_context *current;
|
||||
struct dhcp_relay *relay;
|
||||
struct in6_addr fallback, relay_local, ll_addr, ula_addr;
|
||||
struct in6_addr fallback, ll_addr, ula_addr;
|
||||
int ind, addr_match;
|
||||
};
|
||||
|
||||
@@ -90,7 +89,6 @@ void dhcp6_init(void)
|
||||
void dhcp6_packet(time_t now)
|
||||
{
|
||||
struct dhcp_context *context;
|
||||
struct dhcp_relay *relay;
|
||||
struct iface_param parm;
|
||||
struct cmsghdr *cmptr;
|
||||
struct msghdr msg;
|
||||
@@ -105,7 +103,8 @@ void dhcp6_packet(time_t now)
|
||||
struct iname *tmp;
|
||||
unsigned short port;
|
||||
struct in6_addr dst_addr;
|
||||
|
||||
struct in6_addr all_servers;
|
||||
|
||||
memset(&dst_addr, 0, sizeof(dst_addr));
|
||||
|
||||
msg.msg_control = control_u.control6;
|
||||
@@ -119,6 +118,11 @@ void dhcp6_packet(time_t now)
|
||||
if ((sz = recv_dhcp_packet(daemon->dhcp6fd, &msg)) == -1)
|
||||
return;
|
||||
|
||||
#ifdef HAVE_DUMPFILE
|
||||
dump_packet(DUMP_DHCPV6, (void *)daemon->dhcp_packet.iov_base, sz,
|
||||
(union mysockaddr *)&from, NULL, DHCPV6_SERVER_PORT);
|
||||
#endif
|
||||
|
||||
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
|
||||
if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
|
||||
{
|
||||
@@ -135,9 +139,13 @@ void dhcp6_packet(time_t now)
|
||||
if (!indextoname(daemon->dhcp6fd, if_index, ifr.ifr_name))
|
||||
return;
|
||||
|
||||
if ((port = relay_reply6(&from, sz, ifr.ifr_name)) != 0)
|
||||
if (relay_reply6(&from, sz, ifr.ifr_name))
|
||||
{
|
||||
from.sin6_port = htons(port);
|
||||
#ifdef HAVE_DUMPFILE
|
||||
dump_packet(DUMP_DHCPV6, (void *)daemon->outpacket.iov_base, save_counter(-1), NULL,
|
||||
(union mysockaddr *)&from, DHCPV6_SERVER_PORT);
|
||||
#endif
|
||||
|
||||
while (retry_send(sendto(daemon->dhcp6fd, daemon->outpacket.iov_base,
|
||||
save_counter(-1), 0, (struct sockaddr *)&from,
|
||||
sizeof(from))));
|
||||
@@ -145,7 +153,7 @@ void dhcp6_packet(time_t now)
|
||||
else
|
||||
{
|
||||
struct dhcp_bridge *bridge, *alias;
|
||||
|
||||
|
||||
for (tmp = daemon->if_except; tmp; tmp = tmp->next)
|
||||
if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
|
||||
return;
|
||||
@@ -155,14 +163,12 @@ void dhcp6_packet(time_t now)
|
||||
return;
|
||||
|
||||
parm.current = NULL;
|
||||
parm.relay = NULL;
|
||||
memset(&parm.relay_local, 0, IN6ADDRSZ);
|
||||
parm.ind = if_index;
|
||||
parm.addr_match = 0;
|
||||
memset(&parm.fallback, 0, IN6ADDRSZ);
|
||||
memset(&parm.ll_addr, 0, IN6ADDRSZ);
|
||||
memset(&parm.ula_addr, 0, IN6ADDRSZ);
|
||||
|
||||
|
||||
/* If the interface on which the DHCPv6 request was received is
|
||||
an alias of some other interface (as specified by the
|
||||
--bridge-interface option), change parm.ind so that we look
|
||||
@@ -200,13 +206,25 @@ void dhcp6_packet(time_t now)
|
||||
context->current = context;
|
||||
memset(&context->local6, 0, IN6ADDRSZ);
|
||||
}
|
||||
|
||||
for (relay = daemon->relay6; relay; relay = relay->next)
|
||||
relay->current = relay;
|
||||
|
||||
/* Ignore requests sent to the ALL_SERVERS multicast address for relay when
|
||||
we're listening there for DHCPv6 server reasons. */
|
||||
inet_pton(AF_INET6, ALL_SERVERS, &all_servers);
|
||||
|
||||
if (!IN6_ARE_ADDR_EQUAL(&dst_addr, &all_servers) &&
|
||||
relay_upstream6(if_index, (size_t)sz, &from.sin6_addr, from.sin6_scope_id, now))
|
||||
return;
|
||||
|
||||
if (!iface_enumerate(AF_INET6, &parm, complete_context6))
|
||||
return;
|
||||
|
||||
|
||||
/* Check for a relay again after iface_enumerate/complete_context has had
|
||||
chance to fill in relay->iface_index fields. This handles first time through
|
||||
and any changes in interface config. */
|
||||
if (!IN6_ARE_ADDR_EQUAL(&dst_addr, &all_servers) &&
|
||||
relay_upstream6(if_index, (size_t)sz, &from.sin6_addr, from.sin6_scope_id, now))
|
||||
return;
|
||||
|
||||
if (daemon->if_names || daemon->if_addrs)
|
||||
{
|
||||
|
||||
@@ -218,23 +236,10 @@ void dhcp6_packet(time_t now)
|
||||
return;
|
||||
}
|
||||
|
||||
if (parm.relay)
|
||||
{
|
||||
/* Ignore requests sent to the ALL_SERVERS multicast address for relay when
|
||||
we're listening there for DHCPv6 server reasons. */
|
||||
struct in6_addr all_servers;
|
||||
|
||||
inet_pton(AF_INET6, ALL_SERVERS, &all_servers);
|
||||
|
||||
if (!IN6_ARE_ADDR_EQUAL(&dst_addr, &all_servers))
|
||||
relay_upstream6(parm.relay, sz, &from.sin6_addr, from.sin6_scope_id, now);
|
||||
return;
|
||||
}
|
||||
|
||||
/* May have configured relay, but not DHCP server */
|
||||
if (!daemon->doing_dhcp6)
|
||||
return;
|
||||
|
||||
|
||||
lease_prune(NULL, now); /* lose any expired leases */
|
||||
|
||||
port = dhcp6_reply(parm.current, if_index, ifr.ifr_name, &parm.fallback,
|
||||
@@ -247,11 +252,16 @@ void dhcp6_packet(time_t now)
|
||||
if (port != 0)
|
||||
{
|
||||
from.sin6_port = htons(port);
|
||||
while (retry_send(sendto(daemon->dhcp6fd, daemon->outpacket.iov_base,
|
||||
save_counter(-1), 0, (struct sockaddr *)&from,
|
||||
sizeof(from))));
|
||||
|
||||
#ifdef HAVE_DUMPFILE
|
||||
dump_packet(DUMP_DHCPV6, (void *)daemon->outpacket.iov_base, save_counter(-1),
|
||||
NULL, (union mysockaddr *)&from, DHCPV6_SERVER_PORT);
|
||||
#endif
|
||||
|
||||
while (retry_send(sendto(daemon->dhcp6fd, daemon->outpacket.iov_base,
|
||||
save_counter(-1), 0, (struct sockaddr *)&from, sizeof(from))));
|
||||
}
|
||||
|
||||
|
||||
/* These need to be called _after_ we send DHCPv6 packet, since lease_update_file()
|
||||
may trigger sending an RA packet, which overwrites our buffer. */
|
||||
lease_update_file(now);
|
||||
@@ -292,7 +302,7 @@ void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsi
|
||||
if ((maclen = find_mac(&addr, mac, 0, now)) != 0)
|
||||
break;
|
||||
|
||||
sendto(daemon->icmp6fd, &neigh, sizeof(neigh), 0, &addr.sa, sizeof(addr));
|
||||
while(retry_send(sendto(daemon->icmp6fd, &neigh, sizeof(neigh), 0, &addr.sa, sizeof(addr))));
|
||||
|
||||
ts.tv_sec = 0;
|
||||
ts.tv_nsec = 100000000; /* 100ms */
|
||||
@@ -312,6 +322,7 @@ static int complete_context6(struct in6_addr *local, int prefix,
|
||||
struct dhcp_relay *relay;
|
||||
struct iface_param *param = vparam;
|
||||
struct iname *tmp;
|
||||
int match = !daemon->if_addrs;
|
||||
|
||||
(void)scope; /* warning */
|
||||
|
||||
@@ -333,7 +344,7 @@ static int complete_context6(struct in6_addr *local, int prefix,
|
||||
for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
|
||||
if (tmp->addr.sa.sa_family == AF_INET6 &&
|
||||
IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, local))
|
||||
param->addr_match = 1;
|
||||
match = param->addr_match = 1;
|
||||
|
||||
/* Determine a globally address on the arrival interface, even
|
||||
if we have no matching dhcp-context, because we're only
|
||||
@@ -405,16 +416,12 @@ static int complete_context6(struct in6_addr *local, int prefix,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (relay = daemon->relay6; relay; relay = relay->next)
|
||||
if (IN6_ARE_ADDR_EQUAL(local, &relay->local.addr6) && relay->current == relay &&
|
||||
(IN6_IS_ADDR_UNSPECIFIED(¶m->relay_local) || IN6_ARE_ADDR_EQUAL(local, ¶m->relay_local)))
|
||||
{
|
||||
relay->current = param->relay;
|
||||
param->relay = relay;
|
||||
param->relay_local = *local;
|
||||
}
|
||||
|
||||
|
||||
if (match)
|
||||
for (relay = daemon->relay6; relay; relay = relay->next)
|
||||
if (IN6_ARE_ADDR_EQUAL(local, &relay->local.addr6))
|
||||
relay->iface_index = if_index;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -825,6 +832,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-2022 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;
|
||||
|
||||
290
src/dnsmasq.c
290
src/dnsmasq.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2022 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
|
||||
@@ -17,14 +17,19 @@
|
||||
/* Declare static char *compiler_opts in config.h */
|
||||
#define DNSMASQ_COMPILE_OPTS
|
||||
|
||||
/* dnsmasq.h has to be included first as it sources config.h */
|
||||
#include "dnsmasq.h"
|
||||
|
||||
#if defined(HAVE_IDN) || defined(HAVE_LIBIDN2) || defined(LOCALEDIR)
|
||||
#include <locale.h>
|
||||
#endif
|
||||
|
||||
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);
|
||||
@@ -34,7 +39,6 @@ static void poll_resolv(int force, int do_reload, time_t now);
|
||||
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
int bind_fallback = 0;
|
||||
time_t now;
|
||||
struct sigaction sigact;
|
||||
struct iname *if_tmp;
|
||||
@@ -59,6 +63,8 @@ int main (int argc, char **argv)
|
||||
int did_bind = 0;
|
||||
struct server *serv;
|
||||
char *netlink_warn;
|
||||
#else
|
||||
int bind_fallback = 0;
|
||||
#endif
|
||||
#if defined(HAVE_DHCP) || defined(HAVE_DHCP6)
|
||||
struct dhcp_context *context;
|
||||
@@ -68,8 +74,10 @@ int main (int argc, char **argv)
|
||||
int tftp_prefix_missing = 0;
|
||||
#endif
|
||||
|
||||
#ifdef LOCALEDIR
|
||||
#if defined(HAVE_IDN) || defined(HAVE_LIBIDN2) || defined(LOCALEDIR)
|
||||
setlocale(LC_ALL, "");
|
||||
#endif
|
||||
#ifdef LOCALEDIR
|
||||
bindtextdomain("dnsmasq", LOCALEDIR);
|
||||
textdomain("dnsmasq");
|
||||
#endif
|
||||
@@ -109,7 +117,6 @@ int main (int argc, char **argv)
|
||||
daemon->packet_buff_sz = daemon->edns_pktsz + MAXDNAME + RRFIXEDSZ;
|
||||
daemon->packet = safe_malloc(daemon->packet_buff_sz);
|
||||
|
||||
daemon->addrbuff = safe_malloc(ADDRSTRLEN);
|
||||
if (option_bool(OPT_EXTRALOG))
|
||||
daemon->addrbuff2 = safe_malloc(ADDRSTRLEN);
|
||||
|
||||
@@ -135,6 +142,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)
|
||||
{
|
||||
@@ -205,8 +219,13 @@ int main (int argc, char **argv)
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CONNTRACK
|
||||
if (option_bool(OPT_CONNTRACK) && (daemon->query_port != 0 || daemon->osport))
|
||||
die (_("cannot use --conntrack AND --query-port"), NULL, EC_BADCONF);
|
||||
if (option_bool(OPT_CONNTRACK))
|
||||
{
|
||||
if (daemon->query_port != 0 || daemon->osport)
|
||||
die (_("cannot use --conntrack AND --query-port"), NULL, EC_BADCONF);
|
||||
|
||||
need_cap_net_admin = 1;
|
||||
}
|
||||
#else
|
||||
if (option_bool(OPT_CONNTRACK))
|
||||
die(_("conntrack support not available: set HAVE_CONNTRACK in src/config.h"), NULL, EC_BADCONF);
|
||||
@@ -237,9 +256,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)
|
||||
@@ -327,6 +353,16 @@ int main (int argc, char **argv)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_NFTSET
|
||||
if (daemon->nftsets)
|
||||
{
|
||||
nftset_init();
|
||||
# ifdef HAVE_LINUX_NETWORK
|
||||
need_cap_net_admin = 1;
|
||||
# endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_LINUX_NETWORK)
|
||||
netlink_warn = netlink_init();
|
||||
#elif defined(HAVE_BSD_NETWORK)
|
||||
@@ -351,28 +387,9 @@ int main (int argc, char **argv)
|
||||
#if defined(HAVE_LINUX_NETWORK) && defined(HAVE_DHCP)
|
||||
/* after enumerate_interfaces() */
|
||||
bound_device = whichdevice();
|
||||
|
||||
if (daemon->dhcp)
|
||||
{
|
||||
if (!daemon->relay4 && bound_device)
|
||||
{
|
||||
bindtodevice(bound_device, daemon->dhcpfd);
|
||||
did_bind = 1;
|
||||
}
|
||||
if (daemon->enable_pxe && bound_device)
|
||||
{
|
||||
bindtodevice(bound_device, daemon->pxefd);
|
||||
did_bind = 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_LINUX_NETWORK) && defined(HAVE_DHCP6)
|
||||
if (daemon->doing_dhcp6 && !daemon->relay6 && bound_device)
|
||||
{
|
||||
bindtodevice(bound_device, daemon->dhcp6fd);
|
||||
did_bind = 1;
|
||||
}
|
||||
if ((did_bind = bind_dhcp_devices(bound_device)) & 2)
|
||||
die(_("failed to set SO_BINDTODEVICE on DHCP socket: %s"), NULL, EC_BADNET);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
@@ -390,8 +407,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 +440,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 +450,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);
|
||||
@@ -691,7 +715,11 @@ int main (int argc, char **argv)
|
||||
/* if we are to run scripts, we need to fork a helper before dropping root. */
|
||||
daemon->helperfd = -1;
|
||||
#ifdef HAVE_SCRIPT
|
||||
if ((daemon->dhcp || daemon->dhcp6 || option_bool(OPT_TFTP) || option_bool(OPT_SCRIPT_ARP)) &&
|
||||
if ((daemon->dhcp ||
|
||||
daemon->dhcp6 ||
|
||||
daemon->relay6 ||
|
||||
option_bool(OPT_TFTP) ||
|
||||
option_bool(OPT_SCRIPT_ARP)) &&
|
||||
(daemon->lease_change_command || daemon->luascript))
|
||||
daemon->helperfd = create_helper(pipewrite, err_pipe[1], script_uid, script_gid, max_fd);
|
||||
#endif
|
||||
@@ -895,8 +923,10 @@ int main (int argc, char **argv)
|
||||
my_syslog(LOG_WARNING, _("warning: failed to change owner of %s: %s"),
|
||||
daemon->log_file, strerror(log_err));
|
||||
|
||||
#ifndef HAVE_LINUX_NETWORK
|
||||
if (bind_fallback)
|
||||
my_syslog(LOG_WARNING, _("setting --bind-interfaces option because of OS limitations"));
|
||||
#endif
|
||||
|
||||
if (option_bool(OPT_NOWILD))
|
||||
warn_bound_listeners();
|
||||
@@ -980,7 +1010,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 +1040,7 @@ int main (int argc, char **argv)
|
||||
close(err_pipe[1]);
|
||||
|
||||
if (daemon->port != 0)
|
||||
check_servers();
|
||||
check_servers(0);
|
||||
|
||||
pid = getpid();
|
||||
|
||||
@@ -1025,16 +1055,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,16 +1068,30 @@ 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 defined(HAVE_LINUX_NETWORK)
|
||||
if (bind_dhcp_devices(bound_device) & 2)
|
||||
{
|
||||
static int warned = 0;
|
||||
if (!warned)
|
||||
{
|
||||
my_syslog(LOG_ERR, _("error binding DHCP socket to device %s"), bound_device);
|
||||
warned = 1;
|
||||
}
|
||||
}
|
||||
# endif
|
||||
if (daemon->dhcp || daemon->relay4)
|
||||
{
|
||||
poll_listen(daemon->dhcpfd, POLLIN);
|
||||
@@ -1097,6 +1135,10 @@ int main (int argc, char **argv)
|
||||
while (helper_buf_empty() && do_tftp_script_run());
|
||||
# endif
|
||||
|
||||
# ifdef HAVE_DHCP6
|
||||
while (helper_buf_empty() && do_snoop_script_run());
|
||||
# endif
|
||||
|
||||
if (!helper_buf_empty())
|
||||
poll_listen(daemon->helperfd, POLLOUT);
|
||||
#else
|
||||
@@ -1172,28 +1214,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 +1484,7 @@ static void async_event(int pipe, time_t now)
|
||||
}
|
||||
|
||||
if (check)
|
||||
check_servers();
|
||||
check_servers(0);
|
||||
}
|
||||
|
||||
#ifdef HAVE_DHCP
|
||||
@@ -1537,7 +1595,7 @@ static void async_event(int pipe, time_t now)
|
||||
{
|
||||
/* block in writes until all done */
|
||||
if ((i = fcntl(daemon->helperfd, F_GETFL)) != -1)
|
||||
fcntl(daemon->helperfd, F_SETFL, i & ~O_NONBLOCK);
|
||||
while(retry_send(fcntl(daemon->helperfd, F_SETFL, i & ~O_NONBLOCK)));
|
||||
do {
|
||||
helper_write();
|
||||
} while (!helper_buf_empty() || do_script_run(now));
|
||||
@@ -1625,12 +1683,17 @@ 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);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If we're delaying things, we don't call check_servers(), but
|
||||
reload_servers() may have deleted some servers, rendering the server_array
|
||||
invalid, so just rebuild that here. Once reload_servers() succeeds,
|
||||
we call check_servers() above, which calls build_server_array itself. */
|
||||
build_server_array();
|
||||
latest->mtime = 0;
|
||||
if (!warned)
|
||||
{
|
||||
@@ -1668,11 +1731,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 +1749,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 +1839,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 +1938,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 +1957,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);
|
||||
|
||||
@@ -1939,7 +2009,7 @@ static void check_dns_listeners(time_t now)
|
||||
attribute from the listening socket.
|
||||
Reset that here. */
|
||||
if ((flags = fcntl(confd, F_GETFL, 0)) != -1)
|
||||
fcntl(confd, F_SETFL, flags & ~O_NONBLOCK);
|
||||
while(retry_send(fcntl(confd, F_SETFL, flags & ~O_NONBLOCK)));
|
||||
|
||||
buff = tcp_request(confd, now, &tcp_addr, netmask, auth_dns);
|
||||
|
||||
@@ -2068,7 +2138,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
|
||||
|
||||
352
src/dnsmasq.h
352
src/dnsmasq.h
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2022 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-2022 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,16 @@ 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_FILTER_A 67
|
||||
#define OPT_FILTER_AAAA 68
|
||||
#define OPT_STRIP_ECS 69
|
||||
#define OPT_STRIP_MAC 70
|
||||
#define OPT_LAST 71
|
||||
|
||||
#define OPTION_BITS (sizeof(unsigned int)*8)
|
||||
#define OPTION_SIZE ( (OPT_LAST/OPTION_BITS)+((OPT_LAST%OPTION_BITS)!=0) )
|
||||
@@ -277,11 +287,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 +329,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 +437,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 +507,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,23 +533,24 @@ 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 */
|
||||
#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_USE_RESOLV 1024 /* forward this domain in the normal way */
|
||||
#define SERV_NO_REBIND 2048 /* inhibit dns-rebind protection */
|
||||
#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 */
|
||||
#define SERV_GOT_TCP 32768 /* Got some data from the TCP connection */
|
||||
/* The actual values here matter, since we sort on them to get records in the order
|
||||
IPv6 addr, IPv4 addr, all zero return, resolvconf servers, upstream server, no-data return */
|
||||
#define SERV_LITERAL_ADDRESS 1 /* addr is the answer, or NoDATA is the answer, depending on the next four flags */
|
||||
#define SERV_USE_RESOLV 2 /* forward this domain in the normal way */
|
||||
#define SERV_ALL_ZEROS 4 /* return all zeros for A and AAAA */
|
||||
#define SERV_4ADDR 8 /* addr is IPv4 */
|
||||
#define SERV_6ADDR 16 /* addr is IPv6 */
|
||||
#define SERV_HAS_SOURCE 32 /* source address defined */
|
||||
#define SERV_FOR_NODOTS 64 /* server for names with no domain part only */
|
||||
#define SERV_WARNED_RECURSIVE 128 /* avoid warning spam */
|
||||
#define SERV_FROM_DBUS 256 /* 1 if source is DBus */
|
||||
#define SERV_MARK 512 /* for mark-and-delete and log code */
|
||||
#define SERV_WILDCARD 1024 /* domain has leading '*' */
|
||||
#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 */
|
||||
#define SERV_GOT_TCP 32768 /* Got some data from the TCP connection */
|
||||
|
||||
struct serverfd {
|
||||
int fd;
|
||||
@@ -539,22 +561,61 @@ 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 rebind_domain {
|
||||
char *domain;
|
||||
struct rebind_domain *next;
|
||||
};
|
||||
|
||||
struct ipsets {
|
||||
@@ -563,6 +624,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 */
|
||||
@@ -623,30 +690,45 @@ struct hostsfile {
|
||||
};
|
||||
|
||||
/* packet-dump flags */
|
||||
#define DUMP_QUERY 0x0001
|
||||
#define DUMP_REPLY 0x0002
|
||||
#define DUMP_UP_QUERY 0x0004
|
||||
#define DUMP_UP_REPLY 0x0008
|
||||
#define DUMP_SEC_QUERY 0x0010
|
||||
#define DUMP_SEC_REPLY 0x0020
|
||||
#define DUMP_BOGUS 0x0040
|
||||
#define DUMP_SEC_BOGUS 0x0080
|
||||
|
||||
#define DUMP_QUERY 0x0001
|
||||
#define DUMP_REPLY 0x0002
|
||||
#define DUMP_UP_QUERY 0x0004
|
||||
#define DUMP_UP_REPLY 0x0008
|
||||
#define DUMP_SEC_QUERY 0x0010
|
||||
#define DUMP_SEC_REPLY 0x0020
|
||||
#define DUMP_BOGUS 0x0040
|
||||
#define DUMP_SEC_BOGUS 0x0080
|
||||
#define DUMP_DHCP 0x1000
|
||||
#define DUMP_DHCPV6 0x2000
|
||||
#define DUMP_RA 0x4000
|
||||
#define DUMP_TFTP 0x8000
|
||||
|
||||
/* 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
|
||||
#define FREC_HAS_SUBNET 4
|
||||
#define FREC_NO_CACHE 4
|
||||
#define FREC_DNSKEY_QUERY 8
|
||||
#define FREC_DS_QUERY 16
|
||||
#define FREC_AD_QUESTION 32
|
||||
@@ -655,7 +737,6 @@ struct hostsfile {
|
||||
#define FREC_TEST_PKTSZ 256
|
||||
#define FREC_HAS_EXTRADATA 512
|
||||
#define FREC_HAS_PHEADER 1024
|
||||
#define FREC_NO_CACHE 2048
|
||||
|
||||
#define HASH_SIZE 32 /* SHA-256 digest size */
|
||||
|
||||
@@ -664,14 +745,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 +760,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;
|
||||
@@ -701,6 +783,7 @@ struct frec {
|
||||
#define ACTION_TFTP 5
|
||||
#define ACTION_ARP 6
|
||||
#define ACTION_ARP_DEL 7
|
||||
#define ACTION_RELAY_SNOOP 8
|
||||
|
||||
#define LEASE_NEW 1 /* newly created */
|
||||
#define LEASE_CHANGED 2 /* modified */
|
||||
@@ -891,10 +974,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;
|
||||
};
|
||||
|
||||
@@ -999,7 +1082,14 @@ struct dhcp_relay {
|
||||
union all_addr local, server;
|
||||
char *interface; /* Allowable interface for replies from server, and dest for IPv6 multicast */
|
||||
int iface_index; /* working - interface in which requests arrived, for return */
|
||||
struct dhcp_relay *current, *next;
|
||||
#ifdef HAVE_SCRIPT
|
||||
struct snoop_record {
|
||||
struct in6_addr client, prefix;
|
||||
int prefix_len;
|
||||
struct snoop_record *next;
|
||||
} *snoop_records;
|
||||
#endif
|
||||
struct dhcp_relay *next;
|
||||
};
|
||||
|
||||
extern struct daemon {
|
||||
@@ -1035,8 +1125,13 @@ 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 ipsets *ipsets;
|
||||
struct server *servers, *servers_tail, *local_domains, **serverarray;
|
||||
struct rebind_domain *no_rebind;
|
||||
int server_has_wildcard;
|
||||
int serverarraysz, serverarrayhwm;
|
||||
struct ipsets *ipsets, *nftsets;
|
||||
u32 allowlist_mask;
|
||||
struct allowlist *allowlists;
|
||||
int log_fac; /* log facility */
|
||||
char *log_file; /* optional log file */
|
||||
int max_logs; /* queue limit */
|
||||
@@ -1044,6 +1139,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;
|
||||
@@ -1090,9 +1188,12 @@ extern struct daemon {
|
||||
char *packet; /* packet buffer */
|
||||
int packet_buff_sz; /* size of above */
|
||||
char *namebuff; /* MAXDNAME size buffer */
|
||||
#if (defined(HAVE_CONNTRACK) && defined(HAVE_UBUS)) || defined(HAVE_DNSSEC)
|
||||
/* CONNTRACK UBUS code uses this buffer, as well as DNSSEC code. */
|
||||
char *workspacename;
|
||||
#endif
|
||||
#ifdef HAVE_DNSSEC
|
||||
char *keyname; /* MAXDNAME size buffer */
|
||||
char *workspacename; /* ditto */
|
||||
unsigned long *rr_status; /* ceiling in TTL from DNSSEC or zero for insecure */
|
||||
int rr_status_sz;
|
||||
int dnssec_no_time_check;
|
||||
@@ -1104,16 +1205,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 */
|
||||
@@ -1140,13 +1240,18 @@ extern struct daemon {
|
||||
unsigned char *duid;
|
||||
struct iovec outpacket;
|
||||
int dhcp6fd, icmp6fd;
|
||||
# ifdef HAVE_SCRIPT
|
||||
struct snoop_record *free_snoops;
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* DBus stuff */
|
||||
/* void * here to avoid depending on dbus headers outside dbus.c */
|
||||
void *dbus;
|
||||
#ifdef HAVE_DBUS
|
||||
struct watch *watches;
|
||||
#endif
|
||||
|
||||
/* UBus stuff */
|
||||
#ifdef HAVE_UBUS
|
||||
/* void * here to avoid depending on ubus headers outside ubus.c */
|
||||
@@ -1169,9 +1274,8 @@ extern struct daemon {
|
||||
/* cache.c */
|
||||
void cache_init(void);
|
||||
void next_uid(struct crec *crecp);
|
||||
void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg);
|
||||
void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg, unsigned short type);
|
||||
char *record_source(unsigned int index);
|
||||
char *querystr(char *desc, unsigned short type);
|
||||
int cache_find_non_terminal(char *name, time_t now);
|
||||
struct crec *cache_find_by_addr(struct crec *crecp,
|
||||
union all_addr *addr, time_t now,
|
||||
@@ -1220,18 +1324,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);
|
||||
time_t now, struct ipsets *ipsets, struct ipsets *nftsets, 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 +1355,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 +1364,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 +1393,14 @@ 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_order(const char *a, const char *b);
|
||||
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 +1442,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 +1596,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 +1612,19 @@ void ipset_init(void);
|
||||
int add_to_ipset(const char *setname, const union all_addr *ipaddr, int flags, int remove);
|
||||
#endif
|
||||
|
||||
/* nftset.c */
|
||||
#ifdef HAVE_NFTSET
|
||||
void nftset_init(void);
|
||||
int add_to_nftset(const char *setpath, 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);
|
||||
@@ -1518,6 +1637,9 @@ void queue_tftp(off_t file_len, char *filename, union mysockaddr *peer);
|
||||
void queue_arp(int action, unsigned char *mac, int maclen,
|
||||
int family, union all_addr *addr);
|
||||
int helper_buf_empty(void);
|
||||
#ifdef HAVE_DHCP6
|
||||
void queue_relay_snoop(struct in6_addr *client, int if_index, struct in6_addr *prefix, int prefix_len);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* tftp.c */
|
||||
@@ -1560,10 +1682,13 @@ void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac,
|
||||
unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name,
|
||||
struct in6_addr *fallback, struct in6_addr *ll_addr, struct in6_addr *ula_addr,
|
||||
size_t sz, struct in6_addr *client_addr, time_t now);
|
||||
void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, struct in6_addr *peer_address,
|
||||
int relay_upstream6(int iface_index, ssize_t sz, struct in6_addr *peer_address,
|
||||
u32 scope_id, time_t now);
|
||||
|
||||
unsigned short relay_reply6( struct sockaddr_in6 *peer, ssize_t sz, char *arrival_interface);
|
||||
int relay_reply6( struct sockaddr_in6 *peer, ssize_t sz, char *arrival_interface);
|
||||
# ifdef HAVE_SCRIPT
|
||||
int do_snoop_script_run(void);
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* dhcp-common.c */
|
||||
@@ -1590,7 +1715,7 @@ struct dhcp_config *find_config(struct dhcp_config *configs,
|
||||
int config_has_mac(struct dhcp_config *config, unsigned char *hwaddr, int len, int type);
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
char *whichdevice(void);
|
||||
void bindtodevice(char *device, int fd);
|
||||
int bind_dhcp_devices(char *bound_device);
|
||||
#endif
|
||||
# ifdef HAVE_DHCP6
|
||||
void display_opts6(void);
|
||||
@@ -1651,7 +1776,11 @@ int do_poll(int timeout);
|
||||
size_t rrfilter(struct dns_header *header, size_t plen, int mode);
|
||||
u16 *rrfilter_desc(int type);
|
||||
int expand_workspace(unsigned char ***wkspc, int *szp, int new);
|
||||
|
||||
/* modes. */
|
||||
#define RRFILTER_EDNS0 0
|
||||
#define RRFILTER_DNSSEC 1
|
||||
#define RRFILTER_A 2
|
||||
#define RRFILTER_AAAA 3
|
||||
/* edns0.c */
|
||||
unsigned char *find_pseudoheader(struct dns_header *header, size_t plen,
|
||||
size_t *len, unsigned char **p, int *is_sign, int *is_last);
|
||||
@@ -1659,7 +1788,7 @@ size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *l
|
||||
unsigned short udp_sz, int optno, unsigned char *opt, size_t optlen, int set_do, int replace);
|
||||
size_t add_do_bit(struct dns_header *header, size_t plen, unsigned char *limit);
|
||||
size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *limit,
|
||||
union mysockaddr *source, time_t now, int *check_subnet, int *cacheable);
|
||||
union mysockaddr *source, time_t now, int *cacheable);
|
||||
int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer);
|
||||
|
||||
/* arp.c */
|
||||
@@ -1669,5 +1798,26 @@ int do_arp_script_run(void);
|
||||
/* dump.c */
|
||||
#ifdef HAVE_DUMPFILE
|
||||
void dump_init(void);
|
||||
void dump_packet(int mask, void *packet, size_t len, union mysockaddr *src, union mysockaddr *dst);
|
||||
void dump_packet(int mask, void *packet, size_t len, union mysockaddr *src,
|
||||
union mysockaddr *dst, int port);
|
||||
#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);
|
||||
|
||||
314
src/dnssec.c
314
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;
|
||||
@@ -683,7 +724,8 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
|
||||
|
||||
/* namebuff used for workspace above, restore to leave unchanged on exit */
|
||||
p = (unsigned char*)(rrset[0]);
|
||||
extract_name(header, plen, &p, name, 1, 0);
|
||||
if (!extract_name(header, plen, &p, name, 1, 0))
|
||||
return STAT_BOGUS;
|
||||
|
||||
if (key)
|
||||
{
|
||||
@@ -703,7 +745,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 +767,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 +812,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 +843,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 +874,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -889,9 +955,9 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
|
||||
a.log.keytag = keytag;
|
||||
a.log.algo = algo;
|
||||
if (algo_digest_name(algo))
|
||||
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %hu, algo %hu");
|
||||
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %hu, algo %hu", 0);
|
||||
else
|
||||
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %hu, algo %hu (not supported)");
|
||||
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %hu, algo %hu (not supported)", 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -908,8 +974,8 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
|
||||
return STAT_OK;
|
||||
}
|
||||
|
||||
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DNSKEY");
|
||||
return STAT_BOGUS;
|
||||
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DNSKEY", 0);
|
||||
return STAT_BOGUS | failflags;
|
||||
}
|
||||
|
||||
/* The DNS packet is expected to contain the answer to a DS query
|
||||
@@ -944,26 +1010,29 @@ 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", 0);
|
||||
return STAT_BOGUS | DNSSEC_FAIL_INDET;
|
||||
}
|
||||
|
||||
p = (unsigned char *)(header+1);
|
||||
extract_name(header, plen, &p, name, 1, 4);
|
||||
if (!extract_name(header, plen, &p, name, 1, 4))
|
||||
return STAT_BOGUS;
|
||||
|
||||
p += 4; /* qtype, qclass */
|
||||
|
||||
/* 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");
|
||||
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS", 0);
|
||||
return STAT_BOGUS;
|
||||
}
|
||||
|
||||
if (rc != STAT_SECURE)
|
||||
if (!STAT_ISEQUAL(rc, STAT_SECURE))
|
||||
return rc;
|
||||
|
||||
if (!neganswer)
|
||||
@@ -1015,9 +1084,9 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
|
||||
a.log.algo = algo;
|
||||
a.log.digest = digest;
|
||||
if (ds_digest_name(digest) && algo_digest_name(algo))
|
||||
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %hu, algo %hu, digest %hu");
|
||||
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %hu, algo %hu, digest %hu", 0);
|
||||
else
|
||||
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %hu, algo %hu, digest %hu (not supported)");
|
||||
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %hu, algo %hu, digest %hu (not supported)", 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1050,7 +1119,7 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
|
||||
|
||||
cache_end_insert();
|
||||
|
||||
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, nons ? "no DS/cut" : "no DS");
|
||||
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, nons ? "no DS/cut" : "no DS", 0);
|
||||
}
|
||||
|
||||
return STAT_OK;
|
||||
@@ -1429,7 +1498,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))))
|
||||
@@ -1798,7 +1867,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
||||
int type1, class1, rdlen1 = 0, type2, class2, rdlen2, qclass, qtype, targetidx;
|
||||
int i, j, rc = STAT_INSECURE;
|
||||
int secure = STAT_SECURE;
|
||||
|
||||
|
||||
/* extend rr_status if necessary */
|
||||
if (daemon->rr_status_sz < ntohs(header->ancount) + ntohs(header->nscount))
|
||||
{
|
||||
@@ -1827,7 +1896,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
||||
|
||||
/* Find all the targets we're looking for answers to.
|
||||
The zeroth array element is for the query, subsequent ones
|
||||
for CNAME targets, unless the query is for a CNAME. */
|
||||
for CNAME targets, unless the query is for a CNAME or ANY. */
|
||||
|
||||
if (!expand_workspace(&targets, &target_sz, 0))
|
||||
return STAT_BOGUS;
|
||||
@@ -1846,7 +1915,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
||||
if (qtype == T_RRSIG)
|
||||
return STAT_INSECURE;
|
||||
|
||||
if (qtype != T_CNAME)
|
||||
if (qtype != T_CNAME && qtype != T_ANY)
|
||||
for (j = ntohs(header->ancount); j != 0; j--)
|
||||
{
|
||||
if (!(p1 = skip_name(p1, header, plen, 10)))
|
||||
@@ -1920,7 +1989,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
||||
{
|
||||
/* NSEC and NSEC3 records must be signed. We make this assumption elsewhere. */
|
||||
if (type1 == T_NSEC || type1 == T_NSEC3)
|
||||
rc = STAT_INSECURE;
|
||||
return STAT_BOGUS | DNSSEC_FAIL_NOSIG;
|
||||
else if (nons && i >= ntohs(header->ancount))
|
||||
/* If we're validating a DS reply, rather than looking for the value of AD bit,
|
||||
we only care that NSEC and NSEC3 RRs in the auth section are signed.
|
||||
@@ -1932,15 +2001,16 @@ 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 +2021,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 +2029,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,50 +2068,49 @@ 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)
|
||||
for (j = 0; j <targetidx; j++)
|
||||
if ((p2 = targets[j]))
|
||||
{
|
||||
if (neganswer)
|
||||
*neganswer = 1;
|
||||
|
||||
if (!extract_name(header, plen, &p2, name, 1, 10))
|
||||
return STAT_BOGUS; /* bad packet */
|
||||
|
||||
/* NXDOMAIN or NODATA reply, unanswered question is (name, qclass, qtype) */
|
||||
|
||||
/* For anything other than a DS record, this situation is OK if either
|
||||
the answer is in an unsigned zone, or there's a NSEC records. */
|
||||
if (!prove_non_existence(header, plen, keyname, name, qtype, qclass, NULL, nons, nsec_ttl))
|
||||
{
|
||||
/* Empty DS without NSECS */
|
||||
if (qtype == T_DS)
|
||||
return STAT_BOGUS;
|
||||
|
||||
if ((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 */
|
||||
}
|
||||
}
|
||||
for (j = 0; j <targetidx; j++)
|
||||
if ((p2 = targets[j]))
|
||||
{
|
||||
if (neganswer)
|
||||
*neganswer = 1;
|
||||
|
||||
if (!extract_name(header, plen, &p2, name, 1, 10))
|
||||
return STAT_BOGUS; /* bad packet */
|
||||
|
||||
/* NXDOMAIN or NODATA reply, unanswered question is (name, qclass, qtype) */
|
||||
|
||||
/* For anything other than a DS record, this situation is OK if either
|
||||
the answer is in an unsigned zone, or there's a NSEC records. */
|
||||
if (!prove_non_existence(header, plen, keyname, name, qtype, qclass, NULL, nons, nsec_ttl))
|
||||
{
|
||||
/* Empty DS without NSECS */
|
||||
if (qtype == T_DS)
|
||||
return STAT_BOGUS | DNSSEC_FAIL_NONSEC;
|
||||
|
||||
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 | DNSSEC_FAIL_NONSEC; /* signed zone, no NSECs */
|
||||
}
|
||||
}
|
||||
|
||||
return secure;
|
||||
}
|
||||
@@ -2103,4 +2172,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 */
|
||||
|
||||
726
src/domain-match.c
Normal file
726
src/domain-match.c
Normal file
@@ -0,0 +1,726 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2022 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 && 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 */
|
||||
{
|
||||
/* We've matched a setting which says to use servers without a domain.
|
||||
Continue the search with empty query */
|
||||
if (daemon->serverarray[nlow]->flags & SERV_USE_RESOLV)
|
||||
crop_query = qlen;
|
||||
else
|
||||
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, resolvconf servers, 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;
|
||||
|
||||
/* Short to resolv.conf servers */
|
||||
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_USE_RESOLV); i++);
|
||||
|
||||
if (i != nlow)
|
||||
nhigh = i;
|
||||
else
|
||||
{
|
||||
/* 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, anscount = 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, 0);
|
||||
|
||||
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;
|
||||
|
||||
if (add_resource_record(header, limit, &trunc, sizeof(struct dns_header), &p, daemon->local_ttl, NULL, T_A, C_IN, "4", &addr))
|
||||
anscount++;
|
||||
log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV6, name, (union all_addr *)&addr, NULL, 0);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (add_resource_record(header, limit, &trunc, sizeof(struct dns_header), &p, daemon->local_ttl, NULL, T_AAAA, C_IN, "6", &addr))
|
||||
anscount++;
|
||||
log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV4, name, (union all_addr *)&addr, NULL, 0);
|
||||
}
|
||||
|
||||
if (trunc)
|
||||
header->hb3 |= HB3_TC;
|
||||
header->ancount = htons(anscount);
|
||||
|
||||
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 hostname_order(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,
|
||||
unqualified servers, upstream server, NXDOMAIN literal. */
|
||||
if (rc == 0)
|
||||
rc = ((s2->flags & (SERV_LITERAL_ADDRESS | SERV_4ADDR | SERV_6ADDR | SERV_USE_RESOLV | SERV_ALL_ZEROS)) ^ SERV_LITERAL_ADDRESS) -
|
||||
((s1->flags & (SERV_LITERAL_ADDRESS | SERV_4ADDR | SERV_6ADDR | SERV_USE_RESOLV | 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;
|
||||
}
|
||||
|
||||
/* Must be called before add_update_server() to set daemon->servers_tail */
|
||||
void mark_servers(int flag)
|
||||
{
|
||||
struct server *serv, **up;
|
||||
|
||||
daemon->servers_tail = NULL;
|
||||
|
||||
/* 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;
|
||||
|
||||
daemon->servers_tail = serv;
|
||||
}
|
||||
|
||||
/* --address etc is different: since they are expected to be
|
||||
1) numerous and 2) not reloaded often. We just delete
|
||||
and recreate. */
|
||||
if (flag)
|
||||
for (serv = daemon->local_domains, up = &daemon->local_domains; serv; serv = serv->next)
|
||||
{
|
||||
if (serv->flags & flag)
|
||||
{
|
||||
*up = serv->next;
|
||||
free(serv->domain);
|
||||
free(serv);
|
||||
}
|
||||
else
|
||||
up = &serv->next;
|
||||
}
|
||||
}
|
||||
|
||||
void cleanup_servers(void)
|
||||
{
|
||||
struct server *serv, *tmp, **up;
|
||||
|
||||
/* unlink and free anything still marked. */
|
||||
for (serv = daemon->servers, up = &daemon->servers, daemon->servers_tail = NULL; 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;
|
||||
daemon->servers_tail = serv;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
alloc_domain = canonicalise((char *)domain, NULL);
|
||||
|
||||
if (!alloc_domain)
|
||||
return 0;
|
||||
|
||||
if (flags & SERV_IS_LOCAL)
|
||||
{
|
||||
size_t size;
|
||||
|
||||
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);
|
||||
|
||||
if (!(serv = whine_malloc(size)))
|
||||
{
|
||||
free(alloc_domain);
|
||||
return 0;
|
||||
}
|
||||
|
||||
serv->next = daemon->local_domains;
|
||||
daemon->local_domains = serv;
|
||||
|
||||
if (flags & SERV_4ADDR)
|
||||
((struct serv_addr4*)serv)->addr = local_addr->addr4;
|
||||
|
||||
if (flags & SERV_6ADDR)
|
||||
((struct serv_addr6*)serv)->addr = local_addr->addr6;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Upstream servers. See if there is a suitable candidate, if so unmark
|
||||
and move to the end of the list, for order. The entry found may already
|
||||
be at the end. */
|
||||
struct server **up, *tmp;
|
||||
|
||||
for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp)
|
||||
{
|
||||
tmp = serv->next;
|
||||
if ((serv->flags & SERV_MARK) &&
|
||||
hostname_isequal(alloc_domain, serv->domain))
|
||||
{
|
||||
/* Need to move down? */
|
||||
if (serv->next)
|
||||
{
|
||||
*up = serv->next;
|
||||
daemon->servers_tail->next = serv;
|
||||
daemon->servers_tail = serv;
|
||||
serv->next = NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (serv)
|
||||
{
|
||||
free(alloc_domain);
|
||||
alloc_domain = serv->domain;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(serv = whine_malloc(sizeof(struct server))))
|
||||
{
|
||||
free(alloc_domain);
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset(serv, 0, sizeof(struct server));
|
||||
|
||||
/* Add to the end of the chain, for order */
|
||||
if (daemon->servers_tail)
|
||||
daemon->servers_tail->next = serv;
|
||||
else
|
||||
daemon->servers = serv;
|
||||
daemon->servers_tail = serv;
|
||||
}
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
serv->flags = flags;
|
||||
serv->domain = alloc_domain;
|
||||
serv->domain_len = strlen(alloc_domain);
|
||||
|
||||
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-2022 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;
|
||||
|
||||
97
src/dump.c
97
src/dump.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2022 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,6 +18,8 @@
|
||||
|
||||
#ifdef HAVE_DUMPFILE
|
||||
|
||||
#include <netinet/icmp6.h>
|
||||
|
||||
static u32 packet_count;
|
||||
|
||||
/* https://wiki.wireshark.org/Development/LibpcapFileFormat */
|
||||
@@ -79,7 +81,9 @@ void dump_init(void)
|
||||
}
|
||||
}
|
||||
|
||||
void dump_packet(int mask, void *packet, size_t len, union mysockaddr *src, union mysockaddr *dst)
|
||||
/* port == -1 ->ICMPv6 */
|
||||
void dump_packet(int mask, void *packet, size_t len,
|
||||
union mysockaddr *src, union mysockaddr *dst, int port)
|
||||
{
|
||||
struct ip ip;
|
||||
struct ip6_hdr ip6;
|
||||
@@ -101,7 +105,7 @@ void dump_packet(int mask, void *packet, size_t len, union mysockaddr *src, unio
|
||||
return;
|
||||
|
||||
/* So wireshark can Id the packet. */
|
||||
udp.uh_sport = udp.uh_dport = htons(NAMESERVER_PORT);
|
||||
udp.uh_sport = udp.uh_dport = htons(port);
|
||||
|
||||
if (src)
|
||||
family = src->sa.sa_family;
|
||||
@@ -115,10 +119,19 @@ void dump_packet(int mask, void *packet, size_t len, union mysockaddr *src, unio
|
||||
memset(&ip6, 0, sizeof(ip6));
|
||||
|
||||
ip6.ip6_vfc = 6 << 4;
|
||||
ip6.ip6_plen = htons(sizeof(struct udphdr) + len);
|
||||
ip6.ip6_nxt = IPPROTO_UDP;
|
||||
ip6.ip6_hops = 64;
|
||||
|
||||
if (port == -1)
|
||||
{
|
||||
ip6.ip6_plen = htons(len);
|
||||
ip6.ip6_nxt = IPPROTO_ICMPV6;
|
||||
}
|
||||
else
|
||||
{
|
||||
ip6.ip6_plen = htons(sizeof(struct udphdr) + len);
|
||||
ip6.ip6_nxt = IPPROTO_UDP;
|
||||
}
|
||||
|
||||
if (src)
|
||||
{
|
||||
memcpy(&ip6.ip6_src, &src->in6.sin6_addr, IN6ADDRSZ);
|
||||
@@ -134,9 +147,8 @@ void dump_packet(int mask, void *packet, size_t len, union mysockaddr *src, unio
|
||||
/* start UDP checksum */
|
||||
for (sum = 0, i = 0; i < IN6ADDRSZ; i+=2)
|
||||
{
|
||||
sum += ip6.ip6_src.s6_addr[i] + (ip6.ip6_src.s6_addr[i+1] << 8) ;
|
||||
sum += ip6.ip6_dst.s6_addr[i] + (ip6.ip6_dst.s6_addr[i+1] << 8) ;
|
||||
|
||||
sum += ntohs((ip6.ip6_src.s6_addr[i] << 8) + (ip6.ip6_src.s6_addr[i+1])) ;
|
||||
sum += ntohs((ip6.ip6_dst.s6_addr[i] << 8) + (ip6.ip6_dst.s6_addr[i+1])) ;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -147,9 +159,18 @@ void dump_packet(int mask, void *packet, size_t len, union mysockaddr *src, unio
|
||||
|
||||
ip.ip_v = IPVERSION;
|
||||
ip.ip_hl = sizeof(struct ip) / 4;
|
||||
ip.ip_len = htons(sizeof(struct ip) + sizeof(struct udphdr) + len);
|
||||
ip.ip_ttl = IPDEFTTL;
|
||||
ip.ip_p = IPPROTO_UDP;
|
||||
|
||||
if (port == -1)
|
||||
{
|
||||
ip.ip_len = htons(sizeof(struct ip) + len);
|
||||
ip.ip_p = IPPROTO_ICMP;
|
||||
}
|
||||
else
|
||||
{
|
||||
ip.ip_len = htons(sizeof(struct ip) + sizeof(struct udphdr) + len);
|
||||
ip.ip_p = IPPROTO_UDP;
|
||||
}
|
||||
|
||||
if (src)
|
||||
{
|
||||
@@ -180,31 +201,59 @@ void dump_packet(int mask, void *packet, size_t len, union mysockaddr *src, unio
|
||||
if (len & 1)
|
||||
((unsigned char *)packet)[len] = 0; /* for checksum, in case length is odd. */
|
||||
|
||||
udp.uh_sum = 0;
|
||||
udp.uh_ulen = htons(sizeof(struct udphdr) + len);
|
||||
sum += htons(IPPROTO_UDP);
|
||||
sum += htons(sizeof(struct udphdr) + len);
|
||||
for (i = 0; i < sizeof(struct udphdr)/2; i++)
|
||||
sum += ((u16 *)&udp)[i];
|
||||
for (i = 0; i < (len + 1) / 2; i++)
|
||||
sum += ((u16 *)packet)[i];
|
||||
while (sum >> 16)
|
||||
sum = (sum & 0xffff) + (sum >> 16);
|
||||
udp.uh_sum = (sum == 0xffff) ? sum : ~sum;
|
||||
if (port == -1)
|
||||
{
|
||||
/* ICMP - ICMPv6 packet is a superset of ICMP */
|
||||
struct icmp6_hdr *icmp = packet;
|
||||
|
||||
/* See comment in UDP code below. */
|
||||
sum += htons((family == AF_INET6) ? IPPROTO_ICMPV6 : IPPROTO_ICMP);
|
||||
sum += htons(len);
|
||||
|
||||
icmp->icmp6_cksum = 0;
|
||||
for (i = 0; i < (len + 1) / 2; i++)
|
||||
sum += ((u16 *)packet)[i];
|
||||
while (sum >> 16)
|
||||
sum = (sum & 0xffff) + (sum >> 16);
|
||||
icmp->icmp6_cksum = (sum == 0xffff) ? sum : ~sum;
|
||||
|
||||
pcap_header.incl_len = pcap_header.orig_len = ipsz + len;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Add Remaining part of the pseudoheader. Note that though the
|
||||
IPv6 pseudoheader is very different to the IPv4 one, the
|
||||
net result of this calculation is correct as long as the
|
||||
packet length is less than 65536, which is fine for us. */
|
||||
sum += htons(IPPROTO_UDP);
|
||||
sum += htons(sizeof(struct udphdr) + len);
|
||||
|
||||
udp.uh_sum = 0;
|
||||
udp.uh_ulen = htons(sizeof(struct udphdr) + len);
|
||||
|
||||
for (i = 0; i < sizeof(struct udphdr)/2; i++)
|
||||
sum += ((u16 *)&udp)[i];
|
||||
for (i = 0; i < (len + 1) / 2; i++)
|
||||
sum += ((u16 *)packet)[i];
|
||||
while (sum >> 16)
|
||||
sum = (sum & 0xffff) + (sum >> 16);
|
||||
udp.uh_sum = (sum == 0xffff) ? sum : ~sum;
|
||||
|
||||
pcap_header.incl_len = pcap_header.orig_len = ipsz + sizeof(udp) + len;
|
||||
}
|
||||
|
||||
rc = gettimeofday(&time, NULL);
|
||||
pcap_header.ts_sec = time.tv_sec;
|
||||
pcap_header.ts_usec = time.tv_usec;
|
||||
pcap_header.incl_len = pcap_header.orig_len = ipsz + sizeof(udp) + len;
|
||||
|
||||
if (rc == -1 ||
|
||||
!read_write(daemon->dumpfd, (void *)&pcap_header, sizeof(pcap_header), 0) ||
|
||||
!read_write(daemon->dumpfd, iphdr, ipsz, 0) ||
|
||||
!read_write(daemon->dumpfd, (void *)&udp, sizeof(udp), 0) ||
|
||||
(port != -1 && !read_write(daemon->dumpfd, (void *)&udp, sizeof(udp), 0)) ||
|
||||
!read_write(daemon->dumpfd, (void *)packet, len, 0))
|
||||
my_syslog(LOG_ERR, _("failed to write packet dump"));
|
||||
else
|
||||
my_syslog(LOG_INFO, _("dumping UDP packet %u mask 0x%04x"), ++packet_count, mask);
|
||||
my_syslog(LOG_INFO, _("dumping packet %u mask 0x%04x"), ++packet_count, mask);
|
||||
|
||||
}
|
||||
|
||||
|
||||
165
src/edns0.c
165
src/edns0.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2022 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
|
||||
@@ -178,7 +178,7 @@ size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *l
|
||||
memcpy(buff, datap, rdlen);
|
||||
|
||||
/* now, delete OPT RR */
|
||||
plen = rrfilter(header, plen, 0);
|
||||
plen = rrfilter(header, plen, RRFILTER_EDNS0);
|
||||
|
||||
/* Now, force addition of a new one */
|
||||
p = NULL;
|
||||
@@ -264,44 +264,62 @@ static void encoder(unsigned char *in, char *out)
|
||||
out[3] = char64(in[2]);
|
||||
}
|
||||
|
||||
/* OPT_ADD_MAC = MAC is added (if available)
|
||||
OPT_ADD_MAC + OPT_STRIP_MAC = MAC is replaced, if not available, it is only removed
|
||||
OPT_STRIP_MAC = MAC is removed */
|
||||
static size_t add_dns_client(struct dns_header *header, size_t plen, unsigned char *limit,
|
||||
union mysockaddr *l3, time_t now, int *cacheablep)
|
||||
{
|
||||
int maclen, replace = 2; /* can't get mac address, just delete any incoming. */
|
||||
int replace = 0, maclen = 0;
|
||||
unsigned char mac[DHCP_CHADDR_MAX];
|
||||
char encode[18]; /* handle 6 byte MACs */
|
||||
char encode[18]; /* handle 6 byte MACs ONLY */
|
||||
|
||||
if ((maclen = find_mac(l3, mac, 1, now)) == 6)
|
||||
if ((option_bool(OPT_MAC_B64) || option_bool(OPT_MAC_HEX)) && (maclen = find_mac(l3, mac, 1, now)) == 6)
|
||||
{
|
||||
replace = 1;
|
||||
*cacheablep = 0;
|
||||
|
||||
if (option_bool(OPT_MAC_HEX))
|
||||
print_mac(encode, mac, maclen);
|
||||
else
|
||||
{
|
||||
encoder(mac, encode);
|
||||
encoder(mac+3, encode+4);
|
||||
encode[8] = 0;
|
||||
}
|
||||
if (option_bool(OPT_STRIP_MAC))
|
||||
replace = 1;
|
||||
*cacheablep = 0;
|
||||
|
||||
if (option_bool(OPT_MAC_HEX))
|
||||
print_mac(encode, mac, maclen);
|
||||
else
|
||||
{
|
||||
encoder(mac, encode);
|
||||
encoder(mac+3, encode+4);
|
||||
encode[8] = 0;
|
||||
}
|
||||
}
|
||||
else if (option_bool(OPT_STRIP_MAC))
|
||||
replace = 2;
|
||||
|
||||
return add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMDEVICEID, (unsigned char *)encode, strlen(encode), 0, replace);
|
||||
if (replace != 0 || maclen == 6)
|
||||
plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMDEVICEID, (unsigned char *)encode, strlen(encode), 0, replace);
|
||||
|
||||
return plen;
|
||||
}
|
||||
|
||||
|
||||
/* OPT_ADD_MAC = MAC is added (if available)
|
||||
OPT_ADD_MAC + OPT_STRIP_MAC = MAC is replaced, if not available, it is only removed
|
||||
OPT_STRIP_MAC = MAC is removed */
|
||||
static size_t add_mac(struct dns_header *header, size_t plen, unsigned char *limit,
|
||||
union mysockaddr *l3, time_t now, int *cacheablep)
|
||||
{
|
||||
int maclen;
|
||||
int maclen = 0, replace = 0;
|
||||
unsigned char mac[DHCP_CHADDR_MAX];
|
||||
|
||||
if ((maclen = find_mac(l3, mac, 1, now)) != 0)
|
||||
|
||||
if (option_bool(OPT_ADD_MAC) && (maclen = find_mac(l3, mac, 1, now)) != 0)
|
||||
{
|
||||
*cacheablep = 0;
|
||||
plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_MAC, mac, maclen, 0, 0);
|
||||
if (option_bool(OPT_STRIP_MAC))
|
||||
replace = 1;
|
||||
}
|
||||
else if (option_bool(OPT_STRIP_MAC))
|
||||
replace = 2;
|
||||
|
||||
if (replace != 0 || maclen != 0)
|
||||
plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_MAC, mac, maclen, 0, replace);
|
||||
|
||||
return plen;
|
||||
}
|
||||
|
||||
@@ -378,15 +396,29 @@ static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source,
|
||||
return len + 4;
|
||||
}
|
||||
|
||||
static size_t add_source_addr(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *source, int *cacheable)
|
||||
/* OPT_CLIENT_SUBNET = client subnet is added
|
||||
OPT_CLIENT_SUBNET + OPT_STRIP_ECS = client subnet is replaced
|
||||
OPT_STRIP_ECS = client subnet is removed */
|
||||
static size_t add_source_addr(struct dns_header *header, size_t plen, unsigned char *limit,
|
||||
union mysockaddr *source, int *cacheable)
|
||||
{
|
||||
/* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */
|
||||
|
||||
int len;
|
||||
int replace = 0, len = 0;
|
||||
struct subnet_opt opt;
|
||||
|
||||
len = calc_subnet_opt(&opt, source, cacheable);
|
||||
return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0, 0);
|
||||
if (option_bool(OPT_CLIENT_SUBNET))
|
||||
{
|
||||
if (option_bool(OPT_STRIP_ECS))
|
||||
replace = 1;
|
||||
len = calc_subnet_opt(&opt, source, cacheable);
|
||||
}
|
||||
else if (option_bool(OPT_STRIP_ECS))
|
||||
replace = 2;
|
||||
else
|
||||
return plen;
|
||||
|
||||
return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0, replace);
|
||||
}
|
||||
|
||||
int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer)
|
||||
@@ -427,30 +459,87 @@ 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];
|
||||
int family = source->sa.sa_family;
|
||||
int size = family == AF_INET ? INADDRSZ : IN6ADDRSZ;
|
||||
|
||||
if (daemon->umbrella_org)
|
||||
{
|
||||
PUTSHORT(UMBRELLA_ORG, u);
|
||||
PUTLONG(daemon->umbrella_org, u);
|
||||
}
|
||||
|
||||
PUTSHORT(family == AF_INET ? UMBRELLA_IPV4 : UMBRELLA_IPV6, u);
|
||||
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);
|
||||
}
|
||||
|
||||
return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, EDNS0_OPTION_UMBRELLA, (unsigned char *)&opt, u - (u8 *)&opt, 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. */
|
||||
size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *limit,
|
||||
union mysockaddr *source, time_t now, int *check_subnet, int *cacheable)
|
||||
union mysockaddr *source, time_t now, int *cacheable)
|
||||
{
|
||||
*check_subnet = 0;
|
||||
*cacheable = 1;
|
||||
|
||||
if (option_bool(OPT_ADD_MAC))
|
||||
plen = add_mac(header, plen, limit, source, now, cacheable);
|
||||
|
||||
if (option_bool(OPT_MAC_B64) || option_bool(OPT_MAC_HEX))
|
||||
plen = add_dns_client(header, plen, limit, source, now, cacheable);
|
||||
plen = add_mac(header, plen, limit, source, now, cacheable);
|
||||
plen = add_dns_client(header, plen, limit, source, now, cacheable);
|
||||
|
||||
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))
|
||||
{
|
||||
plen = add_source_addr(header, plen, limit, source, cacheable);
|
||||
*check_subnet = 1;
|
||||
}
|
||||
|
||||
plen = add_source_addr(header, plen, limit, source, cacheable);
|
||||
|
||||
return plen;
|
||||
}
|
||||
|
||||
3046
src/forward.c
3046
src/forward.c
File diff suppressed because it is too large
Load Diff
@@ -28,34 +28,34 @@
|
||||
|
||||
#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;
|
||||
|
||||
if (!extract_name(header, plen, &p, name, 1, 4))
|
||||
break; /* bad packet */
|
||||
return NULL; /* bad packet */
|
||||
|
||||
for (cp = name; (c = *cp); cp++)
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
@@ -67,18 +67,18 @@ unsigned char *hash_questions(struct dns_header *header, size_t plen, char *name
|
||||
|
||||
p += 4;
|
||||
if (!CHECK_LEN(header, p, plen, 0))
|
||||
break; /* bad packet */
|
||||
return NULL; /* bad packet */
|
||||
}
|
||||
|
||||
hash->digest(ctx, hash->digest_size, digest);
|
||||
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
|
||||
typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines
|
||||
#define SHA256_BLOCK_SIZE 32 /* SHA256 outputs a 32 byte digest */
|
||||
typedef unsigned char BYTE; /* 8-bit byte */
|
||||
typedef unsigned int WORD; /* 32-bit word, change to "long" for 16-bit machines */
|
||||
|
||||
typedef struct {
|
||||
BYTE data[64];
|
||||
@@ -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)
|
||||
{
|
||||
@@ -106,7 +109,7 @@ unsigned char *hash_questions(struct dns_header *header, size_t plen, char *name
|
||||
char *cp, c;
|
||||
|
||||
if (!extract_name(header, plen, &p, name, 1, 4))
|
||||
break; /* bad packet */
|
||||
return NULL; /* bad packet */
|
||||
|
||||
for (cp = name; (c = *cp); cp++)
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
@@ -118,7 +121,7 @@ unsigned char *hash_questions(struct dns_header *header, size_t plen, char *name
|
||||
|
||||
p += 4;
|
||||
if (!CHECK_LEN(header, p, plen, 0))
|
||||
break; /* bad packet */
|
||||
return NULL; /* bad packet */
|
||||
}
|
||||
|
||||
sha256_final(&ctx, digest);
|
||||
@@ -235,7 +238,7 @@ static void sha256_final(SHA256_CTX *ctx, BYTE hash[])
|
||||
|
||||
i = ctx->datalen;
|
||||
|
||||
// Pad whatever data is left in the buffer.
|
||||
/* Pad whatever data is left in the buffer. */
|
||||
if (ctx->datalen < 56)
|
||||
{
|
||||
ctx->data[i++] = 0x80;
|
||||
@@ -251,7 +254,7 @@ static void sha256_final(SHA256_CTX *ctx, BYTE hash[])
|
||||
memset(ctx->data, 0, 56);
|
||||
}
|
||||
|
||||
// Append to the padding the total message's length in bits and transform.
|
||||
/* Append to the padding the total message's length in bits and transform. */
|
||||
ctx->bitlen += ctx->datalen * 8;
|
||||
ctx->data[63] = ctx->bitlen;
|
||||
ctx->data[62] = ctx->bitlen >> 8;
|
||||
@@ -263,8 +266,8 @@ static void sha256_final(SHA256_CTX *ctx, BYTE hash[])
|
||||
ctx->data[56] = ctx->bitlen >> 56;
|
||||
sha256_transform(ctx, ctx->data);
|
||||
|
||||
// Since this implementation uses little endian byte ordering and SHA uses big endian,
|
||||
// reverse all the bytes when copying the final state to the output hash.
|
||||
/* Since this implementation uses little endian byte ordering and SHA uses big endian,
|
||||
reverse all the bytes when copying the final state to the output hash. */
|
||||
for (i = 0; i < 4; ++i)
|
||||
{
|
||||
hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff;
|
||||
77
src/helper.c
77
src/helper.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2022 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
|
||||
@@ -233,9 +233,13 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
|
||||
is6 = (data.flags != AF_INET);
|
||||
data.action = ACTION_ARP;
|
||||
}
|
||||
else
|
||||
continue;
|
||||
|
||||
else if (data.action == ACTION_RELAY_SNOOP)
|
||||
{
|
||||
is6 = 1;
|
||||
action_str = "relay-snoop";
|
||||
}
|
||||
else
|
||||
continue;
|
||||
|
||||
/* stringify MAC into dhcp_buff */
|
||||
p = daemon->dhcp_buff;
|
||||
@@ -287,7 +291,7 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
|
||||
char *dot;
|
||||
hostname = (char *)buf;
|
||||
hostname[data.hostname_len - 1] = 0;
|
||||
if (data.action != ACTION_TFTP)
|
||||
if (data.action != ACTION_TFTP && data.action != ACTION_RELAY_SNOOP)
|
||||
{
|
||||
if (!legal_hostname(hostname))
|
||||
hostname = NULL;
|
||||
@@ -333,6 +337,24 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
|
||||
lua_call(lua, 2, 0); /* pass 2 values, expect 0 */
|
||||
}
|
||||
}
|
||||
else if (data.action == ACTION_RELAY_SNOOP)
|
||||
{
|
||||
lua_getglobal(lua, "snoop");
|
||||
if (lua_type(lua, -1) != LUA_TFUNCTION)
|
||||
lua_pop(lua, 1); /* tftp function optional */
|
||||
else
|
||||
{
|
||||
lua_pushstring(lua, action_str); /* arg1 - action */
|
||||
lua_newtable(lua); /* arg2 - data table */
|
||||
lua_pushstring(lua, daemon->addrbuff);
|
||||
lua_setfield(lua, -2, "client_address");
|
||||
lua_pushstring(lua, hostname);
|
||||
lua_setfield(lua, -2, "prefix");
|
||||
lua_pushstring(lua, data.interface);
|
||||
lua_setfield(lua, -2, "client_interface");
|
||||
lua_call(lua, 2, 0); /* pass 2 values, expect 0 */
|
||||
}
|
||||
}
|
||||
else if (data.action == ACTION_ARP)
|
||||
{
|
||||
lua_getglobal(lua, "arp");
|
||||
@@ -433,7 +455,8 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
|
||||
buf = grab_extradata_lua(buf, end, "relay_address");
|
||||
else if (data.giaddr.s_addr != 0)
|
||||
{
|
||||
lua_pushstring(lua, inet_ntoa(data.giaddr));
|
||||
inet_ntop(AF_INET, &data.giaddr, daemon->dhcp_buff2, ADDRSTRLEN);
|
||||
lua_pushstring(lua, daemon->dhcp_buff2);
|
||||
lua_setfield(lua, -2, "relay_address");
|
||||
}
|
||||
|
||||
@@ -553,7 +576,7 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
|
||||
close(pipeout[1]);
|
||||
}
|
||||
|
||||
if (data.action != ACTION_TFTP && data.action != ACTION_ARP)
|
||||
if (data.action != ACTION_TFTP && data.action != ACTION_ARP && data.action != ACTION_RELAY_SNOOP)
|
||||
{
|
||||
#ifdef HAVE_DHCP6
|
||||
my_setenv("DNSMASQ_IAID", is6 ? daemon->dhcp_buff3 : NULL, &err);
|
||||
@@ -611,8 +634,13 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
|
||||
|
||||
if (is6)
|
||||
buf = grab_extradata(buf, end, "DNSMASQ_RELAY_ADDRESS", &err);
|
||||
else
|
||||
my_setenv("DNSMASQ_RELAY_ADDRESS", data.giaddr.s_addr != 0 ? inet_ntoa(data.giaddr) : NULL, &err);
|
||||
else
|
||||
{
|
||||
const char *giaddr = NULL;
|
||||
if (data.giaddr.s_addr != 0)
|
||||
giaddr = inet_ntop(AF_INET, &data.giaddr, daemon->dhcp_buff2, ADDRSTRLEN);
|
||||
my_setenv("DNSMASQ_RELAY_ADDRESS", giaddr, &err);
|
||||
}
|
||||
|
||||
for (i = 0; buf; i++)
|
||||
{
|
||||
@@ -635,6 +663,9 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
|
||||
fcntl(event_fd, F_SETFD, i | FD_CLOEXEC);
|
||||
close(pipefd[0]);
|
||||
|
||||
if (data.action == ACTION_RELAY_SNOOP)
|
||||
strcpy(daemon->packet, data.interface);
|
||||
|
||||
p = strrchr(daemon->lease_change_command, '/');
|
||||
if (err == 0)
|
||||
{
|
||||
@@ -805,6 +836,29 @@ void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t n
|
||||
bytes_in_buf = p - (unsigned char *)buf;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
void queue_relay_snoop(struct in6_addr *client, int if_index, struct in6_addr *prefix, int prefix_len)
|
||||
{
|
||||
/* no script */
|
||||
if (daemon->helperfd == -1)
|
||||
return;
|
||||
|
||||
inet_ntop(AF_INET6, prefix, daemon->addrbuff, ADDRSTRLEN);
|
||||
|
||||
/* 5 for /nnn and zero on the end of the prefix. */
|
||||
buff_alloc(sizeof(struct script_data) + ADDRSTRLEN + 5);
|
||||
memset(buf, 0, sizeof(struct script_data));
|
||||
|
||||
buf->action = ACTION_RELAY_SNOOP;
|
||||
buf->addr6 = *client;
|
||||
buf->hostname_len = sprintf((char *)(buf+1), "%s/%u", daemon->addrbuff, prefix_len) + 1;
|
||||
|
||||
indextoname(daemon->dhcp6fd, if_index, buf->interface);
|
||||
|
||||
bytes_in_buf = sizeof(struct script_data) + buf->hostname_len;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TFTP
|
||||
/* This nastily re-uses DHCP-fields for TFTP stuff */
|
||||
void queue_tftp(off_t file_len, char *filename, union mysockaddr *peer)
|
||||
@@ -882,7 +936,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-2022 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
|
||||
@@ -148,12 +148,19 @@ void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revh
|
||||
if (!(ah->flags & flag))
|
||||
continue;
|
||||
|
||||
if (stat(ah->fname, &buf) == -1 || !(S_ISDIR(buf.st_mode)))
|
||||
if (stat(ah->fname, &buf) == -1)
|
||||
{
|
||||
my_syslog(LOG_ERR, _("bad dynamic directory %s: %s"),
|
||||
ah->fname, strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(S_ISDIR(buf.st_mode)))
|
||||
{
|
||||
my_syslog(LOG_ERR, _("bad dynamic directory %s: %s"),
|
||||
ah->fname, _("not a directory"));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(ah->flags & AH_WD_DONE))
|
||||
{
|
||||
@@ -295,4 +302,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-2022 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-2022 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 */
|
||||
|
||||
35
src/log.c
35
src/log.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2022 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
|
||||
@@ -100,10 +100,23 @@ int log_start(struct passwd *ent_pw, int errfd)
|
||||
/* If we're running as root and going to change uid later,
|
||||
change the ownership here so that the file is always owned by
|
||||
the dnsmasq user. Then logrotate can just copy the owner.
|
||||
Failure of the chown call is OK, (for instance when started as non-root) */
|
||||
if (log_to_file && !log_stderr && ent_pw && ent_pw->pw_uid != 0 &&
|
||||
fchown(log_fd, ent_pw->pw_uid, -1) != 0)
|
||||
ret = errno;
|
||||
Failure of the chown call is OK, (for instance when started as non-root).
|
||||
|
||||
If we've created a file with group-id root, we also make
|
||||
the file group-writable. This gives processes in the root group
|
||||
write access to the file and avoids the problem that on some systems,
|
||||
once the file is owned by the dnsmasq user, it can't be written
|
||||
whilst dnsmasq is running as root during startup.
|
||||
*/
|
||||
if (log_to_file && !log_stderr && ent_pw && ent_pw->pw_uid != 0)
|
||||
{
|
||||
struct stat ls;
|
||||
if (getgid() == 0 && fstat(log_fd, &ls) == 0 && ls.st_gid == 0 &&
|
||||
(ls.st_mode & S_IWGRP) == 0)
|
||||
(void)fchmod(log_fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP);
|
||||
if (fchown(log_fd, ent_pw->pw_uid, -1) != 0)
|
||||
ret = errno;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -118,7 +131,7 @@ int log_reopen(char *log_file)
|
||||
/* NOTE: umask is set to 022 by the time this gets called */
|
||||
|
||||
if (log_file)
|
||||
log_fd = open(log_file, O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR|S_IRGRP);
|
||||
log_fd = open(log_file, O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR|S_IRGRP);
|
||||
else
|
||||
{
|
||||
#if defined(HAVE_SOLARIS_NETWORK) || defined(__ANDROID__)
|
||||
@@ -273,7 +286,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 +303,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-2022 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-2022 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-2022 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
|
||||
|
||||
102
src/netlink.c
102
src/netlink.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2022 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,35 +41,35 @@
|
||||
|
||||
#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;
|
||||
addr.nl_pid = 0; /* autobind */
|
||||
addr.nl_groups = RTMGRP_IPV4_ROUTE;
|
||||
if (option_bool(OPT_CLEVERBIND))
|
||||
addr.nl_groups |= RTMGRP_IPV4_IFADDR;
|
||||
addr.nl_groups |= RTMGRP_IPV4_IFADDR;
|
||||
addr.nl_groups |= RTMGRP_IPV6_ROUTE;
|
||||
if (option_bool(OPT_CLEVERBIND))
|
||||
addr.nl_groups |= RTMGRP_IPV6_IFADDR;
|
||||
addr.nl_groups |= RTMGRP_IPV6_IFADDR;
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
if (daemon->doing_ra || daemon->doing_dhcp6)
|
||||
addr.nl_groups |= RTMGRP_IPV6_IFADDR;
|
||||
#endif
|
||||
|
||||
/* May not be able to have permission to set multicast groups don't die in that case */
|
||||
if ((daemon->netlinkfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) != -1)
|
||||
{
|
||||
@@ -92,14 +92,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 +111,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 +129,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 +148,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 +158,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 +170,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 +192,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 +206,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 +340,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 +369,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 +382,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 */
|
||||
|
||||
721
src/network.c
721
src/network.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2022 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -114,13 +114,8 @@ int iface_check(int family, union all_addr *addr, char *name, int *auth)
|
||||
struct iname *tmp;
|
||||
int ret = 1, match_addr = 0;
|
||||
|
||||
/* Note: have to check all and not bail out early, so that we set the
|
||||
"used" flags.
|
||||
|
||||
May be called with family == AF_LOCALto check interface by name only. */
|
||||
|
||||
if (auth)
|
||||
*auth = 0;
|
||||
/* Note: have to check all and not bail out early, so that we set the "used" flags.
|
||||
May be called with family == AF_LOCAL to check interface by name only. */
|
||||
|
||||
if (daemon->if_names || daemon->if_addrs)
|
||||
{
|
||||
@@ -149,25 +144,29 @@ int iface_check(int family, union all_addr *addr, char *name, int *auth)
|
||||
if (tmp->name && wildcard_match(tmp->name, name))
|
||||
ret = 0;
|
||||
|
||||
|
||||
for (tmp = daemon->authinterface; tmp; tmp = tmp->next)
|
||||
if (tmp->name)
|
||||
{
|
||||
if (strcmp(tmp->name, name) == 0 &&
|
||||
(tmp->addr.sa.sa_family == 0 || tmp->addr.sa.sa_family == family))
|
||||
break;
|
||||
}
|
||||
else if (addr && tmp->addr.sa.sa_family == AF_INET && family == AF_INET &&
|
||||
tmp->addr.in.sin_addr.s_addr == addr->addr4.s_addr)
|
||||
break;
|
||||
else if (addr && tmp->addr.sa.sa_family == AF_INET6 && family == AF_INET6 &&
|
||||
IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, &addr->addr6))
|
||||
break;
|
||||
|
||||
if (tmp && auth)
|
||||
if (auth)
|
||||
{
|
||||
*auth = 1;
|
||||
ret = 1;
|
||||
*auth = 0;
|
||||
|
||||
for (tmp = daemon->authinterface; tmp; tmp = tmp->next)
|
||||
if (tmp->name)
|
||||
{
|
||||
if (strcmp(tmp->name, name) == 0 &&
|
||||
(tmp->addr.sa.sa_family == 0 || tmp->addr.sa.sa_family == family))
|
||||
break;
|
||||
}
|
||||
else if (addr && tmp->addr.sa.sa_family == AF_INET && family == AF_INET &&
|
||||
tmp->addr.in.sin_addr.s_addr == addr->addr4.s_addr)
|
||||
break;
|
||||
else if (addr && tmp->addr.sa.sa_family == AF_INET6 && family == AF_INET6 &&
|
||||
IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, &addr->addr6))
|
||||
break;
|
||||
|
||||
if (tmp)
|
||||
{
|
||||
*auth = 1;
|
||||
ret = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -232,7 +231,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 +252,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 +347,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 +527,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 +666,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 +695,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 +713,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 +784,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 +826,7 @@ int enumerate_interfaces(int reset)
|
||||
|
||||
errno = errsave;
|
||||
spare = param.spare;
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -867,10 +966,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 +1144,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 +1171,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 +1305,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 +1320,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 +1386,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 +1463,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 +1473,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 +1493,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 +1519,121 @@ 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)
|
||||
my_syslog(LOG_INFO, _("more servers are defined but not logged"));
|
||||
|
||||
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)) &&
|
||||
strlen(serv->domain))
|
||||
{
|
||||
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 +1655,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.
|
||||
@@ -1748,7 +1691,7 @@ int reload_servers(char *fname)
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
memset(&source_addr, 0, sizeof(source_addr));
|
||||
|
||||
if ((addr.in.sin_addr.s_addr = inet_addr(token)) != (in_addr_t) -1)
|
||||
if (inet_pton(AF_INET, token, &addr.in.sin_addr) > 0)
|
||||
{
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
source_addr.in.sin_len = addr.in.sin_len = sizeof(source_addr.in);
|
||||
@@ -1786,7 +1729,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;
|
||||
}
|
||||
|
||||
@@ -1799,6 +1742,8 @@ int reload_servers(char *fname)
|
||||
/* Called when addresses are added or deleted from an interface */
|
||||
void newaddress(time_t now)
|
||||
{
|
||||
struct dhcp_relay *relay;
|
||||
|
||||
(void)now;
|
||||
|
||||
if (option_bool(OPT_CLEVERBIND) || option_bool(OPT_LOCAL_SERVICE) ||
|
||||
@@ -1807,6 +1752,12 @@ void newaddress(time_t now)
|
||||
|
||||
if (option_bool(OPT_CLEVERBIND))
|
||||
create_bound_listeners(0);
|
||||
|
||||
#ifdef HAVE_DHCP
|
||||
/* clear cache of subnet->relay index */
|
||||
for (relay = daemon->relay4; relay; relay = relay->next)
|
||||
relay->iface_index = 0;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
if (daemon->doing_dhcp6 || daemon->relay6 || daemon->doing_ra)
|
||||
@@ -1817,10 +1768,8 @@ void newaddress(time_t now)
|
||||
|
||||
if (daemon->doing_dhcp6)
|
||||
lease_find_interfaces(now);
|
||||
|
||||
for (relay = daemon->relay6; relay; relay = relay->next)
|
||||
relay->iface_index = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
94
src/nftset.c
Normal file
94
src/nftset.c
Normal file
@@ -0,0 +1,94 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2022 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"
|
||||
|
||||
#if defined (HAVE_NFTSET) && defined (HAVE_LINUX_NETWORK)
|
||||
|
||||
#include <nftables/libnftables.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
static struct nft_ctx *ctx = NULL;
|
||||
static const char *cmd_add = "add element %s { %s }";
|
||||
static const char *cmd_del = "delete element %s { %s }";
|
||||
|
||||
void nftset_init()
|
||||
{
|
||||
ctx = nft_ctx_new(NFT_CTX_DEFAULT);
|
||||
if (ctx == NULL)
|
||||
die(_("failed to create nftset context"), NULL, EC_MISC);
|
||||
|
||||
/* disable libnftables output */
|
||||
nft_ctx_buffer_error(ctx);
|
||||
}
|
||||
|
||||
int add_to_nftset(const char *setname, const union all_addr *ipaddr, int flags, int remove)
|
||||
{
|
||||
const char *cmd = remove ? cmd_del : cmd_add;
|
||||
int ret, af = (flags & F_IPV4) ? AF_INET : AF_INET6;
|
||||
size_t new_sz;
|
||||
char *new, *err, *nl;
|
||||
static char *cmd_buf = NULL;
|
||||
static size_t cmd_buf_sz = 0;
|
||||
|
||||
inet_ntop(af, ipaddr, daemon->addrbuff, ADDRSTRLEN);
|
||||
|
||||
if (setname[1] == ' ' && (setname[0] == '4' || setname[0] == '6'))
|
||||
{
|
||||
if (setname[0] == '4' && !(flags & F_IPV4))
|
||||
return -1;
|
||||
|
||||
if (setname[0] == '6' && !(flags & F_IPV6))
|
||||
return -1;
|
||||
|
||||
setname += 2;
|
||||
}
|
||||
|
||||
if (cmd_buf_sz == 0)
|
||||
new_sz = 150; /* initial allocation */
|
||||
else
|
||||
new_sz = snprintf(cmd_buf, cmd_buf_sz, cmd, setname, daemon->addrbuff);
|
||||
|
||||
if (new_sz > cmd_buf_sz)
|
||||
{
|
||||
if (!(new = whine_malloc(new_sz + 10)))
|
||||
return 0;
|
||||
|
||||
if (cmd_buf)
|
||||
free(cmd_buf);
|
||||
cmd_buf = new;
|
||||
cmd_buf_sz = new_sz + 10;
|
||||
snprintf(cmd_buf, cmd_buf_sz, cmd, setname, daemon->addrbuff);
|
||||
}
|
||||
|
||||
ret = nft_run_cmd_from_buffer(ctx, cmd_buf);
|
||||
err = (char *)nft_ctx_get_error_buffer(ctx);
|
||||
|
||||
if (ret != 0)
|
||||
{
|
||||
/* Log only first line of error return. */
|
||||
if ((nl = strchr(err, '\n')))
|
||||
*nl = 0;
|
||||
my_syslog(LOG_ERR, "nftset %s %s", setname, err);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
1120
src/option.c
1120
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-2022 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-2022 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_DEBUG, __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define ASSERT(condition) \
|
||||
do { \
|
||||
if (!(condition)) \
|
||||
my_syslog(LOG_ERR, _("[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 *c, *label = NULL;
|
||||
int is_label_numeric = 1;
|
||||
for (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 *c, *label = NULL;
|
||||
int is_label_numeric = 1;
|
||||
size_t num_wildcards = 0;
|
||||
int previous_label_has_wildcard = 1;
|
||||
for (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-2022 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-2022 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
|
||||
|
||||
|
||||
|
||||
|
||||
44
src/radv.c
44
src/radv.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2022 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
|
||||
@@ -123,7 +123,11 @@ void ra_start_unsolicited(time_t now, struct dhcp_context *context)
|
||||
and pick up new interfaces */
|
||||
|
||||
if (context)
|
||||
context->ra_short_period_start = context->ra_time = now;
|
||||
{
|
||||
context->ra_short_period_start = now;
|
||||
/* start after 1 second to get logging right at startup. */
|
||||
context->ra_time = now + 1;
|
||||
}
|
||||
else
|
||||
for (context = daemon->dhcp6; context; context = context->next)
|
||||
if (!(context->flags & CONTEXT_TEMPLATE))
|
||||
@@ -162,7 +166,7 @@ void icmp6_packet(time_t now)
|
||||
return;
|
||||
|
||||
packet = (unsigned char *)daemon->outpacket.iov_base;
|
||||
|
||||
|
||||
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
|
||||
if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
|
||||
{
|
||||
@@ -187,24 +191,36 @@ void icmp6_packet(time_t now)
|
||||
|
||||
if (packet[1] != 0)
|
||||
return;
|
||||
|
||||
|
||||
if (packet[0] == ICMP6_ECHO_REPLY)
|
||||
lease_ping_reply(&from.sin6_addr, packet, interface);
|
||||
else if (packet[0] == ND_ROUTER_SOLICIT)
|
||||
{
|
||||
char *mac = "";
|
||||
struct dhcp_bridge *bridge, *alias;
|
||||
ssize_t rem;
|
||||
unsigned char *p;
|
||||
int opt_sz;
|
||||
|
||||
#ifdef HAVE_DUMPFILE
|
||||
dump_packet(DUMP_RA, (void *)packet, sz, (union mysockaddr *)&from, NULL, -1);
|
||||
#endif
|
||||
|
||||
/* look for link-layer address option for logging */
|
||||
if (sz >= 16 && packet[8] == ICMP6_OPT_SOURCE_MAC && (packet[9] * 8) + 8 <= sz)
|
||||
for (rem = sz - 8, p = &packet[8]; rem >= 2; rem -= opt_sz, p += opt_sz)
|
||||
{
|
||||
if ((packet[9] * 8 - 2) * 3 - 1 >= MAXDNAME) {
|
||||
return;
|
||||
}
|
||||
print_mac(daemon->namebuff, &packet[10], (packet[9] * 8) - 2);
|
||||
mac = daemon->namebuff;
|
||||
opt_sz = p[1] * 8;
|
||||
|
||||
if (opt_sz == 0 || opt_sz > rem)
|
||||
return; /* Bad packet */
|
||||
|
||||
if (p[0] == ICMP6_OPT_SOURCE_MAC && ((opt_sz - 2) * 3 - 1 < MAXDNAME))
|
||||
{
|
||||
print_mac(daemon->namebuff, &p[2], opt_sz - 2);
|
||||
mac = daemon->namebuff;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!option_bool(OPT_QUIET_RA))
|
||||
my_syslog(MS_DHCP | LOG_INFO, "RTR-SOLICIT(%s) %s", interface, mac);
|
||||
|
||||
@@ -543,6 +559,10 @@ static void send_ra_alias(time_t now, int iface, char *iface_name, struct in6_ad
|
||||
setsockopt(daemon->icmp6fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &send_iface, sizeof(send_iface));
|
||||
}
|
||||
|
||||
#ifdef HAVE_DUMPFILE
|
||||
dump_packet(DUMP_RA, (void *)daemon->outpacket.iov_base, save_counter(-1), NULL, (union mysockaddr *)&addr, -1);
|
||||
#endif
|
||||
|
||||
while (retry_send(sendto(daemon->icmp6fd, daemon->outpacket.iov_base,
|
||||
save_counter(-1), 0, (struct sockaddr *)&addr,
|
||||
sizeof(addr))));
|
||||
@@ -746,6 +766,8 @@ static int add_lla(int index, unsigned int type, char *mac, size_t maclen, void
|
||||
add 7 to round up */
|
||||
int len = (maclen + 9) >> 3;
|
||||
unsigned char *p = expand(len << 3);
|
||||
if (!p)
|
||||
return 1;
|
||||
memset(p, 0, len << 3);
|
||||
*p++ = ICMP6_OPT_SOURCE_MAC;
|
||||
*p++ = len;
|
||||
|
||||
1088
src/rfc1035.c
1088
src/rfc1035.c
File diff suppressed because it is too large
Load Diff
112
src/rfc2131.c
112
src/rfc2131.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2022 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
|
||||
@@ -372,9 +372,22 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
|
||||
if (!context)
|
||||
{
|
||||
my_syslog(MS_DHCP | LOG_WARNING, _("no address range available for DHCP request %s %s"),
|
||||
subnet_addr.s_addr ? _("with subnet selector") : _("via"),
|
||||
subnet_addr.s_addr ? inet_ntoa(subnet_addr) : (mess->giaddr.s_addr ? inet_ntoa(mess->giaddr) : iface_name));
|
||||
const char *via;
|
||||
if (subnet_addr.s_addr)
|
||||
{
|
||||
via = _("with subnet selector");
|
||||
inet_ntop(AF_INET, &subnet_addr, daemon->addrbuff, ADDRSTRLEN);
|
||||
}
|
||||
else
|
||||
{
|
||||
via = _("via");
|
||||
if (mess->giaddr.s_addr)
|
||||
inet_ntop(AF_INET, &mess->giaddr, daemon->addrbuff, ADDRSTRLEN);
|
||||
else
|
||||
safe_strncpy(daemon->addrbuff, iface_name, ADDRSTRLEN);
|
||||
}
|
||||
my_syslog(MS_DHCP | LOG_WARNING, _("no address range available for DHCP request %s %s"),
|
||||
via, daemon->addrbuff);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -383,13 +396,19 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
struct dhcp_context *context_tmp;
|
||||
for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)
|
||||
{
|
||||
strcpy(daemon->namebuff, inet_ntoa(context_tmp->start));
|
||||
inet_ntop(AF_INET, &context_tmp->start, daemon->namebuff, MAXDNAME);
|
||||
if (context_tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP subnet: %s/%s"),
|
||||
ntohl(mess->xid), daemon->namebuff, inet_ntoa(context_tmp->netmask));
|
||||
{
|
||||
inet_ntop(AF_INET, &context_tmp->netmask, daemon->addrbuff, ADDRSTRLEN);
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP subnet: %s/%s"),
|
||||
ntohl(mess->xid), daemon->namebuff, daemon->addrbuff);
|
||||
}
|
||||
else
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP range: %s -- %s"),
|
||||
ntohl(mess->xid), daemon->namebuff, inet_ntoa(context_tmp->end));
|
||||
{
|
||||
inet_ntop(AF_INET, &context_tmp->end, daemon->addrbuff, ADDRSTRLEN);
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP range: %s -- %s"),
|
||||
ntohl(mess->xid), daemon->namebuff, daemon->addrbuff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1031,8 +1050,9 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
config->addr.s_addr == option_addr(opt).s_addr)
|
||||
{
|
||||
prettyprint_time(daemon->dhcp_buff, DECLINE_BACKOFF);
|
||||
inet_ntop(AF_INET, &config->addr, daemon->addrbuff, ADDRSTRLEN);
|
||||
my_syslog(MS_DHCP | LOG_WARNING, _("disabling DHCP static address %s for %s"),
|
||||
inet_ntoa(config->addr), daemon->dhcp_buff);
|
||||
daemon->addrbuff, daemon->dhcp_buff);
|
||||
config->flags |= CONFIG_DECLINED;
|
||||
config->decline_time = now;
|
||||
}
|
||||
@@ -1078,7 +1098,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
|
||||
if (have_config(config, CONFIG_ADDR))
|
||||
{
|
||||
char *addrs = inet_ntoa(config->addr);
|
||||
inet_ntop(AF_INET, &config->addr, daemon->addrbuff, ADDRSTRLEN);
|
||||
|
||||
if ((ltmp = lease_find_by_addr(config->addr)) &&
|
||||
ltmp != lease &&
|
||||
@@ -1088,7 +1108,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
unsigned char *mac = extended_hwaddr(ltmp->hwaddr_type, ltmp->hwaddr_len,
|
||||
ltmp->hwaddr, ltmp->clid_len, ltmp->clid, &len);
|
||||
my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is leased to %s"),
|
||||
addrs, print_mac(daemon->namebuff, mac, len));
|
||||
daemon->addrbuff, print_mac(daemon->namebuff, mac, len));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1097,10 +1117,10 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
if (context->router.s_addr == config->addr.s_addr)
|
||||
break;
|
||||
if (tmp)
|
||||
my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is in use by the server or relay"), addrs);
|
||||
my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is in use by the server or relay"), daemon->addrbuff);
|
||||
else if (have_config(config, CONFIG_DECLINED) &&
|
||||
difftime(now, config->decline_time) < (float)DECLINE_BACKOFF)
|
||||
my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it was previously declined"), addrs);
|
||||
my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it was previously declined"), daemon->addrbuff);
|
||||
else
|
||||
conf = config->addr;
|
||||
}
|
||||
@@ -1303,9 +1323,10 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
a lease from one of it's MACs to give the address to another. */
|
||||
if (config && config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type))
|
||||
{
|
||||
inet_ntop(AF_INET, <mp->addr, daemon->addrbuff, ADDRSTRLEN);
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("abandoning lease to %s of %s"),
|
||||
print_mac(daemon->namebuff, ltmp->hwaddr, ltmp->hwaddr_len),
|
||||
inet_ntoa(ltmp->addr));
|
||||
daemon->addrbuff);
|
||||
lease = ltmp;
|
||||
}
|
||||
else
|
||||
@@ -1674,42 +1695,40 @@ static void add_extradata_opt(struct dhcp_lease *lease, unsigned char *opt)
|
||||
static void log_packet(char *type, void *addr, unsigned char *ext_mac,
|
||||
int mac_len, char *interface, char *string, char *err, u32 xid)
|
||||
{
|
||||
struct in_addr a;
|
||||
|
||||
if (!err && !option_bool(OPT_LOG_OPTS) && option_bool(OPT_QUIET_DHCP))
|
||||
return;
|
||||
|
||||
/* addr may be misaligned */
|
||||
daemon->addrbuff[0] = 0;
|
||||
if (addr)
|
||||
memcpy(&a, addr, sizeof(a));
|
||||
inet_ntop(AF_INET, addr, daemon->addrbuff, ADDRSTRLEN);
|
||||
|
||||
print_mac(daemon->namebuff, ext_mac, mac_len);
|
||||
|
||||
if(option_bool(OPT_LOG_OPTS))
|
||||
my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s%s %s%s",
|
||||
ntohl(xid),
|
||||
type,
|
||||
interface,
|
||||
addr ? inet_ntoa(a) : "",
|
||||
addr ? " " : "",
|
||||
daemon->namebuff,
|
||||
string ? string : "",
|
||||
err ? err : "");
|
||||
else
|
||||
my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s%s %s%s",
|
||||
if (option_bool(OPT_LOG_OPTS))
|
||||
my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s%s %s%s",
|
||||
ntohl(xid),
|
||||
type,
|
||||
interface,
|
||||
addr ? inet_ntoa(a) : "",
|
||||
daemon->addrbuff,
|
||||
addr ? " " : "",
|
||||
daemon->namebuff,
|
||||
string ? string : "",
|
||||
err ? err : "");
|
||||
|
||||
else
|
||||
my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s%s %s%s",
|
||||
type,
|
||||
interface,
|
||||
daemon->addrbuff,
|
||||
addr ? " " : "",
|
||||
daemon->namebuff,
|
||||
string ? string : "",
|
||||
err ? err : "");
|
||||
|
||||
#ifdef HAVE_UBUS
|
||||
if (!strcmp(type, "DHCPACK"))
|
||||
ubus_event_bcast("dhcp.ack", daemon->namebuff, addr ? inet_ntoa(a) : NULL, string ? string : NULL, interface);
|
||||
else if (!strcmp(type, "DHCPRELEASE"))
|
||||
ubus_event_bcast("dhcp.release", daemon->namebuff, addr ? inet_ntoa(a) : NULL, string ? string : NULL, interface);
|
||||
if (!strcmp(type, "DHCPACK"))
|
||||
ubus_event_bcast("dhcp.ack", daemon->namebuff, addr ? daemon->addrbuff : NULL, string, interface);
|
||||
else if (!strcmp(type, "DHCPRELEASE"))
|
||||
ubus_event_bcast("dhcp.release", daemon->namebuff, addr ? daemon->addrbuff : NULL, string, interface);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1861,7 +1880,10 @@ static size_t dhcp_packet_size(struct dhcp_packet *mess, unsigned char *agent_id
|
||||
if (option_bool(OPT_LOG_OPTS))
|
||||
{
|
||||
if (mess->siaddr.s_addr != 0)
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("%u next server: %s"), ntohl(mess->xid), inet_ntoa(mess->siaddr));
|
||||
{
|
||||
inet_ntop(AF_INET, &mess->siaddr, daemon->addrbuff, ADDRSTRLEN);
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("%u next server: %s"), ntohl(mess->xid), daemon->addrbuff);
|
||||
}
|
||||
|
||||
if ((mess->flags & htons(0x8000)) && mess->ciaddr.s_addr == 0)
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("%u broadcast response"), ntohl(mess->xid));
|
||||
@@ -2178,8 +2200,9 @@ static int pxe_uefi_workaround(int pxe_arch, struct dhcp_netid *netid, struct dh
|
||||
inet_ntop(AF_INET, &mess->siaddr, (char *)mess->sname, INET_ADDRSTRLEN);
|
||||
}
|
||||
|
||||
snprintf((char *)mess->file, sizeof(mess->file),
|
||||
strchr(found->basename, '.') ? "%s" : "%s.0", found->basename);
|
||||
if (found->basename)
|
||||
snprintf((char *)mess->file, sizeof(mess->file),
|
||||
strchr(found->basename, '.') ? "%s" : "%s.0", found->basename);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -2785,11 +2808,4 @@ static void apply_delay(u32 xid, time_t recvtime, struct dhcp_netid *netid)
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif /* HAVE_DHCP */
|
||||
|
||||
286
src/rfc3315.c
286
src/rfc3315.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2022 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:
|
||||
@@ -2076,95 +2100,116 @@ static unsigned int opt6_uint(unsigned char *opt, int offset, int size)
|
||||
return ret;
|
||||
}
|
||||
|
||||
void relay_upstream6(struct dhcp_relay *relay, ssize_t sz,
|
||||
struct in6_addr *peer_address, u32 scope_id, time_t now)
|
||||
int relay_upstream6(int iface_index, ssize_t sz,
|
||||
struct in6_addr *peer_address, u32 scope_id, time_t now)
|
||||
{
|
||||
/* ->local is same value for all relays on ->current chain */
|
||||
|
||||
union all_addr from;
|
||||
unsigned char *header;
|
||||
unsigned char *inbuff = daemon->dhcp_packet.iov_base;
|
||||
int msg_type = *inbuff;
|
||||
int hopcount;
|
||||
int hopcount, o;
|
||||
struct in6_addr multicast;
|
||||
unsigned int maclen, mactype;
|
||||
unsigned char mac[DHCP_CHADDR_MAX];
|
||||
struct dhcp_relay *relay;
|
||||
|
||||
for (relay = daemon->relay6; relay; relay = relay->next)
|
||||
if (relay->iface_index != 0 && relay->iface_index == iface_index)
|
||||
break;
|
||||
|
||||
/* No relay config. */
|
||||
if (!relay)
|
||||
return 0;
|
||||
|
||||
inet_pton(AF_INET6, ALL_SERVERS, &multicast);
|
||||
get_client_mac(peer_address, scope_id, mac, &maclen, &mactype, now);
|
||||
|
||||
/* source address == relay address */
|
||||
from.addr6 = relay->local.addr6;
|
||||
|
||||
|
||||
/* Get hop count from nested relayed message */
|
||||
if (msg_type == DHCP6RELAYFORW)
|
||||
hopcount = *((unsigned char *)inbuff+1) + 1;
|
||||
else
|
||||
hopcount = 0;
|
||||
|
||||
/* RFC 3315 HOP_COUNT_LIMIT */
|
||||
if (hopcount > 32)
|
||||
return;
|
||||
|
||||
reset_counter();
|
||||
|
||||
if ((header = put_opt6(NULL, 34)))
|
||||
/* RFC 3315 HOP_COUNT_LIMIT */
|
||||
if (hopcount > 32 || !(header = put_opt6(NULL, 34)))
|
||||
return 1;
|
||||
|
||||
header[0] = DHCP6RELAYFORW;
|
||||
header[1] = hopcount;
|
||||
memcpy(&header[18], peer_address, IN6ADDRSZ);
|
||||
|
||||
/* RFC-6939 */
|
||||
if (maclen != 0)
|
||||
{
|
||||
int o;
|
||||
|
||||
header[0] = DHCP6RELAYFORW;
|
||||
header[1] = hopcount;
|
||||
memcpy(&header[2], &relay->local.addr6, IN6ADDRSZ);
|
||||
memcpy(&header[18], peer_address, IN6ADDRSZ);
|
||||
|
||||
/* RFC-6939 */
|
||||
if (maclen != 0)
|
||||
{
|
||||
o = new_opt6(OPTION6_CLIENT_MAC);
|
||||
put_opt6_short(mactype);
|
||||
put_opt6(mac, maclen);
|
||||
end_opt6(o);
|
||||
}
|
||||
|
||||
o = new_opt6(OPTION6_RELAY_MSG);
|
||||
put_opt6(inbuff, sz);
|
||||
o = new_opt6(OPTION6_CLIENT_MAC);
|
||||
put_opt6_short(mactype);
|
||||
put_opt6(mac, maclen);
|
||||
end_opt6(o);
|
||||
|
||||
for (; relay; relay = relay->current)
|
||||
{
|
||||
union mysockaddr to;
|
||||
|
||||
to.sa.sa_family = AF_INET6;
|
||||
to.in6.sin6_addr = relay->server.addr6;
|
||||
to.in6.sin6_port = htons(DHCPV6_SERVER_PORT);
|
||||
to.in6.sin6_flowinfo = 0;
|
||||
to.in6.sin6_scope_id = 0;
|
||||
|
||||
if (IN6_ARE_ADDR_EQUAL(&relay->server.addr6, &multicast))
|
||||
{
|
||||
int multicast_iface;
|
||||
if (!relay->interface || strchr(relay->interface, '*') ||
|
||||
(multicast_iface = if_nametoindex(relay->interface)) == 0 ||
|
||||
setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &multicast_iface, sizeof(multicast_iface)) == -1)
|
||||
my_syslog(MS_DHCP | LOG_ERR, _("Cannot multicast to DHCPv6 server without correct interface"));
|
||||
}
|
||||
|
||||
send_from(daemon->dhcp6fd, 0, daemon->outpacket.iov_base, save_counter(-1), &to, &from, 0);
|
||||
|
||||
if (option_bool(OPT_LOG_OPTS))
|
||||
{
|
||||
inet_ntop(AF_INET6, &relay->local, daemon->addrbuff, ADDRSTRLEN);
|
||||
inet_ntop(AF_INET6, &relay->server, daemon->namebuff, ADDRSTRLEN);
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay %s -> %s"), daemon->addrbuff, daemon->namebuff);
|
||||
}
|
||||
|
||||
/* Save this for replies */
|
||||
relay->iface_index = scope_id;
|
||||
}
|
||||
}
|
||||
|
||||
o = new_opt6(OPTION6_RELAY_MSG);
|
||||
put_opt6(inbuff, sz);
|
||||
end_opt6(o);
|
||||
|
||||
for (; relay; relay = relay->next)
|
||||
if (relay->iface_index != 0 && relay->iface_index == iface_index)
|
||||
{
|
||||
union mysockaddr to;
|
||||
union all_addr from;
|
||||
|
||||
/* source address == relay address */
|
||||
from.addr6 = relay->local.addr6;
|
||||
memcpy(&header[2], &relay->local.addr6, IN6ADDRSZ);
|
||||
|
||||
to.sa.sa_family = AF_INET6;
|
||||
to.in6.sin6_addr = relay->server.addr6;
|
||||
to.in6.sin6_port = htons(DHCPV6_SERVER_PORT);
|
||||
to.in6.sin6_flowinfo = 0;
|
||||
to.in6.sin6_scope_id = 0;
|
||||
|
||||
if (IN6_ARE_ADDR_EQUAL(&relay->server.addr6, &multicast))
|
||||
{
|
||||
int multicast_iface;
|
||||
if (!relay->interface || strchr(relay->interface, '*') ||
|
||||
(multicast_iface = if_nametoindex(relay->interface)) == 0 ||
|
||||
setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &multicast_iface, sizeof(multicast_iface)) == -1)
|
||||
{
|
||||
my_syslog(MS_DHCP | LOG_ERR, _("Cannot multicast DHCP relay via interface %s"), relay->interface);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_DUMPFILE
|
||||
{
|
||||
union mysockaddr fromsock;
|
||||
fromsock.in6.sin6_port = htons(DHCPV6_SERVER_PORT);
|
||||
fromsock.in6.sin6_addr = from.addr6;
|
||||
fromsock.sa.sa_family = AF_INET6;
|
||||
fromsock.in6.sin6_flowinfo = 0;
|
||||
fromsock.in6.sin6_scope_id = 0;
|
||||
|
||||
dump_packet(DUMP_DHCPV6, (void *)daemon->outpacket.iov_base, save_counter(-1), &fromsock, &to, 0);
|
||||
}
|
||||
#endif
|
||||
send_from(daemon->dhcp6fd, 0, daemon->outpacket.iov_base, save_counter(-1), &to, &from, 0);
|
||||
|
||||
if (option_bool(OPT_LOG_OPTS))
|
||||
{
|
||||
inet_ntop(AF_INET6, &relay->local, daemon->addrbuff, ADDRSTRLEN);
|
||||
if (IN6_ARE_ADDR_EQUAL(&relay->server.addr6, &multicast))
|
||||
snprintf(daemon->namebuff, MAXDNAME, _("multicast via %s"), relay->interface);
|
||||
else
|
||||
inet_ntop(AF_INET6, &relay->server, daemon->namebuff, ADDRSTRLEN);
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay at %s -> %s"), daemon->addrbuff, daemon->namebuff);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
unsigned short relay_reply6(struct sockaddr_in6 *peer, ssize_t sz, char *arrival_interface)
|
||||
int relay_reply6(struct sockaddr_in6 *peer, ssize_t sz, char *arrival_interface)
|
||||
{
|
||||
struct dhcp_relay *relay;
|
||||
struct in6_addr link;
|
||||
@@ -2196,11 +2241,76 @@ unsigned short relay_reply6(struct sockaddr_in6 *peer, ssize_t sz, char *arrival
|
||||
put_opt6(opt6_ptr(opt, 0), opt6_len(opt));
|
||||
memcpy(&peer->sin6_addr, &inbuff[18], IN6ADDRSZ);
|
||||
peer->sin6_scope_id = relay->iface_index;
|
||||
return encap_type == DHCP6RELAYREPL ? DHCPV6_SERVER_PORT : DHCPV6_CLIENT_PORT;
|
||||
}
|
||||
}
|
||||
|
||||
if (encap_type == DHCP6RELAYREPL)
|
||||
{
|
||||
peer->sin6_port = ntohs(DHCPV6_SERVER_PORT);
|
||||
return 1;
|
||||
}
|
||||
|
||||
peer->sin6_port = ntohs(DHCPV6_CLIENT_PORT);
|
||||
|
||||
#ifdef HAVE_SCRIPT
|
||||
if (daemon->lease_change_command && encap_type == DHCP6REPLY)
|
||||
{
|
||||
/* decapsulate relayed message */
|
||||
opts = opt6_ptr(opt, 4);
|
||||
end = opt6_ptr(opt, opt6_len(opt));
|
||||
|
||||
for (opt = opts; opt; opt = opt6_next(opt, end))
|
||||
if (opt6_type(opt) == OPTION6_IA_PD && opt6_len(opt) > 12)
|
||||
{
|
||||
void *ia_opts = opt6_ptr(opt, 12);
|
||||
void *ia_end = opt6_ptr(opt, opt6_len(opt));
|
||||
void *ia_opt;
|
||||
|
||||
for (ia_opt = ia_opts; ia_opt; ia_opt = opt6_next(ia_opt, ia_end))
|
||||
/* valid lifetime must not be zero. */
|
||||
if (opt6_type(ia_opt) == OPTION6_IAPREFIX && opt6_len(ia_opt) >= 25 && opt6_uint(ia_opt, 4, 4) != 0)
|
||||
{
|
||||
if (daemon->free_snoops ||
|
||||
(daemon->free_snoops = whine_malloc(sizeof(struct snoop_record))))
|
||||
{
|
||||
struct snoop_record *snoop = daemon->free_snoops;
|
||||
|
||||
daemon->free_snoops = snoop->next;
|
||||
snoop->client = peer->sin6_addr;
|
||||
snoop->prefix_len = opt6_uint(ia_opt, 8, 1);
|
||||
memcpy(&snoop->prefix, opt6_ptr(ia_opt, 9), IN6ADDRSZ);
|
||||
snoop->next = relay->snoop_records;
|
||||
relay->snoop_records = snoop;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef HAVE_SCRIPT
|
||||
int do_snoop_script_run(void)
|
||||
{
|
||||
struct dhcp_relay *relay;
|
||||
struct snoop_record *snoop;
|
||||
|
||||
for (relay = daemon->relay6; relay; relay = relay->next)
|
||||
if ((snoop = relay->snoop_records))
|
||||
{
|
||||
relay->snoop_records = snoop->next;
|
||||
snoop->next = daemon->free_snoops;
|
||||
daemon->free_snoops = snoop;
|
||||
|
||||
queue_relay_snoop(&snoop->client, relay->iface_index, &snoop->prefix, snoop->prefix_len);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2022 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
|
||||
@@ -156,7 +156,7 @@ static int check_rrs(unsigned char *p, struct dns_header *header, size_t plen, i
|
||||
}
|
||||
|
||||
|
||||
/* mode is 0 to remove EDNS0, 1 to filter DNSSEC RRs */
|
||||
/* mode may be remove EDNS0 or DNSSEC RRs or remove A or AAAA from answer section. */
|
||||
size_t rrfilter(struct dns_header *header, size_t plen, int mode)
|
||||
{
|
||||
static unsigned char **rrs;
|
||||
@@ -192,20 +192,37 @@ size_t rrfilter(struct dns_header *header, size_t plen, int mode)
|
||||
if (!ADD_RDLEN(header, p, plen, rdlen))
|
||||
return plen;
|
||||
|
||||
/* Don't remove the answer. */
|
||||
if (i < ntohs(header->ancount) && type == qtype && class == qclass)
|
||||
continue;
|
||||
|
||||
if (mode == 0) /* EDNS */
|
||||
if (mode == RRFILTER_EDNS0) /* EDNS */
|
||||
{
|
||||
/* EDNS mode, remove T_OPT from additional section only */
|
||||
if (i < (ntohs(header->nscount) + ntohs(header->ancount)) || type != T_OPT)
|
||||
continue;
|
||||
}
|
||||
else if (type != T_NSEC && type != T_NSEC3 && type != T_RRSIG)
|
||||
/* DNSSEC mode, remove SIGs and NSECs from all three sections. */
|
||||
continue;
|
||||
|
||||
else if (mode == RRFILTER_DNSSEC)
|
||||
{
|
||||
if (type != T_NSEC && type != T_NSEC3 && type != T_RRSIG)
|
||||
/* DNSSEC mode, remove SIGs and NSECs from all three sections. */
|
||||
continue;
|
||||
|
||||
/* Don't remove the answer. */
|
||||
if (i < ntohs(header->ancount) && type == qtype && class == qclass)
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Only looking at answer section now. */
|
||||
if (i >= ntohs(header->ancount))
|
||||
break;
|
||||
|
||||
if (class != C_IN)
|
||||
continue;
|
||||
|
||||
if (mode == RRFILTER_A && type != T_A)
|
||||
continue;
|
||||
|
||||
if (mode == RRFILTER_AAAA && type != T_AAAA)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!expand_workspace(&rrs, &rr_sz, rr_found + 1))
|
||||
return plen;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2022 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;
|
||||
}
|
||||
|
||||
85
src/tftp.c
85
src/tftp.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2022 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
|
||||
@@ -19,10 +19,10 @@
|
||||
#ifdef HAVE_TFTP
|
||||
|
||||
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 struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, char *client);
|
||||
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(int err, char *packet, char *message, char *file, char *arg2);
|
||||
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)
|
||||
{
|
||||
@@ -95,6 +96,10 @@ void tftp_request(struct listener *listen, time_t now)
|
||||
if ((len = recvmsg(listen->tftpfd, &msg, 0)) < 2)
|
||||
return;
|
||||
|
||||
#ifdef HAVE_DUMPFILE
|
||||
dump_packet(DUMP_TFTP, (void *)packet, len, (union mysockaddr *)&peer, NULL, TFTP_PORT);
|
||||
#endif
|
||||
|
||||
/* Can always get recvd interface for IPv6 */
|
||||
if (!check_dest)
|
||||
{
|
||||
@@ -361,7 +366,7 @@ void tftp_request(struct listener *listen, time_t now)
|
||||
!(mode = next(&p, end)) ||
|
||||
(strcasecmp(mode, "octet") != 0 && strcasecmp(mode, "netascii") != 0))
|
||||
{
|
||||
len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), daemon->addrbuff);
|
||||
len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), daemon->addrbuff, NULL);
|
||||
is_err = 1;
|
||||
}
|
||||
else
|
||||
@@ -471,7 +476,7 @@ void tftp_request(struct listener *listen, time_t now)
|
||||
strncat(daemon->namebuff, filename, (MAXDNAME-1) - strlen(daemon->namebuff));
|
||||
|
||||
/* check permissions and open file */
|
||||
if ((transfer->file = check_tftp_fileperm(&len, prefix)))
|
||||
if ((transfer->file = check_tftp_fileperm(&len, prefix, daemon->addrbuff)))
|
||||
{
|
||||
if ((len = get_block(packet, transfer)) == -1)
|
||||
len = tftp_err_oops(packet, daemon->namebuff);
|
||||
@@ -481,6 +486,10 @@ void tftp_request(struct listener *listen, time_t now)
|
||||
}
|
||||
|
||||
send_from(transfer->sockfd, !option_bool(OPT_SINGLE_PORT), packet, len, &peer, &addra, if_index);
|
||||
|
||||
#ifdef HAVE_DUMPFILE
|
||||
dump_packet(DUMP_TFTP, (void *)packet, len, NULL, (union mysockaddr *)&peer, TFTP_PORT);
|
||||
#endif
|
||||
|
||||
if (is_err)
|
||||
free_transfer(transfer);
|
||||
@@ -491,7 +500,7 @@ void tftp_request(struct listener *listen, time_t now)
|
||||
}
|
||||
}
|
||||
|
||||
static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix)
|
||||
static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, char *client)
|
||||
{
|
||||
char *packet = daemon->packet, *namebuff = daemon->namebuff;
|
||||
struct tftp_file *file;
|
||||
@@ -508,7 +517,7 @@ static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix)
|
||||
{
|
||||
if (errno == ENOENT)
|
||||
{
|
||||
*len = tftp_err(ERR_FNF, packet, _("file %s not found"), namebuff);
|
||||
*len = tftp_err(ERR_FNF, packet, _("file %s not found for %s"), namebuff, client);
|
||||
return NULL;
|
||||
}
|
||||
else if (errno == EACCES)
|
||||
@@ -561,8 +570,7 @@ static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix)
|
||||
return file;
|
||||
|
||||
perm:
|
||||
errno = EACCES;
|
||||
*len = tftp_err(ERR_PERM, packet, _("cannot access %s: %s"), namebuff);
|
||||
*len = tftp_err(ERR_PERM, packet, _("cannot access %s: %s"), namebuff, strerror(EACCES));
|
||||
if (fd != -1)
|
||||
close(fd);
|
||||
return NULL;
|
||||
@@ -583,11 +591,31 @@ 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, NULL);
|
||||
while(retry_send(sendto(transfer->sockfd, daemon->packet, len, 0, &peer.sa, sa_len(&peer))));
|
||||
|
||||
#ifdef HAVE_DUMPFILE
|
||||
dump_packet(DUMP_TFTP, (void *)daemon->packet, len, NULL, (union mysockaddr *)&peer, TFTP_PORT);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; transfer = tmp)
|
||||
{
|
||||
tmp = transfer->next;
|
||||
@@ -602,7 +630,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);
|
||||
@@ -618,9 +646,14 @@ void check_tftp_listeners(time_t now)
|
||||
}
|
||||
|
||||
if (len != 0)
|
||||
send_from(transfer->sockfd, !option_bool(OPT_SINGLE_PORT), daemon->packet, len,
|
||||
&transfer->peer, &transfer->source, transfer->if_index);
|
||||
|
||||
{
|
||||
send_from(transfer->sockfd, !option_bool(OPT_SINGLE_PORT), daemon->packet, len,
|
||||
&transfer->peer, &transfer->source, transfer->if_index);
|
||||
#ifdef HAVE_DUMPFILE
|
||||
dump_packet(DUMP_TFTP, (void *)daemon->packet, len, NULL, (union mysockaddr *)&transfer->peer, TFTP_PORT);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (endcon || len == 0)
|
||||
{
|
||||
strcpy(daemon->namebuff, transfer->file->filename);
|
||||
@@ -726,33 +759,35 @@ static void sanitise(char *buf)
|
||||
}
|
||||
|
||||
#define MAXMESSAGE 500 /* limit to make packet < 512 bytes and definitely smaller than buffer */
|
||||
static ssize_t tftp_err(int err, char *packet, char *message, char *file)
|
||||
static ssize_t tftp_err(int err, char *packet, char *message, char *file, char *arg2)
|
||||
{
|
||||
struct errmess {
|
||||
unsigned short op, err;
|
||||
char message[];
|
||||
} *mess = (struct errmess *)packet;
|
||||
ssize_t len, ret = 4;
|
||||
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);
|
||||
len = snprintf(mess->message, MAXMESSAGE, message, file, arg2);
|
||||
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);
|
||||
return tftp_err(ERR_NOTDEF, packet, _("cannot read %s: %s"), daemon->namebuff);
|
||||
if (file != daemon->namebuff)
|
||||
strcpy(daemon->namebuff, file);
|
||||
return tftp_err(ERR_NOTDEF, packet, _("cannot read %s: %s"), daemon->namebuff, strerror(errno));
|
||||
}
|
||||
|
||||
/* return -1 for error, zero for done. */
|
||||
|
||||
267
src/ubus.c
267
src/ubus.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2022 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
|
||||
@@ -21,17 +21,44 @@
|
||||
#include <libubus.h>
|
||||
|
||||
static struct blob_buf b;
|
||||
static int notify;
|
||||
static int error_logged = 0;
|
||||
|
||||
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 =
|
||||
@@ -50,17 +77,16 @@ static void ubus_subscribe_cb(struct ubus_context *ctx, struct ubus_object *obj)
|
||||
(void)ctx;
|
||||
|
||||
my_syslog(LOG_DEBUG, _("UBus subscription callback: %s subscriber(s)"), obj->has_subscribers ? "1" : "0");
|
||||
notify = obj->has_subscribers;
|
||||
}
|
||||
|
||||
static void ubus_destroy(struct ubus_context *ubus)
|
||||
{
|
||||
// Forces re-initialization when we're reusing the same definitions later on.
|
||||
ubus_object.id = 0;
|
||||
ubus_object_type.id = 0;
|
||||
|
||||
ubus_free(ubus);
|
||||
daemon->ubus = NULL;
|
||||
|
||||
/* Forces re-initialization when we're reusing the same definitions later on. */
|
||||
ubus_object.id = 0;
|
||||
ubus_object_type.id = 0;
|
||||
}
|
||||
|
||||
static void ubus_disconnect_cb(struct ubus_context *ubus)
|
||||
@@ -76,42 +102,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()
|
||||
@@ -160,6 +171,16 @@ void check_ubus_listeners()
|
||||
}
|
||||
}
|
||||
|
||||
#define CHECK(stmt) \
|
||||
do { \
|
||||
int e = (stmt); \
|
||||
if (e) \
|
||||
{ \
|
||||
my_syslog(LOG_ERR, _("UBus command failed: %d (%s)"), e, #stmt); \
|
||||
return (UBUS_STATUS_UNKNOWN_ERROR); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
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)
|
||||
@@ -170,36 +191,196 @@ static int ubus_handle_metrics(struct ubus_context *ctx, struct ubus_object *obj
|
||||
(void)method;
|
||||
(void)msg;
|
||||
|
||||
blob_buf_init(&b, BLOBMSG_TYPE_TABLE);
|
||||
CHECK(blob_buf_init(&b, BLOBMSG_TYPE_TABLE));
|
||||
|
||||
for (i=0; i < __METRIC_MAX; i++)
|
||||
blobmsg_add_u32(&b, get_metric_name(i), daemon->metrics[i]);
|
||||
CHECK(blobmsg_add_u32(&b, get_metric_name(i), daemon->metrics[i]));
|
||||
|
||||
return ubus_send_reply(ctx, req, b.head);
|
||||
CHECK(ubus_send_reply(ctx, req, b.head));
|
||||
return UBUS_STATUS_OK;
|
||||
}
|
||||
|
||||
#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
|
||||
|
||||
#undef CHECK
|
||||
|
||||
#define CHECK(stmt) \
|
||||
do { \
|
||||
int e = (stmt); \
|
||||
if (e) \
|
||||
{ \
|
||||
my_syslog(LOG_ERR, _("UBus command failed: %d (%s)"), e, #stmt); \
|
||||
return; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
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;
|
||||
int ret;
|
||||
|
||||
if (!ubus || !notify)
|
||||
if (!ubus || !ubus_object.has_subscribers)
|
||||
return;
|
||||
|
||||
blob_buf_init(&b, BLOBMSG_TYPE_TABLE);
|
||||
CHECK(blob_buf_init(&b, BLOBMSG_TYPE_TABLE));
|
||||
if (mac)
|
||||
blobmsg_add_string(&b, "mac", mac);
|
||||
CHECK(blobmsg_add_string(&b, "mac", mac));
|
||||
if (ip)
|
||||
blobmsg_add_string(&b, "ip", ip);
|
||||
CHECK(blobmsg_add_string(&b, "ip", ip));
|
||||
if (name)
|
||||
blobmsg_add_string(&b, "name", name);
|
||||
CHECK(blobmsg_add_string(&b, "name", name));
|
||||
if (interface)
|
||||
blobmsg_add_string(&b, "interface", interface);
|
||||
CHECK(blobmsg_add_string(&b, "interface", interface));
|
||||
|
||||
ret = ubus_notify(ubus, &ubus_object, type, b.head, -1);
|
||||
if (!ret)
|
||||
my_syslog(LOG_ERR, _("Failed to send UBus event: %s"), ubus_strerror(ret));
|
||||
CHECK(ubus_notify(ubus, &ubus_object, type, b.head, -1));
|
||||
}
|
||||
|
||||
#ifdef HAVE_CONNTRACK
|
||||
void ubus_event_bcast_connmark_allowlist_refused(u32 mark, const char *name)
|
||||
{
|
||||
struct ubus_context *ubus = (struct ubus_context *)daemon->ubus;
|
||||
|
||||
if (!ubus || !ubus_object.has_subscribers)
|
||||
return;
|
||||
|
||||
CHECK(blob_buf_init(&b, 0));
|
||||
CHECK(blobmsg_add_u32(&b, "mark", mark));
|
||||
CHECK(blobmsg_add_string(&b, "name", name));
|
||||
|
||||
CHECK(ubus_notify(ubus, &ubus_object, "connmark-allowlist.refused", b.head, -1));
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (!ubus || !ubus_object.has_subscribers)
|
||||
return;
|
||||
|
||||
CHECK(blob_buf_init(&b, 0));
|
||||
CHECK(blobmsg_add_u32(&b, "mark", mark));
|
||||
CHECK(blobmsg_add_string(&b, "name", name));
|
||||
CHECK(blobmsg_add_string(&b, "value", value));
|
||||
CHECK(blobmsg_add_u32(&b, "ttl", ttl));
|
||||
|
||||
/* Set timeout to allow UBus subscriber to configure firewall rules before returning. */
|
||||
CHECK(ubus_notify(ubus, &ubus_object, "connmark-allowlist.resolved", b.head, /* timeout: */ 1000));
|
||||
}
|
||||
#endif
|
||||
|
||||
#undef CHECK
|
||||
|
||||
#endif /* HAVE_UBUS */
|
||||
|
||||
107
src/util.c
107
src/util.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2022 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
|
||||
@@ -115,7 +115,8 @@ u64 rand64(void)
|
||||
return (u64)out[outleft+1] + (((u64)out[outleft]) << 32);
|
||||
}
|
||||
|
||||
/* returns 2 if names is OK but contains one or more underscores */
|
||||
/* returns 1 if name is OK and ascii printable
|
||||
* returns 2 if name should be processed by IDN */
|
||||
static int check_name(char *in)
|
||||
{
|
||||
/* remove trailing .
|
||||
@@ -123,7 +124,9 @@ static int check_name(char *in)
|
||||
size_t dotgap = 0, l = strlen(in);
|
||||
char c;
|
||||
int nowhite = 0;
|
||||
int idn_encode = 0;
|
||||
int hasuscore = 0;
|
||||
int hasucase = 0;
|
||||
|
||||
if (l == 0 || l > MAXDNAME) return 0;
|
||||
|
||||
@@ -136,28 +139,49 @@ static int check_name(char *in)
|
||||
for (; (c = *in); in++)
|
||||
{
|
||||
if (c == '.')
|
||||
dotgap = 0;
|
||||
dotgap = 0;
|
||||
else if (++dotgap > MAXLABEL)
|
||||
return 0;
|
||||
return 0;
|
||||
else if (isascii((unsigned char)c) && iscntrl((unsigned char)c))
|
||||
/* iscntrl only gives expected results for ascii */
|
||||
return 0;
|
||||
#if !defined(HAVE_IDN) && !defined(HAVE_LIBIDN2)
|
||||
/* iscntrl only gives expected results for ascii */
|
||||
return 0;
|
||||
else if (!isascii((unsigned char)c))
|
||||
return 0;
|
||||
#if !defined(HAVE_IDN) && !defined(HAVE_LIBIDN2)
|
||||
return 0;
|
||||
#else
|
||||
idn_encode = 1;
|
||||
#endif
|
||||
else if (c != ' ')
|
||||
{
|
||||
nowhite = 1;
|
||||
if (c == '_')
|
||||
hasuscore = 1;
|
||||
}
|
||||
{
|
||||
nowhite = 1;
|
||||
#if defined(HAVE_LIBIDN2) && (!defined(IDN2_VERSION_NUMBER) || IDN2_VERSION_NUMBER < 0x02000003)
|
||||
if (c == '_')
|
||||
hasuscore = 1;
|
||||
#else
|
||||
(void)hasuscore;
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_IDN) || defined(HAVE_LIBIDN2)
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
hasucase = 1;
|
||||
#else
|
||||
(void)hasucase;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (!nowhite)
|
||||
return 0;
|
||||
|
||||
return hasuscore ? 2 : 1;
|
||||
#if defined(HAVE_LIBIDN2) && (!defined(IDN2_VERSION_NUMBER) || IDN2_VERSION_NUMBER < 0x02000003)
|
||||
/* Older libidn2 strips underscores, so don't do IDN processing
|
||||
if the name has an underscore unless it also has non-ascii characters. */
|
||||
idn_encode = idn_encode || (hasucase && !hasuscore);
|
||||
#else
|
||||
idn_encode = idn_encode || hasucase;
|
||||
#endif
|
||||
|
||||
return (idn_encode) ? 2 : 1;
|
||||
}
|
||||
|
||||
/* Hostnames have a more limited valid charset than domain names
|
||||
@@ -204,17 +228,11 @@ char *canonicalise(char *in, int *nomem)
|
||||
if (!(rc = check_name(in)))
|
||||
return NULL;
|
||||
|
||||
#if defined(HAVE_LIBIDN2) && (!defined(IDN2_VERSION_NUMBER) || IDN2_VERSION_NUMBER < 0x02000003)
|
||||
/* older libidn2 strips underscores, so don't do IDN processing
|
||||
if the name has an underscore (check_name() returned 2) */
|
||||
if (rc != 2)
|
||||
#endif
|
||||
#if defined(HAVE_IDN) || defined(HAVE_LIBIDN2)
|
||||
if (rc == 2)
|
||||
{
|
||||
# ifdef HAVE_LIBIDN2
|
||||
rc = idn2_to_ascii_lz(in, &ret, IDN2_NONTRANSITIONAL);
|
||||
if (rc == IDN2_DISALLOWED)
|
||||
rc = idn2_to_ascii_lz(in, &ret, IDN2_TRANSITIONAL);
|
||||
# else
|
||||
rc = idna_to_ascii_lz(in, &ret, 0);
|
||||
# endif
|
||||
@@ -234,12 +252,14 @@ char *canonicalise(char *in, int *nomem)
|
||||
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
(void)rc;
|
||||
#endif
|
||||
|
||||
if ((ret = whine_malloc(strlen(in)+1)))
|
||||
strcpy(ret, in);
|
||||
else if (nomem)
|
||||
*nomem = 1;
|
||||
*nomem = 1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -316,7 +336,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)
|
||||
{
|
||||
@@ -347,7 +367,7 @@ int sa_len(union mysockaddr *addr)
|
||||
}
|
||||
|
||||
/* don't use strcasecmp and friends here - they may be messed up by LOCALE */
|
||||
int hostname_isequal(const char *a, const char *b)
|
||||
int hostname_order(const char *a, const char *b)
|
||||
{
|
||||
unsigned int c1, c2;
|
||||
|
||||
@@ -360,11 +380,19 @@ int hostname_isequal(const char *a, const char *b)
|
||||
if (c2 >= 'A' && c2 <= 'Z')
|
||||
c2 += 'a' - 'A';
|
||||
|
||||
if (c1 != c2)
|
||||
return 0;
|
||||
if (c1 < c2)
|
||||
return -1;
|
||||
else if (c1 > c2)
|
||||
return 1;
|
||||
|
||||
} while (c1);
|
||||
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hostname_isequal(const char *a, const char *b)
|
||||
{
|
||||
return hostname_order(a, b) == 0;
|
||||
}
|
||||
|
||||
/* is b equal to or a subdomain of a return 2 for equal, 1 for subdomain */
|
||||
@@ -408,13 +436,12 @@ int hostname_issubdomain(char *a, char *b)
|
||||
time_t dnsmasq_time(void)
|
||||
{
|
||||
#ifdef HAVE_BROKEN_RTC
|
||||
struct tms dummy;
|
||||
static long tps = 0;
|
||||
struct timespec ts;
|
||||
|
||||
if (tps == 0)
|
||||
tps = sysconf(_SC_CLK_TCK);
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0)
|
||||
die(_("cannot read monotonic clock: %s"), NULL, EC_MISC);
|
||||
|
||||
return (time_t)(times(&dummy)/tps);
|
||||
return ts.tv_sec;
|
||||
#else
|
||||
return time(NULL);
|
||||
#endif
|
||||
@@ -436,7 +463,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)
|
||||
{
|
||||
@@ -518,7 +555,7 @@ void prettyprint_time(char *buf, unsigned int t)
|
||||
if ((x = (t/60)%60))
|
||||
p += sprintf(&buf[p], "%um", x);
|
||||
if ((x = t%60))
|
||||
p += sprintf(&buf[p], "%us", x);
|
||||
sprintf(&buf[p], "%us", x);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -564,7 +601,7 @@ int parse_hex(char *in, unsigned char *out, int maxlen,
|
||||
int j, bytes = (1 + (r - in))/2;
|
||||
for (j = 0; j < bytes; j++)
|
||||
{
|
||||
char sav = sav;
|
||||
char sav;
|
||||
if (j < bytes - 1)
|
||||
{
|
||||
sav = in[(j+1)*2];
|
||||
|
||||
Reference in New Issue
Block a user