Compare commits

..
113 Commits
Author SHA1 Message Date
Dominik DL6ER e7ccd95c04 Add EDE return when no matching key found. 2021-07-09 22:12:42 +01:00
Simon Kelley 719f79a8fd Subtle change to priority of --server types.
Make --server=/example.com/1.2.3.4 take priority over
--server=/example.com/ (AKA --address=/example.com/ or --local=/example.com/)

This corrects a regression in the domain-match rewrite, and appears
to be the more useful order. It got swapped because I didn't consider
that both could usefully co-exist.
2021-07-06 21:02:35 +01:00
Kevin Darbyshire-Bryant 96f6444958 Fix thinko in a92c6d77dc 2021-07-05 21:00:47 +01:00
Simon Kelley df25f204ba Fix logical error in d0ae3f5a4d
The code which checked for a possible local answer to a domain,
like --address=/example.com/1.2.3.4 could return false positives,
causing upstream NXDOMAIN replies to be rewritten as NOERROR.

Thanks to Dominik DL6ER for the bug report and analysis.
2021-07-05 20:56:11 +01:00
Simon Kelley 8acdc3ede7 Add calls to dump internally generated answers for dumpmask=0x0002 2021-07-04 23:12:14 +01:00
Simon Kelley 857b445522 Fix order of calls to resize-packet() and add_pseudoheader().
Avoids malformed replies with EDE in certain circumstances.
2021-07-04 22:38:26 +01:00
Simon Kelley 5bcca1219a Support IPv6 in --bogus-nxdomian and --ignore-address 2021-07-04 22:27:00 +01:00
Simon Kelley 4558c26fcd Make --rebind-localhost-ok apply to :: and 0.0.0.0
Also make the definition of local IPv6 addresses
the same for --bogus-priv and rebind protection.
2021-07-04 21:09:10 +01:00
Simon Kelley a92c6d77dc Tidy domain parsing, make --server=/*/1.2.3.4 equivalent to --server=1.2.3.4 2021-07-03 12:56:50 +01:00
Petr Menšík 0c95a5ff53 Modify and propagate changed lease.
If hostname is reset on existing lease, propagate such change to leases
file and script.
2021-07-02 16:58:48 +01:00
Simon Kelley cb6d06bb54 Rationalise SERV_MARK use. 2021-07-01 23:00:22 +01:00
Simon Kelley 3ef955c85a Fix oversight in build_server_array().
The index computation went awry when servers are disabled
by the loop-detection system.

Thanks to Xingcong Li for spotting this.
2021-07-01 22:40:31 +01:00
Simon Kelley 5e95c16c32 Allow wildcards in domain patterns.
Domain patterns in --address, --server and --local have, for many years,
matched complete labels only, so
--server=/google.com/1.2.3.4
will apply to google.com and www.google.com but NOT supergoogle.com

This commit introduces an optional '*' at the LHS of the domain string which
changes this behaviour so as to include substring matches _within_ labels. So,
--server=/*google.com/1.2.3.4
applies to google.com, www.google.com AND supergoogle.com.
2021-07-01 22:28:24 +01:00
Simon Kelley 4205e2ebcf Reuse workspace bit in struct server ->flags. 2021-07-01 13:22:10 +01:00
Etan Kissling 9d806c51c2 Fix ipset support.
This fixes a problem with ipset processing that got recently introduced
when `extract_request` filtering was tightened. During the recent change
an incorrect assumption was made that `extract_request` was only called
for requests but with ipset it is also called when processing responses.

The fix ensures that the new filters only apply to requests (QR=0 @ hdr)

Signed-off-by: Etan Kissling <etan.kissling@gmail.com>
2021-06-30 12:31:51 +01:00
Simon Kelley a38bb31727 Revert "Treat failure of ubus_add_object() in ubus_init() as retry-able."
This reverts commit 8a1ef367e2.
2021-06-30 12:30:15 +01:00
Simon Kelley 8a1ef367e2 Treat failure of ubus_add_object() in ubus_init() as retry-able.
3c93e8eb41 regularised ubus_init()
by avoiding logging calls (it can be called before logging is up)
but it instead returned any error from ubus_add_object() which
made such an error fatal. It turns out this is awkward, so this
patch returns NULL always, so that the event-loop will continue
attemping to connect to ubus forever.

This is not necessarily optimal either, and should be looked at
by a UBUS grown-up, but it does solve the immediate problem.
2021-06-27 21:32:10 +01:00
Simon Kelley 1291865c92 Fix trivial breakage of DBUS done by 85bc7534da 2021-06-27 21:16:30 +01:00
Simon Kelley a9ebbee7b6 Compiler warnings. 2021-06-27 21:03:52 +01:00
Simon Kelley 06df5ad7d0 Tidy up interface to dbus and ubus modules.
Consistently treat a non-NULL return from [ud]bus-init() as a fatal error:
either die() if still starting, or log an error and disable
the relevant module if dnsmasq has already started.

Also rationalise calls to set and check listeners depending on
configuration.
2021-06-27 20:56:58 +01:00
Simon Kelley 66b863c989 Fix problem with re-allocation of serverarray. 2021-06-26 21:13:41 +01:00
Simon Kelley c9efe8e5e1 Rationalise domain parsing for --rev-server and --domain. 2021-06-26 18:51:05 +01:00
Simon Kelley d515223bb5 Don't re-use datastructures for --address and --local.
Doing so makes the loading process quadratic, which is a problem
when there are a large number.
2021-06-26 01:00:37 +01:00
Simon Kelley b908f4334b Merge branch 'extended-error' 2021-06-26 00:38:55 +01:00
Simon Kelley 6261aba026 Initial implementation of RFC-8914 extended DNS errors. 2021-06-26 00:38:01 +01:00
Simon Kelley 85bc7534da Rationalise --server parsing and datastructure building.
Use add_update_server for everything.
2021-06-25 22:09:08 +01:00
Simon Kelley 1b30fd1732 Merge branch 'master' of ssh://thekelleys.org.uk/var/local/git/dnsmasq 2021-06-25 10:46:53 +01:00
Simon Kelley 8c9196bff8 Correct domain search algorithm.
For reasons unknown, I (srk) assumed that the orginal
substring domain matching algorithm was still in use,
where example.com would match eg. sexample.com

In fact the far more sensible label-based match, where
example.com (or .example.com) matches example.com and
www.example.com, but not sexample.com, has been in use
since release 2.22. This commit implements the 2.22 to 2.85
behaviour in the new domain-search code.

Thanks to Kevin Darbyshire-Bryant for spotting my mistake.
2021-06-25 10:46:06 +01:00
Simon Kelley b1daf44954 Correct domain search algorithm.
For reasons unknown, I (srk) assumed that the orginal
substring domain matching algorithm was still in use,
where example.comKevin Darbyshire-Bryant <kevin@darbyshire-bryant.me.uk> would match eg. sexample.com

In fact the far more sensible label-based match, where
example.com (or .example.com) matches example.com and
www.example.com, but not sexample.com, has been in use
since release 2.22. This commit implements the 2.22 to 2.85
behaviour in the new domain-search code.

Thanks to Kevin Darbyshire-Bryant for spotting my mistake.
2021-06-24 23:28:47 +01:00
Simon Kelley 11c52d032b Initial changes for extended DNS error codes. 2021-06-21 17:37:46 +01:00
Simon Kelley be291d979d Include EDNS0 in connmark REFUSED replies. 2021-06-21 16:59:42 +01:00
Simon Kelley 6d1edd8d32 Use correct packet-size limit in make_local_answer() 2021-06-21 15:59:07 +01:00
Simon Kelley 25ff956c7d Tidy up name buffer use in report_addresses().
Buffer may need to be twice MAXDNAME is escaping is
enabled in extract_name. The name may include weird characters.
2021-06-21 15:05:28 +01:00
Simon Kelley 38179500f8 CHANGELOG entry for new connmark code. 2021-06-21 14:35:36 +01:00
Simon Kelley 5f7be5f0d6 Fix compiler warning. 2021-06-21 14:31:54 +01:00
Etan Kissling 627056febb Connection track mark based DNS query filtering.
This 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.

Signed-off-by: Etan Kissling <etan_kissling@apple.com>
(addressed reviewer feedback)
Signed-off-by: Etan Kissling <etan.kissling@gmail.com>
2021-06-21 14:14:55 +01:00
Simon Kelley cbd76447fd Further work from a0a3b8ad3e
When query longer than longest domain, crop directly to length
of longest domain.
2021-06-21 00:01:51 +01:00
Simon Kelley a60a233329 Fix bug introduced in 6860cf932b
Breakage 0f --no-rebind-domain due to incomplete edit.

Thanks to Kevin Darbyshire-Bryant for spotting this.
2021-06-20 23:02:54 +01:00
Simon Kelley a0a3b8ad3e Fix bug in 6860cf932b
The optimisation based on the assumption that
"try now points to the last domain that sorts before the query"
fails in the specific edge case that the query sorts before
_any_ of the server domains. Handle this case.

Thanks to Xingcong Li for finding a test case for this bug.
2021-06-20 22:57:54 +01:00
Simon Kelley d0ae3f5a4d Fix specific NOERR/NXDOMAIN confusion.
In the specific case of configuring an A record for a domain

address=/example.com/1.2.3.4

queries for *example.com for any other type will now return
NOERR, and not the previous erroneous NXDOMAIN. The same thing
applies for

address=/example.com/::1:2:3:4
address=/example.com/#
2021-06-17 23:11:17 +01:00
Simon Kelley 6860cf932b Optimise lokkup_domain() 2021-06-17 21:30:40 +01:00
Simon Kelley 0276e0805b merge development machines.
Merge branch 'master' of ssh://thekelleys.org.uk/var/local/git/dnsmasq
2021-06-16 14:05:49 +01:00
Simon Kelley 06ff3d8a26 Log the correct name when we retry a DNSSEC query.
If we retry a DNSSEC query because our client retries on us, and
we have an answer but are waiting on a DNSSEC query to validate it,
log the name of the DNSSEC query, not the client's query.
2021-06-16 13:59:57 +01:00
Simon Kelley 1a3b69aa56 Fix error in new domain-search code.
SERV_USE_RESOLV set implies struct serv_local,
so don't can't set ->arrayposn

Thanks to Xingcong Li for the cod review which led to this.
2021-06-16 09:57:41 +01:00
Simon Kelley 8237d06ab7 Typo in FAQ.
Reported by Alexander Traud.
2021-06-15 23:14:59 +01:00
Simon Kelley 1c9f136b57 Man page update, lease times can be given in days or weeks. 2021-06-15 22:07:59 +01:00
Simon Kelley 5ab7e4a475 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.
2021-06-15 15:27:29 +01:00
Simon Kelley 3236f358f8 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.
2021-06-13 21:29:22 +01:00
Simon Kelley 4a6550d69a Move make_local_answer() to domain-match.c 2021-06-10 21:40:52 +01:00
Simon Kelley ff523d0c67 Fix TCP replies with --domain-needed. 2021-06-10 21:31:38 +01:00
Simon Kelley 3c93e8eb41 Re-order UBus initialisation to avoid logging before logs set up. 2021-06-08 23:13:48 +01:00
Simon Kelley 88a482fdb9 Rename hash_questions.c, for consistency. 2021-06-08 22:16:05 +01:00
Simon Kelley 12a9aa7c62 Major rewrite of the DNS server and domain handling code.
This should be largely transparent, but it drastically
improves performance and reduces memory foot-print when
configuring large numbers domains of the form
local=/adserver.com/
or
local=/adserver.com/#

Lookup times now grow as log-to-base-2 of the number of domains,
rather than greater than linearly, as before.
The change makes multiple addresses associated with a domain work
address=/example.com/1.2.3.4
address=/example.com/5.6.7.8
It also handles multiple upstream servers for a domain better; using
the same try/retry alogrithms as non domain-specific servers. This
also applies to DNSSEC-generated queries.

Finally, some of the oldest and gnarliest code in dnsmasq has had
a significant clean-up. It's far from perfect, but it _is_ better.
2021-06-08 22:10:55 +01:00
Simon Kelley 50ccf9c585 Fix manpage typo.
Thanks to Bart Wolther for the report.
2021-04-30 00:01:48 +01:00
Simon Kelley d100eb05a3 <nettle/version.h> -> <nettle/bignum.h> for backward compatibility. 2021-04-14 21:15:34 +01:00
Petr Menšík 10d8b5f001 Reduce code duplication, reuse existing functions
dhcp_config_free and dhcp_opt_free already implement the same algorithm.
Reuse them. Adds forgotten hostname cleanup to config free.
2021-04-14 21:08:31 +01:00
Simon Kelley ffd3ceb856 Remove old patch droppings in manpage. 2021-04-14 01:00:48 +01:00
Brian Hartvigsen d942aa9321 Support Cisco Umbrella/OpenDNS Device ID & Remote IP
This is based on the information at
https://docs.umbrella.com/umbrella-api/docs/identifying-dns-traffic and
https://docs.umbrella.com/umbrella-api/docs/identifying-dns-traffic2 .
Using --umbrella by itself will enable Remote IP reporting. This can not
be used for any policy filtering in Cisco Umbrella/OpenDNS. Additional
information can be supplied using specific option specifications,
multiple can be separated by a comma:

--umbrella=orgid:1234,deviceid=0123456789abcdef

Specifies that you want to report organization 1234 using device
0123456789abcdef. For Cisco Umbrella Enterprise, see "Register (Create)
a device" (https://docs.umbrella.com/umbrella-api/docs/create-a-device)
for how to get a Device ID and "Organization ID endpoint"
(https://docs.umbrella.com/umbrella-api/docs/organization-endpoint) to
get organizations ID. For OpenDNS Home Users, there is no organization,
see Registration API endpoint
(https://docs.umbrella.com/umbrella-api/docs/registration-api-endpoint2)
for how to get a Device ID. Asset ID should be ignored unless
specifically instructed to use by support.

Signed-off-by: Brian Hartvigsen <brian.andrew@brianandjenny.com>
2021-04-14 00:16:14 +01:00
Simon Kelley 6469fefe89 Fix build failure with HAVE_CRYPTOHASH. 2021-04-13 23:33:46 +01:00
Simon Kelley b082842ee7 Add NO_LOOP to config.h docs. 2021-04-12 17:43:59 +01:00
Geert Stappers 3573ca0eec Chomp file ends
Removed empty lines from end of src/*.[ch] files.
If the new last line became '#endif'
was the condition of the '#if' added.
2021-04-09 17:27:36 +01:00
Simon Kelley ad90eb075d Fix bug in TCP process handling.
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.
2021-04-09 16:08:05 +01:00
Simon Kelley d55e2d086d Handle DHCPREBIND requests in the DHCPv6 server.
Patch by srk, based on submitted patch from liaichun@huawei.com
2021-04-09 15:19:28 +01:00
Simon Kelley fe9c966a49 Fix debian/changelog format problem. 2021-04-08 22:41:46 +01:00
Simon Kelley 9f20afb1a8 Manpage typo. 2021-04-07 21:39:39 +01:00
Simon Kelley f61afcfc70 Tidy error logging in 961daf8f92 2021-04-07 20:54:36 +01:00
Simon Kelley 961daf8f92 Handle resource exhaustion of struct frec_src same as struct frec.
Ie, by returning REFUSED response and (rate-limited) logging.
2021-04-06 23:52:09 +01:00
Simon Kelley 64a16cb376 Combine queries for the same DNS name if close in time.
If two queries arrive a second or so apart, they cannot be a try and
a retry from the same client (retries are at least three seconds apart.)

It's therefore safe not to forward the second query, but answer them
both when the reply arrives for the first.
2021-04-06 23:29:46 +01:00
Simon Kelley ea6b0b2665 Subtly change behaviour on repeated DNS query.
This changes the behaviour introduced in
141a26f979

We re-introduce the distinction between a query
which is retried from the same source, and one which is
repeated from different sources.

In the later case, we still forward the query, to avoid
problems when the reply to the first query is lost
(see f8cf456920) but we suppress the behaviour
that's used on a retry, when the query is sent to
all available servers in parallel.

Retry -> all servers.
Repeat -> next server.

This avoids a significant increase in upstream traffic on
busy instances which see lots of queries for common names.

It does mean the clients which repeat queries from new source ports,
rather than retrying them from the same source port, will see
different behaviour, but it in fact restores the pre-2.83 behaviour,
so it's not expected to be a practical problem.
2021-04-05 21:01:09 +01:00
Matthias Andree 89df73ac05 CHANGELOG: fix typo recieved->received 2021-04-03 23:01:46 +01:00
Simon Kelley 7d3f3c9983 Fold NMU into Debian packaging. 2021-04-03 22:43:14 +01:00
Simon Kelley 1bdbea2461 Fold NMU into Debian packaging. 2021-03-30 22:42:29 +01:00
Simon Kelley dfb1f7ccf1 TFTP tweak.
Check sender of all received packets, as specified in RFC 1350 para 4.

My understanding of the example in the RFC is that it in fact only
applies to server-to-client packets, and packet loss or duplication
cannot result in a client sending from more than one port to a server.
This check is not, therefore, strictly needed on the server side.
It's still useful, and adds a little security against packet
spoofing. (though if you're running TFTP on a public network with
bad actors, nothing can really save you.)
2021-03-30 21:32:07 +01:00
Matthias Andree b5d1b20727 Fix spacing in translatable strings.
Signed-off-by: Matthias Andree <matthias.andree@gmx.de>
2021-03-28 00:58:54 +00:00
Matthias Andree 2a407a76be CHANGELOG: spell-check and correct first few entries
Signed-off-by: Matthias Andree <matthias.andree@gmx.de>
2021-03-28 00:56:13 +00:00
Matthias Andree d1640a6338 po/de.po: Rework German translation...
for consistency, wording/language, typoes.

Signed-off-by: Matthias Andree <matthias.andree@gmx.de>
2021-03-28 00:55:44 +00:00
Simon Kelley 26b5c40d95 Replace ad-hoc libnettle version detecion with MIN_VERSION macro. 2021-03-27 23:29:36 +00:00
Petr Menšík 0b3ecf7432 Enable DNSSEC compilation on nettle 2.7.1
RHEL/CentOS 7 does not compile with DNSSEC enabled, because older
version is not supported. Add few defines to compile also on older
nettle versions.

Adds also major version 4 check, taking into account higher major
version.
2021-03-27 23:26:48 +00:00
Petr Menšík 8f9bd61505 Correct missing SERV_DO_DNSSEC flag, add new spot
One change to server_test_type forgot to set SERV_DO_DNSSEC. One new
place still can be reused.

Fixes commit e10a9239e1, thanks to
Xingcong Li for spotting it.
2021-03-27 23:16:09 +00:00
Simon Kelley ea28d0ef8a Scale the DNS random scket pool on the value of dns-forward-max. 2021-03-26 22:02:04 +00:00
Simon Kelley 4a8c098840 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 slighly 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.
2021-03-26 21:19:39 +00:00
黎醒聪 ffa4628faa Fix thinko in 51f7bc924c 2021-03-22 22:00:26 +00:00
Petr Menšík e10a9239e1 Move repeated test pattern to server_test_type
Use static function to test similar checks in multiple places.
2021-03-21 22:57:02 +00:00
Petr Menšík 51f7bc924c Create common function for forward dump, log and send
One part in dnssec retry path did not dump sent retry into dump file.
Make sure it is dumped all times it is sent by common function shared on
multiple places. Reduce a bit also server sending.
2021-03-21 22:56:05 +00:00
Petr Menšík 6c0bf79078 Reduce few repetitions in forward code 2021-03-21 22:54:12 +00:00
Matthias Andree 4b03170920 Update German translation for 2.85rc1.
Signed-off-by: Matthias Andree <matthias.andree@gmx.de>
2021-03-21 22:41:58 +00:00
Simon Kelley 1de6bbc108 Fix FTBS on FreeBSD due to Linux-specific optimisation of if_nametoindex() 2021-03-19 22:24:08 +00:00
Simon Kelley 023ace8e54 Merge branch 'random-port' 2021-03-17 20:42:21 +00:00
Simon Kelley 74d4fcd756 Use random source ports where possible if source addresses/interfaces in use.
CVE-2021-3448 applies.

It's possible to specify the source address or interface to be
used when contacting upstream nameservers: server=8.8.8.8@1.2.3.4
or server=8.8.8.8@1.2.3.4#66 or server=8.8.8.8@eth0, and all of
these have, until now, used a single socket, bound to a fixed
port. This was originally done to allow an error (non-existent
interface, or non-local address) to be detected at start-up. This
means that any upstream servers specified in such a way don't use
random source ports, and are more susceptible to cache-poisoning
attacks.

We now use random ports where possible, even when the
source is specified, so server=8.8.8.8@1.2.3.4 or
server=8.8.8.8@eth0 will use random source
ports. server=8.8.8.8@1.2.3.4#66 or any use of --query-port will
use the explicitly configured port, and should only be done with
understanding of the security implications.
Note that this change changes non-existing interface, or non-local
source address errors from fatal to run-time. The error will be
logged and communiction with the server not possible.
2021-03-17 20:39:33 +00:00
Simon Kelley 9eaa91bfc3 Teach --bogus-nxdomain and --ignore-address to take a subnet argument. 2021-03-17 20:31:06 +00:00
Petr Menšík 484bd75ce4 tftp warning fix.
At least on Fedora 32 with GCC 10.2.1, dnsmasq compilation emits warning:

tftp.c: In function ‘tftp_request’:
tftp.c:754:3: warning: ‘strcpy’ source argument is the same as
destination [-Wrestrict]
  754 |   strcpy(daemon->namebuff, file);

And indeed it is the same source always on line 477, sometimes also on
571 in tftp.c

Attached patch fixes the warning and possible undefined behaviour on
tftp error.
2021-03-17 14:40:04 +00:00
Simon Kelley 4c30e9602b Only log changes to DNS listeners when --log-debug is set. 2021-03-12 22:09:14 +00:00
Simon Kelley b260d222af Add --log-debug option and MS_DEBUG flag to my_syslog(). 2021-03-12 21:57:57 +00:00
Simon Kelley 6528d62cd2 Merge i18n message files. 2021-03-11 23:46:45 +00:00
Simon Kelley b7cf754f6f 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.
2021-03-11 23:39:33 +00:00
Simon Kelley 14e3f6ba19 Bump version in Debian changelog. 2021-03-02 21:41:04 +00:00
Petr Menšík a8c1474562 Obtain MTU of interface only when it would be used
MTU were obtained early during iface_allowed check. But often it
returned from the function without ever using it. Because calls to
kernel might be costy, move fetching it only when it would be assigned.
2021-03-02 21:38:02 +00:00
Petr Menšík 8b8a4148ec Move flags to recvmsg function in netlink
netlink_multicast used 3 calls to fcntl in order to set O_NONBLOCK on
socket. It is possible to pass MSG_DONTWAIT flag just to recvmsg function,
without setting it permanently on socket. Save few kernel calls and use
recvmsg flags.

It is supported since kernel 2.2, should be fine for any device still
receiving updates.
2021-03-02 21:36:45 +00:00
Simon Kelley 9e147480ed Always use <poll.h>
Previously we were always using <sys/poll.h> since
HAVE_POLL_H is never set. This looks like an autoconfism
that has crept in, but we don't use autoconf.

poll.h is the correct header file, as far as I can tell.
2021-03-02 21:17:28 +00:00
Petr Menšík 4c0aecc685 Correct occasional --bind-dynamic synchronization break
Request only one re-read of addresses and/or routes

Previous implementation re-reads systemd addresses exactly the same
number of time equal number of notifications received.
This is not necessary, we need just notification of change, then re-read
the current state and adapt listeners. Repeated re-reading slows netlink
processing and highers CPU usage on mass interface changes.

Continue reading multicast events from netlink, even when ENOBUFS
arrive. Broadcasts are not trusted anyway and refresh would be done in
iface_enumerate. Save queued events sent again.

Remove sleeping on netlink ENOBUFS

With reduced number of written events netlink should receive ENOBUFS
rarely. It does not make sense to wait if it is received. It is just a
signal some packets got missing. Fast reading all pending packets is required,
seq checking ensures it already. Finishes changes by
commit 1d07667ac7.

Move restart from iface_enumerate to enumerate_interfaces

When ENOBUFS is received, restart of reading addresses is done. But
previously found addresses might not have been found this time. In order
to catch this, restart both IPv4 and IPv6 enumeration with clearing
found interfaces first. It should deliver up-to-date state also after
ENOBUFS.

Read all netlink messages before netlink restart

Before writing again into netlink socket, try fetching all pending
messages. They would be ignored, only might trigger new address
synchronization. Should ensure new try has better chance to succeed.

ENOBUFS error handling was improved. Netlink is correctly drained before
sending a new request again. It seems ENOBUFS supression is no longer
necessary or wanted. Let kernel tell us when it failed and handle it a
good way.
2021-03-02 18:21:32 +00:00
Simon Kelley d556b8a5d5 Case error in x86-64_EFI pxe CSA 2021-02-28 21:36:03 +00:00
Simon Kelley e7c0d7b348 dhcp-host selection fix for v4/v6.
Avoid treating a --dhcp-host which has an IPv6 address
as eligable for use with DHCPv4 on the grounds that it has
no address, and vice-versa.
2021-02-28 17:56:54 +00:00
Simon Kelley 17360439dc Clarification on dhcp-host and DNS. 2021-02-24 15:54:36 +00:00
Simon Kelley 9e169a9bea Belated CHANGELOG update. 2021-02-22 23:07:48 +00:00
Simon Kelley 305cb79c57 Simplify preceding fix.
Remove distinction between retry with same QID/SP and
retry for same query with different QID/SP. If the
QID/SP are the same as an existing one, simply retry,
if a new QID/SP is seen, add to the list to be replied to.
2021-02-18 21:50:33 +00:00
Simon Kelley 141a26f979 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.
2021-02-17 23:56:32 +00:00
Simon Kelley cfcafdd27c Tweak f1204a875e
This gets, eg, v2.65test1 and v2.65test11 in the correct order.
2021-02-01 23:46:43 +00:00
Simon Kelley f1204a875e Tweak sort order of tags in get-version.
We want to sort such that the most recent/relevant tag is first
and gets used to set the compiled-in version.

The solution is far from general, but works for the tag formats
used by dnsmasq. v2.84 sorts before v2.83, but v2.83 sorts
before v2.83rc1 and 2.83rc1 sorts before v2.83test1
2021-01-29 23:20:06 +00:00
Simon Kelley 20295012b8 Fix possible free-memory ref in e75069f79a 2021-01-24 22:25:13 +00:00
Simon Kelley 807e82343a Bump Debian changelog version. 2021-01-24 22:02:56 +00:00
Simon Kelley c8e8f5c204 Bump copyright notices for 2021. Happy New Year! 2021-01-24 21:59:37 +00:00
Simon Kelley a69b017902 Change HAVE_NETTLEHASH compile-time to HAVE_CRYPTOHASH.
HAVE_NETTLEHASH is retained for backwards compatibility, but deprecated.
2021-01-24 21:53:28 +00:00
Simon Kelley e75069f79a Tidy initialisation in hash_questions.c 2021-01-22 22:50:25 +00:00
70 changed files with 9768 additions and 7299 deletions

No files matched your search

+163 -2
View File
@@ -1,3 +1,164 @@
version 2.86
Handle DHCPREBIND requests in the DHCPv6 server code.
Thanks to Aichun Li for spotting this ommision, and the initial
patch.
Fix bug which caused dnsmasq to lose track of processes forked
to handle TCP DNS connections under heavy load. The code
checked that at least one free process table slot was
available before listening on TCP sockets, but didn't take
into account that more than one TCP connection could
arrive, so that check was not sufficient to ensure that
there would be slots for all new processes. It compounded
this error by silently failing to store the process when
it did run out of slots. Even when this bug is triggered,
all the right things happen, and answers are still returned.
Only under very exceptional circumstances, does the bug
manifest itself: see
https://lists.thekelleys.org.uk/pipermail/dnsmasq-discuss/2021q2/014976.html
Thanks to Tijs Van Buggenhout for finding the conditions under
which the bug manifests itself, and then working out
exactly what was going on.
Major rewrite of the DNS server and domain handling code.
This should be largely transparent, but it drastically
improves performance and reduces memory foot-print when
configuring large numbers domains of the form
local=/adserver.com/
or
local=/adserver.com/#
Lookup times now grow as log-to-base-2 of the number of domains,
rather than greater than linearly, as before.
The change makes multiple addresses associated with a domain work
address=/example.com/1.2.3.4
address=/example.com/5.6.7.8
It also handles multiple upstream servers for a domain better; using
the same try/retry alogrithms as non domain-specific servers. This
also applies to DNSSEC-generated queries.
Finally, some of the oldest and gnarliest code in dnsmasq has had
a significant clean-up. It's far from perfect, but it _is_ better.
Revise resource handling for number of concurrent DNS queries. This
used to have a global limit, but that has a problem when using
different servers for different upstream domains. Queries which are
routed by domain to an upstream server which is not responding will
build up and trigger the limit, which breaks DNS service for
all other domains which could be handled by other servers. The
change is to make the limit per server-group, where a server group
is the set of servers configured for a particular domain. In the
common case, where only default servers are declared, there is
no effective change.
Improve efficiency of DNSSEC. The sharing point for DNSSEC RR data
used to be when it entered the cache, having been validated. After
that queries requiring the KEY or DS records would share the cached
values. There is a common case in dual-stack hosts that queries for
A and AAAA records for the same domain are made simultaneously.
If required keys were not in the cache, this would result in two
requests being sent upstream for the same key data (and all the
subsequent chain-of-trust queries.) Now we combine these requests
and elide the duplicates, resulting in fewer queries upstream
and better performance. To keep a better handle on what's
going on, the "extra" logging mode has been modified to associate
queries and answers for DNSSEC queries in the same way as ordinary
queries. The requesting address and port have been removed from
DNSSEC logging lines, since this is no longer strictly defined.
Connection track mark based DNS query filtering. Thanks to
Etan Kissling for implementing this It extends query filtering
support beyond what is currently possible
with the `--ipset` configuration option, by adding support for:
1) Specifying allowlists on a per-client basis, based on their
associated Linux connection track mark.
2) Dynamic configuration of allowlists via Ubus.
3) Reporting when a DNS query resolves or is rejected via Ubus.
4) DNS name patterns containing wildcards.
Disallowed queries are not forwarded; they are rejected
with a REFUSED error code.
version 2.85
Fix problem with DNS retries in 2.83/2.84.
The new logic in 2.83/2.84 which merges distinct requests
for the same domain causes problems with clients which do
retries as distinct requests (differing IDs and/or source ports.)
The retries just get piggy-backed on the first, failed, request.
The logic is now changed so that distinct requests for repeated
queries still get merged into a single ID/source port, but
they now always trigger a re-try upstream.
Thanks to Nicholas Mu for his analysis.
Tweak sort order of tags in get-version. v2.84 sorts
before v2.83, but v2.83 sorts before v2.83rc1 and 2.83rc1
sorts before v2.83test1. This fixes the problem which lead
to 2.84 announcing itself as 2.84rc2.
Avoid treating a --dhcp-host which has an IPv6 address
as eligible for use with DHCPv4 on the grounds that it has
no address, and vice-versa. Thanks to Viktor Papp for
spotting the problem. (This bug was fixed was back in 2.67, and
then regressed in 2.81).
Add --dynamic-host option: A and AAAA records which take their
network part from the network of a local interface. Useful
for routers with dynamically prefixes. Thanks
to Fred F for the suggestion.
Teach --bogus-nxdomain and --ignore-address to take an IPv4 subnet.
Use random source ports where possible if source
addresses/interfaces in use.
CVE-2021-3448 applies. Thanks to Petr Menšík for spotting this.
It's possible to specify the source address or interface to be
used when contacting upstream name servers: server=8.8.8.8@1.2.3.4
or server=8.8.8.8@1.2.3.4#66 or server=8.8.8.8@eth0, and all of
these have, until now, used a single socket, bound to a fixed
port. This was originally done to allow an error (non-existent
interface, or non-local address) to be detected at start-up. This
means that any upstream servers specified in such a way don't use
random source ports, and are more susceptible to cache-poisoning
attacks.
We now use random ports where possible, even when the
source is specified, so server=8.8.8.8@1.2.3.4 or
server=8.8.8.8@eth0 will use random source
ports. server=8.8.8.8@1.2.3.4#66 or any use of --query-port will
use the explicitly configured port, and should only be done with
understanding of the security implications.
Note that this change changes non-existing interface, or non-local
source address errors from fatal to run-time. The error will be
logged and communication with the server not possible.
Change the method of allocation of random source ports for DNS.
Previously, without min-port or max-port configured, dnsmasq would
default to the compiled in defaults for those, which are 1024 and
65535. Now, when neither are configured, it defaults instead to
the kernel's ephemeral port range, which is typically
32768 to 60999 on Linux systems. This change eliminates the
possibility that dnsmasq may be using a registered port > 1024
when a long-running daemon starts up and wishes to claim it.
This change does likely slightly reduce the number of random ports
and therefore the protection from reply spoofing. The older
behaviour can be restored using the min-port and max-port config
switches should that be a concern.
Scale the size of the DNS random-port pool based on the
value of the --dns-forward-max configuration.
Tweak TFTP code to check sender of all received packets, as
specified in RFC 1350 para 4.
version 2.84
Fix a problem, introduced in 2.83, which could see DNS replies
being sent via the wrong socket. On machines running both
IPv4 and IPv6 this could result in sporadic messages of
the form "failed to send packet: Network is unreachable" and
the lost of the query. Since the error is sporadic and of
low probability, the client retry would normally succeed.
Change HAVE_NETTLEHASH compile-time to HAVE_CRYPTOHASH.
version 2.83 version 2.83
Use the values of --min-port and --max-port in outgoing Use the values of --min-port and --max-port in outgoing
TCP connections to upstream DNS servers. TCP connections to upstream DNS servers.
@@ -19,13 +180,13 @@ version 2.83
Handle multiple identical near simultaneous DNS queries better. Handle multiple identical near simultaneous DNS queries better.
Previously, such queries would all be forwarded 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 not a problem, _except_ that is means that an answer for any
of the forwarded queries will be accepted and cached. of the forwarded queries will be accepted and cached.
An attacker can send a query multiple times, and for each repeat, An attacker can send a query multiple times, and for each repeat,
another {port, ID} becomes capable of accepting the answer he is another {port, ID} becomes capable of accepting the answer he is
sending in the blind, to random IDs and ports. The chance of a 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 of the query. The new behaviour detects repeated queries and
merely stores the clients sending repeats so that when the merely stores the clients sending repeats so that when the
first query completes, the answer can be sent to all the first query completes, the answer can be sent to all the
+1 -1
View File
@@ -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 A: Ethernet (and 802.11 wireless) are supported on all platforms. On
Linux all network types (including FireWire) are supported. 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 A: Dnsmasq from v2.63 can operate in one of three different "networking
modes". This is unfortunate as it requires users configuring dnsmasq modes". This is unfortunate as it requires users configuring dnsmasq
+5 -3
View File
@@ -1,4 +1,4 @@
# dnsmasq is Copyright (c) 2000-2016 Simon Kelley # dnsmasq is Copyright (c) 2000-2021 Simon Kelley
# #
# This program is free software; you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@@ -63,8 +63,10 @@ ct_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_CONNTRACK $(PKG_CON
lua_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LUASCRIPT $(PKG_CONFIG) --cflags lua5.2` lua_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` 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' \ 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` HAVE_NETTLEHASH $(PKG_CONFIG) --cflags nettle`
nettle_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC $(PKG_CONFIG) --libs 'nettle hogweed' \ 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` HAVE_NETTLEHASH $(PKG_CONFIG) --libs nettle`
gmp_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC NO_GMP --copy -lgmp` 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` sunos_libs = `if uname | grep SunOS >/dev/null 2>&1; then echo -lsocket -lnsl -lposix4; fi`
@@ -77,10 +79,10 @@ copts_conf = .copts_$(sum)
objs = cache.o rfc1035.o util.o option.o forward.o network.o \ 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 \ 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 \ 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 \ 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 \ poll.o rrfilter.o edns0.o arp.o crypto.o dump.o ubus.o \
metrics.o hash_questions.o metrics.o hash-questions.o domain-match.o
hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \ hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \
dns-protocol.h radv-protocol.h ip6addr.h metrics.h dns-protocol.h radv-protocol.h ip6addr.h metrics.h
+2 -1
View File
@@ -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 \ radv.c slaac.c auth.c ipset.c domain.c \
dnssec.c dnssec-openssl.c blockdata.c tables.c \ dnssec.c dnssec-openssl.c blockdata.c tables.c \
loop.c inotify.c poll.c rrfilter.c edns0.c arp.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 LOCAL_MODULE := dnsmasq
+5 -2
View File
@@ -9,7 +9,10 @@
# If we can find one which matches $v[0-9].* then we assume it's # 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. # 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 # 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. # Change directory to the toplevel source directory.
if test -z "$1" || ! test -d "$1" || ! cd "$1"; then 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]` vers=`cat $1/VERSION | sed 's/[(), ]/,/ g' | tr ',' '\n' | grep ^v[0-9]`
if [ $? -eq 0 ]; then 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 else
cat $1/VERSION cat $1/VERSION
fi fi
+39 -1
View File
@@ -1,3 +1,41 @@
dnsmasq (2.86-1) unstable; urgency=low
* Fix debian/changelog format error. (closes: #986626)
-- Simon Kelley <simon@thekelleys.org.uk> Thu, 08 Apr 2021 22:39:00 +0100
dnsmasq (2.85-1) unstable; urgency=low
* New upstream.
* Includes fix to CVE-2021-3448.
* Fix manpage typos. (closes: #986150)
-- Simon Kelley <simon@thekelleys.org.uk> Sat, 03 Apr 2021 22:17:23 +0100
dnsmasq (2.84-1.2) unstable; urgency=medium
* Non-maintainer upload.
* Bump old-version in dpkg-maintscript-helper dir_to_symlink calls to also
clean up after upgrades to an earlier version in testing.
-- Andreas Beckmann <anbe@debian.org> Thu, 01 Apr 2021 16:01:51 +0200
dnsmasq (2.84-1.1) unstable; urgency=medium
* Non-maintainer upload.
* Fix symlink to directory conversion for /usr/share/doc/dnsmasq.
This is achieved by directly calling dpkg-maintscript-helper in the preinst,
postinst, and postrm scripts, since the package does not use debhelper.
(Closes: #985282)
-- Sébastien Villemot <sebastien@debian.org> Sun, 28 Mar 2021 10:55:07 +0200
dnsmasq (2.84-1) unstable; urgency=low
* New upstream.
-- Simon Kelley <simon@thekelleys.org.uk> Sun, 24 Jan 2021 22:02:01 +0000
dnsmasq (2.83-1) unstable; urgency=high dnsmasq (2.83-1) unstable; urgency=high
* New upstream. * New upstream.
@@ -1043,7 +1081,7 @@ dnsmasq (2.6-2) unstable; urgency=low
* Added note about the --bind-interfaces option to * Added note about the --bind-interfaces option to
readme.Debian (closes: #241700) 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 dnsmasq (2.6-1) unstable; urgency=low
+1 -1
View File
@@ -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/ It was downloaded from: http://www.thekelleys.org.uk/dnsmasq/
+3
View File
@@ -1,6 +1,9 @@
#!/bin/sh #!/bin/sh
set -e 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 ---------------------- # Code copied from dh_systemd_enable ----------------------
# This will only remove masks created by d-s-h on package removal. # This will only remove masks created by d-s-h on package removal.
deb-systemd-helper unmask dnsmasq.service >/dev/null || true deb-systemd-helper unmask dnsmasq.service >/dev/null || true
+3
View File
@@ -1,6 +1,9 @@
#!/bin/sh #!/bin/sh
set -e 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 if [ purge = "$1" ]; then
update-rc.d dnsmasq remove >/dev/null update-rc.d dnsmasq remove >/dev/null
fi fi
+5
View 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 -1
View File
@@ -176,7 +176,7 @@ binary-indep: checkroot
-d debian/trees/daemon/usr/lib/tmpfiles.d \ -d debian/trees/daemon/usr/lib/tmpfiles.d \
-d debian/trees/daemon/etc/insserv.conf.d -d debian/trees/daemon/etc/insserv.conf.d
install -m 644 debian/conffiles debian/trees/daemon/DEBIAN 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 \ if ! dpkg-vendor --derives-from Ubuntu; then \
rm -f debian/dnsmasq.postinst.debhelper debian/dnsmasq.postrm.debhelper; \ rm -f debian/dnsmasq.postinst.debhelper debian/dnsmasq.postrm.debhelper; \
dh_runit -pdnsmasq -Pdebian/trees/daemon; \ dh_runit -pdnsmasq -Pdebian/trees/daemon; \
+87 -19
View File
@@ -135,6 +135,9 @@ running, will go exclusively to the file.) When logging to a file,
dnsmasq will close and reopen the file when it receives SIGUSR2. This dnsmasq will close and reopen the file when it receives SIGUSR2. This
allows the log file to be rotated without stopping dnsmasq. allows the log file to be rotated without stopping dnsmasq.
.TP .TP
.B --log-debug
Enable extra logging intended for debugging rather than information.
.TP
.B --log-async[=<lines>] .B --log-async[=<lines>]
Enable asynchronous logging and optionally set the limit on the Enable asynchronous logging and optionally set the limit on the
number of lines number of lines
@@ -181,7 +184,7 @@ OS: this was the default behaviour in versions prior to 2.43.
.B --min-port=<port> .B --min-port=<port>
Do not use ports less than that given as source for outbound DNS Do not use ports less than that given as source for outbound DNS
queries. Dnsmasq picks random ports as source for outbound queries: 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, than that specified. Useful for systems behind firewalls. If not specified,
defaults to 1024. defaults to 1024.
.TP .TP
@@ -296,7 +299,7 @@ option requires non-standard networking APIs and it is only available
under Linux. On other platforms it falls-back to \fB--bind-interfaces\fP mode. under Linux. On other platforms it falls-back to \fB--bind-interfaces\fP mode.
.TP .TP
.B \-y, --localise-queries .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 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 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 interface to which the query was sent, then return only the
@@ -323,17 +326,17 @@ are re-written. So
.B --alias=192.168.0.10-192.168.0.40,10.0.0.0,255.255.255.0 .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 maps 192.168.0.10->192.168.0.40 to 10.0.0.10->10.0.0.40
.TP .TP
.B \-B, --bogus-nxdomain=<ipaddr> .B \-B, --bogus-nxdomain=<ipaddr>[/prefix]
Transform replies which contain the IP address given into "No such Transform replies which contain the specified address or subnet into "No such
domain" replies. This is intended to counteract a devious move made by 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 Verisign in September 2003 when they started returning the address of
an advertising web page in response to queries for unregistered names, an advertising web page in response to queries for unregistered names,
instead of the correct NXDOMAIN response. This option tells dnsmasq to instead of the correct NXDOMAIN response. This option tells dnsmasq to
fake the correct response when it sees this behaviour. As at Sept 2003 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 the IP address being returned by Verisign is 64.94.110.11
.TP .TP
.B --ignore-address=<ipaddr> .B --ignore-address=<ipaddr>[/prefix]
Ignore replies to A-record queries which include the specified address. 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. 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 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. forged answer to a DNS request for certain domain, before the correct answer can arrive.
@@ -368,7 +371,10 @@ provides service at that name, rather than the default which is
.TP .TP
.B --enable-ubus[=<service-name>] .B --enable-ubus[=<service-name>]
Enable dnsmasq UBus interface. It sends notifications via UBus on 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 Requires that dnsmasq has been built with UBus support. If the service
name is given, dnsmasq provides service at that namespace, rather than name is given, dnsmasq provides service at that namespace, rather than
the default which is the default which is
@@ -428,7 +434,7 @@ Tells dnsmasq to never forward A or AAAA queries for plain names, without dots
or domain parts, to upstream nameservers. If the name is not known or domain parts, to upstream nameservers. If the name is not known
from /etc/hosts or DHCP then a "not found" answer is returned. from /etc/hosts or DHCP then a "not found" answer is returned.
.TP .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 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 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 optional domains are given, that server is used only for those domains
@@ -454,13 +460,22 @@ repeated domain or ipaddr parts as required.
More specific domains take precedence over less specific domains, so: More specific domains take precedence over less specific domains, so:
.B --server=/google.com/1.2.3.4 .B --server=/google.com/1.2.3.4
.B --server=/www.google.com/2.3.4.5 .B --server=/www.google.com/2.3.4.5
will send queries for *.google.com to 1.2.3.4, except *www.google.com, will send queries for google.com and gmail.google.com to 1.2.3.4, but www.google.com
which will go to 2.3.4.5 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 The special server address '#' means, "use the standard servers", so
.B --server=/google.com/1.2.3.4 .B --server=/google.com/1.2.3.4
.B --server=/www.google.com/# .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. be forwarded as usual.
Also permitted is a -S Also permitted is a -S
@@ -489,7 +504,7 @@ source address specified but the port may be specified directly as
part of the source address. Forcing queries to an interface is not part of the source address. Forcing queries to an interface is not
implemented on all platforms supported by dnsmasq. implemented on all platforms supported by dnsmasq.
.TP .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 This is functionally the same as
.B --server, .B --server,
but provides some syntactic sugar to make specifying address-to-name queries easier. For example but provides some syntactic sugar to make specifying address-to-name queries easier. For example
@@ -507,7 +522,7 @@ To include multiple IP addresses for a single query, use
Note that /etc/hosts and DHCP leases override this for individual Note that /etc/hosts and DHCP leases override this for individual
names. A common use of this is to redirect the entire doubleclick.net 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 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 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 \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 query not answered from \fB/etc/hosts\fP or DHCP and not sent to an
@@ -533,6 +548,32 @@ These IP sets must already exist. See
.BR ipset (8) .BR ipset (8)
for more details. for more details.
.TP .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>] .B \-m, --mx-host=<mx name>[[,<hostname>],<preference>]
Return an MX record named <mx name> pointing to the given hostname (if Return an MX record named <mx name> pointing to the given hostname (if
given), or given), or
@@ -591,6 +632,12 @@ If the time-to-live is given, it overrides the default, which is zero
or the value of \fB--local-ttl\fP. The value is a positive integer and gives or the value of \fB--local-ttl\fP. The value is a positive integer and gives
the time-to-live in seconds. the time-to-live in seconds.
.TP .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>] .B \-Y, --txt-record=<name>[[,<text>],<text>]
Return a TXT DNS record. The value of TXT record is a set of strings, 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 so any number may be included, delimited by commas; use quotes to put
@@ -702,7 +749,13 @@ will add the /24 and /96 subnets of the requestor for IPv4 and IPv6 requestors,
will add 1.2.3.0/24 for IPv4 requestors and ::/0 for IPv6 requestors. 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 .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. will add 1.2.3.0/24 for both IPv4 and IPv6 requestors.
.TP
.B --umbrella[=deviceid:<deviceid>[,orgid:<orgid>]]
Embeds the requestor's IP address in DNS queries forwarded upstream.
If device id or organization id are specified, the information is
included in the forwarded queries and may be able to be used in
filtering policies and reporting. The order of the deviceid and orgid
attributes is irrelevant, but must be separated by a comma.
.TP .TP
.B \-c, --cache-size=<cachesize> .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. 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 +769,8 @@ identical queries without forwarding them again.
Set the maximum number of concurrent DNS queries. The default value is Set the maximum number of concurrent DNS queries. The default value is
150, which should be fine for most setups. The only known situation 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 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 .TP
.B --dnssec .B --dnssec
Validate DNS replies and cache DNSSEC data. When forwarding DNS queries, dnsmasq requests the Validate DNS replies and cache DNSSEC data. When forwarding DNS queries, dnsmasq requests the
@@ -860,7 +914,7 @@ in
.B --dhcp-host .B --dhcp-host
options. If the lease time is given, then leases options. If the lease time is given, then leases
will be given for that length of time. The lease time is in seconds, 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 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 minimum lease time is two minutes. For IPv6 ranges, the lease time
maybe "deprecated"; this sets the preferred lifetime sent in a DHCP maybe "deprecated"; this sets the preferred lifetime sent in a DHCP
@@ -1045,6 +1099,19 @@ option, but aliases are possible by using CNAMEs. (See
.B --cname .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" The special keyword "ignore"
tells dnsmasq to never offer a DHCP lease to a machine. The machine tells dnsmasq to never offer a DHCP lease to a machine. The machine
can be specified by hardware address, client ID or hostname, for can be specified by hardware address, client ID or hostname, for
@@ -1436,7 +1503,7 @@ functions when supported by a suitable DHCP server.
This specifies a boot option which may appear in a PXE boot menu. <CSA> is 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 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, 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 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 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, boot server and directs the PXE client to download the file by TFTP,
@@ -1495,7 +1562,6 @@ instance
will enable dnsmasq to also provide proxy PXE service to those PXE clients with will enable dnsmasq to also provide proxy PXE service to those PXE clients with
.I HW-Client .I HW-Client
in as their identifier. in as their identifier.
>>>>>>> 907def3... pxe: support pxe clients with custom vendor-class
.TP .TP
.B \-X, --dhcp-lease-max=<number> .B \-X, --dhcp-lease-max=<number>
Limits dnsmasq to the specified maximum number of DHCP leases. The Limits dnsmasq to the specified maximum number of DHCP leases. The
@@ -2367,6 +2433,8 @@ IPv4 and IPv6 addresses from /etc/hosts (and
.B --host-record .B --host-record
and and
.B --interface-name .B --interface-name
and
.B ---dynamic-host
provided the address falls into one of the subnets specified in the provided the address falls into one of the subnets specified in the
.B --auth-zone. .B --auth-zone.
.PP .PP
+709 -723
View File
File diff suppressed because it is too large. Load diff
+504 -467
View File
File diff suppressed because it is too large. Load diff
+539 -504
View File
File diff suppressed because it is too large. Load diff
+504 -467
View File
File diff suppressed because it is too large. Load diff
+540 -500
View File
File diff suppressed because it is too large. Load diff
+539 -504
View File
File diff suppressed because it is too large. Load diff
+535 -498
View File
File diff suppressed because it is too large. Load diff
+504 -467
View File
File diff suppressed because it is too large. Load diff
+539 -504
View File
File diff suppressed because it is too large. Load diff
+535 -498
View File
File diff suppressed because it is too large. Load diff
+1 -3
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 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; return 0;
} }
+1 -4
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -867,6 +867,3 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
} }
#endif #endif
+1 -2
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -174,4 +174,3 @@ struct blockdata *blockdata_read(int fd, size_t len)
{ {
return blockdata_alloc_real(fd, NULL, len); return blockdata_alloc_real(fd, NULL, len);
} }
+1 -3
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 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 */ #endif /* HAVE_BSD_NETWORK */
+63 -20
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -1602,21 +1602,18 @@ int cache_make_stat(struct txt_record *t)
case TXT_STAT_SERVERS: case TXT_STAT_SERVERS:
/* sum counts from different records for same server */ /* sum counts from different records for same server */
for (serv = daemon->servers; serv; serv = serv->next) for (serv = daemon->servers; serv; serv = serv->next)
serv->flags &= ~SERV_COUNTED; serv->flags &= ~SERV_MARK;
for (serv = daemon->servers; serv; serv = serv->next) for (serv = daemon->servers; serv; serv = serv->next)
if (!(serv->flags & if (!(serv->flags & SERV_MARK))
(SERV_NO_ADDR | SERV_LITERAL_ADDRESS | SERV_COUNTED | SERV_USE_RESOLV | SERV_NO_REBIND)))
{ {
char *new, *lenp; char *new, *lenp;
int port, newlen, bytes_avail, bytes_needed; int port, newlen, bytes_avail, bytes_needed;
unsigned int queries = 0, failed_queries = 0; unsigned int queries = 0, failed_queries = 0;
for (serv1 = serv; serv1; serv1 = serv1->next) for (serv1 = serv; serv1; serv1 = serv1->next)
if (!(serv1->flags & if (!(serv1->flags & SERV_MARK) && sockaddr_isequal(&serv->addr, &serv1->addr))
(SERV_NO_ADDR | SERV_LITERAL_ADDRESS | SERV_COUNTED | SERV_USE_RESOLV | SERV_NO_REBIND)) &&
sockaddr_isequal(&serv->addr, &serv1->addr))
{ {
serv1->flags |= SERV_COUNTED; serv1->flags |= SERV_MARK;
queries += serv1->queries; queries += serv1->queries;
failed_queries += serv1->failed_queries; failed_queries += serv1->failed_queries;
} }
@@ -1644,6 +1641,7 @@ int cache_make_stat(struct txt_record *t)
} }
t->txt = (unsigned char *)buff; t->txt = (unsigned char *)buff;
t->len = p - buff; t->len = p - buff;
return 1; return 1;
} }
@@ -1686,20 +1684,17 @@ void dump_cache(time_t now)
/* sum counts from different records for same server */ /* sum counts from different records for same server */
for (serv = daemon->servers; serv; serv = serv->next) for (serv = daemon->servers; serv; serv = serv->next)
serv->flags &= ~SERV_COUNTED; serv->flags &= ~SERV_MARK;
for (serv = daemon->servers; serv; serv = serv->next) for (serv = daemon->servers; serv; serv = serv->next)
if (!(serv->flags & if (!(serv->flags & SERV_MARK))
(SERV_NO_ADDR | SERV_LITERAL_ADDRESS | SERV_COUNTED | SERV_USE_RESOLV | SERV_NO_REBIND)))
{ {
int port; int port;
unsigned int queries = 0, failed_queries = 0; unsigned int queries = 0, failed_queries = 0;
for (serv1 = serv; serv1; serv1 = serv1->next) for (serv1 = serv; serv1; serv1 = serv1->next)
if (!(serv1->flags & if (!(serv1->flags & SERV_MARK) && sockaddr_isequal(&serv->addr, &serv1->addr))
(SERV_NO_ADDR | SERV_LITERAL_ADDRESS | SERV_COUNTED | SERV_USE_RESOLV | SERV_NO_REBIND)) &&
sockaddr_isequal(&serv->addr, &serv1->addr))
{ {
serv1->flags |= SERV_COUNTED; serv1->flags |= SERV_MARK;
queries += serv1->queries; queries += serv1->queries;
failed_queries += serv1->failed_queries; failed_queries += serv1->failed_queries;
} }
@@ -1867,10 +1862,44 @@ char *querystr(char *desc, unsigned short type)
return buff ? buff : ""; return buff ? buff : "";
} }
static char *edestr(int ede)
{
switch (ede)
{
case EDE_OTHER: return "other";
case EDE_USUPDNSKEY: return "unsupported DNSKEY algorithm";
case EDE_USUPDS: return "unsupported DS digest";
case EDE_STALE: return "stale answer";
case EDE_FORGED: return "forged";
case EDE_DNSSEC_IND: return "DNSSEC indeterminate";
case EDE_DNSSEC_BOGUS: return "DNSSEC bogus";
case EDE_SIG_EXP: return "DNSSEC signature expired";
case EDE_SIG_NYV: return "DNSSEC sig not yet valid";
case EDE_NO_DNSKEY: return "DNSKEY missing";
case EDE_NO_RRSIG: return "RRSIG missing";
case EDE_NO_ZONEKEY: return "no zone key bit set";
case EDE_NO_NSEC: return "NSEC(3) missing";
case EDE_CACHED_ERR: return "cached error";
case EDE_NOT_READY: return "not ready";
case EDE_BLOCKED: return "blocked";
case EDE_CENSORED: return "censored";
case EDE_FILTERED: return "filtered";
case EDE_PROHIBITED: return "prohibited";
case EDE_STALE_NXD: return "stale NXDOMAIN";
case EDE_NOT_AUTH: return "not authoritative";
case EDE_NOT_SUP: return "not supported";
case EDE_NO_AUTH: return "no reachable authority";
case EDE_NETERR: return "network error";
case EDE_INVALID_DATA: return "invalid data";
default: return "unknown";
}
}
void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg) void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg)
{ {
char *source, *dest = daemon->addrbuff; char *source, *dest = daemon->addrbuff;
char *verb = "is"; char *verb = "is";
char *extra = "";
if (!option_bool(OPT_LOG)) if (!option_bool(OPT_LOG))
return; return;
@@ -1893,6 +1922,12 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg)
dest = "not implemented"; dest = "not implemented";
else else
sprintf(daemon->addrbuff, "%u", rcode); sprintf(daemon->addrbuff, "%u", rcode);
if (addr->log.ede != EDE_UNSET)
{
extra = daemon->addrbuff;
sprintf(extra, " (EDE: %s)", edestr(addr->log.ede));
}
} }
else else
inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6, inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
@@ -1938,7 +1973,15 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg)
else if (flags & F_UPSTREAM) else if (flags & F_UPSTREAM)
source = "reply"; source = "reply";
else if (flags & F_SECSTAT) else if (flags & F_SECSTAT)
{
if (addr && addr->log.ede != EDE_UNSET)
{
extra = daemon->addrbuff;
sprintf(extra, " (EDE: %s)", edestr(addr->log.ede));
}
source = "validation"; source = "validation";
dest = arg;
}
else if (flags & F_AUTH) else if (flags & F_AUTH)
source = "auth"; source = "auth";
else if (flags & F_SERVER) else if (flags & F_SERVER)
@@ -1971,14 +2014,14 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg)
if (option_bool(OPT_EXTRALOG)) if (option_bool(OPT_EXTRALOG))
{ {
int port = prettyprint_addr(daemon->log_source_addr, daemon->addrbuff2);
if (flags & F_NOEXTRA) 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 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 else
my_syslog(LOG_INFO, "%s %s %s %s", source, name, verb, dest); my_syslog(LOG_INFO, "%s %s %s %s", source, name, verb, dest);
} }
+8 -11
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -29,7 +29,6 @@
#define UDP_TEST_TIME 60 /* How often to reset our idea of max packet size. */ #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 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 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 LEASE_RETRY 60 /* on error, retry writing leasefile after LEASE_RETRY seconds */
#define CACHESIZ 150 /* default cache size */ #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 */ #define TTL_FLOOR_LIMIT 3600 /* don't allow --min-cache-ttl to raise TTL above this under any circumstances */
@@ -120,8 +119,8 @@ HAVE_AUTH
define this to include the facility to act as an authoritative DNS define this to include the facility to act as an authoritative DNS
server for one or more zones. server for one or more zones.
HAVE_NETTLEHASH HAVE_CRYPTOHASH
include just hash function from nettle, but no DNSSEC. include just hash function from crypto library, but no DNSSEC.
HAVE_DNSSEC HAVE_DNSSEC
include DNSSEC validator. include DNSSEC validator.
@@ -144,6 +143,7 @@ NO_SCRIPT
NO_LARGEFILE NO_LARGEFILE
NO_AUTH NO_AUTH
NO_DUMPFILE NO_DUMPFILE
NO_LOOP
NO_INOTIFY NO_INOTIFY
these are available to explicitly disable compile time options which would these are available to explicitly disable compile time options which would
otherwise be enabled automatically or which are enabled by default otherwise be enabled automatically or which are enabled by default
@@ -190,7 +190,7 @@ RESOLVFILE
/* #define HAVE_IDN */ /* #define HAVE_IDN */
/* #define HAVE_LIBIDN2 */ /* #define HAVE_LIBIDN2 */
/* #define HAVE_CONNTRACK */ /* #define HAVE_CONNTRACK */
/* #define HAVE_NETTLEHASH */ /* #define HAVE_CRYPTOHASH */
/* #define HAVE_DNSSEC */ /* #define HAVE_DNSSEC */
@@ -424,10 +424,10 @@ static char *compile_opts =
"no-" "no-"
#endif #endif
"auth " "auth "
#if !defined(HAVE_NETTLEHASH) && !defined(HAVE_DNSSEC) #if !defined(HAVE_CRYPTOHASH) && !defined(HAVE_DNSSEC)
"no-" "no-"
#endif #endif
"nettlehash " "cryptohash "
#ifndef HAVE_DNSSEC #ifndef HAVE_DNSSEC
"no-" "no-"
#endif #endif
@@ -448,7 +448,4 @@ static char *compile_opts =
#endif #endif
"dumpfile"; "dumpfile";
#endif #endif /* defined(HAVE_DHCP) */
+2 -5
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 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; return NFCT_CB_CONTINUE;
} }
#endif #endif /* HAVE_CONNTRACK */
+63 -46
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -16,21 +16,34 @@
#include "dnsmasq.h" #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/rsa.h>
#include <nettle/ecdsa.h> #include <nettle/ecdsa.h>
#include <nettle/ecc-curve.h> #include <nettle/ecc-curve.h>
#if MIN_VERSION(3, 1)
#include <nettle/eddsa.h> #include <nettle/eddsa.h>
#if NETTLE_VERSION_MAJOR == 3 && NETTLE_VERSION_MINOR >= 6 #endif
#if MIN_VERSION(3, 6)
# include <nettle/gostdsa.h> # include <nettle/gostdsa.h>
#endif #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 /* Implement a "hash-function" to the nettle API, which simply returns
the input data, concatenated into a single, statically maintained, buffer. 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; ctx->len += length;
} }
static void null_hash_digest(void *ctx, size_t length, uint8_t *dst) static void null_hash_digest(void *ctx, size_t length, uint8_t *dst)
{ {
(void)length; (void)length;
@@ -103,35 +115,7 @@ static struct nettle_hash null_hash = {
(nettle_hash_digest_func *) null_hash_digest (nettle_hash_digest_func *) null_hash_digest
}; };
/* Find pointer to correct hash function in nettle library */ #endif /* MIN_VERSION(3, 1) */
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
}
/* expand ctx and digest memory allocations if necessary and init hash function */ /* 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) 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; 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, 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) 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 struct ecc_point *key_256 = NULL, *key_384 = NULL;
static mpz_t x, y; static mpz_t x, y;
static struct dsa_signature *sig_struct; 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_256r1() (&nettle_secp_256r1)
#define nettle_get_secp_384r1() (&nettle_secp_384r1) #define nettle_get_secp_384r1() (&nettle_secp_384r1)
#endif #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); 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, static int dnsmasq_gostdsa_verify(struct blockdata *key_data, unsigned int key_len,
unsigned char *sig, size_t sig_len, unsigned char *sig, size_t sig_len,
unsigned char *digest, size_t digest_len, int algo) 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 #endif
#if MIN_VERSION(3, 1)
static int dnsmasq_eddsa_verify(struct blockdata *key_data, unsigned int key_len, static int dnsmasq_eddsa_verify(struct blockdata *key_data, unsigned int key_len,
unsigned char *sig, size_t sig_len, unsigned char *sig, size_t sig_len,
unsigned char *digest, size_t digest_len, int algo) 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, ((struct null_hash_digest *)digest)->buff,
sig); sig);
#if NETTLE_VERSION_MAJOR == 3 && NETTLE_VERSION_MINOR >= 6 #if MIN_VERSION(3, 6)
case 16: case 16:
if (key_len != ED448_KEY_SIZE || if (key_len != ED448_KEY_SIZE ||
sig_len != ED448_SIGNATURE_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; return 0;
} }
#endif
static int (*verify_func(int algo))(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len, 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) unsigned char *digest, size_t digest_len, int algo)
@@ -399,7 +381,7 @@ static int (*verify_func(int algo))(struct blockdata *key_data, unsigned int key
case 5: case 7: case 8: case 10: case 5: case 7: case 8: case 10:
return dnsmasq_rsa_verify; return dnsmasq_rsa_verify;
#if NETTLE_VERSION_MAJOR == 3 && NETTLE_VERSION_MINOR >= 6 #if MIN_VERSION(3, 6)
case 12: case 12:
return dnsmasq_gostdsa_verify; return dnsmasq_gostdsa_verify;
#endif #endif
@@ -407,8 +389,10 @@ static int (*verify_func(int algo))(struct blockdata *key_data, unsigned int key
case 13: case 14: case 13: case 14:
return dnsmasq_ecdsa_verify; return dnsmasq_ecdsa_verify;
#if MIN_VERSION(3, 1)
case 15: case 16: case 15: case 16:
return dnsmasq_eddsa_verify; return dnsmasq_eddsa_verify;
#endif
} }
return NULL; 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 #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) */
+7 -11
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -215,7 +215,7 @@ static void dbus_read_servers(DBusMessage *message)
domain = NULL; domain = NULL;
if (!skip) 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); } while (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING);
} }
@@ -276,7 +276,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
{ {
const char *str = NULL; const char *str = NULL;
union mysockaddr addr, source_addr; union mysockaddr addr, source_addr;
int flags = 0; u16 flags = 0;
char interface[IF_NAMESIZE]; char interface[IF_NAMESIZE];
char *str_addr, *str_domain = NULL; char *str_addr, *str_domain = NULL;
@@ -361,10 +361,6 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
strcpy(str_addr, str); 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 */ /* parse the IP address */
if ((addr_err = parse_server(str_addr, &addr, &source_addr, (char *) &interface, &flags))) if ((addr_err = parse_server(str_addr, &addr, &source_addr, (char *) &interface, &flags)))
{ {
@@ -377,7 +373,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
/* 0.0.0.0 for server address == NULL, for Dbus */ /* 0.0.0.0 for server address == NULL, for Dbus */
if (addr.in.sin_family == AF_INET && if (addr.in.sin_family == AF_INET &&
addr.in.sin_addr.s_addr == 0) addr.in.sin_addr.s_addr == 0)
flags |= SERV_NO_ADDR; flags |= SERV_LITERAL_ADDRESS;
if (strings) if (strings)
{ {
@@ -392,7 +388,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
else else
p = NULL; 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)); } while ((str_domain = p));
} }
else else
@@ -407,7 +403,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
dbus_message_iter_get_basic(&string_iter, &str); dbus_message_iter_get_basic(&string_iter, &str);
dbus_message_iter_next (&string_iter); 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); } while (dbus_message_iter_get_arg_type(&string_iter) == DBUS_TYPE_STRING);
} }
@@ -715,7 +711,7 @@ DBusHandlerResult message_handler(DBusConnection *connection,
if (new_servers) if (new_servers)
{ {
my_syslog(LOG_INFO, _("setting upstream servers from DBus")); my_syslog(LOG_INFO, _("setting upstream servers from DBus"));
check_servers(); check_servers(0);
if (option_bool(OPT_RELOAD)) if (option_bool(OPT_RELOAD))
clear_cache = 1; clear_cache = 1;
} }
+5 -7
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -281,14 +281,15 @@ static int is_config_in_context(struct dhcp_context *context, struct dhcp_config
if (!context) /* called via find_config() from lease_update_from_configs() */ if (!context) /* called via find_config() from lease_update_from_configs() */
return 1; return 1;
if (!(config->flags & (CONFIG_ADDR | CONFIG_ADDR6)))
return 1;
#ifdef HAVE_DHCP6 #ifdef HAVE_DHCP6
if (context->flags & CONTEXT_V6) if (context->flags & CONTEXT_V6)
{ {
struct addrlist *addr_list; struct addrlist *addr_list;
if (!(config->flags & CONFIG_ADDR6)) if (config->flags & CONFIG_ADDR6)
return 1;
for (; context; context = context->current) for (; context; context = context->current)
for (addr_list = config->addr6; addr_list; addr_list = addr_list->next) for (addr_list = config->addr6; addr_list; addr_list = addr_list->next)
{ {
@@ -302,9 +303,6 @@ static int is_config_in_context(struct dhcp_context *context, struct dhcp_config
else else
#endif #endif
{ {
if (!(config->flags & CONFIG_ADDR))
return 1;
for (; context; context = context->current) for (; context; context = context->current)
if ((config->flags & CONFIG_ADDR) && is_same_net(config->addr, context->start, context->netmask)) if ((config->flags & CONFIG_ADDR) && is_same_net(config->addr, context->start, context->netmask))
return 1; return 1;
+1 -1
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
+1 -2
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -72,4 +72,3 @@
#define DHCP6NOBINDING 3 #define DHCP6NOBINDING 3
#define DHCP6NOTONLINK 4 #define DHCP6NOTONLINK 4
#define DHCP6USEMULTI 5 #define DHCP6USEMULTI 5
+2 -4
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -825,6 +825,4 @@ void dhcp_construct_contexts(time_t now)
} }
} }
#endif #endif /* HAVE_DHCP6 */
+34 -1
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 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_MAC 65001 /* dyndns.org temporary assignment */
#define EDNS0_OPTION_CLIENT_SUBNET 8 /* IANA */ #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_NOMDEVICEID 65073 /* Nominum temporary assignment */
#define EDNS0_OPTION_NOMCPEID 65074 /* 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 { struct dns_header {
u16 id; u16 id;
+98 -57
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -24,7 +24,7 @@ struct daemon *daemon;
static volatile pid_t pid = 0; static volatile pid_t pid = 0;
static volatile int pipewrite; 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 check_dns_listeners(time_t now);
static void sig_handler(int sig); static void sig_handler(int sig);
static void async_event(int pipe, time_t now); static void async_event(int pipe, time_t now);
@@ -135,6 +135,13 @@ int main (int argc, char **argv)
} }
#endif #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 #ifdef HAVE_DHCP
if (!daemon->lease_file) if (!daemon->lease_file)
{ {
@@ -237,6 +244,13 @@ int main (int argc, char **argv)
die(_("Ubus not available: set HAVE_UBUS in src/config.h"), NULL, EC_BADCONF); die(_("Ubus not available: set HAVE_UBUS in src/config.h"), NULL, EC_BADCONF);
#endif #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) if (daemon->max_port < daemon->min_port)
die(_("max_port cannot be smaller than min_port"), NULL, EC_BADCONF); die(_("max_port cannot be smaller than min_port"), NULL, EC_BADCONF);
@@ -390,8 +404,16 @@ int main (int argc, char **argv)
if (daemon->port != 0) if (daemon->port != 0)
{ {
cache_init(); cache_init();
blockdata_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 #ifdef HAVE_INOTIFY
@@ -415,8 +437,6 @@ int main (int argc, char **argv)
#ifdef HAVE_DBUS #ifdef HAVE_DBUS
{ {
char *err; char *err;
daemon->dbus = NULL;
daemon->watches = NULL;
if ((err = dbus_init())) if ((err = dbus_init()))
die(_("DBus error: %s"), err, EC_MISC); die(_("DBus error: %s"), err, EC_MISC);
} }
@@ -427,8 +447,9 @@ int main (int argc, char **argv)
if (option_bool(OPT_UBUS)) if (option_bool(OPT_UBUS))
#ifdef HAVE_UBUS #ifdef HAVE_UBUS
{ {
daemon->ubus = NULL; char *err;
ubus_init(); if ((err = ubus_init()))
die(_("UBus error: %s"), err, EC_MISC);
} }
#else #else
die(_("UBus not available: set HAVE_UBUS in src/config.h"), NULL, EC_BADCONF); die(_("UBus not available: set HAVE_UBUS in src/config.h"), NULL, EC_BADCONF);
@@ -980,7 +1001,7 @@ int main (int argc, char **argv)
a single file will be sent to may clients (the file only needs a single file will be sent to may clients (the file only needs
one fd). */ one fd). */
max_fd -= 30; /* use other than TFTP */ max_fd -= 30 + daemon->numrrand; /* use other than TFTP */
if (max_fd < 0) if (max_fd < 0)
max_fd = 5; max_fd = 5;
@@ -1010,7 +1031,7 @@ int main (int argc, char **argv)
close(err_pipe[1]); close(err_pipe[1]);
if (daemon->port != 0) if (daemon->port != 0)
check_servers(); check_servers(0);
pid = getpid(); pid = getpid();
@@ -1025,16 +1046,10 @@ int main (int argc, char **argv)
while (1) while (1)
{ {
int t, timeout = -1; int timeout = -1;
poll_reset(); 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 */ /* Whilst polling for the dbus, or doing a tftp transfer, wake every quarter second */
if (daemon->tftp_trans || if (daemon->tftp_trans ||
(option_bool(OPT_DBUS) && !daemon->dbus)) (option_bool(OPT_DBUS) && !daemon->dbus))
@@ -1044,7 +1059,10 @@ int main (int argc, char **argv)
else if (is_dad_listeners()) else if (is_dad_listeners())
timeout = 1000; timeout = 1000;
set_dns_listeners();
#ifdef HAVE_DBUS #ifdef HAVE_DBUS
if (option_bool(OPT_DBUS))
set_dbus_listeners(); set_dbus_listeners();
#endif #endif
@@ -1172,24 +1190,40 @@ int main (int argc, char **argv)
#ifdef HAVE_DBUS #ifdef HAVE_DBUS
/* if we didn't create a DBus connection, retry now. */ /* 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 (!daemon->dbus)
if ((err = dbus_init())) {
my_syslog(LOG_WARNING, _("DBus error: %s"), err); char *err = dbus_init();
if (daemon->dbus) if (daemon->dbus)
my_syslog(LOG_INFO, _("connected to system 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 #endif
#ifdef HAVE_UBUS #ifdef HAVE_UBUS
/* if we didn't create a UBus connection, retry now. */
if (option_bool(OPT_UBUS)) if (option_bool(OPT_UBUS))
{ {
/* if we didn't create a UBus connection, retry now. */
if (!daemon->ubus) if (!daemon->ubus)
{ {
ubus_init(); char *err = ubus_init();
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(); check_ubus_listeners();
@@ -1426,7 +1460,7 @@ static void async_event(int pipe, time_t now)
} }
if (check) if (check)
check_servers(); check_servers(0);
} }
#ifdef HAVE_DHCP #ifdef HAVE_DHCP
@@ -1625,7 +1659,7 @@ static void poll_resolv(int force, int do_reload, time_t now)
{ {
my_syslog(LOG_INFO, _("reading %s"), latest->name); my_syslog(LOG_INFO, _("reading %s"), latest->name);
warned = 0; warned = 0;
check_servers(); check_servers(0);
if (option_bool(OPT_RELOAD) && do_reload) if (option_bool(OPT_RELOAD) && do_reload)
clear_cache_and_reload(now); clear_cache_and_reload(now);
} }
@@ -1668,11 +1702,12 @@ void clear_cache_and_reload(time_t now)
#endif #endif
} }
static int set_dns_listeners(time_t now) static void set_dns_listeners(void)
{ {
struct serverfd *serverfdp; struct serverfd *serverfdp;
struct listener *listener; struct listener *listener;
int wait = 0, i; struct randfd_list *rfl;
int i;
#ifdef HAVE_TFTP #ifdef HAVE_TFTP
int tftp = 0; int tftp = 0;
@@ -1685,66 +1720,68 @@ static int set_dns_listeners(time_t now)
} }
#endif #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) for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
poll_listen(serverfdp->fd, POLLIN); poll_listen(serverfdp->fd, POLLIN);
if (daemon->port != 0 && !daemon->osport) for (i = 0; i < daemon->numrrand; i++)
for (i = 0; i < RANDOM_SOCKS; i++)
if (daemon->randomsocks[i].refcount != 0) if (daemon->randomsocks[i].refcount != 0)
poll_listen(daemon->randomsocks[i].fd, POLLIN); 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) for (listener = daemon->listeners; listener; listener = listener->next)
{ {
/* only listen for queries if we have resources */ if (listener->fd != -1)
if (listener->fd != -1 && wait == 0)
poll_listen(listener->fd, POLLIN); poll_listen(listener->fd, POLLIN);
/* death of a child goes through the select loop, so /* Only listen for TCP connections when a process slot
we don't need to explicitly arrange to wake up here */ is available. Death of a child goes through the select loop, so
if (listener->tcpfd != -1) we don't need to explicitly arrange to wake up here,
for (i = 0; i < MAX_PROCS; i++) we'll be called again when a slot becomes available. */
if (daemon->tcp_pids[i] == 0 && daemon->tcp_pipes[i] == -1) if (listener->tcpfd != -1 && i >= 0)
{
poll_listen(listener->tcpfd, POLLIN); poll_listen(listener->tcpfd, POLLIN);
break;
}
#ifdef HAVE_TFTP #ifdef HAVE_TFTP
/* tftp == 0 in single-port mode. */ /* tftp == 0 in single-port mode. */
if (tftp <= daemon->tftp_max && listener->tftpfd != -1) if (tftp <= daemon->tftp_max && listener->tftpfd != -1)
poll_listen(listener->tftpfd, POLLIN); poll_listen(listener->tftpfd, POLLIN);
#endif #endif
} }
if (!option_bool(OPT_DEBUG)) if (!option_bool(OPT_DEBUG))
for (i = 0; i < MAX_PROCS; i++) for (i = 0; i < MAX_PROCS; i++)
if (daemon->tcp_pipes[i] != -1) if (daemon->tcp_pipes[i] != -1)
poll_listen(daemon->tcp_pipes[i], POLLIN); poll_listen(daemon->tcp_pipes[i], POLLIN);
return wait;
} }
static void check_dns_listeners(time_t now) static void check_dns_listeners(time_t now)
{ {
struct serverfd *serverfdp; struct serverfd *serverfdp;
struct listener *listener; struct listener *listener;
struct randfd_list *rfl;
int i; int i;
int pipefd[2]; int pipefd[2];
for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next) for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
if (poll_check(serverfdp->fd, POLLIN)) 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 < daemon->numrrand; i++)
for (i = 0; i < RANDOM_SOCKS; i++)
if (daemon->randomsocks[i].refcount != 0 && if (daemon->randomsocks[i].refcount != 0 &&
poll_check(daemon->randomsocks[i].fd, POLLIN)) poll_check(daemon->randomsocks[i].fd, POLLIN))
reply_query(daemon->randomsocks[i].fd, daemon->randomsocks[i].family, now); 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 /* 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 pipe, or vice versa. Therefore send tcp_pids to zero when we wait() the
@@ -1773,7 +1810,16 @@ static void check_dns_listeners(time_t now)
tftp_request(listener, now); tftp_request(listener, now);
#endif #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; int confd, client_ok = 1;
struct irec *iface = NULL; struct irec *iface = NULL;
@@ -1863,7 +1909,6 @@ static void check_dns_listeners(time_t now)
close(pipefd[0]); close(pipefd[0]);
else else
{ {
int i;
#ifdef HAVE_LINUX_NETWORK #ifdef HAVE_LINUX_NETWORK
/* The child process inherits the netlink socket, /* The child process inherits the netlink socket,
which it never uses, but when the parent (us) which it never uses, but when the parent (us)
@@ -1883,13 +1928,9 @@ static void check_dns_listeners(time_t now)
read_write(pipefd[0], &a, 1, 1); read_write(pipefd[0], &a, 1, 1);
#endif #endif
for (i = 0; i < MAX_PROCS; i++) /* i holds index of free slot */
if (daemon->tcp_pids[i] == 0 && daemon->tcp_pipes[i] == -1)
{
daemon->tcp_pids[i] = p; daemon->tcp_pids[i] = p;
daemon->tcp_pipes[i] = pipefd[0]; daemon->tcp_pipes[i] = pipefd[0];
break;
}
} }
close(confd); close(confd);
@@ -2068,7 +2109,7 @@ int delay_dhcp(time_t start, int sec, int fd, uint32_t addr, unsigned short id)
poll_reset(); poll_reset();
if (fd != -1) if (fd != -1)
poll_listen(fd, POLLIN); poll_listen(fd, POLLIN);
set_dns_listeners(now); set_dns_listeners();
set_log_writer(); set_log_writer();
#ifdef HAVE_DHCP6 #ifdef HAVE_DHCP6
+164 -63
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 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/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#define COPYRIGHT "Copyright (c) 2000-2020 Simon Kelley" #define COPYRIGHT "Copyright (c) 2000-2021 Simon Kelley"
/* We do defines that influence behavior of stdio.h, so complain /* We do defines that influence behavior of stdio.h, so complain
if included too early. */ if included too early. */
@@ -95,11 +95,7 @@ typedef unsigned long long u64;
#if defined(HAVE_SOLARIS_NETWORK) #if defined(HAVE_SOLARIS_NETWORK)
# include <sys/sockio.h> # include <sys/sockio.h>
#endif #endif
#if defined(HAVE_POLL_H)
#include <poll.h> #include <poll.h>
#else
# include <sys/poll.h>
#endif
#include <sys/wait.h> #include <sys/wait.h>
#include <sys/time.h> #include <sys/time.h>
#include <sys/un.h> #include <sys/un.h>
@@ -157,7 +153,11 @@ extern int capget(cap_user_header_t header, cap_user_data_t data);
#include <priv.h> #include <priv.h>
#endif #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> # include <nettle/nettle-meta.h>
#endif #endif
@@ -269,7 +269,11 @@ struct event_desc {
#define OPT_IGNORE_CLID 59 #define OPT_IGNORE_CLID 59
#define OPT_SINGLE_PORT 60 #define OPT_SINGLE_PORT 60
#define OPT_LEASE_RENEW 61 #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_LAST 66
#define OPTION_BITS (sizeof(unsigned int)*8) #define OPTION_BITS (sizeof(unsigned int)*8)
#define OPTION_SIZE ( (OPT_LAST/OPTION_BITS)+((OPT_LAST%OPTION_BITS)!=0) ) #define OPTION_SIZE ( (OPT_LAST/OPTION_BITS)+((OPT_LAST%OPTION_BITS)!=0) )
@@ -277,11 +281,13 @@ struct event_desc {
#define option_val(x) ((1u) << ((x) % OPTION_BITS)) #define option_val(x) ((1u) << ((x) % OPTION_BITS))
#define option_bool(x) (option_var(x) & option_val(x)) #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 /* 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. */ 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_TFTP LOG_USER
#define MS_DHCP LOG_DAEMON #define MS_DHCP LOG_DAEMON
#define MS_SCRIPT LOG_MAIL #define MS_SCRIPT LOG_MAIL
#define MS_DEBUG LOG_NEWS
/* Note that this is used widely as a container for IPv4/IPv6 addresses, /* 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 so for that reason, was well as to avoid wasting memory in almost every
@@ -317,12 +323,14 @@ union all_addr {
/* for log_query */ /* for log_query */
struct { struct {
unsigned short keytag, algo, digest, rcode; unsigned short keytag, algo, digest, rcode;
int ede;
} log; } log;
}; };
struct bogus_addr { struct bogus_addr {
struct in_addr addr; int is6, prefix;
union all_addr addr;
struct bogus_addr *next; struct bogus_addr *next;
}; };
@@ -423,10 +431,17 @@ struct host_record {
struct host_record *next; struct host_record *next;
}; };
#define IN4 1
#define IN6 2
#define INP4 4
#define INP6 8
struct interface_name { struct interface_name {
char *name; /* domain name */ char *name; /* domain name */
char *intr; /* interface 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 addrlist *addr;
struct interface_name *next; struct interface_name *next;
}; };
@@ -486,7 +501,7 @@ struct crec {
#define F_NO_RR (1u<<25) #define F_NO_RR (1u<<25)
#define F_IPSET (1u<<26) #define F_IPSET (1u<<26)
#define F_NOEXTRA (1u<<27) #define F_NOEXTRA (1u<<27)
#define F_SERVFAIL (1u<<28) /* currently unused. */ #define F_DOMAINSRV (1u<<28)
#define F_RCODE (1u<<29) #define F_RCODE (1u<<29)
#define F_SRV (1u<<30) #define F_SRV (1u<<30)
@@ -512,19 +527,20 @@ union mysockaddr {
#define IFACE_PERMANENT 4 #define IFACE_PERMANENT 4
#define SERV_FROM_RESOLV 1 /* 1 for servers from resolv, 0 for command line. */ /* The actual values here matter, since we sort on them to get records in the order
#define SERV_NO_ADDR 2 /* no server, this domain is local only */ IPv6 addr, IPv4 addr, all zero return, no-data return, send upstream. */
#define SERV_LITERAL_ADDRESS 4 /* addr is the answer, not the server */ #define SERV_LITERAL_ADDRESS 1 /* addr is the answer, or NoDATA is the answer, depending on the next three flags */
#define SERV_HAS_DOMAIN 8 /* server for one domain only */ #define SERV_ALL_ZEROS 2 /* return all zeros for A and AAAA */
#define SERV_4ADDR 4 /* addr is IPv4 */
#define SERV_6ADDR 8 /* addr is IPv6 */
#define SERV_HAS_SOURCE 16 /* source address defined */ #define SERV_HAS_SOURCE 16 /* source address defined */
#define SERV_FOR_NODOTS 32 /* server for names with no domain part only */ #define SERV_FOR_NODOTS 32 /* server for names with no domain part only */
#define SERV_WARNED_RECURSIVE 64 /* avoid warning spam */ #define SERV_WARNED_RECURSIVE 64 /* avoid warning spam */
#define SERV_FROM_DBUS 128 /* 1 if source is DBus */ #define SERV_FROM_DBUS 128 /* 1 if source is DBus */
#define SERV_MARK 256 /* for mark-and-delete */ #define SERV_MARK 256 /* for mark-and-delete and log code */
#define SERV_TYPE (SERV_HAS_DOMAIN | SERV_FOR_NODOTS) #define SERV_WILDCARD 512 /* domain has leading '*' */
#define SERV_COUNTED 512 /* workspace for log code */
#define SERV_USE_RESOLV 1024 /* forward this domain in the normal way */ #define SERV_USE_RESOLV 1024 /* forward this domain in the normal way */
#define SERV_NO_REBIND 2048 /* inhibit dns-rebind protection */ #define SERV_FROM_RESOLV 2048 /* 1 for servers from resolv, 0 for command line. */
#define SERV_FROM_FILE 4096 /* read from --servers-file */ #define SERV_FROM_FILE 4096 /* read from --servers-file */
#define SERV_LOOP 8192 /* server causes forwarding loop */ #define SERV_LOOP 8192 /* server causes forwarding loop */
#define SERV_DO_DNSSEC 16384 /* Validate DNSSEC when using this server */ #define SERV_DO_DNSSEC 16384 /* Validate DNSSEC when using this server */
@@ -539,21 +555,55 @@ struct serverfd {
}; };
struct randfd { struct randfd {
struct server *serv;
int fd; 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 { struct server {
u16 flags, domain_len;
char *domain;
struct server *next;
int serial, arrayposn;
int last_server;
union mysockaddr addr, source_addr; union mysockaddr addr, source_addr;
char interface[IF_NAMESIZE+1]; char interface[IF_NAMESIZE+1];
unsigned int ifindex; /* corresponding to interface, above */
struct serverfd *sfd; struct serverfd *sfd;
char *domain; /* set if this server only handles a domain. */ int tcpfd, edns_pktsz;
int flags, tcpfd, edns_pktsz;
time_t pktsz_reduced; time_t pktsz_reduced;
unsigned int queries, failed_queries; unsigned int queries, failed_queries;
time_t forwardtime;
int forwardcount;
#ifdef HAVE_LOOP #ifdef HAVE_LOOP
u32 uid; u32 uid;
#endif #endif
};
/* 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 server *next;
}; };
@@ -563,6 +613,12 @@ struct ipsets {
struct ipsets *next; struct ipsets *next;
}; };
struct allowlist {
u32 mark, mask;
char **patterns;
struct allowlist *next;
};
struct irec { struct irec {
union mysockaddr addr; union mysockaddr addr;
struct in_addr netmask; /* only valid for IPv4 */ struct in_addr netmask; /* only valid for IPv4 */
@@ -632,17 +688,28 @@ struct hostsfile {
#define DUMP_BOGUS 0x0040 #define DUMP_BOGUS 0x0040
#define DUMP_SEC_BOGUS 0x0080 #define DUMP_SEC_BOGUS 0x0080
/* DNSSEC status values. */ /* DNSSEC status values. */
#define STAT_SECURE 1 #define STAT_SECURE 0x10000
#define STAT_INSECURE 2 #define STAT_INSECURE 0x20000
#define STAT_BOGUS 3 #define STAT_BOGUS 0x30000
#define STAT_NEED_DS 4 #define STAT_NEED_DS 0x40000
#define STAT_NEED_KEY 5 #define STAT_NEED_KEY 0x50000
#define STAT_TRUNCATED 6 #define STAT_TRUNCATED 0x60000
#define STAT_SECURE_WILDCARD 7 #define STAT_SECURE_WILDCARD 0x70000
#define STAT_OK 8 #define STAT_OK 0x80000
#define STAT_ABANDONED 9 #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_NOREBIND 1
#define FREC_CHECKING_DISABLED 2 #define FREC_CHECKING_DISABLED 2
@@ -669,8 +736,7 @@ struct frec {
struct frec_src *next; struct frec_src *next;
} frec_src; } frec_src;
struct server *sentto; /* NULL means free */ struct server *sentto; /* NULL means free */
struct randfd *rfd4; struct randfd_list *rfds;
struct randfd *rfd6;
unsigned short new_id; unsigned short new_id;
int forwardall, flags; int forwardall, flags;
time_t time; time_t time;
@@ -680,6 +746,7 @@ struct frec {
struct blockdata *stash; /* Saved reply, whilst we validate */ struct blockdata *stash; /* Saved reply, whilst we validate */
size_t stash_len; size_t stash_len;
struct frec *dependent; /* Query awaiting internally-generated DNSKEY or DS query */ 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. */ struct frec *blocking_query; /* Query which is blocking us. */
#endif #endif
struct frec *next; struct frec *next;
@@ -1036,8 +1103,12 @@ extern struct daemon {
char *lease_change_command; char *lease_change_command;
struct iname *if_names, *if_addrs, *if_except, *dhcp_except, *auth_peers, *tftp_interfaces; struct iname *if_names, *if_addrs, *if_except, *dhcp_except, *auth_peers, *tftp_interfaces;
struct bogus_addr *bogus_addr, *ignore_addr; struct bogus_addr *bogus_addr, *ignore_addr;
struct server *servers; struct server *servers, *local_domains, **serverarray, *no_rebind;
int server_has_wildcard;
int serverarraysz, serverarrayhwm;
struct ipsets *ipsets; struct ipsets *ipsets;
u32 allowlist_mask;
struct allowlist *allowlists;
int log_fac; /* log facility */ int log_fac; /* log facility */
char *log_file; /* optional log file */ char *log_file; /* optional log file */
int max_logs; /* queue limit */ int max_logs; /* queue limit */
@@ -1045,6 +1116,9 @@ extern struct daemon {
int port, query_port, min_port, max_port; 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; 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; char *dns_client_id;
u32 umbrella_org;
u32 umbrella_asset;
u8 umbrella_device[8];
struct hostsfile *addn_hosts; struct hostsfile *addn_hosts;
struct dhcp_context *dhcp, *dhcp6; struct dhcp_context *dhcp, *dhcp6;
struct ra_interface *ra_interfaces; struct ra_interface *ra_interfaces;
@@ -1105,16 +1179,15 @@ extern struct daemon {
struct serverfd *sfds; struct serverfd *sfds;
struct irec *interfaces; struct irec *interfaces;
struct listener *listeners; struct listener *listeners;
struct server *last_server;
time_t forwardtime;
int forwardcount;
struct server *srv_save; /* Used for resend on DoD */ struct server *srv_save; /* Used for resend on DoD */
size_t packet_len; /* " " */ size_t packet_len; /* " " */
struct randfd *rfd_save; /* " " */ int fd_save; /* " " */
pid_t tcp_pids[MAX_PROCS]; pid_t tcp_pids[MAX_PROCS];
int tcp_pipes[MAX_PROCS]; int tcp_pipes[MAX_PROCS];
int pipe_to_parent; int pipe_to_parent;
struct randfd randomsocks[RANDOM_SOCKS]; int numrrand;
struct randfd *randomsocks;
struct randfd_list *rfl_spare, *rfl_poll;
int v6pktinfo; int v6pktinfo;
struct addrlist *interface_addrs; /* list of all addresses/prefix lengths associated with all local interfaces */ 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 */ int log_id, log_display_id; /* ids of transactions for logging */
@@ -1221,18 +1294,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 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, unsigned int extract_request(struct dns_header *header, size_t qlen,
char *name, unsigned short *typep); char *name, unsigned short *typep);
size_t setup_reply(struct dns_header *header, size_t qlen, void setup_reply(struct dns_header *header, unsigned int flags, int ede);
union all_addr *addrp, unsigned int flags,
unsigned long ttl);
int extract_addresses(struct dns_header *header, size_t qlen, char *name, int extract_addresses(struct dns_header *header, size_t qlen, char *name,
time_t now, char **ipsets, int is_sign, int check_rebind, time_t now, char **ipsets, int is_sign, int check_rebind,
int no_cache_dnssec, int secure, int *doctored); 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, size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
struct in_addr local_addr, struct in_addr local_netmask, struct in_addr local_addr, struct in_addr local_netmask,
time_t now, int ad_reqd, int do_bit, int have_pseudoheader); 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, int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
struct bogus_addr *baddr, time_t now); time_t now);
int check_for_ignored_address(struct dns_header *header, size_t qlen, struct bogus_addr *baddr); int check_for_ignored_address(struct dns_header *header, size_t qlen);
int check_for_local_domain(char *name, time_t now); int check_for_local_domain(char *name, time_t now);
size_t resize_packet(struct dns_header *header, size_t plen, size_t resize_packet(struct dns_header *header, size_t plen,
unsigned char *pheader, size_t hlen); unsigned char *pheader, size_t hlen);
@@ -1251,6 +1325,7 @@ int in_zone(struct auth_zone *zone, char *name, char **cut);
#endif #endif
/* dnssec.c */ /* 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); 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_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); int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class);
@@ -1259,8 +1334,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); int dnskey_keytag(int alg, int flags, unsigned char *key, int keylen);
size_t filter_rrsigs(struct dns_header *header, size_t plen); size_t filter_rrsigs(struct dns_header *header, size_t plen);
int setup_timestamp(void); int setup_timestamp(void);
int errflags_to_ede(int status);
#endif
/* hash_questions.c */ /* hash_questions.c */
void hash_questions_init(void);
unsigned char *hash_questions(struct dns_header *header, size_t plen, char *name); unsigned char *hash_questions(struct dns_header *header, size_t plen, char *name);
/* crypto.c */ /* crypto.c */
@@ -1285,12 +1363,13 @@ void safe_strncpy(char *dest, const char *src, size_t size);
void safe_pipe(int *fd, int read_noblock); void safe_pipe(int *fd, int read_noblock);
void *whine_malloc(size_t size); void *whine_malloc(size_t size);
int sa_len(union mysockaddr *addr); int sa_len(union mysockaddr *addr);
int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2); int sockaddr_isequal(const union mysockaddr *s1, const union mysockaddr *s2);
int hostname_isequal(const char *a, const char *b); int hostname_isequal(const char *a, const char *b);
int hostname_issubdomain(char *a, char *b); int hostname_issubdomain(char *a, char *b);
time_t dnsmasq_time(void); time_t dnsmasq_time(void);
int netmask_length(struct in_addr mask); 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(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); int is_same_net6(struct in6_addr *a, struct in6_addr *b, int prefixlen);
u64 addr6part(struct in6_addr *addr); u64 addr6part(struct in6_addr *addr);
void setaddr6part(struct in6_addr *addr, u64 host); void setaddr6part(struct in6_addr *addr, u64 host);
@@ -1332,37 +1411,28 @@ void set_option_bool(unsigned int opt);
void reset_option_bool(unsigned int opt); void reset_option_bool(unsigned int opt);
struct hostsfile *expand_filelist(struct hostsfile *list); struct hostsfile *expand_filelist(struct hostsfile *list);
char *parse_server(char *arg, union mysockaddr *addr, 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); int option_read_dynfile(char *file, int flags);
/* forward.c */ /* 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); void receive_query(struct listener *listen, time_t now);
unsigned char *tcp_request(int confd, time_t now, unsigned char *tcp_request(int confd, time_t now,
union mysockaddr *local_addr, struct in_addr netmask, int auth_dns); union mysockaddr *local_addr, struct in_addr netmask, int auth_dns);
void server_gone(struct server *server); 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, int send_from(int fd, int nowild, char *packet, size_t len,
union mysockaddr *to, union all_addr *source, union mysockaddr *to, union all_addr *source,
unsigned int iface); unsigned int iface);
void resend_query(void); void resend_query(void);
struct randfd *allocate_rfd(int family); int allocate_rfd(struct randfd_list **fdlp, struct server *serv);
void free_rfd(struct randfd *rfd); void free_rfds(struct randfd_list **fdlp);
/* network.c */ /* network.c */
int indextoname(int fd, int index, char *name); 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 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); void pre_allocate_sfds(void);
int reload_servers(char *fname); int reload_servers(char *fname);
void mark_servers(int flag); void check_servers(int no_loop_call);
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);
int enumerate_interfaces(int reset); int enumerate_interfaces(int reset);
void create_wildcard_listeners(void); void create_wildcard_listeners(void);
void create_bound_listeners(int dienow); void create_bound_listeners(int dienow);
@@ -1495,10 +1565,14 @@ void emit_dbus_signal(int action, struct dhcp_lease *lease, char *hostname);
/* ubus.c */ /* ubus.c */
#ifdef HAVE_UBUS #ifdef HAVE_UBUS
void ubus_init(void); char *ubus_init(void);
void set_ubus_listeners(void); void set_ubus_listeners(void);
void check_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); 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 #endif
/* ipset.c */ /* ipset.c */
@@ -1507,6 +1581,13 @@ void ipset_init(void);
int add_to_ipset(const char *setname, const union all_addr *ipaddr, int flags, int remove); int add_to_ipset(const char *setname, const union all_addr *ipaddr, int flags, int remove);
#endif #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 */ /* helper.c */
#if defined(HAVE_SCRIPT) #if defined(HAVE_SCRIPT)
int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd); int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd);
@@ -1672,3 +1753,23 @@ int do_arp_script_run(void);
void dump_init(void); 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);
#endif #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);
+107 -41
View File
@@ -215,14 +215,6 @@ static int is_check_date(unsigned long curtime)
return !daemon->dnssec_no_time_check; 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. /* Return bytes of canonicalised rrdata one by one.
Init state->ip with the RR, and state->end with the end of same. Init state->ip with the RR, and state->end with the end of same.
Init state->op to NULL. Init state->op to NULL.
@@ -534,6 +526,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
struct crec *crecp = NULL; struct crec *crecp = NULL;
u16 *rr_desc = rrfilter_desc(type); u16 *rr_desc = rrfilter_desc(type);
u32 sig_expiration, sig_inception; u32 sig_expiration, sig_inception;
int failflags = DNSSEC_FAIL_NOSIG | DNSSEC_FAIL_NYV | DNSSEC_FAIL_EXP | DNSSEC_FAIL_NOKEYSUP;
unsigned long curtime = time(0); unsigned long curtime = time(0);
int time_check = is_check_date(curtime); int time_check = is_check_date(curtime);
@@ -558,6 +551,8 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
char *name_start; char *name_start;
u32 nsigttl, ttl, orig_ttl; u32 nsigttl, ttl, orig_ttl;
failflags &= ~DNSSEC_FAIL_NOSIG;
p = sigs[j]; p = sigs[j];
GETLONG(ttl, p); GETLONG(ttl, p);
GETSHORT(rdlen, p); /* rdlen >= 18 checked previously */ GETSHORT(rdlen, p); /* rdlen >= 18 checked previously */
@@ -574,9 +569,28 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
if (!extract_name(header, plen, &p, keyname, 1, 0)) if (!extract_name(header, plen, &p, keyname, 1, 0))
return STAT_BOGUS; return STAT_BOGUS;
if ((time_check && !check_date_range(curtime, sig_inception, sig_expiration)) || if (!time_check)
labels > name_labels || failflags &= ~(DNSSEC_FAIL_NYV | DNSSEC_FAIL_EXP);
!(hash = hash_find(algo_digest_name(algo))) || 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)) !hash_init(hash, &ctx, &digest))
continue; continue;
@@ -730,7 +744,8 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
} }
} }
return STAT_BOGUS; /* If we reach this point, no verifying key was found */
return STAT_BOGUS | failflags | DNSSEC_FAIL_NOKEY;
} }
@@ -751,17 +766,18 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
unsigned long ttl, sig_ttl; unsigned long ttl, sig_ttl;
struct blockdata *key; struct blockdata *key;
union all_addr a; union all_addr a;
int failflags = DNSSEC_FAIL_NOSIG | DNSSEC_FAIL_NODSSUP | DNSSEC_FAIL_NOZONE | DNSSEC_FAIL_NOKEY;
if (ntohs(header->qdcount) != 1 || if (ntohs(header->qdcount) != 1 ||
RCODE(header) == SERVFAIL || RCODE(header) == REFUSED || RCODE(header) == SERVFAIL || RCODE(header) == REFUSED ||
!extract_name(header, plen, &p, name, 1, 4)) !extract_name(header, plen, &p, name, 1, 4))
return STAT_BOGUS; return STAT_BOGUS | DNSSEC_FAIL_NOKEY;
GETSHORT(qtype, p); GETSHORT(qtype, p);
GETSHORT(qclass, p); GETSHORT(qclass, p);
if (qtype != T_DNSKEY || qclass != class || ntohs(header->ancount) == 0) 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 */ /* See if we have cached a DS record which validates this key */
if (!(crecp = cache_find_by_name(NULL, name, now, F_DS))) if (!(crecp = cache_find_by_name(NULL, name, now, F_DS)))
@@ -795,14 +811,17 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
GETSHORT(flags, p); GETSHORT(flags, p);
if (*p++ != 3) if (*p++ != 3)
return STAT_BOGUS; return STAT_BOGUS | DNSSEC_FAIL_NOKEY;
algo = *p++; algo = *p++;
keytag = dnskey_keytag(algo, flags, p, rdlen - 4); keytag = dnskey_keytag(algo, flags, p, rdlen - 4);
key = NULL; key = NULL;
/* key must have zone key flag set */ /* key must have zone key flag set */
if (flags & 0x100) if (flags & 0x100)
{
key = blockdata_alloc((char*)p, rdlen - 4); key = blockdata_alloc((char*)p, rdlen - 4);
failflags &= ~DNSSEC_FAIL_NOZONE;
}
p = psave; p = psave;
@@ -823,15 +842,23 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
unsigned char *digest, *ds_digest; unsigned char *digest, *ds_digest;
const struct nettle_hash *hash; const struct nettle_hash *hash;
int sigcnt, rrcnt; int sigcnt, rrcnt;
int wire_len;
if (recp1->addr.ds.algo == algo && if (recp1->addr.ds.algo == algo &&
recp1->addr.ds.keytag == keytag && recp1->addr.ds.keytag == keytag &&
recp1->uid == (unsigned int)class && recp1->uid == (unsigned int)class)
(hash = hash_find(ds_digest_name(recp1->addr.ds.digest))) &&
hash_init(hash, &ctx, &digest))
{ {
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 /* Note that digest may be different between DSs, so
we can't move this outside the loop. */ we can't move this outside the loop. */
@@ -846,15 +873,26 @@ 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)) && (ds_digest = blockdata_retrieve(recp1->addr.ds.keydata, recp1->addr.ds.keylen, NULL)) &&
memcmp(ds_digest, digest, recp1->addr.ds.keylen) == 0 && memcmp(ds_digest, digest, recp1->addr.ds.keylen) == 0 &&
explore_rrset(header, plen, class, T_DNSKEY, name, keyname, &sigcnt, &rrcnt) && explore_rrset(header, plen, class, T_DNSKEY, name, keyname, &sigcnt, &rrcnt) &&
sigcnt != 0 && rrcnt != 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) 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; valid = 1;
break; break;
} }
} }
} }
}
blockdata_free(key); blockdata_free(key);
} }
@@ -936,7 +974,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
} }
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DNSKEY"); log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DNSKEY");
return STAT_BOGUS; return STAT_BOGUS | failflags;
} }
/* The DNS packet is expected to contain the answer to a DS query /* The DNS packet is expected to contain the answer to a DS query
@@ -971,10 +1009,11 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
else else
rc = dnssec_validate_reply(now, header, plen, name, keyname, NULL, 0, &neganswer, &nons, &neg_ttl); 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); my_syslog(LOG_WARNING, _("Insecure DS reply received for %s, check domain configuration and upstream DNS server DNSSEC support"), name);
rc = STAT_BOGUS; log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS - not secure");
return STAT_BOGUS | DNSSEC_FAIL_INDET;
} }
p = (unsigned char *)(header+1); p = (unsigned char *)(header+1);
@@ -984,13 +1023,13 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
/* If the key needed to validate the DS is on the same domain as the DS, we'll /* 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 loop getting nowhere. Stop that now. This can happen of the DS answer comes
from the DS's zone, and not the parent zone. */ 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");
return STAT_BOGUS; return STAT_BOGUS;
} }
if (rc != STAT_SECURE) if (!STAT_ISEQUAL(rc, STAT_SECURE))
return rc; return rc;
if (!neganswer) if (!neganswer)
@@ -1959,15 +1998,15 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
if (check_unsigned && i < ntohs(header->ancount)) if (check_unsigned && i < ntohs(header->ancount))
{ {
rc = zone_status(name, class1, keyname, now); rc = zone_status(name, class1, keyname, now);
if (rc == STAT_SECURE) if (STAT_ISEQUAL(rc, STAT_SECURE))
rc = STAT_BOGUS; rc = STAT_BOGUS | DNSSEC_FAIL_NOSIG;
if (class) if (class)
*class = class1; /* Class for NEED_DS or NEED_KEY */ *class = class1; /* Class for NEED_DS or NEED_KEY */
} }
else else
rc = STAT_INSECURE; rc = STAT_INSECURE;
if (rc != STAT_INSECURE) if (!STAT_ISEQUAL(rc, STAT_INSECURE))
return rc; return rc;
} }
} }
@@ -1978,7 +2017,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
strcpy(daemon->workspacename, keyname); strcpy(daemon->workspacename, keyname);
rc = zone_status(daemon->workspacename, class1, keyname, now); 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) if (class)
*class = class1; /* Class for NEED_DS or NEED_KEY */ *class = class1; /* Class for NEED_DS or NEED_KEY */
@@ -1986,13 +2025,13 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
} }
/* Zone is insecure, don't need to validate RRset */ /* Zone is insecure, don't need to validate RRset */
if (rc == STAT_SECURE) if (STAT_ISEQUAL(rc, STAT_SECURE))
{ {
unsigned long sig_ttl; unsigned long sig_ttl;
rc = validate_rrset(now, header, plen, class1, type1, sigcnt, rc = validate_rrset(now, header, plen, class1, type1, sigcnt,
rrcnt, name, keyname, &wildname, NULL, 0, 0, 0, &sig_ttl); 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) if (class)
*class = class1; /* Class for DS or DNSKEY */ *class = class1; /* Class for DS or DNSKEY */
@@ -2025,21 +2064,21 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
Note that we may not yet have validated the NSEC/NSEC3 RRsets. Note that we may not yet have validated the NSEC/NSEC3 RRsets.
That's not a problem since if the RRsets later fail That's not a problem since if the RRsets later fail
we'll return BOGUS then. */ 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)) !prove_non_existence(header, plen, keyname, name, type1, class1, wildname, NULL, NULL))
return STAT_BOGUS; return STAT_BOGUS | DNSSEC_FAIL_NONSEC;
rc = STAT_SECURE; rc = STAT_SECURE;
} }
} }
} }
if (rc == STAT_INSECURE) if (STAT_ISEQUAL(rc, STAT_INSECURE))
secure = STAT_INSECURE; secure = STAT_INSECURE;
} }
/* OK, all the RRsets validate, now see if we have a missing answer or CNAME target. */ /* OK, all the RRsets validate, now see if we have a missing answer or CNAME target. */
if (secure == STAT_SECURE) if (STAT_ISEQUAL(secure, STAT_SECURE))
for (j = 0; j <targetidx; j++) for (j = 0; j <targetidx; j++)
if ((p2 = targets[j])) if ((p2 = targets[j]))
{ {
@@ -2057,16 +2096,16 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
{ {
/* Empty DS without NSECS */ /* Empty DS without NSECS */
if (qtype == T_DS) if (qtype == T_DS)
return STAT_BOGUS; return STAT_BOGUS | DNSSEC_FAIL_NONSEC;
if ((rc = zone_status(name, qclass, keyname, now)) != STAT_SECURE) if (STAT_ISEQUAL((rc = zone_status(name, qclass, keyname, now)), STAT_SECURE))
{ {
if (class) if (class)
*class = qclass; /* Class for NEED_DS or NEED_KEY */ *class = qclass; /* Class for NEED_DS or NEED_KEY */
return rc; return rc;
} }
return STAT_BOGUS; /* signed zone, no NSECs */ return STAT_BOGUS | DNSSEC_FAIL_NONSEC; /* signed zone, no NSECs */
} }
} }
@@ -2130,4 +2169,31 @@ size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char
return ret; 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 */ #endif /* HAVE_DNSSEC */
+698
View File
@@ -0,0 +1,698 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 dated June, 1991, or
(at your option) version 3 dated 29 June, 2007.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dnsmasq.h"
static int order(char *qdomain, size_t qlen, struct server *serv);
static int order_qsort(const void *a, const void *b);
static int order_servers(struct server *s, struct server *s2);
/* If the server is USE_RESOLV or LITERAL_ADDRES, it lives on the local_domains chain. */
#define SERV_IS_LOCAL (SERV_USE_RESOLV | SERV_LITERAL_ADDRESS)
void build_server_array(void)
{
struct server *serv;
int count = 0;
for (serv = daemon->servers; serv; serv = serv->next)
#ifdef HAVE_LOOP
if (!(serv->flags & SERV_LOOP))
#endif
{
count++;
if (serv->flags & SERV_WILDCARD)
daemon->server_has_wildcard = 1;
}
for (serv = daemon->local_domains; serv; serv = serv->next)
{
count++;
if (serv->flags & SERV_WILDCARD)
daemon->server_has_wildcard = 1;
}
daemon->serverarraysz = count;
if (count > daemon->serverarrayhwm)
{
struct server **new;
count += 10; /* A few extra without re-allocating. */
if ((new = whine_malloc(count * sizeof(struct server *))))
{
if (daemon->serverarray)
free(daemon->serverarray);
daemon->serverarray = new;
daemon->serverarrayhwm = count;
}
}
count = 0;
for (serv = daemon->servers; serv; serv = serv->next)
#ifdef HAVE_LOOP
if (!(serv->flags & SERV_LOOP))
#endif
{
daemon->serverarray[count] = serv;
serv->serial = count;
serv->last_server = -1;
count++;
}
for (serv = daemon->local_domains; serv; serv = serv->next, count++)
daemon->serverarray[count] = serv;
qsort(daemon->serverarray, daemon->serverarraysz, sizeof(struct server *), order_qsort);
/* servers need the location in the array to find all the whole
set of equivalent servers from a pointer to a single one. */
for (count = 0; count < daemon->serverarraysz; count++)
if (!(daemon->serverarray[count]->flags & SERV_IS_LOCAL))
daemon->serverarray[count]->arrayposn = count;
}
/* we're looking for the server whose domain is the longest exact match
to the RH end of qdomain, or a local address if the flags match.
Add '.' to the LHS of the query string so
server=/.example.com/ works.
A flag of F_SERVER returns an upstream server only.
A flag of F_DNSSECOK returns a DNSSEC capable server only and
also disables NODOTS servers from consideration.
A flag of F_DOMAINSRV returns a domain-specific server only.
A flag of F_CONFIG returns anything that generates a local
reply of IPv4 or IPV6.
return 0 if nothing found, 1 otherwise.
*/
int lookup_domain(char *domain, int flags, int *lowout, int *highout)
{
int rc, crop_query, nodots;
ssize_t qlen;
int try, high, low = 0;
int nlow = 0, nhigh = 0;
char *cp, *qdomain = domain;
/* may be no configured servers. */
if (daemon->serverarraysz == 0)
return 0;
/* find query length and presence of '.' */
for (cp = qdomain, nodots = 1, qlen = 0; *cp; qlen++, cp++)
if (*cp == '.')
nodots = 0;
/* Handle empty name, and searches for DNSSEC queries without
diverting to NODOTS servers. */
if (qlen == 0 || flags & F_DNSSECOK)
nodots = 0;
/* Search shorter and shorter RHS substrings for a match */
while (qlen >= 0)
{
/* Note that when we chop off a label, all the possible matches
MUST be at a larger index than the nearest failing match with one more
character, since the array is sorted longest to smallest. Hence
we don't reset low to zero here, we can go further below and crop the
search string to the size of the largest remaining server
when this match fails. */
high = daemon->serverarraysz;
crop_query = 1;
/* binary search */
while (1)
{
try = (low + high)/2;
if ((rc = order(qdomain, qlen, daemon->serverarray[try])) == 0)
break;
if (rc < 0)
{
if (high == try)
{
/* qdomain is longer or same length as longest domain, and try == 0
crop the query to the longest domain. */
crop_query = qlen - daemon->serverarray[try]->domain_len;
break;
}
high = try;
}
else
{
if (low == try)
{
/* try now points to the last domain that sorts before the query, so
we know that a substring of the query shorter than it is required to match, so
find the largest domain that's shorter than try. Note that just going to
try+1 is not optimal, consider searching bbb in (aaa,ccc,bb). try will point
to aaa, since ccc sorts after bbb, but the first domain that has a chance to
match is bb. So find the length of the first domain later than try which is
is shorter than it.
There's a nasty edge case when qdomain sorts before _any_ of the
server domains, where try _doesn't point_ to the last domain that sorts
before the query, since no such domain exists. In that case, the loop
exits via the rc < 0 && high == try path above and this code is
not executed. */
ssize_t len, old = daemon->serverarray[try]->domain_len;
while (++try != daemon->serverarraysz)
{
if (old != (len = daemon->serverarray[try]->domain_len))
{
crop_query = qlen - len;
break;
}
}
break;
}
low = try;
}
};
if (rc == 0)
{
int found = 1;
if (daemon->server_has_wildcard)
{
/* if we have example.com and *example.com we need to check against *example.com,
but the binary search may have found either. Use the fact that example.com is sorted before *example.com
We favour example.com in the case that both match (ie www.example.com) */
while (try != 0 && order(qdomain, qlen, daemon->serverarray[try-1]) == 0)
try--;
if (!(qdomain == domain || *qdomain == 0 || *(qdomain-1) == '.'))
{
while (try < daemon->serverarraysz-1 && order(qdomain, qlen, daemon->serverarray[try+1]) == 0)
try++;
if (!(daemon->serverarray[try]->flags & SERV_WILDCARD))
found = 0;
}
}
if (found)
{
/* We've matched a setting which says to use servers without a domain.
Continue the search with empty query */
if (daemon->serverarray[try]->flags & SERV_USE_RESOLV)
crop_query = qlen;
else if (filter_servers(try, flags, &nlow, &nhigh))
/* We have a match, but it may only be (say) an IPv6 address, and
if the query wasn't for an AAAA record, it's no good, and we need
to continue generalising */
break;
}
}
/* crop_query must be at least one always. */
if (crop_query == 0)
crop_query = 1;
/* strip chars off the query based on the largest possible remaining match,
then continue to the start of the next label unless we have a wildcard
domain somewhere, in which case we have to go one at a time. */
qlen -= crop_query;
qdomain += crop_query;
if (!daemon->server_has_wildcard)
while (qlen > 0 && (*(qdomain-1) != '.'))
qlen--, qdomain++;
}
/* domain has no dots, and we have at least one server configured to handle such,
These servers always sort to the very end of the array.
A configured server eg server=/lan/ will take precdence. */
if (nodots &&
(daemon->serverarray[daemon->serverarraysz-1]->flags & SERV_FOR_NODOTS) &&
(nlow == nhigh || daemon->serverarray[nlow]->domain_len == 0))
filter_servers(daemon->serverarraysz-1, flags, &nlow, &nhigh);
if (lowout)
*lowout = nlow;
if (highout)
*highout = nhigh;
if (nlow == nhigh)
return 0;
return 1;
}
/* Return first server in group of equivalent servers; this is the "master" record. */
int server_samegroup(struct server *a, struct server *b)
{
return order_servers(a, b) == 0;
}
int filter_servers(int seed, int flags, int *lowout, int *highout)
{
int nlow = seed, nhigh = seed;
int i;
/* expand nlow and nhigh to cover all the records with the same domain
nlow is the first, nhigh - 1 is the last. nlow=nhigh means no servers,
which can happen below. */
while (nlow > 0 && order_servers(daemon->serverarray[nlow-1], daemon->serverarray[nlow]) == 0)
nlow--;
while (nhigh < daemon->serverarraysz-1 && order_servers(daemon->serverarray[nhigh], daemon->serverarray[nhigh+1]) == 0)
nhigh++;
nhigh++;
#define SERV_LOCAL_ADDRESS (SERV_6ADDR | SERV_4ADDR | SERV_ALL_ZEROS)
if (flags & F_CONFIG)
{
/* We're just lookin for any matches that return an RR. */
for (i = nlow; i < nhigh; i++)
if (daemon->serverarray[i]->flags & SERV_LOCAL_ADDRESS)
break;
/* failed, return failure. */
if (i == nhigh)
nhigh = nlow;
}
else
{
/* Now the servers are on order between low and high, in the order
IPv6 addr, IPv4 addr, return zero for both, send upstream, no-data return.
See which of those match our query in that priority order and narrow (low, high) */
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_6ADDR); i++);
if (i != nlow && (flags & F_IPV6))
nhigh = i;
else
{
nlow = i;
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_4ADDR); i++);
if (i != nlow && (flags & F_IPV4))
nhigh = i;
else
{
nlow = i;
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_ALL_ZEROS); i++);
if (i != nlow && (flags & (F_IPV4 | F_IPV6)))
nhigh = i;
else
{
nlow = i;
/* now look for a server */
for (i = nlow; i < nhigh && !(daemon->serverarray[i]->flags & SERV_LITERAL_ADDRESS); i++);
if (i != nlow)
{
/* If we want a server that can do DNSSEC, and this one can't,
return nothing, similarly if were looking only for a server
for a particular domain. */
if ((flags & F_DNSSECOK) && !(daemon->serverarray[nlow]->flags & SERV_DO_DNSSEC))
nlow = nhigh;
else if ((flags & F_DOMAINSRV) && daemon->serverarray[nlow]->domain_len == 0)
nlow = nhigh;
else
nhigh = i;
}
else
{
/* --local=/domain/, only return if we don't need a server. */
if (flags & (F_DNSSECOK | F_DOMAINSRV | F_SERVER))
nhigh = i;
}
}
}
}
}
*lowout = nlow;
*highout = nhigh;
return (nlow != nhigh);
}
int is_local_answer(time_t now, int first, char *name)
{
int flags = 0;
int rc = 0;
if ((flags = daemon->serverarray[first]->flags) & SERV_LITERAL_ADDRESS)
{
if (flags & SERV_4ADDR)
rc = F_IPV4;
else if (flags & SERV_6ADDR)
rc = F_IPV6;
else if (flags & SERV_ALL_ZEROS)
rc = F_IPV4 | F_IPV6;
else
{
/* argument first is the first struct server which matches the query type;
now roll back to the server which is just the same domain, to check if that
provides an answer of a different type. */
for (;first > 0 && order_servers(daemon->serverarray[first-1], daemon->serverarray[first]) == 0; first--);
if ((daemon->serverarray[first]->flags & SERV_LOCAL_ADDRESS) ||
check_for_local_domain(name, now))
rc = F_NOERR;
else
rc = F_NXDOMAIN;
}
}
return rc;
}
size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header *header, char *name, char *limit, int first, int last, int ede)
{
int trunc = 0;
unsigned char *p;
int start;
union all_addr addr;
if (flags & (F_NXDOMAIN | F_NOERR))
log_query(flags | gotname | F_NEG | F_CONFIG | F_FORWARD, name, NULL, NULL);
setup_reply(header, flags, ede);
if (!(p = skip_questions(header, size)))
return 0;
if (flags & gotname & F_IPV4)
for (start = first; start != last; start++)
{
struct serv_addr4 *srv = (struct serv_addr4 *)daemon->serverarray[start];
if (srv->flags & SERV_ALL_ZEROS)
memset(&addr, 0, sizeof(addr));
else
addr.addr4 = srv->addr;
header->ancount = htons(ntohs(header->ancount) + 1);
add_resource_record(header, limit, &trunc, sizeof(struct dns_header), &p, daemon->local_ttl, NULL, T_A, C_IN, "4", &addr);
log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV6, name, (union all_addr *)&addr, NULL);
}
if (flags & gotname & F_IPV6)
for (start = first; start != last; start++)
{
struct serv_addr6 *srv = (struct serv_addr6 *)daemon->serverarray[start];
if (srv->flags & SERV_ALL_ZEROS)
memset(&addr, 0, sizeof(addr));
else
addr.addr6 = srv->addr;
header->ancount = htons(ntohs(header->ancount) + 1);
add_resource_record(header, limit, &trunc, sizeof(struct dns_header), &p, daemon->local_ttl, NULL, T_AAAA, C_IN, "6", &addr);
log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV4, name, (union all_addr *)&addr, NULL);
}
if (trunc)
header->hb3 |= HB3_TC;
return p - (unsigned char *)header;
}
#ifdef HAVE_DNSSEC
int dnssec_server(struct server *server, char *keyname, int *firstp, int *lastp)
{
int first, last, index;
/* Find server to send DNSSEC query to. This will normally be the
same as for the original query, but may be another if
servers for domains are involved. */
if (!lookup_domain(keyname, F_DNSSECOK, &first, &last))
return -1;
for (index = first; index != last; index++)
if (daemon->serverarray[index] == server)
break;
/* No match to server used for original query.
Use newly looked up set. */
if (index == last)
index = daemon->serverarray[first]->last_server == -1 ?
first : daemon->serverarray[first]->last_server;
if (firstp)
*firstp = first;
if (lastp)
*lastp = last;
return index;
}
#endif
/* order by size, then by dictionary order */
static int order(char *qdomain, size_t qlen, struct server *serv)
{
size_t dlen = 0;
/* servers for dotless names always sort last
searched for name is never dotless. */
if (serv->flags & SERV_FOR_NODOTS)
return -1;
dlen = serv->domain_len;
if (qlen < dlen)
return 1;
if (qlen > dlen)
return -1;
return strcmp(qdomain, serv->domain);
}
static int order_servers(struct server *s1, struct server *s2)
{
int rc;
/* need full comparison of dotless servers in
order_qsort() and filter_servers() */
if (s1->flags & SERV_FOR_NODOTS)
return (s2->flags & SERV_FOR_NODOTS) ? 0 : 1;
if ((rc = order(s1->domain, s1->domain_len, s2)) != 0)
return rc;
/* For identical domains, sort wildcard ones first */
if (s1->flags & SERV_WILDCARD)
return (s2->flags & SERV_WILDCARD) ? 0 : 1;
return (s2->flags & SERV_WILDCARD) ? -1 : 0;
}
static int order_qsort(const void *a, const void *b)
{
int rc;
struct server *s1 = *((struct server **)a);
struct server *s2 = *((struct server **)b);
rc = order_servers(s1, s2);
/* Sort all literal NODATA and local IPV4 or IPV6 responses together,
in a very specific order. We flip the SERV_LITERAL_ADDRESS bit
so the order is IPv6 literal, IPv4 literal, all-zero literal,
upstream server, NXDOMAIN literal. */
if (rc == 0)
rc = ((s2->flags & (SERV_LITERAL_ADDRESS | SERV_4ADDR | SERV_6ADDR | SERV_ALL_ZEROS)) ^ SERV_LITERAL_ADDRESS) -
((s1->flags & (SERV_LITERAL_ADDRESS | SERV_4ADDR | SERV_6ADDR | SERV_ALL_ZEROS)) ^ SERV_LITERAL_ADDRESS);
/* Finally, order by appearance in /etc/resolv.conf etc, for --strict-order */
if (rc == 0)
if (!(s1->flags & SERV_LITERAL_ADDRESS))
rc = s1->serial - s2->serial;
return rc;
}
void mark_servers(int flag)
{
struct server *serv;
/* mark everything with argument flag */
for (serv = daemon->servers; serv; serv = serv->next)
if (serv->flags & flag)
serv->flags |= SERV_MARK;
else
serv->flags &= ~SERV_MARK;
for (serv = daemon->local_domains; serv; serv = serv->next)
if (serv->flags & flag)
serv->flags |= SERV_MARK;
else
serv->flags &= ~SERV_MARK;
}
void cleanup_servers(void)
{
struct server *serv, *tmp, **up;
/* unlink and free anything still marked. */
for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp)
{
tmp = serv->next;
if (serv->flags & SERV_MARK)
{
server_gone(serv);
*up = serv->next;
free(serv->domain);
free(serv);
}
else
up = &serv->next;
}
for (serv = daemon->local_domains, up = &daemon->local_domains; serv; serv = tmp)
{
tmp = serv->next;
if (serv->flags & SERV_MARK)
{
*up = serv->next;
free(serv->domain);
free(serv);
}
else
up = &serv->next;
}
}
int add_update_server(int flags,
union mysockaddr *addr,
union mysockaddr *source_addr,
const char *interface,
const char *domain,
union all_addr *local_addr)
{
struct server *serv = NULL;
char *alloc_domain;
if (!domain)
domain = "";
/* .domain == domain, for historical reasons. */
if (*domain == '.')
while (*domain == '.') domain++;
else if (*domain == '*')
{
domain++;
if (*domain != 0)
flags |= SERV_WILDCARD;
}
if (*domain == 0)
alloc_domain = whine_malloc(1);
else if (!(alloc_domain = canonicalise((char *)domain, NULL)))
return 0;
/* See if there is a suitable candidate, and unmark
only do this for forwarding servers, not
address or local, to avoid delays on large numbers. */
if (flags & SERV_IS_LOCAL)
for (serv = daemon->servers; serv; serv = serv->next)
if ((serv->flags & SERV_MARK) &&
hostname_isequal(alloc_domain, serv->domain))
break;
if (serv)
{
free(alloc_domain);
alloc_domain = serv->domain;
}
else
{
size_t size;
if (flags & SERV_LITERAL_ADDRESS)
{
if (flags & SERV_6ADDR)
size = sizeof(struct serv_addr6);
else if (flags & SERV_4ADDR)
size = sizeof(struct serv_addr4);
else
size = sizeof(struct serv_local);
}
else
size = sizeof(struct server);
if (!(serv = whine_malloc(size)))
return 0;
if (flags & SERV_IS_LOCAL)
{
serv->next = daemon->local_domains;
daemon->local_domains = serv;
}
else
{
struct server *s;
/* Add to the end of the chain, for order */
if (!daemon->servers)
daemon->servers = serv;
else
{
for (s = daemon->servers; s->next; s = s->next);
s->next = serv;
}
serv->next = NULL;
}
}
if (!(flags & SERV_IS_LOCAL))
memset(serv, 0, sizeof(struct server));
serv->flags = flags;
serv->domain = alloc_domain;
serv->domain_len = strlen(alloc_domain);
if (flags & SERV_4ADDR)
((struct serv_addr4*)serv)->addr = local_addr->addr4;
if (flags & SERV_6ADDR)
((struct serv_addr6*)serv)->addr = local_addr->addr6;
if (!(flags & SERV_IS_LOCAL))
{
#ifdef HAVE_LOOP
serv->uid = rand32();
#endif
if (interface)
safe_strncpy(serv->interface, interface, sizeof(serv->interface));
if (addr)
serv->addr = *addr;
if (source_addr)
serv->source_addr = *source_addr;
}
return 1;
}
+1 -1
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
+64 -1
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -427,6 +427,66 @@ int check_source(struct dns_header *header, size_t plen, unsigned char *pseudohe
return 1; return 1;
} }
/* See https://docs.umbrella.com/umbrella-api/docs/identifying-dns-traffic for
* detailed information on packet formating.
*/
#define UMBRELLA_VERSION 1
#define UMBRELLA_TYPESZ 2
#define UMBRELLA_ASSET 0x0004
#define UMBRELLA_ASSETSZ sizeof(daemon->umbrella_asset)
#define UMBRELLA_ORG 0x0008
#define UMBRELLA_ORGSZ sizeof(daemon->umbrella_org)
#define UMBRELLA_IPV4 0x0010
#define UMBRELLA_IPV6 0x0020
#define UMBRELLA_DEVICE 0x0040
#define UMBRELLA_DEVICESZ sizeof(daemon->umbrella_device)
struct umbrella_opt {
u8 magic[4];
u8 version;
u8 flags;
/* We have 4 possible fields since we'll never send both IPv4 and
* IPv6, so using the larger of the two to calculate max buffer size.
* Each field also has a type header. So the following accounts for
* the type headers and each field size to get a max buffer size.
*/
u8 fields[4 * UMBRELLA_TYPESZ + UMBRELLA_ORGSZ + IN6ADDRSZ + UMBRELLA_DEVICESZ + UMBRELLA_ASSETSZ];
};
static size_t add_umbrella_opt(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *source, int *cacheable)
{
*cacheable = 0;
struct umbrella_opt opt = {{"ODNS"}, UMBRELLA_VERSION, 0, {}};
u8 *u = &opt.fields[0];
if (daemon->umbrella_org) {
PUTSHORT(UMBRELLA_ORG, u);
PUTLONG(daemon->umbrella_org, u);
}
int family = source->sa.sa_family;
PUTSHORT(family == AF_INET ? UMBRELLA_IPV4 : UMBRELLA_IPV6, u);
int size = family == AF_INET ? INADDRSZ : IN6ADDRSZ;
memcpy(u, get_addrp(source, family), size);
u += size;
if (option_bool(OPT_UMBRELLA_DEVID)) {
PUTSHORT(UMBRELLA_DEVICE, u);
memcpy(u, (char *)&daemon->umbrella_device, UMBRELLA_DEVICESZ);
u += UMBRELLA_DEVICESZ;
}
if (daemon->umbrella_asset) {
PUTSHORT(UMBRELLA_ASSET, u);
PUTLONG(daemon->umbrella_asset, u);
}
int len = u - &opt.magic[0];
return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, EDNS0_OPTION_UMBRELLA, (unsigned char *)&opt, len, 0, 1);
}
/* Set *check_subnet if we add a client subnet option, which needs to checked /* 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 in the reply. Set *cacheable to zero if we add an option which the answer
may depend on. */ may depend on. */
@@ -446,6 +506,9 @@ size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *l
plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMCPEID, plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMCPEID,
(unsigned char *)daemon->dns_client_id, strlen(daemon->dns_client_id), 0, 1); (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)) if (option_bool(OPT_CLIENT_SUBNET))
{ {
plen = add_source_addr(header, plen, limit, source, cacheable); plen = add_source_addr(header, plen, limit, source, cacheable);
+1175 -1117
View File
File diff suppressed because it is too large. Load diff
+20 -17
View File
@@ -28,27 +28,27 @@
#include "dnsmasq.h" #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) unsigned char *hash_questions(struct dns_header *header, size_t plen, char *name)
{ {
int q; int q;
unsigned char *p = (unsigned char *)(header+1); 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)) hash->init(ctx);
{
/* 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;
}
for (q = ntohs(header->qdcount); q != 0; q--) for (q = ntohs(header->qdcount); q != 0; q--)
{ {
@@ -74,7 +74,7 @@ unsigned char *hash_questions(struct dns_header *header, size_t plen, char *name
return digest; return digest;
} }
#else /* HAVE_DNSSEC */ #else /* HAVE_DNSSEC || HAVE_CRYPTOHASH */
#define SHA256_BLOCK_SIZE 32 // SHA256 outputs a 32 byte digest #define SHA256_BLOCK_SIZE 32 // SHA256 outputs a 32 byte digest
typedef unsigned char BYTE; // 8-bit byte typedef unsigned char BYTE; // 8-bit byte
@@ -91,6 +91,9 @@ static void sha256_init(SHA256_CTX *ctx);
static void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len); static void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len);
static void sha256_final(SHA256_CTX *ctx, BYTE hash[]); 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) unsigned char *hash_questions(struct dns_header *header, size_t plen, char *name)
{ {
+2 -6
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -236,7 +236,6 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
else else
continue; continue;
/* stringify MAC into dhcp_buff */ /* stringify MAC into dhcp_buff */
p = daemon->dhcp_buff; p = daemon->dhcp_buff;
if (data.hwaddr_type != ARPHRD_ETHER || data.hwaddr_len == 0) if (data.hwaddr_type != ARPHRD_ETHER || data.hwaddr_len == 0)
@@ -882,7 +881,4 @@ void helper_write(void)
} }
} }
#endif #endif /* HAVE_SCRIPT */
+1 -2
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -295,4 +295,3 @@ int inotify_check(time_t now)
} }
#endif /* INOTIFY */ #endif /* INOTIFY */
+1 -2
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 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))[1] == 0 \
&& ((__const uint32_t *) (a))[2] == 0 \ && ((__const uint32_t *) (a))[2] == 0 \
&& ((__const uint32_t *) (a))[3] == 0) && ((__const uint32_t *) (a))[3] == 0)
+3 -6
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -1021,6 +1021,7 @@ void lease_set_hostname(struct dhcp_lease *lease, const char *name, int auth, ch
} }
kill_name(lease_tmp); kill_name(lease_tmp);
lease_tmp->flags |= LEASE_CHANGED; /* run script on change */
break; break;
} }
} }
@@ -1201,8 +1202,4 @@ void lease_add_extradata(struct dhcp_lease *lease, unsigned char *data, unsigned
} }
#endif #endif
#endif #endif /* HAVE_DHCP */
+8 -2
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -273,7 +273,7 @@ static void log_write(void)
/* priority is one of LOG_DEBUG, LOG_INFO, LOG_NOTICE, etc. See sys/syslog.h. /* 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 OR'd to priority can be MS_TFTP, MS_DHCP, ... to be able to do log separation between
DNS, DHCP and TFTP services. 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, ...) void my_syslog(int priority, const char *format, ...)
{ {
va_list ap; va_list ap;
@@ -290,6 +290,12 @@ void my_syslog(int priority, const char *format, ...)
func = "-dhcp"; func = "-dhcp";
else if ((LOG_FACMASK & priority) == MS_SCRIPT) else if ((LOG_FACMASK & priority) == MS_SCRIPT)
func = "-script"; func = "-script";
else if ((LOG_FACMASK & priority) == MS_DEBUG)
{
if (!option_bool(OPT_LOG_DEBUG))
return;
func = "-debug";
}
#ifdef LOG_PRI #ifdef LOG_PRI
priority = LOG_PRI(priority); priority = LOG_PRI(priority);
+12 -16
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 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() void loop_send_probes()
{ {
struct server *serv; struct server *serv;
struct randfd_list *rfds = NULL;
if (!option_bool(OPT_LOOP_DETECT)) if (!option_bool(OPT_LOOP_DETECT))
return; return;
@@ -29,27 +30,22 @@ void loop_send_probes()
/* Loop through all upstream servers not for particular domains, and send a query to that server which is /* 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. */ 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) for (serv = daemon->servers; serv; serv = serv->next)
if (!(serv->flags & if (strlen(serv->domain) == 0 &&
(SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND | SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_LOOP))) !(serv->flags & (SERV_FOR_NODOTS)))
{ {
ssize_t len = loop_make_probe(serv->uid); ssize_t len = loop_make_probe(serv->uid);
int fd; int fd;
struct randfd *rfd = NULL;
if (serv->sfd) serv->flags &= ~SERV_LOOP;
fd = serv->sfd->fd;
else if ((fd = allocate_rfd(&rfds, serv)) == -1)
{
if (!(rfd = allocate_rfd(serv->addr.sa.sa_family)))
continue; continue;
fd = rfd->fd;
}
while (retry_send(sendto(fd, daemon->packet, len, 0, while (retry_send(sendto(fd, daemon->packet, len, 0,
&serv->addr.sa, sa_len(&serv->addr)))); &serv->addr.sa, sa_len(&serv->addr))));
free_rfd(rfd);
} }
free_rfds(&rfds);
} }
static ssize_t loop_make_probe(u32 uid) static ssize_t loop_make_probe(u32 uid)
@@ -102,12 +98,12 @@ int detect_loop(char *query, int type)
uid = strtol(query, NULL, 16); uid = strtol(query, NULL, 16);
for (serv = daemon->servers; serv; serv = serv->next) for (serv = daemon->servers; serv; serv = serv->next)
if (!(serv->flags & if (strlen(serv->domain) == 0 &&
(SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND | SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_LOOP)) && !(serv->flags & SERV_LOOP) &&
uid == serv->uid) uid == serv->uid)
{ {
serv->flags |= SERV_LOOP; serv->flags |= SERV_LOOP;
check_servers(); /* log new state */ check_servers(1); /* log new state - don't send more probes. */
return 1; return 1;
} }
+1 -1
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
+47 -32
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -43,17 +43,24 @@
# define NDA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg)))) # 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 struct iovec iov;
static u32 netlink_pid; 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) char *netlink_init(void)
{ {
struct sockaddr_nl addr; struct sockaddr_nl addr;
socklen_t slen = sizeof(addr); socklen_t slen = sizeof(addr);
int opt = 1;
addr.nl_family = AF_NETLINK; addr.nl_family = AF_NETLINK;
addr.nl_pad = 0; addr.nl_pad = 0;
@@ -92,14 +99,10 @@ char *netlink_init(void)
iov.iov_len = 100; iov.iov_len = 100;
iov.iov_base = safe_malloc(iov.iov_len); 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; return NULL;
} }
static ssize_t netlink_recv(void) static ssize_t netlink_recv(int flags)
{ {
struct msghdr msg; struct msghdr msg;
struct sockaddr_nl nladdr; struct sockaddr_nl nladdr;
@@ -115,7 +118,8 @@ static ssize_t netlink_recv(void)
msg.msg_iovlen = 1; msg.msg_iovlen = 1;
msg.msg_flags = 0; 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 */ /* make buffer big enough */
if (rc != -1 && (msg.msg_flags & MSG_TRUNC)) if (rc != -1 && (msg.msg_flags & MSG_TRUNC))
@@ -132,7 +136,7 @@ static ssize_t netlink_recv(void)
/* read it for real */ /* read it for real */
msg.msg_flags = 0; 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 */ /* Make sure this is from the kernel */
if (rc == -1 || nladdr.nl_pid == 0) if (rc == -1 || nladdr.nl_pid == 0)
@@ -151,7 +155,9 @@ static ssize_t netlink_recv(void)
/* family = AF_UNSPEC finds ARP table entries. /* family = AF_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)()) int iface_enumerate(int family, void *parm, int (*callback)())
{ {
struct sockaddr_nl addr; struct sockaddr_nl addr;
@@ -159,6 +165,7 @@ int iface_enumerate(int family, void *parm, int (*callback)())
ssize_t len; ssize_t len;
static unsigned int seq = 0; static unsigned int seq = 0;
int callback_ok = 1; int callback_ok = 1;
unsigned state = 0;
struct { struct {
struct nlmsghdr nlh; struct nlmsghdr nlh;
@@ -170,7 +177,6 @@ int iface_enumerate(int family, void *parm, int (*callback)())
addr.nl_family = AF_NETLINK; addr.nl_family = AF_NETLINK;
again:
if (family == AF_UNSPEC) if (family == AF_UNSPEC)
req.nlh.nlmsg_type = RTM_GETNEIGH; req.nlh.nlmsg_type = RTM_GETNEIGH;
else if (family == AF_LOCAL) else if (family == AF_LOCAL)
@@ -193,12 +199,12 @@ int iface_enumerate(int family, void *parm, int (*callback)())
while (1) while (1)
{ {
if ((len = netlink_recv()) == -1) if ((len = netlink_recv(0)) == -1)
{ {
if (errno == ENOBUFS) if (errno == ENOBUFS)
{ {
sleep(1); nl_multicast_state(state);
goto again; return -1;
} }
return 0; return 0;
} }
@@ -207,7 +213,7 @@ int iface_enumerate(int family, void *parm, int (*callback)())
if (h->nlmsg_pid != netlink_pid || h->nlmsg_type == NLMSG_ERROR) if (h->nlmsg_pid != netlink_pid || h->nlmsg_type == NLMSG_ERROR)
{ {
/* May be multicast arriving async */ /* May be multicast arriving async */
nl_async(h); state = nl_async(h, state);
} }
else if (h->nlmsg_seq != seq) else if (h->nlmsg_seq != seq)
{ {
@@ -341,26 +347,28 @@ int iface_enumerate(int family, void *parm, int (*callback)())
} }
} }
void netlink_multicast(void) static void nl_multicast_state(unsigned state)
{ {
ssize_t len; ssize_t len;
struct nlmsghdr *h; struct nlmsghdr *h;
int flags;
do {
/* don't risk blocking reading netlink messages here. */ /* don't risk blocking reading netlink messages here. */
if ((flags = fcntl(daemon->netlinkfd, F_GETFL)) == -1 || while ((len = netlink_recv(MSG_DONTWAIT)) != -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)) for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len))
nl_async(h); state = nl_async(h, state);
} while (errno == ENOBUFS);
/* restore non-blocking status */
fcntl(daemon->netlinkfd, F_SETFL, flags);
} }
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) if (h->nlmsg_type == NLMSG_ERROR)
{ {
@@ -368,7 +376,8 @@ static void nl_async(struct nlmsghdr *h)
if (err->error != 0) if (err->error != 0)
my_syslog(LOG_ERR, _("netlink returns error: %s"), strerror(-(err->error))); 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. /* 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. If this happens and we still have a DNS packet in the buffer, we re-send it.
@@ -380,11 +389,17 @@ static void nl_async(struct nlmsghdr *h)
if (rtm->rtm_type == RTN_UNICAST && rtm->rtm_scope == RT_SCOPE_LINK && if (rtm->rtm_type == RTN_UNICAST && rtm->rtm_scope == RT_SCOPE_LINK &&
(rtm->rtm_table == RT_TABLE_MAIN || (rtm->rtm_table == RT_TABLE_MAIN ||
rtm->rtm_table == RT_TABLE_LOCAL)) 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) }
else if ((h->nlmsg_type == RTM_NEWADDR || h->nlmsg_type == RTM_DELADDR) &&
(state & STATE_NEWADDR)==0)
{
queue_event(EVENT_NEWADDR); queue_event(EVENT_NEWADDR);
state |= STATE_NEWADDR;
} }
#endif return state;
}
#endif /* HAVE_LINUX_NETWORK */
+187 -252
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -232,7 +232,7 @@ static int iface_allowed(struct iface_param *param, int if_index, char *label,
union mysockaddr *addr, struct in_addr netmask, int prefixlen, int iface_flags) union mysockaddr *addr, struct in_addr netmask, int prefixlen, int iface_flags)
{ {
struct irec *iface; struct irec *iface;
int mtu = 0, loopback; int loopback;
struct ifreq ifr; struct ifreq ifr;
int tftp_ok = !!option_bool(OPT_TFTP); int tftp_ok = !!option_bool(OPT_TFTP);
int dhcp_ok = 1; int dhcp_ok = 1;
@@ -253,9 +253,6 @@ static int iface_allowed(struct iface_param *param, int if_index, char *label,
if (loopback) if (loopback)
dhcp_ok = 0; dhcp_ok = 0;
if (ioctl(param->fd, SIOCGIFMTU, &ifr) != -1)
mtu = ifr.ifr_mtu;
if (!label) if (!label)
label = ifr.ifr_name; label = ifr.ifr_name;
else else
@@ -351,8 +348,31 @@ static int iface_allowed(struct iface_param *param, int if_index, char *label,
/* Update addresses from interface_names. These are a set independent /* Update addresses from interface_names. These are a set independent
of the set we're listening on. */ of the set we're listening on. */
for (int_name = daemon->int_names; int_name; int_name = int_name->next) for (int_name = daemon->int_names; int_name; int_name = int_name->next)
if (strncmp(label, int_name->intr, IF_NAMESIZE) == 0 && if (strncmp(label, int_name->intr, IF_NAMESIZE) == 0)
(addr->sa.sa_family == int_name->family || int_name->family == 0)) {
struct addrlist *lp;
al = NULL;
if (addr->sa.sa_family == AF_INET && (int_name->flags & (IN4 | INP4)))
{
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) if (param->spare)
{ {
@@ -364,18 +384,61 @@ static int iface_allowed(struct iface_param *param, int if_index, char *label,
if (al) 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; 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 else
al = whine_malloc(sizeof(struct addrlist));
if (al)
{ {
al->addr.addr6 = addr->in6.sin6_addr;
al->flags = ADDRLIST_IPV6; al->flags = ADDRLIST_IPV6;
al->addr.addr6 = newaddr;
/* Privacy addresses and addresses still undergoing DAD and deprecated addresses /* Privacy addresses and addresses still undergoing DAD and deprecated addresses
don't appear in forward queries, but will in reverse ones. */ don't appear in forward queries, but will in reverse ones. */
if (!(iface_flags & IFACE_PERMANENT) || (iface_flags & (IFACE_DEPRECATED | IFACE_TENTATIVE))) if (!(iface_flags & IFACE_PERMANENT) || (iface_flags & (IFACE_DEPRECATED | IFACE_TENTATIVE)))
@@ -383,6 +446,13 @@ static int iface_allowed(struct iface_param *param, int if_index, char *label,
} }
} }
} }
if (al)
{
al->next = int_name->addr;
int_name->addr = al;
}
}
} }
/* check whether the interface IP has been added already /* check whether the interface IP has been added already
@@ -458,6 +528,11 @@ static int iface_allowed(struct iface_param *param, int if_index, char *label,
/* add to list */ /* add to list */
if ((iface = whine_malloc(sizeof(struct irec)))) 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->addr = *addr;
iface->netmask = netmask; iface->netmask = netmask;
iface->tftp_ok = tftp_ok; iface->tftp_ok = tftp_ok;
@@ -592,7 +667,7 @@ static int release_listener(struct listener *l)
int port; int port;
port = prettyprint_addr(&l->iface->addr, daemon->addrbuff); 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); l->iface->name, l->iface->index, daemon->addrbuff, port);
/* In case it ever returns */ /* In case it ever returns */
l->iface->done = 0; l->iface->done = 0;
@@ -621,6 +696,7 @@ int enumerate_interfaces(int reset)
#ifdef HAVE_AUTH #ifdef HAVE_AUTH
struct auth_zone *zone; struct auth_zone *zone;
#endif #endif
struct server *serv;
/* Do this max once per select cycle - also inhibits netlink socket use /* Do this max once per select cycle - also inhibits netlink socket use
in TCP child processes. */ in TCP child processes. */
@@ -639,6 +715,25 @@ int enumerate_interfaces(int reset)
if ((param.fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) if ((param.fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
return 0; 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 */ /* Mark interfaces for garbage collection */
for (iface = daemon->interfaces; iface; iface = iface->next) for (iface = daemon->interfaces; iface; iface = iface->next)
iface->found = 0; iface->found = 0;
@@ -690,9 +785,14 @@ int enumerate_interfaces(int reset)
param.spare = spare; param.spare = spare;
ret = iface_enumerate(AF_INET6, &param, iface_allowed_v6); ret = iface_enumerate(AF_INET6, &param, iface_allowed_v6);
if (ret < 0)
if (ret) goto again;
else if (ret)
{
ret = iface_enumerate(AF_INET, &param, iface_allowed_v4); ret = iface_enumerate(AF_INET, &param, iface_allowed_v4);
if (ret < 0)
goto again;
}
errsave = errno; errsave = errno;
close(param.fd); close(param.fd);
@@ -1045,7 +1145,7 @@ void create_bound_listeners(int dienow)
if (!dienow) if (!dienow)
{ {
int port = prettyprint_addr(&iface->addr, daemon->addrbuff); 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); iface->name, iface->index, daemon->addrbuff, port);
} }
} }
@@ -1072,7 +1172,7 @@ void create_bound_listeners(int dienow)
if (!dienow) if (!dienow)
{ {
int port = prettyprint_addr(&if_tmp->addr, daemon->addrbuff); 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,65 +1306,12 @@ void join_multicast(int dienow)
} }
#endif #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) int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifindex, int is_tcp)
{ {
union mysockaddr addr_copy = *addr; union mysockaddr addr_copy = *addr;
unsigned short port; unsigned short port;
int tries = 1, done = 0; int tries = 1;
unsigned int ports_avail = ((unsigned short)daemon->max_port - (unsigned short)daemon->min_port) + 1; unsigned short ports_avail = 1;
if (addr_copy.sa.sa_family == AF_INET) if (addr_copy.sa.sa_family == AF_INET)
port = addr_copy.in.sin_port; port = addr_copy.in.sin_port;
@@ -1274,36 +1321,44 @@ int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifind
/* cannot set source _port_ for TCP connections. */ /* cannot set source _port_ for TCP connections. */
if (is_tcp) if (is_tcp)
port = 0; 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 (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; 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) if (addr_copy.sa.sa_family == AF_INET)
{
if (port == 0 && addr_copy.in.sin_addr.s_addr == 0)
break;
addr_copy.in.sin_port = port; addr_copy.in.sin_port = port;
}
else else
{
if (port == 0 && IN6_IS_ADDR_UNSPECIFIED(&addr_copy.in6.sin6_addr))
break;
addr_copy.in6.sin6_port = port; addr_copy.in6.sin6_port = port;
}
if (bind(fd, (struct sockaddr *)&addr_copy, sa_len(&addr_copy)) != -1) if (bind(fd, (struct sockaddr *)&addr_copy, sa_len(&addr_copy)) != -1)
{
done = 1;
break; break;
}
if (errno != EADDRINUSE && errno != EACCES) if (errno != EADDRINUSE && errno != EACCES)
return 0; return 0;
port = htons(daemon->min_port + (rand16() % ((unsigned short)ports_avail))); if (--tries == 0)
}
if (!done)
return 0; return 0;
port = htons(daemon->min_port + (rand16() % ports_avail));
}
if (!is_tcp && ifindex > 0) if (!is_tcp && ifindex > 0)
{ {
#if defined(IP_UNICAST_IF) #if defined(IP_UNICAST_IF)
@@ -1332,38 +1387,33 @@ int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifind
return 1; 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; struct serverfd *sfd;
unsigned int ifindex = 0;
int errsave; int errsave;
int opt = 1; int opt = 1;
/* when using random ports, servers which would otherwise use /* when using random ports, servers which would otherwise use
the INADDR_ANY/port0 socket have sfd set to NULL */ the INADDR_ANY/port0 socket have sfd set to NULL, this is
if (!daemon->osport && intname[0] == 0) anything without an explictly set source port. */
if (!daemon->osport)
{ {
errno = 0; errno = 0;
if (addr->sa.sa_family == AF_INET && if (addr->sa.sa_family == AF_INET &&
addr->in.sin_addr.s_addr == INADDR_ANY &&
addr->in.sin_port == htons(0)) addr->in.sin_port == htons(0))
return NULL; return NULL;
if (addr->sa.sa_family == AF_INET6 && if (addr->sa.sa_family == AF_INET6 &&
memcmp(&addr->in6.sin6_addr, &in6addr_any, sizeof(in6addr_any)) == 0 &&
addr->in6.sin6_port == htons(0)) addr->in6.sin6_port == htons(0))
return NULL; 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 */ /* may have a suitable one already */
for (sfd = daemon->sfds; sfd; sfd = sfd->next ) for (sfd = daemon->sfds; sfd; sfd = sfd->next )
if (sockaddr_isequal(&sfd->source_addr, addr) && if (ifindex == sfd->ifindex &&
strcmp(intname, sfd->interface) == 0 && sockaddr_isequal(&sfd->source_addr, addr) &&
ifindex == sfd->ifindex) strcmp(intname, sfd->interface) == 0)
return sfd; return sfd;
/* need to make a new one. */ /* need to make a new one. */
@@ -1414,7 +1464,7 @@ void pre_allocate_sfds(void)
#ifdef HAVE_SOCKADDR_SA_LEN #ifdef HAVE_SOCKADDR_SA_LEN
addr.in.sin_len = sizeof(struct sockaddr_in); addr.in.sin_len = sizeof(struct sockaddr_in);
#endif #endif
if ((sfd = allocate_sfd(&addr, ""))) if ((sfd = allocate_sfd(&addr, "", 0)))
sfd->preallocated = 1; sfd->preallocated = 1;
memset(&addr, 0, sizeof(addr)); memset(&addr, 0, sizeof(addr));
@@ -1424,13 +1474,12 @@ void pre_allocate_sfds(void)
#ifdef HAVE_SOCKADDR_SA_LEN #ifdef HAVE_SOCKADDR_SA_LEN
addr.in6.sin6_len = sizeof(struct sockaddr_in6); addr.in6.sin6_len = sizeof(struct sockaddr_in6);
#endif #endif
if ((sfd = allocate_sfd(&addr, ""))) if ((sfd = allocate_sfd(&addr, "", 0)))
sfd->preallocated = 1; sfd->preallocated = 1;
} }
for (srv = daemon->servers; srv; srv = srv->next) for (srv = daemon->servers; srv; srv = srv->next)
if (!(srv->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND)) && if (!allocate_sfd(&srv->source_addr, srv->interface, srv->ifindex) &&
!allocate_sfd(&srv->source_addr, srv->interface) &&
errno != 0 && errno != 0 &&
option_bool(OPT_NOWILD)) option_bool(OPT_NOWILD))
{ {
@@ -1445,128 +1494,7 @@ void pre_allocate_sfds(void)
} }
} }
void mark_servers(int flag) void check_servers(int no_loop_check)
{
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)
{ {
struct irec *iface; struct irec *iface;
struct server *serv; struct server *serv;
@@ -1574,6 +1502,14 @@ void check_servers(void)
int port = 0, count; int port = 0, count;
int locals = 0; int locals = 0;
#ifdef HAVE_LOOP
if (!no_loop_check)
loop_send_probes();
#endif
/* clear all marks. */
mark_servers(0);
/* interface may be new since startup */ /* interface may be new since startup */
if (!option_bool(OPT_NOWILD)) if (!option_bool(OPT_NOWILD))
enumerate_interfaces(0); enumerate_interfaces(0);
@@ -1583,8 +1519,6 @@ void check_servers(void)
sfd->used = sfd->preallocated; sfd->used = sfd->preallocated;
for (count = 0, serv = daemon->servers; serv; serv = serv->next) 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. */ /* Init edns_pktsz for newly created server records. */
if (serv->edns_pktsz == 0) if (serv->edns_pktsz == 0)
@@ -1598,7 +1532,7 @@ void check_servers(void)
/* Disable DNSSEC validation when using server=/domain/.... servers /* Disable DNSSEC validation when using server=/domain/.... servers
unless there's a configured trust anchor. */ unless there's a configured trust anchor. */
if (serv->flags & SERV_HAS_DOMAIN) if (strlen(serv->domain) != 0)
{ {
struct ds_config *ds; struct ds_config *ds;
char *domain = serv->domain; char *domain = serv->domain;
@@ -1639,7 +1573,7 @@ void check_servers(void)
/* Do we need a socket set? */ /* Do we need a socket set? */
if (!serv->sfd && if (!serv->sfd &&
!(serv->sfd = allocate_sfd(&serv->source_addr, serv->interface)) && !(serv->sfd = allocate_sfd(&serv->source_addr, serv->interface, serv->ifindex)) &&
errno != 0) errno != 0)
{ {
my_syslog(LOG_WARNING, my_syslog(LOG_WARNING,
@@ -1651,37 +1585,26 @@ void check_servers(void)
if (serv->sfd) if (serv->sfd)
serv->sfd->used = 1; serv->sfd->used = 1;
}
if (!(serv->flags & SERV_NO_REBIND) && !(serv->flags & SERV_LITERAL_ADDRESS))
{
if (++count > SERVERS_LOGGED) if (++count > SERVERS_LOGGED)
continue; continue;
if (serv->flags & (SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_USE_RESOLV)) if (strlen(serv->domain) != 0 || (serv->flags & SERV_FOR_NODOTS))
{ {
char *s1, *s2, *s3 = ""; char *s1, *s2, *s3 = "", *s4 = "";
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && !(serv->flags & SERV_DO_DNSSEC)) if (option_bool(OPT_DNSSEC_VALID) && !(serv->flags & SERV_DO_DNSSEC))
s3 = _("(no DNSSEC)"); s3 = _("(no DNSSEC)");
#endif #endif
if (!(serv->flags & SERV_HAS_DOMAIN)) if (serv->flags & SERV_FOR_NODOTS)
s1 = _("unqualified"), s2 = _("names"); s1 = _("unqualified"), s2 = _("names");
else if (strlen(serv->domain) == 0) else if (strlen(serv->domain) == 0)
s1 = _("default"), s2 = ""; s1 = _("default"), s2 = "";
else else
s1 = _("domain"), s2 = serv->domain; s1 = _("domain"), s2 = serv->domain, s4 = (serv->flags & SERV_WILDCARD) ? "*" : "";
if (serv->flags & SERV_NO_ADDR) my_syslog(LOG_INFO, _("using nameserver %s#%d for %s %s%s %s"), daemon->namebuff, port, s1, s4, s2, s3);
{
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 #ifdef HAVE_LOOP
else if (serv->flags & SERV_LOOP) else if (serv->flags & SERV_LOOP)
@@ -1691,7 +1614,23 @@ void check_servers(void)
my_syslog(LOG_INFO, _("using nameserver %s#%d(via %s)"), daemon->namebuff, port, serv->interface); my_syslog(LOG_INFO, _("using nameserver %s#%d(via %s)"), daemon->namebuff, port, serv->interface);
else else
my_syslog(LOG_INFO, _("using nameserver %s#%d"), daemon->namebuff, port); my_syslog(LOG_INFO, _("using nameserver %s#%d"), daemon->namebuff, port);
} }
for (count = 0, serv = daemon->local_domains; serv; serv = serv->next)
{
if (++count > SERVERS_LOGGED)
continue;
if ((serv->flags & SERV_LITERAL_ADDRESS) &&
!(serv->flags & (SERV_6ADDR | SERV_4ADDR | SERV_ALL_ZEROS)))
{
count--;
if (++locals <= LOCALS_LOGGED)
my_syslog(LOG_INFO, _("using only locally-known addresses for %s"), serv->domain);
}
else if (serv->flags & SERV_USE_RESOLV)
my_syslog(LOG_INFO, _("using standard nameservers for %s"), serv->domain);
} }
if (locals > LOCALS_LOGGED) if (locals > LOCALS_LOGGED)
@@ -1713,7 +1652,8 @@ void check_servers(void)
up = &sfd->next; 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. /* Return zero if no servers found, in that case we keep polling.
@@ -1786,7 +1726,7 @@ int reload_servers(char *fname)
continue; 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; gotone = 1;
} }
@@ -1819,8 +1759,3 @@ void newaddress(time_t now)
lease_find_interfaces(now); lease_find_interfaces(now);
#endif #endif
} }
+412 -214
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -168,6 +168,11 @@ struct myoption {
#define LOPT_SINGLE_PORT 359 #define LOPT_SINGLE_PORT 359
#define LOPT_SCRIPT_TIME 360 #define LOPT_SCRIPT_TIME 360
#define LOPT_PXE_VENDOR 361 #define LOPT_PXE_VENDOR 361
#define LOPT_DYNHOST 362
#define LOPT_LOG_DEBUG 363
#define LOPT_UMBRELLA 364
#define LOPT_CMARK_ALST_EN 365
#define LOPT_CMARK_ALST 366
#ifdef HAVE_GETOPT_LONG #ifdef HAVE_GETOPT_LONG
static const struct option opts[] = static const struct option opts[] =
@@ -321,6 +326,8 @@ static const struct myoption opts[] =
{ "auth-sec-servers", 1, 0, LOPT_AUTHSFS }, { "auth-sec-servers", 1, 0, LOPT_AUTHSFS },
{ "auth-peer", 1, 0, LOPT_AUTHPEER }, { "auth-peer", 1, 0, LOPT_AUTHPEER },
{ "ipset", 1, 0, LOPT_IPSET }, { "ipset", 1, 0, LOPT_IPSET },
{ "connmark-allowlist-enable", 2, 0, LOPT_CMARK_ALST_EN },
{ "connmark-allowlist", 1, 0, LOPT_CMARK_ALST },
{ "synth-domain", 1, 0, LOPT_SYNTH }, { "synth-domain", 1, 0, LOPT_SYNTH },
{ "dnssec", 0, 0, LOPT_SEC_VALID }, { "dnssec", 0, 0, LOPT_SEC_VALID },
{ "trust-anchor", 1, 0, LOPT_TRUST_ANCHOR }, { "trust-anchor", 1, 0, LOPT_TRUST_ANCHOR },
@@ -341,6 +348,9 @@ static const struct myoption opts[] =
{ "dumpfile", 1, 0, LOPT_DUMPFILE }, { "dumpfile", 1, 0, LOPT_DUMPFILE },
{ "dumpmask", 1, 0, LOPT_DUMPMASK }, { "dumpmask", 1, 0, LOPT_DUMPMASK },
{ "dhcp-ignore-clid", 0, 0, LOPT_IGNORE_CLID }, { "dhcp-ignore-clid", 0, 0, LOPT_IGNORE_CLID },
{ "dynamic-host", 1, 0, LOPT_DYNHOST },
{ "log-debug", 0, 0, LOPT_LOG_DEBUG },
{ "umbrella", 2, 0, LOPT_UMBRELLA },
{ NULL, 0, 0, 0 } { NULL, 0, 0, 0 }
}; };
@@ -491,6 +501,7 @@ static struct {
{ LOPT_RA, OPT_RA, NULL, gettext_noop("Send router-advertisements for interfaces doing DHCPv6"), NULL }, { LOPT_RA, OPT_RA, NULL, gettext_noop("Send router-advertisements for interfaces doing DHCPv6"), NULL },
{ LOPT_DUID, ARG_ONE, "<enterprise>,<duid>", gettext_noop("Specify DUID_EN-type DHCPv6 server DUID"), NULL }, { LOPT_DUID, ARG_ONE, "<enterprise>,<duid>", gettext_noop("Specify DUID_EN-type DHCPv6 server DUID"), NULL },
{ LOPT_HOST_REC, ARG_DUP, "<name>,<address>[,<ttl>]", gettext_noop("Specify host (A/AAAA and PTR) records"), NULL }, { LOPT_HOST_REC, ARG_DUP, "<name>,<address>[,<ttl>]", gettext_noop("Specify host (A/AAAA and PTR) records"), NULL },
{ LOPT_DYNHOST, ARG_DUP, "<name>,[<IPv4>][,<IPv6>],<interface-name>", gettext_noop("Specify host record in interface subnet"), NULL },
{ LOPT_CAA, ARG_DUP, "<name>,<flags>,<tag>,<value>", gettext_noop("Specify certification authority authorization record"), NULL }, { LOPT_CAA, ARG_DUP, "<name>,<flags>,<tag>,<value>", gettext_noop("Specify certification authority authorization record"), NULL },
{ LOPT_RR, ARG_DUP, "<name>,<RR-number>,[<data>]", gettext_noop("Specify arbitrary DNS resource record"), NULL }, { LOPT_RR, ARG_DUP, "<name>,<RR-number>,[<data>]", gettext_noop("Specify arbitrary DNS resource record"), NULL },
{ LOPT_CLVERBIND, OPT_CLEVERBIND, NULL, gettext_noop("Bind to interfaces in use - check for new interfaces"), NULL }, { LOPT_CLVERBIND, OPT_CLEVERBIND, NULL, gettext_noop("Bind to interfaces in use - check for new interfaces"), NULL },
@@ -501,6 +512,8 @@ static struct {
{ LOPT_AUTHSFS, ARG_DUP, "<NS>[,<NS>...]", gettext_noop("Secondary authoritative nameservers for forward domains"), NULL }, { LOPT_AUTHSFS, ARG_DUP, "<NS>[,<NS>...]", gettext_noop("Secondary authoritative nameservers for forward domains"), NULL },
{ LOPT_AUTHPEER, ARG_DUP, "<ipaddr>[,<ipaddr>...]", gettext_noop("Peers which are allowed to do zone transfer"), NULL }, { LOPT_AUTHPEER, ARG_DUP, "<ipaddr>[,<ipaddr>...]", gettext_noop("Peers which are allowed to do zone transfer"), NULL },
{ LOPT_IPSET, ARG_DUP, "/<domain>[/<domain>...]/<ipset>...", gettext_noop("Specify ipsets to which matching domains should be added"), NULL }, { LOPT_IPSET, ARG_DUP, "/<domain>[/<domain>...]/<ipset>...", gettext_noop("Specify ipsets to which matching domains should be added"), NULL },
{ LOPT_CMARK_ALST_EN, ARG_ONE, "[=<mask>]", gettext_noop("Enable filtering of DNS queries with connection-track marks."), NULL },
{ LOPT_CMARK_ALST, ARG_DUP, "<connmark>[/<mask>][,<pattern>[/<pattern>...]]", gettext_noop("Set allowed DNS patterns for a connection-track mark."), NULL },
{ LOPT_SYNTH, ARG_DUP, "<domain>,<range>,[<prefix>]", gettext_noop("Specify a domain and address range for synthesised names"), NULL }, { LOPT_SYNTH, ARG_DUP, "<domain>,<range>,[<prefix>]", gettext_noop("Specify a domain and address range for synthesised names"), NULL },
{ LOPT_SEC_VALID, OPT_DNSSEC_VALID, NULL, gettext_noop("Activate DNSSEC validation"), NULL }, { LOPT_SEC_VALID, OPT_DNSSEC_VALID, NULL, gettext_noop("Activate DNSSEC validation"), NULL },
{ LOPT_TRUST_ANCHOR, ARG_DUP, "<domain>,[<class>],...", gettext_noop("Specify trust anchor key digest."), NULL }, { LOPT_TRUST_ANCHOR, ARG_DUP, "<domain>,[<class>],...", gettext_noop("Specify trust anchor key digest."), NULL },
@@ -512,6 +525,7 @@ static struct {
{ LOPT_QUIET_DHCP, OPT_QUIET_DHCP, NULL, gettext_noop("Do not log routine DHCP."), NULL }, { LOPT_QUIET_DHCP, OPT_QUIET_DHCP, NULL, gettext_noop("Do not log routine DHCP."), NULL },
{ LOPT_QUIET_DHCP6, OPT_QUIET_DHCP6, NULL, gettext_noop("Do not log routine DHCPv6."), NULL }, { LOPT_QUIET_DHCP6, OPT_QUIET_DHCP6, NULL, gettext_noop("Do not log routine DHCPv6."), NULL },
{ LOPT_QUIET_RA, OPT_QUIET_RA, NULL, gettext_noop("Do not log RA."), NULL }, { LOPT_QUIET_RA, OPT_QUIET_RA, NULL, gettext_noop("Do not log RA."), NULL },
{ LOPT_LOG_DEBUG, OPT_LOG_DEBUG, NULL, gettext_noop("Log debugging information."), NULL },
{ LOPT_LOCAL_SERVICE, OPT_LOCAL_SERVICE, NULL, gettext_noop("Accept queries only from directly-connected networks."), NULL }, { LOPT_LOCAL_SERVICE, OPT_LOCAL_SERVICE, NULL, gettext_noop("Accept queries only from directly-connected networks."), NULL },
{ LOPT_LOOP_DETECT, OPT_LOOP_DETECT, NULL, gettext_noop("Detect and remove DNS forwarding loops."), NULL }, { LOPT_LOOP_DETECT, OPT_LOOP_DETECT, NULL, gettext_noop("Detect and remove DNS forwarding loops."), NULL },
{ LOPT_IGNORE_ADDR, ARG_DUP, "<ipaddr>", gettext_noop("Ignore DNS responses containing ipaddr."), NULL }, { LOPT_IGNORE_ADDR, ARG_DUP, "<ipaddr>", gettext_noop("Ignore DNS responses containing ipaddr."), NULL },
@@ -521,6 +535,7 @@ static struct {
{ LOPT_DUMPFILE, ARG_ONE, "<path>", gettext_noop("Path to debug packet dump file"), NULL }, { LOPT_DUMPFILE, ARG_ONE, "<path>", gettext_noop("Path to debug packet dump file"), NULL },
{ LOPT_DUMPMASK, ARG_ONE, "<hex>", gettext_noop("Mask which packets to dump"), NULL }, { LOPT_DUMPMASK, ARG_ONE, "<hex>", gettext_noop("Mask which packets to dump"), NULL },
{ LOPT_SCRIPT_TIME, OPT_LEASE_RENEW, NULL, gettext_noop("Call dhcp-script when lease expiry changes."), NULL }, { LOPT_SCRIPT_TIME, OPT_LEASE_RENEW, NULL, gettext_noop("Call dhcp-script when lease expiry changes."), NULL },
{ LOPT_UMBRELLA, ARG_ONE, "[=<optspec>]", gettext_noop("Send Cisco Umbrella identifiers including remote IP."), NULL },
{ 0, 0, NULL, NULL, NULL } { 0, 0, NULL, NULL, NULL }
}; };
@@ -635,6 +650,9 @@ static char *canonicalise_opt(char *s)
if (!s) if (!s)
return 0; return 0;
if (strlen(s) == 0)
return "";
unhide_metas(s); unhide_metas(s);
if (!(ret = canonicalise(s, &nomem)) && nomem) if (!(ret = canonicalise(s, &nomem)) && nomem)
{ {
@@ -647,7 +665,7 @@ static char *canonicalise_opt(char *s)
return ret; return ret;
} }
static int atoi_check(char *a, int *res) static int numeric_check(char *a)
{ {
char *p; char *p;
@@ -660,10 +678,32 @@ static int atoi_check(char *a, int *res)
if (*p < '0' || *p > '9') if (*p < '0' || *p > '9')
return 0; return 0;
return 1;
}
static int atoi_check(char *a, int *res)
{
if (!numeric_check(a))
return 0;
*res = atoi(a); *res = atoi(a);
return 1; return 1;
} }
static int strtoul_check(char *a, u32 *res)
{
unsigned long x;
if (!numeric_check(a))
return 0;
x = strtoul(a, NULL, 10);
if (errno || x > UINT32_MAX) {
errno = 0;
return 0;
}
*res = (u32)x;
return 1;
}
static int atoi_check16(char *a, int *res) static int atoi_check16(char *a, int *res)
{ {
if (!(atoi_check(a, res)) || if (!(atoi_check(a, res)) ||
@@ -781,7 +821,7 @@ static char *parse_mysockaddr(char *arg, union mysockaddr *addr)
return NULL; return NULL;
} }
char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_addr, char *interface, int *flags) char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_addr, char *interface, u16 *flags)
{ {
int source_port = 0, serv_port = NAMESERVER_PORT; int source_port = 0, serv_port = NAMESERVER_PORT;
char *portno, *source; char *portno, *source;
@@ -789,10 +829,12 @@ char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_a
int scope_index = 0; int scope_index = 0;
char *scope_id; char *scope_id;
if (!arg || strlen(arg) == 0)
{
*flags |= SERV_NO_ADDR;
*interface = 0; *interface = 0;
if (strcmp(arg, "#") == 0)
{
if (flags)
*flags |= SERV_USE_RESOLV;
return NULL; return NULL;
} }
@@ -813,7 +855,8 @@ char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_a
if (interface_opt) if (interface_opt)
{ {
#if defined(SO_BINDTODEVICE) #if defined(SO_BINDTODEVICE)
safe_strncpy(interface, interface_opt, IF_NAMESIZE); safe_strncpy(interface, source, IF_NAMESIZE);
source = interface_opt;
#else #else
return _("interface binding not supported"); return _("interface binding not supported");
#endif #endif
@@ -889,66 +932,52 @@ char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_a
return NULL; return NULL;
} }
static struct server *add_rev4(struct in_addr addr, int msize) static int domain_rev4(char *domain, struct in_addr addr, int msize)
{ {
struct server *serv = opt_malloc(sizeof(struct server));
in_addr_t a = ntohl(addr.s_addr); in_addr_t a = ntohl(addr.s_addr);
char *p;
memset(serv, 0, sizeof(struct server)); *domain = 0;
p = serv->domain = opt_malloc(29); /* strlen("xxx.yyy.zzz.ttt.in-addr.arpa")+1 */
switch (msize) switch (msize)
{ {
case 32: case 32:
p += sprintf(p, "%u.", a & 0xff); domain += sprintf(domain, "%u.", a & 0xff);
/* fall through */ /* fall through */
case 24: case 24:
p += sprintf(p, "%d.", (a >> 8) & 0xff); domain += sprintf(domain, "%d.", (a >> 8) & 0xff);
/* fall through */ /* fall through */
case 16: case 16:
p += sprintf(p, "%d.", (a >> 16) & 0xff); domain += sprintf(domain, "%d.", (a >> 16) & 0xff);
/* fall through */ /* fall through */
case 8: case 8:
p += sprintf(p, "%d.", (a >> 24) & 0xff); domain += sprintf(domain, "%d.", (a >> 24) & 0xff);
break; break;
default: default:
free(serv->domain); return 0;
free(serv);
return NULL;
} }
p += sprintf(p, "in-addr.arpa"); domain += sprintf(domain, "in-addr.arpa");
serv->flags = SERV_HAS_DOMAIN;
serv->next = daemon->servers;
daemon->servers = serv;
return serv;
return 1;
} }
static struct server *add_rev6(struct in6_addr *addr, int msize) static int domain_rev6(char *domain, struct in6_addr *addr, int msize)
{ {
struct server *serv = opt_malloc(sizeof(struct server));
char *p;
int i; int i;
memset(serv, 0, sizeof(struct server)); if (msize%4)
p = serv->domain = opt_malloc(73); /* strlen("32*<n.>ip6.arpa")+1 */ return 0;
*domain = 0;
for (i = msize-1; i >= 0; i -= 4) for (i = msize-1; i >= 0; i -= 4)
{ {
int dig = ((unsigned char *)addr)[i>>3]; int dig = ((unsigned char *)addr)[i>>3];
p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4); domain += sprintf(domain, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
} }
p += sprintf(p, "ip6.arpa"); domain += sprintf(domain, "ip6.arpa");
serv->flags = SERV_HAS_DOMAIN; return 1;
serv->next = daemon->servers;
daemon->servers = serv;
return serv;
} }
#ifdef HAVE_DHCP #ifdef HAVE_DHCP
@@ -1038,6 +1067,8 @@ static void dhcp_config_free(struct dhcp_config *config)
if (config->flags & CONFIG_CLID) if (config->flags & CONFIG_CLID)
free(config->clid); free(config->clid);
if (config->flags & CONFIG_NAME)
free(config->hostname);
#ifdef HAVE_DHCP6 #ifdef HAVE_DHCP6
if (config->flags & CONFIG_ADDR6) if (config->flags & CONFIG_ADDR6)
@@ -1633,16 +1664,6 @@ void reset_option_bool(unsigned int opt)
option_var(opt) &= ~(option_val(opt)); option_var(opt) &= ~(option_val(opt));
} }
static void server_list_free(struct server *list)
{
while (list)
{
struct server *tmp = list;
list = list->next;
free(tmp);
}
}
static int one_opt(int option, char *arg, char *errstr, char *gen_err, int command_line, int servers_only) static int one_opt(int option, char *arg, char *errstr, char *gen_err, int command_line, int servers_only)
{ {
int i; int i;
@@ -2231,7 +2252,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
set_option_bool(OPT_RESOLV_DOMAIN); set_option_bool(OPT_RESOLV_DOMAIN);
else else
{ {
char *d; char *d, *d_raw = arg;
comma = split(arg); comma = split(arg);
if (!(d = canonicalise_opt(arg))) if (!(d = canonicalise_opt(arg)))
ret_err(gen_err); ret_err(gen_err);
@@ -2272,21 +2293,15 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
ret_err_free(gen_err, new); ret_err_free(gen_err, new);
else else
{ {
/* generate the equivalent of char domain[29]; /* strlen("xxx.yyy.zzz.ttt.in-addr.arpa")+1 */
local=/xxx.yyy.zzz.in-addr.arpa/ */ /* local=/xxx.yyy.zzz.in-addr.arpa/ */
struct server *serv = add_rev4(new->start, msize); /* domain_rev4 can't fail here, msize checked above. */
if (!serv) domain_rev4(domain, new->start, msize);
ret_err_free(_("bad prefix"), new); add_update_server(SERV_LITERAL_ADDRESS, NULL, NULL, NULL, domain, NULL);
serv->flags |= SERV_NO_ADDR;
/* local=/<domain>/ */ /* local=/<domain>/ */
serv = opt_malloc(sizeof(struct server)); /* d_raw can't failed to canonicalise here, checked above. */
memset(serv, 0, sizeof(struct server)); add_update_server(SERV_LITERAL_ADDRESS, NULL, NULL, NULL, d_raw, NULL);
serv->domain = d;
serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
serv->next = daemon->servers;
daemon->servers = serv;
} }
} }
} }
@@ -2318,18 +2333,15 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
ret_err_free(gen_err, new); ret_err_free(gen_err, new);
else else
{ {
char domain[73]; /* strlen("32*<n.>ip6.arpa")+1 */
/* generate the equivalent of /* generate the equivalent of
local=/xxx.yyy.zzz.ip6.arpa/ */ local=/xxx.yyy.zzz.ip6.arpa/ */
struct server *serv = add_rev6(&new->start6, msize); domain_rev6(domain, &new->start6, msize);
serv->flags |= SERV_NO_ADDR; add_update_server(SERV_LITERAL_ADDRESS, NULL, NULL, NULL, domain, NULL);
/* local=/<domain>/ */ /* local=/<domain>/ */
serv = opt_malloc(sizeof(struct server)); /* d_raw can't failed to canonicalise here, checked above. */
memset(serv, 0, sizeof(struct server)); add_update_server(SERV_LITERAL_ADDRESS, NULL, NULL, NULL, d_raw, NULL);
serv->domain = d;
serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
serv->next = daemon->servers;
daemon->servers = serv;
} }
} }
} }
@@ -2402,6 +2414,41 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
daemon->dns_client_id = opt_string_alloc(arg); daemon->dns_client_id = opt_string_alloc(arg);
break; break;
case LOPT_UMBRELLA: /* --umbrella */
set_option_bool(OPT_UMBRELLA);
while (arg) {
comma = split(arg);
if (strstr(arg, "deviceid:")) {
arg += 9;
if (strlen(arg) != 16)
ret_err(gen_err);
for (char *p = arg; *p; p++) {
if (!isxdigit((int)*p))
ret_err(gen_err);
}
set_option_bool(OPT_UMBRELLA_DEVID);
u8 *u = daemon->umbrella_device;
char word[3];
for (u8 i = 0; i < sizeof(daemon->umbrella_device); i++, arg+=2) {
memcpy(word, &(arg[0]), 2);
*u++ = strtoul(word, NULL, 16);
}
}
else if (strstr(arg, "orgid:")) {
if (!strtoul_check(arg+6, &daemon->umbrella_org)) {
ret_err(gen_err);
}
}
else if (strstr(arg, "assetid:")) {
if (!strtoul_check(arg+8, &daemon->umbrella_asset)) {
ret_err(gen_err);
}
}
arg = comma;
}
break;
case LOPT_ADD_MAC: /* --add-mac */ case LOPT_ADD_MAC: /* --add-mac */
if (!arg) if (!arg)
set_option_bool(OPT_ADD_MAC); set_option_bool(OPT_ADD_MAC);
@@ -2480,11 +2527,33 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
case 'B': /* --bogus-nxdomain */ case 'B': /* --bogus-nxdomain */
case LOPT_IGNORE_ADDR: /* --ignore-address */ case LOPT_IGNORE_ADDR: /* --ignore-address */
{ {
struct in_addr addr; union all_addr addr;
int prefix, is6 = 0;
struct bogus_addr *baddr;
unhide_metas(arg); unhide_metas(arg);
if (arg && (inet_pton(AF_INET, arg, &addr) > 0))
if (!arg ||
((comma = split_chr(arg, '/')) && !atoi_check(comma, &prefix)))
ret_err(gen_err);
if (inet_pton(AF_INET6, arg, &addr.addr6) == 1)
is6 = 1;
else if (inet_pton(AF_INET, arg, &addr.addr4) != 1)
ret_err(gen_err);
if (!comma)
{ {
struct bogus_addr *baddr = opt_malloc(sizeof(struct bogus_addr)); if (is6)
prefix = 128;
else
prefix = 32;
}
if (prefix > 128 || (!is6 && prefix > 32))
ret_err(gen_err);
baddr = opt_malloc(sizeof(struct bogus_addr));
if (option == 'B') if (option == 'B')
{ {
baddr->next = daemon->bogus_addr; baddr->next = daemon->bogus_addr;
@@ -2495,10 +2564,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
baddr->next = daemon->ignore_addr; baddr->next = daemon->ignore_addr;
daemon->ignore_addr = baddr; daemon->ignore_addr = baddr;
} }
baddr->prefix = prefix;
baddr->is6 = is6;
baddr->addr = addr; baddr->addr = addr;
}
else
ret_err(gen_err); /* error */
break; break;
} }
@@ -2544,97 +2613,94 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
} while (arg); } while (arg);
break; break;
case 'S': /* --server */
case LOPT_LOCAL: /* --local */
case 'A': /* --address */
case LOPT_NO_REBIND: /* --rebind-domain-ok */ case LOPT_NO_REBIND: /* --rebind-domain-ok */
{ {
struct server *serv, *newlist = NULL; struct server *new;
unhide_metas(arg); unhide_metas(arg);
if (arg && (*arg == '/' || option == LOPT_NO_REBIND)) if (*arg == '/')
{
int rebind = !(*arg == '/');
char *end = NULL;
if (!rebind)
arg++; arg++;
while (rebind || (end = split_chr(arg, '/')))
{ do {
char *domain = NULL; comma = split_chr(arg, '/');
/* elide leading dots - they are implied in the search algorithm */ new = opt_malloc(sizeof(struct serv_local));
while (*arg == '.') arg++; new->domain = opt_string_alloc(arg);
/* # matches everything and becomes a zero length domain string */ new->domain_len = strlen(arg);
if (strcmp(arg, "#") == 0) new->next = daemon->no_rebind;
domain = ""; daemon->no_rebind = new;
else if (strlen (arg) != 0 && !(domain = canonicalise_opt(arg))) arg = comma;
ret_err(gen_err); } while (arg && *arg);
serv = opt_malloc(sizeof(struct server));
memset(serv, 0, sizeof(struct server));
serv->next = newlist;
newlist = serv;
serv->domain = domain;
serv->flags = domain ? SERV_HAS_DOMAIN : SERV_FOR_NODOTS;
arg = end;
if (rebind)
break; break;
} }
if (!newlist)
ret_err(gen_err); case 'S': /* --server */
case LOPT_LOCAL: /* --local */
case 'A': /* --address */
{
char *lastdomain = NULL, *domain = "";
u16 flags = 0;
char *err;
union all_addr addr;
union mysockaddr serv_addr, source_addr;
char interface[IF_NAMESIZE+1];
unhide_metas(arg);
/* split the domain args, if any and skip to the end of them. */
if (arg && *arg == '/')
{
char *last;
domain = lastdomain = ++arg;
while ((last = split_chr(arg, '/')))
{
lastdomain = arg;
arg = last;
}
}
if (!arg || !*arg)
flags = SERV_LITERAL_ADDRESS;
else if (option == 'A')
{
/* # as literal address means return zero address for 4 and 6 */
if (strcmp(arg, "#") == 0)
flags = SERV_ALL_ZEROS | SERV_LITERAL_ADDRESS;
else if (inet_pton(AF_INET, arg, &addr.addr4) > 0)
flags = SERV_4ADDR | SERV_LITERAL_ADDRESS;
else if (inet_pton(AF_INET6, arg, &addr.addr6) > 0)
flags = SERV_6ADDR | SERV_LITERAL_ADDRESS;
else
ret_err(_("Bad address in --address"));
} }
else else
{ {
newlist = opt_malloc(sizeof(struct server)); if ((err = parse_server(arg, &serv_addr, &source_addr, interface, &flags)))
memset(newlist, 0, sizeof(struct server)); ret_err(err);
#ifdef HAVE_LOOP
newlist->uid = rand32(); /* server=//1.2.3.4 is special. */
#endif if (strlen(domain) == 0 && lastdomain)
flags |= SERV_FOR_NODOTS;
} }
if (servers_only && option == 'S') if (servers_only && option == 'S')
newlist->flags |= SERV_FROM_FILE; flags |= SERV_FROM_FILE;
if (option == 'A') while (1)
{ {
newlist->flags |= SERV_LITERAL_ADDRESS; if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, &addr))
if (!(newlist->flags & SERV_TYPE))
{
server_list_free(newlist);
ret_err(gen_err); ret_err(gen_err);
}
}
else if (option == LOPT_NO_REBIND)
newlist->flags |= SERV_NO_REBIND;
if (!arg || !*arg) if (!lastdomain || domain == lastdomain)
{ break;
if (!(newlist->flags & SERV_NO_REBIND))
newlist->flags |= SERV_NO_ADDR; /* no server */ domain += strlen(domain) + 1;
} }
else if (strcmp(arg, "#") == 0)
newlist->flags |= SERV_USE_RESOLV; /* treat in ordinary way */
else
{
char *err = parse_server(arg, &newlist->addr, &newlist->source_addr, newlist->interface, &newlist->flags);
if (err)
{
server_list_free(newlist);
ret_err(err);
}
}
serv = newlist;
while (serv->next)
{
serv->next->flags |= serv->flags & ~(SERV_HAS_DOMAIN | SERV_FOR_NODOTS);
serv->next->addr = serv->addr;
serv->next->source_addr = serv->source_addr;
strcpy(serv->next->interface, serv->interface);
serv = serv->next;
}
serv->next = daemon->servers;
daemon->servers = newlist;
break; break;
} }
@@ -2642,9 +2708,12 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
{ {
char *string; char *string;
int size; int size;
struct server *serv; u16 flags = 0;
char domain[73]; /* strlen("32*<n.>ip6.arpa")+1 */
struct in_addr addr4; struct in_addr addr4;
struct in6_addr addr6; struct in6_addr addr6;
union mysockaddr serv_addr, source_addr;
char interface[IF_NAMESIZE+1];
unhide_metas(arg); unhide_metas(arg);
if (!arg) if (!arg)
@@ -2657,22 +2726,27 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
if (inet_pton(AF_INET, arg, &addr4)) if (inet_pton(AF_INET, arg, &addr4))
{ {
serv = add_rev4(addr4, size); if (!domain_rev4(domain, addr4, size))
if (!serv) ret_err(_("bad IPv4 prefix"));
ret_err(_("bad prefix"));
} }
else if (inet_pton(AF_INET6, arg, &addr6)) else if (inet_pton(AF_INET6, arg, &addr6))
serv = add_rev6(&addr6, size); {
if (!domain_rev6(domain, &addr6, size))
ret_err(_("bad IPv6 prefix"));
}
else else
ret_err(gen_err); ret_err(gen_err);
string = parse_server(comma, &serv->addr, &serv->source_addr, serv->interface, &serv->flags); if (!comma)
flags |= SERV_LITERAL_ADDRESS;
if (string) else if ((string = parse_server(comma, &serv_addr, &source_addr, interface, &flags)))
ret_err(string); ret_err(string);
if (servers_only) if (servers_only)
serv->flags |= SERV_FROM_FILE; flags |= SERV_FROM_FILE;
if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
ret_err(gen_err);
break; break;
} }
@@ -2743,6 +2817,135 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
} }
#endif #endif
case LOPT_CMARK_ALST_EN: /* --connmark-allowlist-enable */
#ifndef HAVE_CONNTRACK
ret_err(_("recompile with HAVE_CONNTRACK defined to enable connmark-allowlist directives"));
break;
#else
{
u32 mask = UINT32_MAX;
if (arg)
if (!strtoul_check(arg, &mask) || mask < 1)
ret_err(gen_err);
set_option_bool(OPT_CMARK_ALST_EN);
daemon->allowlist_mask = mask;
break;
}
#endif
case LOPT_CMARK_ALST: /* --connmark-allowlist */
#ifndef HAVE_CONNTRACK
ret_err(_("recompile with HAVE_CONNTRACK defined to enable connmark-allowlist directives"));
break;
#else
{
struct allowlist *allowlists;
char **patterns, **patterns_pos;
u32 mark, mask = UINT32_MAX;
size_t num_patterns = 0;
char *c, *m = NULL;
char *separator;
unhide_metas(arg);
if (!arg)
ret_err(gen_err);
c = arg;
if (*c < '0' || *c > '9')
ret_err(gen_err);
while (*c && *c != ',')
{
if (*c == '/')
{
if (m)
ret_err(gen_err);
*c = '\0';
m = ++c;
}
if (*c < '0' || *c > '9')
ret_err(gen_err);
c++;
}
separator = c;
if (!*separator)
break;
while (c && *c)
{
char *end = strchr(++c, '/');
if (end)
*end = '\0';
if (strcmp(c, "*") && !is_valid_dns_name_pattern(c))
ret_err(gen_err);
if (end)
*end = '/';
if (num_patterns >= UINT16_MAX - 1)
ret_err(gen_err);
num_patterns++;
c = end;
}
*separator = '\0';
if (!strtoul_check(arg, &mark) || mark < 1 || mark > UINT32_MAX)
ret_err(gen_err);
if (m)
if (!strtoul_check(m, &mask) || mask < 1 || mask > UINT32_MAX || (mark & ~mask))
ret_err(gen_err);
if (num_patterns)
*separator = ',';
for (allowlists = daemon->allowlists; allowlists; allowlists = allowlists->next)
if (allowlists->mark == mark && allowlists->mask == mask)
ret_err(gen_err);
patterns = opt_malloc((num_patterns + 1) * sizeof(char *));
if (!patterns)
goto fail_cmark_allowlist;
patterns_pos = patterns;
c = separator;
while (c && *c)
{
char *end = strchr(++c, '/');
if (end)
*end = '\0';
if (!(*patterns_pos++ = opt_string_alloc(c)))
goto fail_cmark_allowlist;
if (end)
*end = '/';
c = end;
}
*patterns_pos++ = NULL;
allowlists = opt_malloc(sizeof(struct allowlist));
if (!allowlists)
goto fail_cmark_allowlist;
memset(allowlists, 0, sizeof(struct allowlist));
allowlists->mark = mark;
allowlists->mask = mask;
allowlists->patterns = patterns;
allowlists->next = daemon->allowlists;
daemon->allowlists = allowlists;
break;
fail_cmark_allowlist:
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;
}
ret_err(gen_err);
}
#endif
case 'c': /* --cache-size */ case 'c': /* --cache-size */
{ {
int size; int size;
@@ -4075,36 +4278,66 @@ err:
} }
case LOPT_INTNAME: /* --interface-name */ case LOPT_INTNAME: /* --interface-name */
case LOPT_DYNHOST: /* --dynamic-host */
{ {
struct interface_name *new, **up; struct interface_name *new, **up;
char *domain = NULL; char *domain = arg;
comma = split(arg); arg = split(arg);
if (!comma || !(domain = canonicalise_opt(arg)))
ret_err(_("bad interface name"));
new = opt_malloc(sizeof(struct interface_name)); new = opt_malloc(sizeof(struct interface_name));
new->next = NULL; memset(new, 0, sizeof(struct interface_name));
new->addr = NULL; new->flags = IN4 | IN6;
/* Add to the end of the list, so that first name /* Add to the end of the list, so that first name
of an interface is used for PTR lookups. */ of an interface is used for PTR lookups. */
for (up = &daemon->int_names; *up; up = &((*up)->next)); for (up = &daemon->int_names; *up; up = &((*up)->next));
*up = new; *up = new;
new->name = domain;
new->family = 0; while ((comma = split(arg)))
arg = split_chr(comma, '/');
if (arg)
{ {
if (strcmp(arg, "4") == 0) if (inet_pton(AF_INET, arg, &new->proto4))
new->family = AF_INET; new->flags |= INP4;
else if (strcmp(arg, "6") == 0) else if (inet_pton(AF_INET6, arg, &new->proto6))
new->family = AF_INET6; new->flags |= INP6;
else
break;
arg = comma;
}
if ((comma = split_chr(arg, '/')))
{
if (strcmp(comma, "4") == 0)
new->flags &= ~IN6;
else if (strcmp(comma, "6") == 0)
new->flags &= ~IN4;
else else
ret_err_free(gen_err, new); ret_err_free(gen_err, new);
} }
new->intr = opt_string_alloc(comma);
new->intr = opt_string_alloc(arg);
if (option == LOPT_DYNHOST)
{
if (!(new->flags & (INP4 | INP6)))
ret_err(_("missing address in dynamic host"));
if (!(new->flags & IN4) || !(new->flags & IN6))
arg = NULL; /* provoke error below */
new->flags &= ~(IN4 | IN6);
}
else
{
if (new->flags & (INP4 | INP6))
arg = NULL; /* provoke error below */
}
if (!domain || !arg || !(new->name = canonicalise_opt(domain)))
ret_err(option == LOPT_DYNHOST ?
_("bad dynamic host") : _("bad interface name"));
break; break;
} }
@@ -4431,8 +4664,7 @@ err:
} }
else else
{ {
int nomem; char *canon = canonicalise_opt(arg);
char *canon = canonicalise(arg, &nomem);
struct name_list *nl; struct name_list *nl;
if (!canon) if (!canon)
{ {
@@ -4885,9 +5117,9 @@ void read_servers_file(void)
} }
mark_servers(SERV_FROM_FILE); mark_servers(SERV_FROM_FILE);
cleanup_servers();
read_file(daemon->servers_file, f, LOPT_REV_SERV); read_file(daemon->servers_file, f, LOPT_REV_SERV);
cleanup_servers();
check_servers(0);
} }
@@ -4903,30 +5135,8 @@ static void clear_dynamic_conf(void)
if (configs->flags & CONFIG_BANK) if (configs->flags & CONFIG_BANK)
{ {
struct hwaddr_config *mac, *tmp; *up = cp;
struct dhcp_netid_list *list, *tmplist; dhcp_config_free(configs);
for (mac = configs->hwaddr; mac; mac = tmp)
{
tmp = mac->next;
free(mac);
}
if (configs->flags & CONFIG_CLID)
free(configs->clid);
for (list = configs->netid; list; list = tmplist)
{
free(list->list);
tmplist = list->next;
free(list);
}
if (configs->flags & CONFIG_NAME)
free(configs->hostname);
*up = configs->next;
free(configs);
} }
else else
up = &configs->next; up = &configs->next;
@@ -4936,7 +5146,6 @@ static void clear_dynamic_conf(void)
static void clear_dynamic_opt(void) static void clear_dynamic_opt(void)
{ {
struct dhcp_opt *opts, *cp, **up; struct dhcp_opt *opts, *cp, **up;
struct dhcp_netid *id, *next;
for (up = &daemon->dhcp_opts, opts = daemon->dhcp_opts; opts; opts = cp) for (up = &daemon->dhcp_opts, opts = daemon->dhcp_opts; opts; opts = cp)
{ {
@@ -4944,17 +5153,8 @@ static void clear_dynamic_opt(void)
if (opts->flags & DHOPT_BANK) if (opts->flags & DHOPT_BANK)
{ {
if ((opts->flags & DHOPT_VENDOR)) *up = cp;
free(opts->u.vendor_class); dhcp_opt_free(opts);
free(opts->val);
for (id = opts->netid; id; id = next)
{
next = id->next;
free(id->net);
free(id);
}
*up = opts->next;
free(opts);
} }
else else
up = &opts->next; up = &opts->next;
@@ -5034,8 +5234,6 @@ void read_opts(int argc, char **argv, char *compile_opts)
daemon->soa_refresh = SOA_REFRESH; daemon->soa_refresh = SOA_REFRESH;
daemon->soa_retry = SOA_RETRY; daemon->soa_retry = SOA_RETRY;
daemon->soa_expiry = SOA_EXPIRY; daemon->soa_expiry = SOA_EXPIRY;
daemon->max_port = MAX_PORT;
daemon->min_port = MIN_PORT;
#ifndef NO_ID #ifndef NO_ID
add_txt("version.bind", "dnsmasq-" VERSION, 0 ); add_txt("version.bind", "dnsmasq-" VERSION, 0 );
+1 -1
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
+386
View File
@@ -0,0 +1,386 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 dated June, 1991, or
(at your option) version 3 dated 29 June, 2007.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dnsmasq.h"
#ifdef HAVE_CONNTRACK
#define LOG(...) \
do { \
my_syslog(LOG_WARNING, __VA_ARGS__); \
} while (0)
#define ASSERT(condition) \
do { \
if (!(condition)) \
LOG("[pattern.c:%d] Assertion failure: %s", __LINE__, #condition); \
} while (0)
/**
* Determines whether a given string value matches against a glob pattern
* which may contain zero-or-more-character wildcards denoted by '*'.
*
* Based on "Glob Matching Can Be Simple And Fast Too" by Russ Cox,
* See https://research.swtch.com/glob
*
* @param value A string value.
* @param num_value_bytes The number of bytes of the string value.
* @param pattern A glob pattern.
* @param num_pattern_bytes The number of bytes of the glob pattern.
*
* @return 1 If the provided value matches against the glob pattern.
* @return 0 Otherwise.
*/
static int is_string_matching_glob_pattern(
const char *value,
size_t num_value_bytes,
const char *pattern,
size_t num_pattern_bytes)
{
ASSERT(value);
ASSERT(pattern);
size_t value_index = 0;
size_t next_value_index = 0;
size_t pattern_index = 0;
size_t next_pattern_index = 0;
while (value_index < num_value_bytes || pattern_index < num_pattern_bytes)
{
if (pattern_index < num_pattern_bytes)
{
char pattern_character = pattern[pattern_index];
if ('a' <= pattern_character && pattern_character <= 'z')
pattern_character -= 'a' - 'A';
if (pattern_character == '*')
{
// zero-or-more-character wildcard
// Try to match at value_index, otherwise restart at value_index + 1 next.
next_pattern_index = pattern_index;
pattern_index++;
if (value_index < num_value_bytes)
next_value_index = value_index + 1;
else
next_value_index = 0;
continue;
}
else
{
// ordinary character
if (value_index < num_value_bytes)
{
char value_character = value[value_index];
if ('a' <= value_character && value_character <= 'z')
value_character -= 'a' - 'A';
if (value_character == pattern_character)
{
pattern_index++;
value_index++;
continue;
}
}
}
}
if (next_value_index)
{
pattern_index = next_pattern_index;
value_index = next_value_index;
continue;
}
return 0;
}
return 1;
}
/**
* Determines whether a given string value represents a valid DNS name.
*
* - DNS names must adhere to RFC 1123: 1 to 253 characters in length, consisting of a sequence of labels
* delimited by dots ("."). Each label must be 1 to 63 characters in length, contain only
* ASCII letters ("a"-"Z"), digits ("0"-"9"), or hyphens ("-") and must not start or end with a hyphen.
*
* - A valid name must be fully qualified, i.e., consist of at least two labels.
* The final label must not be fully numeric, and must not be the "local" pseudo-TLD.
*
* - Examples:
* Valid: "example.com"
* Invalid: "ipcamera", "ipcamera.local", "8.8.8.8"
*
* @param value A string value.
*
* @return 1 If the provided string value is a valid DNS name.
* @return 0 Otherwise.
*/
int is_valid_dns_name(const char *value)
{
ASSERT(value);
size_t num_bytes = 0;
size_t num_labels = 0;
const char *label = NULL;
int is_label_numeric = 1;
for (const char *c = value;; c++)
{
if (*c &&
*c != '-' && *c != '.' &&
(*c < '0' || *c > '9') &&
(*c < 'A' || *c > 'Z') &&
(*c < 'a' || *c > 'z'))
{
LOG("Invalid DNS name: Invalid character %c.", *c);
return 0;
}
if (*c)
num_bytes++;
if (!label)
{
if (!*c || *c == '.')
{
LOG("Invalid DNS name: Empty label.");
return 0;
}
if (*c == '-')
{
LOG("Invalid DNS name: Label starts with hyphen.");
return 0;
}
label = c;
}
if (*c && *c != '.')
{
if (*c < '0' || *c > '9')
is_label_numeric = 0;
}
else
{
if (c[-1] == '-')
{
LOG("Invalid DNS name: Label ends with hyphen.");
return 0;
}
size_t num_label_bytes = (size_t) (c - label);
if (num_label_bytes > 63)
{
LOG("Invalid DNS name: Label is too long (%zu).", num_label_bytes);
return 0;
}
num_labels++;
if (!*c)
{
if (num_labels < 2)
{
LOG("Invalid DNS name: Not enough labels (%zu).", num_labels);
return 0;
}
if (is_label_numeric)
{
LOG("Invalid DNS name: Final label is fully numeric.");
return 0;
}
if (num_label_bytes == 5 &&
(label[0] == 'l' || label[0] == 'L') &&
(label[1] == 'o' || label[1] == 'O') &&
(label[2] == 'c' || label[2] == 'C') &&
(label[3] == 'a' || label[3] == 'A') &&
(label[4] == 'l' || label[4] == 'L'))
{
LOG("Invalid DNS name: \"local\" pseudo-TLD.");
return 0;
}
if (num_bytes < 1 || num_bytes > 253)
{
LOG("DNS name has invalid length (%zu).", num_bytes);
return 0;
}
return 1;
}
label = NULL;
is_label_numeric = 1;
}
}
}
/**
* Determines whether a given string value represents a valid DNS name pattern.
*
* - DNS names must adhere to RFC 1123: 1 to 253 characters in length, consisting of a sequence of labels
* delimited by dots ("."). Each label must be 1 to 63 characters in length, contain only
* ASCII letters ("a"-"Z"), digits ("0"-"9"), or hyphens ("-") and must not start or end with a hyphen.
*
* - Patterns follow the syntax of DNS names, but additionally allow the wildcard character "*" to be used up to
* twice per label to match 0 or more characters within that label. Note that the wildcard never matches a dot
* (e.g., "*.example.com" matches "api.example.com" but not "api.us.example.com").
*
* - A valid name or pattern must be fully qualified, i.e., consist of at least two labels.
* The final label must not be fully numeric, and must not be the "local" pseudo-TLD.
* A pattern must end with at least two literal (non-wildcard) labels.
*
* - Examples:
* Valid: "example.com", "*.example.com", "video*.example.com", "api*.*.example.com", "*-prod-*.example.com"
* Invalid: "ipcamera", "ipcamera.local", "*", "*.com", "8.8.8.8"
*
* @param value A string value.
*
* @return 1 If the provided string value is a valid DNS name pattern.
* @return 0 Otherwise.
*/
int is_valid_dns_name_pattern(const char *value)
{
ASSERT(value);
size_t num_bytes = 0;
size_t num_labels = 0;
const char *label = NULL;
int is_label_numeric = 1;
size_t num_wildcards = 0;
int previous_label_has_wildcard = 1;
for (const char *c = value;; c++)
{
if (*c &&
*c != '*' && // Wildcard.
*c != '-' && *c != '.' &&
(*c < '0' || *c > '9') &&
(*c < 'A' || *c > 'Z') &&
(*c < 'a' || *c > 'z'))
{
LOG("Invalid DNS name pattern: Invalid character %c.", *c);
return 0;
}
if (*c && *c != '*')
num_bytes++;
if (!label)
{
if (!*c || *c == '.')
{
LOG("Invalid DNS name pattern: Empty label.");
return 0;
}
if (*c == '-')
{
LOG("Invalid DNS name pattern: Label starts with hyphen.");
return 0;
}
label = c;
}
if (*c && *c != '.')
{
if (*c < '0' || *c > '9')
is_label_numeric = 0;
if (*c == '*')
{
if (num_wildcards >= 2)
{
LOG("Invalid DNS name pattern: Wildcard character used more than twice per label.");
return 0;
}
num_wildcards++;
}
}
else
{
if (c[-1] == '-')
{
LOG("Invalid DNS name pattern: Label ends with hyphen.");
return 0;
}
size_t num_label_bytes = (size_t) (c - label) - num_wildcards;
if (num_label_bytes > 63)
{
LOG("Invalid DNS name pattern: Label is too long (%zu).", num_label_bytes);
return 0;
}
num_labels++;
if (!*c)
{
if (num_labels < 2)
{
LOG("Invalid DNS name pattern: Not enough labels (%zu).", num_labels);
return 0;
}
if (num_wildcards != 0 || previous_label_has_wildcard)
{
LOG("Invalid DNS name pattern: Wildcard within final two labels.");
return 0;
}
if (is_label_numeric)
{
LOG("Invalid DNS name pattern: Final label is fully numeric.");
return 0;
}
if (num_label_bytes == 5 &&
(label[0] == 'l' || label[0] == 'L') &&
(label[1] == 'o' || label[1] == 'O') &&
(label[2] == 'c' || label[2] == 'C') &&
(label[3] == 'a' || label[3] == 'A') &&
(label[4] == 'l' || label[4] == 'L'))
{
LOG("Invalid DNS name pattern: \"local\" pseudo-TLD.");
return 0;
}
if (num_bytes < 1 || num_bytes > 253)
{
LOG("DNS name pattern has invalid length after removing wildcards (%zu).", num_bytes);
return 0;
}
return 1;
}
label = NULL;
is_label_numeric = 1;
previous_label_has_wildcard = num_wildcards != 0;
num_wildcards = 0;
}
}
}
/**
* Determines whether a given DNS name matches against a DNS name pattern.
*
* @param name A valid DNS name.
* @param pattern A valid DNS name pattern.
*
* @return 1 If the provided DNS name matches against the DNS name pattern.
* @return 0 Otherwise.
*/
int is_dns_name_matching_pattern(const char *name, const char *pattern)
{
ASSERT(name);
ASSERT(is_valid_dns_name(name));
ASSERT(pattern);
ASSERT(is_valid_dns_name_pattern(pattern));
const char *n = name;
const char *p = pattern;
do {
const char *name_label = n;
while (*n && *n != '.')
n++;
const char *pattern_label = p;
while (*p && *p != '.')
p++;
if (!is_string_matching_glob_pattern(
name_label, (size_t) (n - name_label),
pattern_label, (size_t) (p - pattern_label)))
break;
if (*n)
n++;
if (*p)
p++;
} while (*n && *p);
return !*n && !*p;
}
#endif
+1 -1
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
+1 -4
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 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_RT_INFO 24
#define ICMP6_OPT_RDNSS 25 #define ICMP6_OPT_RDNSS 25
#define ICMP6_OPT_DNSSL 31 #define ICMP6_OPT_DNSSL 31
+1 -1
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
+165 -133
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -364,7 +364,7 @@ int private_net(struct in_addr addr, int ban_localhost)
return return
(((ip_addr & 0xFF000000) == 0x7F000000) && ban_localhost) /* 127.0.0.0/8 (loopback) */ || (((ip_addr & 0xFF000000) == 0x7F000000) && ban_localhost) /* 127.0.0.0/8 (loopback) */ ||
((ip_addr & 0xFF000000) == 0x00000000) /* RFC 5735 section 3. "here" network */ || (((ip_addr & 0xFF000000) == 0x00000000) && ban_localhost) /* RFC 5735 section 3. "here" network */ ||
((ip_addr & 0xFF000000) == 0x0A000000) /* 10.0.0.0/8 (private) */ || ((ip_addr & 0xFF000000) == 0x0A000000) /* 10.0.0.0/8 (private) */ ||
((ip_addr & 0xFFF00000) == 0xAC100000) /* 172.16.0.0/12 (private) */ || ((ip_addr & 0xFFF00000) == 0xAC100000) /* 172.16.0.0/12 (private) */ ||
((ip_addr & 0xFFFF0000) == 0xC0A80000) /* 192.168.0.0/16 (private) */ || ((ip_addr & 0xFFFF0000) == 0xC0A80000) /* 192.168.0.0/16 (private) */ ||
@@ -375,12 +375,21 @@ int private_net(struct in_addr addr, int ban_localhost)
((ip_addr & 0xFFFFFFFF) == 0xFFFFFFFF) /* 255.255.255.255/32 (broadcast)*/ ; ((ip_addr & 0xFFFFFFFF) == 0xFFFFFFFF) /* 255.255.255.255/32 (broadcast)*/ ;
} }
static int private_net6(struct in6_addr *a) static int private_net6(struct in6_addr *a, int ban_localhost)
{ {
/* Block IPv4-mapped IPv6 addresses in private IPv4 address space */
if (IN6_IS_ADDR_V4MAPPED(a))
{
struct in_addr v4;
v4.s_addr = ((const uint32_t *) (a))[3];
return private_net(v4, ban_localhost);
}
return return
IN6_IS_ADDR_UNSPECIFIED(a) || /* RFC 6303 4.3 */ (IN6_IS_ADDR_UNSPECIFIED(a) && ban_localhost) || /* RFC 6303 4.3 */
IN6_IS_ADDR_LOOPBACK(a) || /* RFC 6303 4.3 */ (IN6_IS_ADDR_LOOPBACK(a) && ban_localhost) || /* RFC 6303 4.3 */
IN6_IS_ADDR_LINKLOCAL(a) || /* RFC 6303 4.5 */ IN6_IS_ADDR_LINKLOCAL(a) || /* RFC 6303 4.5 */
IN6_IS_ADDR_SITELOCAL(a) ||
((unsigned char *)a)[0] == 0xfd || /* RFC 6303 4.4 */ ((unsigned char *)a)[0] == 0xfd || /* RFC 6303 4.4 */
((u32 *)a)[0] == htonl(0x20010db8); /* RFC 6303 4.6 */ ((u32 *)a)[0] == htonl(0x20010db8); /* RFC 6303 4.6 */
} }
@@ -795,30 +804,11 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
private_net(addr.addr4, !option_bool(OPT_LOCAL_REBIND))) private_net(addr.addr4, !option_bool(OPT_LOCAL_REBIND)))
return 1; return 1;
/* Block IPv4-mapped IPv6 addresses in private IPv4 address space */ if ((flags & F_IPV6) &&
if (flags & F_IPV6) private_net6(&addr.addr6, !option_bool(OPT_LOCAL_REBIND)))
{
if (IN6_IS_ADDR_V4MAPPED(&addr.addr6))
{
struct in_addr v4;
v4.s_addr = ((const uint32_t *) (&addr.addr6))[3];
if (private_net(v4, !option_bool(OPT_LOCAL_REBIND)))
return 1; return 1;
} }
/* Check for link-local (LL) and site-local (ULA) IPv6 addresses */
if (IN6_IS_ADDR_LINKLOCAL(&addr.addr6) ||
IN6_IS_ADDR_SITELOCAL(&addr.addr6))
return 1;
/* Check for the IPv6 loopback address (::1) when
option rebind-localhost-ok is NOT set */
if (!option_bool(OPT_LOCAL_REBIND) &&
IN6_IS_ADDR_LOOPBACK(&addr.addr6))
return 1;
}
}
#ifdef HAVE_IPSET #ifdef HAVE_IPSET
if (ipsets && (flags & (F_IPV4 | F_IPV6))) if (ipsets && (flags & (F_IPV4 | F_IPV6)))
{ {
@@ -884,6 +874,92 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
return 0; return 0;
} }
#if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS)
/* Don't pass control chars and weird escapes to UBus. */
static int safe_name(char *name)
{
unsigned char *r;
for (r = (unsigned char *)name; *r; r++)
if (!isprint((int)*r))
return 0;
return 1;
}
void report_addresses(struct dns_header *header, size_t len, u32 mark)
{
unsigned char *p, *endrr;
int i;
unsigned long attl;
struct allowlist *allowlists;
char **pattern_pos;
if (RCODE(header) != NOERROR)
return;
for (allowlists = daemon->allowlists; allowlists; allowlists = allowlists->next)
if (allowlists->mark == (mark & daemon->allowlist_mask & allowlists->mask))
for (pattern_pos = allowlists->patterns; *pattern_pos; pattern_pos++)
if (!strcmp(*pattern_pos, "*"))
return;
if (!(p = skip_questions(header, len)))
return;
for (i = ntohs(header->ancount); i != 0; i--)
{
int aqtype, aqclass, ardlen;
if (!extract_name(header, len, &p, daemon->namebuff, 1, 10))
return;
if (!CHECK_LEN(header, p, len, 10))
return;
GETSHORT(aqtype, p);
GETSHORT(aqclass, p);
GETLONG(attl, p);
GETSHORT(ardlen, p);
if (!CHECK_LEN(header, p, len, ardlen))
return;
endrr = p+ardlen;
if (aqclass == C_IN)
{
if (aqtype == T_CNAME)
{
if (!extract_name(header, len, &p, daemon->workspacename, 1, 0))
return;
if (safe_name(daemon->namebuff) && safe_name(daemon->workspacename))
ubus_event_bcast_connmark_allowlist_resolved(mark, daemon->namebuff, daemon->workspacename, attl);
}
if (aqtype == T_A)
{
struct in_addr addr;
char ip[INET_ADDRSTRLEN];
if (ardlen != INADDRSZ)
return;
memcpy(&addr, p, ardlen);
if (inet_ntop(AF_INET, &addr, ip, sizeof ip) && safe_name(daemon->namebuff))
ubus_event_bcast_connmark_allowlist_resolved(mark, daemon->namebuff, ip, attl);
}
else if (aqtype == T_AAAA)
{
struct in6_addr addr;
char ip[INET6_ADDRSTRLEN];
if (ardlen != IN6ADDRSZ)
return;
memcpy(&addr, p, ardlen);
if (inet_ntop(AF_INET6, &addr, ip, sizeof ip) && safe_name(daemon->namebuff))
ubus_event_bcast_connmark_allowlist_resolved(mark, daemon->namebuff, ip, attl);
}
}
p = endrr;
}
}
#endif
/* If the packet holds exactly one query /* If the packet holds exactly one query
return F_IPV4 or F_IPV6 and leave the name from the query in name */ return F_IPV4 or F_IPV6 and leave the name from the query in name */
unsigned int extract_request(struct dns_header *header, size_t qlen, char *name, unsigned short *typep) unsigned int extract_request(struct dns_header *header, size_t qlen, char *name, unsigned short *typep)
@@ -894,9 +970,14 @@ unsigned int extract_request(struct dns_header *header, size_t qlen, char *name,
if (typep) if (typep)
*typep = 0; *typep = 0;
*name = 0; /* return empty name if no query found. */
if (ntohs(header->qdcount) != 1 || OPCODE(header) != QUERY) if (ntohs(header->qdcount) != 1 || OPCODE(header) != QUERY)
return 0; /* must be exactly one query. */ return 0; /* must be exactly one query. */
if (!(header->hb3 & HB3_QR) && (ntohs(header->ancount) != 0 || ntohs(header->nscount) != 0))
return 0; /* non-standard query. */
if (!extract_name(header, qlen, &p, name, 1, 4)) if (!extract_name(header, qlen, &p, name, 1, 4))
return 0; /* bad packet */ return 0; /* bad packet */
@@ -916,24 +997,20 @@ unsigned int extract_request(struct dns_header *header, size_t qlen, char *name,
return F_IPV4 | F_IPV6; return F_IPV4 | F_IPV6;
} }
#ifdef HAVE_DNSSEC
/* F_DNSSECOK as agument to search_servers() inhibits forwarding /* F_DNSSECOK as agument to search_servers() inhibits forwarding
to servers for domains without a trust anchor. This make the to servers for domains without a trust anchor. This make the
behaviour for DS and DNSKEY queries we forward the same behaviour for DS and DNSKEY queries we forward the same
as for DS and DNSKEY queries we originate. */ as for DS and DNSKEY queries we originate. */
if (qtype == T_DS || qtype == T_DNSKEY) if (option_bool(OPT_DNSSEC_VALID) && (qtype == T_DS || qtype == T_DNSKEY))
return F_DNSSECOK; return F_DNSSECOK;
#endif
return F_QUERY; return F_QUERY;
} }
size_t setup_reply(struct dns_header *header, size_t qlen, void setup_reply(struct dns_header *header, unsigned int flags, int ede)
union all_addr *addrp, unsigned int flags, unsigned long ttl)
{ {
unsigned char *p;
if (!(p = skip_questions(header, qlen)))
return 0;
/* clear authoritative and truncated flags, set QR flag */ /* clear authoritative and truncated flags, set QR flag */
header->hb3 = (header->hb3 & ~(HB3_AA | HB3_TC )) | HB3_QR; header->hb3 = (header->hb3 & ~(HB3_AA | HB3_TC )) | HB3_QR;
/* clear AD flag, set RA flag */ /* clear AD flag, set RA flag */
@@ -946,40 +1023,19 @@ size_t setup_reply(struct dns_header *header, size_t qlen,
SET_RCODE(header, NOERROR); /* empty domain */ SET_RCODE(header, NOERROR); /* empty domain */
else if (flags == F_NXDOMAIN) else if (flags == F_NXDOMAIN)
SET_RCODE(header, NXDOMAIN); SET_RCODE(header, NXDOMAIN);
else if (flags == F_SERVFAIL)
{
union all_addr a;
a.log.rcode = SERVFAIL;
log_query(F_CONFIG | F_RCODE, "error", &a, NULL);
SET_RCODE(header, SERVFAIL);
}
else if (flags & ( F_IPV4 | F_IPV6)) else if (flags & ( F_IPV4 | F_IPV6))
{ {
if (flags & F_IPV4)
{ /* we know the address */
SET_RCODE(header, NOERROR); SET_RCODE(header, NOERROR);
header->ancount = htons(1);
header->hb3 |= HB3_AA; header->hb3 |= HB3_AA;
add_resource_record(header, NULL, NULL, sizeof(struct dns_header), &p, ttl, NULL, T_A, C_IN, "4", addrp);
}
if (flags & F_IPV6)
{
SET_RCODE(header, NOERROR);
header->ancount = htons(ntohs(header->ancount) + 1);
header->hb3 |= HB3_AA;
add_resource_record(header, NULL, NULL, sizeof(struct dns_header), &p, ttl, NULL, T_AAAA, C_IN, "6", addrp);
}
} }
else /* nowhere to forward to */ else /* nowhere to forward to */
{ {
union all_addr a; union all_addr a;
a.log.rcode = REFUSED; a.log.rcode = REFUSED;
a.log.ede = ede;
log_query(F_CONFIG | F_RCODE, "error", &a, NULL); log_query(F_CONFIG | F_RCODE, "error", &a, NULL);
SET_RCODE(header, REFUSED); SET_RCODE(header, REFUSED);
} }
return p - (unsigned char *)header;
} }
/* check if name matches local names ie from /etc/hosts or DHCP or local mx names. */ /* check if name matches local names ie from /etc/hosts or DHCP or local mx names. */
@@ -1017,11 +1073,7 @@ int check_for_local_domain(char *name, time_t now)
return 0; return 0;
} }
/* Is the packet a reply with the answer address equal to addr? static int check_bad_address(struct dns_header *header, size_t qlen, struct bogus_addr *baddr, char *name, unsigned long *ttlp)
If so mung is into an NXDOMAIN reply and also put that information
in the cache. */
int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
struct bogus_addr *baddr, time_t now)
{ {
unsigned char *p; unsigned char *p;
int i, qtype, qclass, rdlen; int i, qtype, qclass, rdlen;
@@ -1034,21 +1086,65 @@ int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
for (i = ntohs(header->ancount); i != 0; i--) for (i = ntohs(header->ancount); i != 0; i--)
{ {
if (!extract_name(header, qlen, &p, name, 1, 10)) if (name && !extract_name(header, qlen, &p, name, 1, 10))
return 0; /* bad packet */ return 0; /* bad packet */
if (!name && !(p = skip_name(p, header, qlen, 10)))
return 0;
GETSHORT(qtype, p); GETSHORT(qtype, p);
GETSHORT(qclass, p); GETSHORT(qclass, p);
GETLONG(ttl, p); GETLONG(ttl, p);
GETSHORT(rdlen, p); GETSHORT(rdlen, p);
if (qclass == C_IN && qtype == T_A) if (ttlp)
*ttlp = ttl;
if (qclass == C_IN)
{ {
if (qtype == T_A)
{
struct in_addr addr;
if (!CHECK_LEN(header, p, qlen, INADDRSZ)) if (!CHECK_LEN(header, p, qlen, INADDRSZ))
return 0; return 0;
memcpy(&addr, p, INADDRSZ);
for (baddrp = baddr; baddrp; baddrp = baddrp->next) for (baddrp = baddr; baddrp; baddrp = baddrp->next)
if (memcmp(&baddrp->addr, p, INADDRSZ) == 0) if (!baddrp->is6 && is_same_net_prefix(addr, baddrp->addr.addr4, baddrp->prefix))
return 1;
}
else if (qtype == T_AAAA)
{
struct in6_addr addr;
if (!CHECK_LEN(header, p, qlen, IN6ADDRSZ))
return 0;
memcpy(&addr, p, IN6ADDRSZ);
for (baddrp = baddr; baddrp; baddrp = baddrp->next)
if (baddrp->is6 && is_same_net6(&addr, &baddrp->addr.addr6, baddrp->prefix))
return 1;
}
}
if (!ADD_RDLEN(header, p, qlen, rdlen))
return 0;
}
return 0;
}
/* Is the packet a reply with the answer address equal to addr?
If so mung is into an NXDOMAIN reply and also put that information
in the cache. */
int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name, time_t now)
{
unsigned long ttl;
if (check_bad_address(header, qlen, daemon->bogus_addr, name, &ttl))
{ {
/* Found a bogus address. Insert that info here, since there no SOA record /* Found a bogus address. Insert that info here, since there no SOA record
to get the ttl from in the normal processing */ to get the ttl from in the normal processing */
@@ -1058,53 +1154,15 @@ int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
return 1; return 1;
} }
}
if (!ADD_RDLEN(header, p, qlen, rdlen))
return 0;
}
return 0; return 0;
} }
int check_for_ignored_address(struct dns_header *header, size_t qlen, struct bogus_addr *baddr) int check_for_ignored_address(struct dns_header *header, size_t qlen)
{ {
unsigned char *p; return check_bad_address(header, qlen, daemon->ignore_addr, NULL, NULL);
int i, qtype, qclass, rdlen;
struct bogus_addr *baddrp;
/* skip over questions */
if (!(p = skip_questions(header, qlen)))
return 0; /* bad packet */
for (i = ntohs(header->ancount); i != 0; i--)
{
if (!(p = skip_name(p, header, qlen, 10)))
return 0; /* bad packet */
GETSHORT(qtype, p);
GETSHORT(qclass, p);
p += 4; /* TTL */
GETSHORT(rdlen, p);
if (qclass == C_IN && qtype == T_A)
{
if (!CHECK_LEN(header, p, qlen, INADDRSZ))
return 0;
for (baddrp = baddr; baddrp; baddrp = baddrp->next)
if (memcmp(&baddrp->addr, p, INADDRSZ) == 0)
return 1;
} }
if (!ADD_RDLEN(header, p, qlen, rdlen))
return 0;
}
return 0;
}
int add_resource_record(struct dns_header *header, char *limit, int *truncp, int nameoffset, unsigned char **pp, int add_resource_record(struct dns_header *header, char *limit, int *truncp, int nameoffset, unsigned char **pp,
unsigned long ttl, int *offset, unsigned short type, unsigned short class, char *format, ...) unsigned long ttl, int *offset, unsigned short type, unsigned short class, char *format, ...)
{ {
@@ -1566,36 +1624,11 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
anscount++; anscount++;
} }
} }
else if (option_bool(OPT_BOGUSPRIV) && ( else if (option_bool(OPT_BOGUSPRIV) &&
(is_arpa == F_IPV6 && private_net6(&addr.addr6)) || ((is_arpa == F_IPV6 && private_net6(&addr.addr6, 1)) || (is_arpa == F_IPV4 && private_net(addr.addr4, 1))) &&
(is_arpa == F_IPV4 && private_net(addr.addr4, 1)))) !lookup_domain(name, F_DOMAINSRV, NULL, NULL))
{ {
struct server *serv;
unsigned int namelen = strlen(name);
char *nameend = name + namelen;
/* see if have rev-server set */
for (serv = daemon->servers; serv; serv = serv->next)
{
unsigned int domainlen;
char *matchstart;
if ((serv->flags & (SERV_HAS_DOMAIN | SERV_NO_ADDR)) != SERV_HAS_DOMAIN)
continue;
domainlen = strlen(serv->domain);
if (domainlen == 0 || domainlen > namelen)
continue;
matchstart = nameend - domainlen;
if (hostname_isequal(matchstart, serv->domain) &&
(namelen == domainlen || *(matchstart-1) == '.' ))
break;
}
/* if no configured server, not in cache, enabled and private IPV4 address, return NXDOMAIN */ /* if no configured server, not in cache, enabled and private IPV4 address, return NXDOMAIN */
if (!serv)
{
ans = 1; ans = 1;
sec_data = 0; sec_data = 0;
nxdomain = 1; nxdomain = 1;
@@ -1604,7 +1637,6 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
name, &addr, NULL); name, &addr, NULL);
} }
} }
}
for (flag = F_IPV4; flag; flag = (flag == F_IPV4) ? F_IPV6 : 0) for (flag = F_IPV4; flag; flag = (flag == F_IPV4) ? F_IPV6 : 0)
{ {
+2 -9
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -2785,11 +2785,4 @@ static void apply_delay(u32 xid, time_t recvtime, struct dhcp_netid *netid)
} }
} }
#endif #endif /* HAVE_DHCP */
+28 -4
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 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 DHCP6RENEW:
case DHCP6REBIND:
{ {
int address_assigned = 0;
/* set reply message type */ /* set reply message type */
*outmsgtypep = DHCP6REPLY; *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)) for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end))
{ {
@@ -951,6 +954,17 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
if (!(lease = lease6_find(state->clid, state->clid_len, if (!(lease = lease6_find(state->clid, state->clid_len,
state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA,
state->iaid, &req_addr))) state->iaid, &req_addr)))
{
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 /* 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 returns the IA containing no addresses with a Status Code option set
@@ -968,7 +982,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
preferred_time = valid_time = 0; preferred_time = valid_time = 0;
break; break;
} }
}
if ((this_context = address6_available(state->context, &req_addr, tagif, 1)) || if ((this_context = address6_available(state->context, &req_addr, tagif, 1)) ||
(this_context = address6_valid(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) if (preferred_time == 0)
message = _("deprecated"); message = _("deprecated");
address_assigned = 1;
} }
else else
{ {
@@ -1023,9 +1039,17 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
end_opt6(o); 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); tagif = add_options(state, 0);
break; break;
} }
case DHCP6CONFIRM: case DHCP6CONFIRM:
+1 -1
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
+23 -4
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -22,7 +22,7 @@ static void handle_tftp(time_t now, struct tftp_transfer *transfer, ssize_t len)
static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix); static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix);
static void free_transfer(struct tftp_transfer *transfer); 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(int err, char *packet, char *message, char *file);
static ssize_t tftp_err_oops(char *packet, char *file); static ssize_t tftp_err_oops(char *packet, const char *file);
static ssize_t get_block(char *packet, struct tftp_transfer *transfer); static ssize_t get_block(char *packet, struct tftp_transfer *transfer);
static char *next(char **p, char *end); static char *next(char **p, char *end);
static void sanitise(char *buf); static void sanitise(char *buf);
@@ -39,6 +39,7 @@ static void sanitise(char *buf);
#define ERR_PERM 2 #define ERR_PERM 2
#define ERR_FULL 3 #define ERR_FULL 3
#define ERR_ILL 4 #define ERR_ILL 4
#define ERR_TID 5
void tftp_request(struct listener *listen, time_t now) void tftp_request(struct listener *listen, time_t now)
{ {
@@ -583,9 +584,25 @@ void check_tftp_listeners(time_t now)
for (transfer = daemon->tftp_trans; transfer; transfer = transfer->next) for (transfer = daemon->tftp_trans; transfer; transfer = transfer->next)
if (poll_check(transfer->sockfd, POLLIN)) if (poll_check(transfer->sockfd, POLLIN))
{ {
union mysockaddr peer;
socklen_t addr_len = sizeof(union mysockaddr);
ssize_t len;
/* we overwrote the buffer... */ /* we overwrote the buffer... */
daemon->srv_save = NULL; daemon->srv_save = NULL;
handle_tftp(now, transfer, recv(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0));
if ((len = recvfrom(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0, &peer.sa, &addr_len)) > 0)
{
if (sockaddr_isequal(&peer, &transfer->peer))
handle_tftp(now, transfer, len);
else
{
/* Wrong source address. See rfc1350 para 4. */
prettyprint_addr(&peer, daemon->addrbuff);
len = tftp_err(ERR_TID, daemon->packet, _("ignoring packet from %s (TID mismatch)"), daemon->addrbuff);
sendto(transfer->sockfd, daemon->packet, len, 0, &peer.sa, sa_len(&peer));
}
}
} }
for (transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; transfer = tmp) for (transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; transfer = tmp)
@@ -736,6 +753,7 @@ static ssize_t tftp_err(int err, char *packet, char *message, char *file)
char *errstr = strerror(errno); char *errstr = strerror(errno);
memset(packet, 0, daemon->packet_buff_sz); memset(packet, 0, daemon->packet_buff_sz);
if (file)
sanitise(file); sanitise(file);
mess->op = htons(OP_ERR); mess->op = htons(OP_ERR);
@@ -748,9 +766,10 @@ static ssize_t tftp_err(int err, char *packet, char *message, char *file)
return ret; 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 */ /* May have >1 refs to file, so potentially mangle a copy of the name */
if (file != daemon->namebuff)
strcpy(daemon->namebuff, file); strcpy(daemon->namebuff, file);
return tftp_err(ERR_NOTDEF, packet, _("cannot read %s: %s"), daemon->namebuff); return tftp_err(ERR_NOTDEF, packet, _("cannot read %s: %s"), daemon->namebuff);
} }
+189 -22
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -28,10 +28,38 @@ static int ubus_handle_metrics(struct ubus_context *ctx, struct ubus_object *obj
struct ubus_request_data *req, const char *method, struct ubus_request_data *req, const char *method,
struct blob_attr *msg); 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 void ubus_subscribe_cb(struct ubus_context *ctx, struct ubus_object *obj);
static const struct ubus_method ubus_object_methods[] = { static const struct ubus_method ubus_object_methods[] = {
UBUS_METHOD_NOARG("metrics", ubus_handle_metrics), 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 = static struct ubus_object_type ubus_object_type =
@@ -76,42 +104,27 @@ static void ubus_disconnect_cb(struct ubus_context *ubus)
} }
} }
void ubus_init() char *ubus_init()
{ {
struct ubus_context *ubus = NULL; struct ubus_context *ubus = NULL;
int ret = 0; int ret = 0;
ubus = ubus_connect(NULL); if (!(ubus = ubus_connect(NULL)))
if (!ubus) return NULL;
{
if (!error_logged)
{
my_syslog(LOG_ERR, _("Cannot initialize UBus: connection failed"));
error_logged = 1;
}
ubus_destroy(ubus);
return;
}
ubus_object.name = daemon->ubus_name; ubus_object.name = daemon->ubus_name;
ret = ubus_add_object(ubus, &ubus_object); ret = ubus_add_object(ubus, &ubus_object);
if (ret) if (ret)
{ {
if (!error_logged)
{
my_syslog(LOG_ERR, _("Cannot add object to UBus: %s"), ubus_strerror(ret));
error_logged = 1;
}
ubus_destroy(ubus); ubus_destroy(ubus);
return; return (char *)ubus_strerror(ret);
} }
ubus->connection_lost = ubus_disconnect_cb; ubus->connection_lost = ubus_disconnect_cb;
daemon->ubus = ubus; daemon->ubus = ubus;
error_logged = 0; error_logged = 0;
my_syslog(LOG_INFO, _("Connected to system UBus")); return NULL;
} }
void set_ubus_listeners() void set_ubus_listeners()
@@ -178,6 +191,122 @@ static int ubus_handle_metrics(struct ubus_context *ctx, struct ubus_object *obj
return ubus_send_reply(ctx, req, b.head); return ubus_send_reply(ctx, req, b.head);
} }
#ifdef HAVE_CONNTRACK
static int ubus_handle_set_connmark_allowlist(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
const struct blobmsg_policy *policy = set_connmark_allowlist_policy;
size_t policy_len = countof(set_connmark_allowlist_policy);
struct allowlist *allowlists = NULL, **allowlists_pos;
char **patterns = NULL, **patterns_pos;
u32 mark, mask = UINT32_MAX;
size_t num_patterns = 0;
struct blob_attr *tb[policy_len];
struct blob_attr *attr;
if (blobmsg_parse(policy, policy_len, tb, blob_data(msg), blob_len(msg)))
return UBUS_STATUS_INVALID_ARGUMENT;
if (!tb[SET_CONNMARK_ALLOWLIST_MARK])
return UBUS_STATUS_INVALID_ARGUMENT;
mark = blobmsg_get_u32(tb[SET_CONNMARK_ALLOWLIST_MARK]);
if (!mark)
return UBUS_STATUS_INVALID_ARGUMENT;
if (tb[SET_CONNMARK_ALLOWLIST_MASK])
{
mask = blobmsg_get_u32(tb[SET_CONNMARK_ALLOWLIST_MASK]);
if (!mask || (mark & ~mask))
return UBUS_STATUS_INVALID_ARGUMENT;
}
if (tb[SET_CONNMARK_ALLOWLIST_PATTERNS])
{
struct blob_attr *head = blobmsg_data(tb[SET_CONNMARK_ALLOWLIST_PATTERNS]);
size_t len = blobmsg_data_len(tb[SET_CONNMARK_ALLOWLIST_PATTERNS]);
__blob_for_each_attr(attr, head, len)
{
char *pattern;
if (blob_id(attr) != BLOBMSG_TYPE_STRING)
return UBUS_STATUS_INVALID_ARGUMENT;
if (!(pattern = blobmsg_get_string(attr)))
return UBUS_STATUS_INVALID_ARGUMENT;
if (strcmp(pattern, "*") && !is_valid_dns_name_pattern(pattern))
return UBUS_STATUS_INVALID_ARGUMENT;
num_patterns++;
}
}
for (allowlists_pos = &daemon->allowlists; *allowlists_pos; allowlists_pos = &(*allowlists_pos)->next)
if ((*allowlists_pos)->mark == mark && (*allowlists_pos)->mask == mask)
{
struct allowlist *allowlists_next = (*allowlists_pos)->next;
for (patterns_pos = (*allowlists_pos)->patterns; *patterns_pos; patterns_pos++)
{
free(*patterns_pos);
*patterns_pos = NULL;
}
free((*allowlists_pos)->patterns);
(*allowlists_pos)->patterns = NULL;
free(*allowlists_pos);
*allowlists_pos = allowlists_next;
break;
}
if (!num_patterns)
return UBUS_STATUS_OK;
patterns = whine_malloc((num_patterns + 1) * sizeof(char *));
if (!patterns)
goto fail;
patterns_pos = patterns;
if (tb[SET_CONNMARK_ALLOWLIST_PATTERNS])
{
struct blob_attr *head = blobmsg_data(tb[SET_CONNMARK_ALLOWLIST_PATTERNS]);
size_t len = blobmsg_data_len(tb[SET_CONNMARK_ALLOWLIST_PATTERNS]);
__blob_for_each_attr(attr, head, len)
{
char *pattern;
if (!(pattern = blobmsg_get_string(attr)))
goto fail;
if (!(*patterns_pos = whine_malloc(strlen(pattern) + 1)))
goto fail;
strcpy(*patterns_pos++, pattern);
}
}
allowlists = whine_malloc(sizeof(struct allowlist));
if (!allowlists)
goto fail;
memset(allowlists, 0, sizeof(struct allowlist));
allowlists->mark = mark;
allowlists->mask = mask;
allowlists->patterns = patterns;
allowlists->next = daemon->allowlists;
daemon->allowlists = allowlists;
return UBUS_STATUS_OK;
fail:
if (patterns)
{
for (patterns_pos = patterns; *patterns_pos; patterns_pos++)
{
free(*patterns_pos);
*patterns_pos = NULL;
}
free(patterns);
patterns = NULL;
}
if (allowlists)
{
free(allowlists);
allowlists = NULL;
}
return UBUS_STATUS_UNKNOWN_ERROR;
}
#endif
void ubus_event_bcast(const char *type, const char *mac, const char *ip, const char *name, const char *interface) 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; struct ubus_context *ubus = (struct ubus_context *)daemon->ubus;
@@ -197,9 +326,47 @@ void ubus_event_bcast(const char *type, const char *mac, const char *ip, const c
blobmsg_add_string(&b, "interface", interface); blobmsg_add_string(&b, "interface", interface);
ret = ubus_notify(ubus, &ubus_object, type, b.head, -1); ret = ubus_notify(ubus, &ubus_object, type, b.head, -1);
if (!ret) if (ret)
my_syslog(LOG_ERR, _("Failed to send UBus event: %s"), ubus_strerror(ret)); my_syslog(LOG_ERR, _("Failed to send UBus event: %s"), ubus_strerror(ret));
} }
#ifdef HAVE_CONNTRACK
void ubus_event_bcast_connmark_allowlist_refused(u32 mark, const char *name)
{
struct ubus_context *ubus = (struct ubus_context *)daemon->ubus;
int ret;
if (!ubus || !notify)
return;
blob_buf_init(&b, 0);
blobmsg_add_u32(&b, "mark", mark);
blobmsg_add_string(&b, "name", name);
ret = ubus_notify(ubus, &ubus_object, "connmark-allowlist.refused", b.head, -1);
if (ret)
my_syslog(LOG_ERR, _("Failed to send UBus event: %s"), ubus_strerror(ret));
}
void ubus_event_bcast_connmark_allowlist_resolved(u32 mark, const char *name, const char *value, u32 ttl)
{
struct ubus_context *ubus = (struct ubus_context *)daemon->ubus;
int ret;
if (!ubus || !notify)
return;
blob_buf_init(&b, 0);
blobmsg_add_u32(&b, "mark", mark);
blobmsg_add_string(&b, "name", name);
blobmsg_add_string(&b, "value", value);
blobmsg_add_u32(&b, "ttl", ttl);
ret = ubus_notify(ubus, &ubus_object, "connmark-allowlist.resolved", b.head, /* timeout: */ 1000);
if (ret)
my_syslog(LOG_ERR, _("Failed to send UBus event: %s"), ubus_strerror(ret));
}
#endif
#endif /* HAVE_UBUS */ #endif /* HAVE_UBUS */
+12 -2
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -316,7 +316,7 @@ void *whine_malloc(size_t size)
return ret; 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) if (s1->sa.sa_family == s2->sa.sa_family)
{ {
@@ -438,6 +438,16 @@ 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); 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) int is_same_net6(struct in6_addr *a, struct in6_addr *b, int prefixlen)
{ {
int pfbytes = prefixlen >> 3; int pfbytes = prefixlen >> 3;