mirror of
http://thekelleys.org.uk/git/dnsmasq.git
synced 2026-11-03 13:38:32 +00:00
Compare commits
59
Commits
v2.91test4
..
v2.91
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9af15871e6 | ||
|
|
5897e79d05 | ||
|
|
fc9135ca9f | ||
|
|
e427d4b0e6 | ||
|
|
9df1bd0cc1 | ||
|
|
5990074ab0 | ||
|
|
dbb69bd192 | ||
|
|
d17581c4c6 | ||
|
|
2c9ed7f425 | ||
|
|
717ff6adc3 | ||
|
|
f9f8d19bf5 | ||
|
|
535be2f5d3 | ||
|
|
bceab45dbe | ||
|
|
368ceff6e0 | ||
|
|
77c4e95d4a | ||
|
|
e44165c0f7 | ||
|
|
a1a214c393 | ||
|
|
94b7144a1b | ||
|
|
e72910dec8 | ||
|
|
0b6144583b | ||
|
|
f31667317d | ||
|
|
5226b712a3 | ||
|
|
1f84cde024 | ||
|
|
046bfa2af0 | ||
|
|
0762732647 | ||
|
|
efb8f10450 | ||
|
|
6dbdf16fd1 | ||
|
|
6e6a45a7d9 | ||
|
|
a4569c22cc | ||
|
|
199e65c4d9 | ||
|
|
bb8811d472 | ||
|
|
995a16ca0c | ||
|
|
65f9c1aca1 | ||
|
|
b72ecb3a59 | ||
|
|
c221030f89 | ||
|
|
5bbea085d0 | ||
|
|
622cf03ab9 | ||
|
|
8ce27433f8 | ||
|
|
5d894620b4 | ||
|
|
71766c0c35 | ||
|
|
da58455508 | ||
|
|
b915c9a661 | ||
|
|
424aaa0f9d | ||
|
|
c72c895869 | ||
|
|
b7156116c2 | ||
|
|
7d915a0bb9 | ||
|
|
509afcd1d2 | ||
|
|
51343bd9a2 | ||
|
|
b58276a73c | ||
|
|
f162d344c0 | ||
|
|
0003db15cb | ||
|
|
275f4a4475 | ||
|
|
12e4565fef | ||
|
|
7af26eed32 | ||
|
|
63dc6eb316 | ||
|
|
6656790f24 | ||
|
|
c8de423038 | ||
|
|
c52653f97c | ||
|
|
e24c341068 |
No files matched your search
@@ -1,3 +1,6 @@
|
||||
[submodule "debian"]
|
||||
path = debian
|
||||
url = git://thekelleys.org.uk/dnsmasq-debian.git
|
||||
[submodule "submodules/dnsmasq-debian"]
|
||||
path = submodules/dnsmasq-debian
|
||||
url = https://thekelleys.org.uk/git/dnsmasq-debian
|
||||
@@ -7,9 +7,9 @@ version 2.91
|
||||
Literal address records are smaller and don't have
|
||||
this field and don't need to be ordered on it.
|
||||
To actually provoke this bug seems to need the same server-literal
|
||||
to be repeated twice, eg --address=/a/1.1.1.1 --address-/a/1.1.1.1
|
||||
to be repeated twice, e.g., --address=/a/1.1.1.1 --address-/a/1.1.1.1
|
||||
which is clearly rare in the wild, but if it did exist it could
|
||||
provoke a SIGSEV. Thanks to Daniel Rhea for fuzzing this one.
|
||||
provoke a SIGSEGV. Thanks to Daniel Rhea for fuzzing this one.
|
||||
|
||||
Fix buffer overflow when configured lease-change script name
|
||||
is too long.
|
||||
@@ -17,7 +17,7 @@ version 2.91
|
||||
|
||||
Improve behaviour in the face of non-responsive upstream TCP DNS
|
||||
servers. Without shorter timeouts, clients are blocked for too long
|
||||
and fail wuth their own timeouts.
|
||||
and fail with their own timeouts.
|
||||
|
||||
Set --fast-dns-retries by default when doing DNSSEC. A single
|
||||
downstream query can trigger many upstream queries. On an
|
||||
@@ -36,7 +36,7 @@ version 2.91
|
||||
empty answer.
|
||||
|
||||
Fix handling of EDNS0 UDP packet sizes.
|
||||
When talking upstream we always add a pseudoheader, and set the
|
||||
When talking upstream we always add a pseudo header, and set the
|
||||
UDP packet size to --edns-packet-max. Answering queries from
|
||||
downstream, we get the answer (either from upstream or local
|
||||
data) If local data won't fit the advertised size (or 512 if
|
||||
@@ -75,7 +75,58 @@ version 2.91
|
||||
Fix erroneous "DNSSEC validated" state with non-DNSSEC
|
||||
upstream servers. Thanks to Dominik Derigs for the bug report.
|
||||
|
||||
Handle queries with EDNS client subnet fields better. If dnsmasq
|
||||
is configured to add an EDNS client subnet to a query, it is careful
|
||||
to suppress use of the cache, since a cached answer may not be valid
|
||||
for a query with a different client subnet. Extend this behaviour
|
||||
to queries which arrive a dnsmasq already carrying an EDNS client
|
||||
subnet.
|
||||
|
||||
Handle DS queries to auth zones. When dnsmasq is configured to
|
||||
act as an authoritative server and has an authoritative zone
|
||||
configured, and receives a query for that zone _as_forwarder_
|
||||
it answers the query directly rather than forwarding it. This
|
||||
doesn't affect the answer, but it saves dnsmasq forwarding the
|
||||
query to the recursor upstream, which then bounces it back to dnsmasq
|
||||
in auth mode. The exception should be when the query is for the root
|
||||
of zone, for a DS RR. The answer to that has to come from the parent,
|
||||
via the recursor, and will typically be a proof-of-non-existence
|
||||
since dnsmasq doesn't support signed zones. This patch suppresses
|
||||
local answers and forces forwarding to the upstream recursor for such
|
||||
queries. It stops breakage when a DNSSEC validating client makes
|
||||
queries to dnsmasq acting as forwarder for a zone for which it is
|
||||
authoritative.
|
||||
|
||||
Implement "DNS-0x20 encoding", for extra protection against
|
||||
reply-spoof attacks. Since DNS queries are case-insensitive,
|
||||
it's possible to randomly flip the case of letters in a query
|
||||
and still get the correct answer back.
|
||||
This adds an extra dimension for a cache-poisoning attacker
|
||||
to guess when sending replies in-the-blind since it's expected
|
||||
that the legitimate answer will have the same pattern of upper
|
||||
and lower case as the query, so any replies which don't can be
|
||||
ignored as malicious. The amount of extra entropy clearly depends
|
||||
on the number of a-z and A-Z characters in the query, and this
|
||||
implementation puts a hard limit of 32 bits to make resource
|
||||
allocation easy. This about doubles entropy over the standard
|
||||
random ID and random port combination. This technique can interact
|
||||
badly with rare broken DNS servers which don't preserve the case
|
||||
of the query in their reply. The first time a reply is returned
|
||||
which matches the query in all respects except case, a warning
|
||||
will be logged. In this release, 0x020-encoding is default-off
|
||||
and must be explicitly enabled with --do-0x20-encoding. In future
|
||||
releases it may default on. You can avoid a future release
|
||||
changing the behaviour of an installation with --no-x20-encode.
|
||||
|
||||
Fix a long-standing problem when two queries which are identical
|
||||
in every repect _except_ case, get combined by dnsmasq. If
|
||||
dnsmasq gets eg, two queries for example.com and Example.com
|
||||
in quick succession it will get the answer for example.com from
|
||||
upstream and send that answer to both requestors. This means that
|
||||
the query for Example.com will get an answer for example.com, and
|
||||
in the modern DNS, that answer may not be accepted.
|
||||
|
||||
|
||||
version 2.90
|
||||
Fix reversion in --rev-server introduced in 2.88 which
|
||||
caused breakage if the prefix length is not exactly divisible
|
||||
@@ -1944,7 +1995,7 @@ version 2.61
|
||||
|
||||
Set the environment variable DNSMASQ_LOG_DHCP when running
|
||||
the script id --log-dhcp is in effect, so that script can
|
||||
taylor their logging verbosity. Suggestion from Malte
|
||||
tailor their logging verbosity. Suggestion from Malte
|
||||
Forkel.
|
||||
|
||||
Arrange that addresses specified with --listen-address
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
# dnsmasq is Copyright (c) 2000-2025 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
|
||||
@@ -71,8 +71,8 @@ nft_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_NFTSET $(PKG_CONFIG
|
||||
nft_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_NFTSET $(PKG_CONFIG) --libs libnftables`
|
||||
version = -DVERSION='\"`$(top)/bld/get-version $(top)`\"'
|
||||
|
||||
sum?=$(shell $(CC) -DDNSMASQ_COMPILE_OPTS $(COPTS) -E $(top)/$(SRC)/dnsmasq.h | ( md5sum 2>/dev/null || md5 ) | cut -f 1 -d ' ')
|
||||
sum!=$(CC) -DDNSMASQ_COMPILE_OPTS $(COPTS) -E $(top)/$(SRC)/dnsmasq.h | ( md5sum 2>/dev/null || md5 ) | cut -f 1 -d ' '
|
||||
sum?=$(shell echo $(CC) -DDNSMASQ_COMPILE_FLAGS="$(CFLAGS)" -DDNSMASQ_COMPILE_OPTS $(COPTS) -E $(top)/$(SRC)/dnsmasq.h | ( md5sum 2>/dev/null || md5 ) | cut -f 1 -d ' ')
|
||||
sum!=echo $(CC) -DDNSMASQ_COMPILE_FLAGS="$(CFLAGS)" -DDNSMASQ_COMPILE_OPTS $(COPTS) -E $(top)/$(SRC)/dnsmasq.h | ( md5sum 2>/dev/null || md5 ) | cut -f 1 -d ' '
|
||||
copts_conf = .copts_$(sum)
|
||||
|
||||
objs = cache.o rfc1035.o util.o option.o forward.o network.o \
|
||||
|
||||
-1
Submodule debian deleted from 05f03dca94.
+127
-99
@@ -1,4 +1,4 @@
|
||||
.TH DNSMASQ 8 2021-08-16
|
||||
.TH DNSMASQ 8 2025-02-05
|
||||
.SH NAME
|
||||
dnsmasq \- A lightweight DHCP and caching DNS server.
|
||||
.SH SYNOPSIS
|
||||
@@ -852,6 +852,18 @@ name on successive queries, for load-balancing. This turns off that
|
||||
behaviour, so that the records are always returned in the order
|
||||
that they are received from upstream.
|
||||
.TP
|
||||
.B --do-0x20-encode, --no-0x20-encode
|
||||
Dnsmasq can scramble the case of letters in DNS queries it sends upstream as a security feature.
|
||||
This technique can interact badly with rare broken DNS servers which don't preserve the case
|
||||
of the query in their reply. The first time a reply is returned
|
||||
which matches the query in all respects except case, a warning
|
||||
will be logged. If this coincides with DNS not functioning, it
|
||||
is necessary to disable the feature. As at version 2.91, 0x20 encoding
|
||||
is disabled by default, and must be enabled with --do-0x20-encode. The default
|
||||
may change in the future, so to be sure of its status after an upgrade, set --do-0x20-encode
|
||||
or --no-0x20-encode in your config. --no-0x20-encode overrides --do-x20-encode or a future default
|
||||
0x20-encode enable.
|
||||
.TP
|
||||
.B --use-stale-cache[=<max TTL excess in s>]
|
||||
When set, if a DNS name exists in the cache, but its time-to-live has expired, dnsmasq will return the data anyway. (It attempts to refresh the
|
||||
data with an upstream query after returning the stale data.) This can improve speed and reliability. It comes at the expense
|
||||
@@ -882,7 +894,7 @@ ie capable of returning DNSSEC records with data. If they are not,
|
||||
then dnsmasq will not be able to determine the trusted status of
|
||||
answers and this means that DNS service will be entirely broken.
|
||||
.TP
|
||||
.B --trust-anchor=[<class>],<domain>,<key-tag>,<algorithm>,<digest-type>,<digest>
|
||||
.B --trust-anchor=<domain>,[<class>,]<key-tag>,<algorithm>,<digest-type>,<digest>
|
||||
Provide DS records to act a trust anchors for DNSSEC
|
||||
validation. Typically these will be the DS record(s) for Key Signing
|
||||
key(s) (KSK) of the root zone,
|
||||
@@ -1188,7 +1200,7 @@ available, though it normally is for direct-connected clients, or
|
||||
clients using DHCP relays which support RFC 6939.
|
||||
|
||||
|
||||
For DHCPv4, the special option id:* means "ignore any client-id
|
||||
For DHCPv4, the special option \fBid:*\fP means "ignore any client-id
|
||||
and use MAC addresses only." This is useful when a client presents a client-id sometimes
|
||||
but not others.
|
||||
|
||||
@@ -1216,36 +1228,34 @@ the result is undefined. A corollary to this is that the name associated with a
|
||||
does not appear in the DNS until the host obtains a DHCP lease.
|
||||
|
||||
|
||||
The special keyword "ignore"
|
||||
tells dnsmasq to never offer a DHCP lease to a machine. The machine
|
||||
can be specified by hardware address, client ID or hostname, for
|
||||
instance
|
||||
.B --dhcp-host=00:20:e0:3b:13:af,ignore
|
||||
This is
|
||||
useful when there is another DHCP server on the network which should
|
||||
The special keyword \fBignore\fP tells dnsmasq never to offer a DHCP lease
|
||||
to a machine. The machine
|
||||
can be specified by hardware address, client ID or hostname. For
|
||||
example: \fB--dhcp-host=00:20:e0:3b:13:af,ignore\fP.
|
||||
This can be useful when there is another DHCP server on the network which should
|
||||
be used by some machines.
|
||||
|
||||
The set:<tag> construct sets the tag
|
||||
The \fBset:<tag>\fP construct sets the tag
|
||||
whenever this \fB--dhcp-host\fP directive is in use. This can be used to
|
||||
selectively send DHCP options just for this host. More than one tag
|
||||
can be set in a \fB--dhcp-host\fP directive (but not in other places where
|
||||
"set:<tag>" is allowed). When a host matches any
|
||||
\fBset:<tag>\fP is allowed). When a host matches any
|
||||
\fB--dhcp-host\fP directive (or one implied by /etc/ethers) then the special
|
||||
tag "known" is set. This allows dnsmasq to be configured to
|
||||
tag \fBknown\fP is set. This allows dnsmasq to be configured to
|
||||
ignore requests from unknown machines using
|
||||
.B --dhcp-ignore=tag:!known
|
||||
\fB--dhcp-ignore=tag:!known\fP.
|
||||
If the host matches only a \fB--dhcp-host\fP directive which cannot
|
||||
be used because it specifies an address on different subnet, the tag "known-othernet" is set.
|
||||
be used because it specifies an address on a different subnet, the tag \fBknown-othernet\fP is set.
|
||||
|
||||
The tag:<tag> construct filters which dhcp-host directives are used; more than
|
||||
one can be provided, in this case the request must match all of them. Tagged
|
||||
The \fBtag:<tag>\fP construct filters which dhcp-host directives are used. More than
|
||||
one tag can be provided. In this case, the request must match all tags. Tagged
|
||||
directives are used in preference to untagged ones. Note that one of <hwaddr>,
|
||||
<client_id> or <hostname> still needs to be specified (can be a wildcard).
|
||||
<client_id> or <hostname> still must be specified (can be a wildcard).
|
||||
|
||||
Ethernet addresses (but not client-ids) may have
|
||||
wildcard bytes, so for example
|
||||
.B --dhcp-host=00:20:e0:3b:13:*,ignore
|
||||
will cause dnsmasq to ignore a range of hardware addresses. Note that
|
||||
will cause dnsmasq to ignore the given range of hardware addresses. Note that
|
||||
the "*" will need to be escaped or quoted on a command line, but not
|
||||
in the configuration file.
|
||||
|
||||
@@ -1259,11 +1269,11 @@ is 6.
|
||||
|
||||
As a special case, in DHCPv4, it is possible to include more than one
|
||||
hardware address. eg:
|
||||
.B --dhcp-host=11:22:33:44:55:66,12:34:56:78:90:12,192.168.0.2
|
||||
\fB--dhcp-host=11:22:33:44:55:66,12:34:56:78:90:12,192.168.0.2\fP.
|
||||
This allows an IP address to be associated with
|
||||
multiple hardware addresses, and gives dnsmasq permission to abandon a
|
||||
DHCP lease to one of the hardware addresses when another one asks for
|
||||
a lease. Beware that this is a dangerous thing to do, it will only
|
||||
a lease. Beware that this is a dangerous thing to do: it will only
|
||||
work reliably if only one of the hardware addresses is active at any
|
||||
time and there is no way for dnsmasq to enforce this. It is, for instance,
|
||||
useful to allocate a stable IP address to a laptop which
|
||||
@@ -1310,27 +1320,25 @@ options containing the same information. /etc/ethers is re-read when
|
||||
dnsmasq receives SIGHUP. IPv6 addresses are NOT read from /etc/ethers.
|
||||
.TP
|
||||
.B \-O, --dhcp-option=[tag:<tag>,[tag:<tag>,]][encap:<opt>,][vi-encap:<enterprise>,][vendor:[<vendor-class>],][<opt>|option:<opt-name>|option6:<opt>|option6:<opt-name>],[<value>[,<value>]]
|
||||
Specify different or extra options to DHCP clients. By default,
|
||||
Send various extra options to DHCP clients. By default,
|
||||
dnsmasq sends some standard options to DHCP clients, the netmask and
|
||||
broadcast address are set to the same as the host running dnsmasq, and
|
||||
the DNS server and default route are set to the address of the machine
|
||||
running dnsmasq. (Equivalent rules apply for IPv6.) If the domain name option has been set, that is sent.
|
||||
This configuration allows these defaults to be overridden,
|
||||
or other options specified. The option, to be sent may be given as a
|
||||
decimal number or as "option:<option-name>" The option numbers are
|
||||
or other options specified. The option to be sent may be given as a
|
||||
decimal number or as \fBoption:<option-name>\fP.
|
||||
|
||||
Option numbers are
|
||||
specified in RFC2132 and subsequent RFCs. The set of option-names
|
||||
known by dnsmasq can be discovered by running "dnsmasq --help dhcp".
|
||||
For example, to set the default route option to
|
||||
192.168.4.4, do
|
||||
.B --dhcp-option=3,192.168.4.4
|
||||
or
|
||||
.B --dhcp-option = option:router, 192.168.4.4
|
||||
and to set the time-server address to 192.168.0.4, do
|
||||
.B --dhcp-option = 42,192.168.0.4
|
||||
or
|
||||
.B --dhcp-option = option:ntp-server, 192.168.0.4
|
||||
The special address 0.0.0.0 is taken to mean "the address of the
|
||||
machine running dnsmasq".
|
||||
known by dnsmasq can be discovered by running \fBdnsmasq --help dhcp\fP.
|
||||
For example, to set the default route option to 192.168.4.4, use
|
||||
\fB--dhcp-option=3,192.168.4.4\fP or
|
||||
\fB--dhcp-option=option:router,192.168.4.4\fP
|
||||
and to set the time-server address to 192.168.0.4, use
|
||||
\fB--dhcp-option=42,192.168.0.4\fP or
|
||||
\fB--dhcp-option=option:ntp-server,192.168.0.4\fP.
|
||||
The special address 0.0.0.0 means "the address of the system running dnsmasq".
|
||||
|
||||
An option without data is valid, and includes just the option without data.
|
||||
(There is only one option with a zero length data field currently defined for DHCPv4, 80:rapid commit, so this feature is not very useful in practice). Options for which dnsmasq normally
|
||||
@@ -1351,30 +1359,29 @@ to option 120 are handled as per RFC 3361. Dotted-quad IP addresses
|
||||
which are followed by a slash and then a netmask size are encoded as
|
||||
described in RFC 3442.
|
||||
|
||||
IPv6 options are specified using the
|
||||
.B option6:
|
||||
IPv6 options are specified using the \fBoption6:\fP
|
||||
keyword, followed by the option number or option name. The IPv6 option
|
||||
name space is disjoint from the IPv4 option name space. IPv6 addresses
|
||||
in options must be bracketed with square brackets, eg.
|
||||
.B --dhcp-option=option6:ntp-server,[1234::56]
|
||||
\fB --dhcp-option=option6:ntp-server,[1234::56]\fP.
|
||||
For IPv6, [::] means "the global address of
|
||||
the machine running dnsmasq", whilst [fd00::] is replaced with the
|
||||
ULA, if it exists, and [fe80::] with the link-local address.
|
||||
|
||||
Be careful: no checking is done that the correct type of data for the
|
||||
option number is sent, it is quite possible to
|
||||
persuade dnsmasq to generate illegal DHCP packets with injudicious use
|
||||
of this flag. When the value is a decimal number, dnsmasq must determine how
|
||||
Be careful: data-type suitability for the option number sent is not checked.
|
||||
It is quite possible to persuade dnsmasq to generate illegal DHCP packets with
|
||||
injudicious use of this flag.
|
||||
When the value is a decimal number, dnsmasq must determine how
|
||||
large the data item is. It does this by examining the option number and/or the
|
||||
value, but can be overridden by appending a single letter flag as follows:
|
||||
b = one byte, s = two bytes, i = four bytes. This is mainly useful with
|
||||
encapsulated vendor class options (see below) where dnsmasq cannot
|
||||
determine data size from the option number. Option data which
|
||||
determine data size from the option number. Option data which
|
||||
consists solely of periods and digits will be interpreted by dnsmasq
|
||||
as an IP address, and inserted into an option as such. To force a
|
||||
literal string, use quotes. For instance when using option 66 to send
|
||||
a literal IP address as TFTP server name, it is necessary to do
|
||||
.B --dhcp-option=66,"1.2.3.4"
|
||||
a literal IP address as TFTP server name, it is necessary to do
|
||||
\fB--dhcp-option=66,"1.2.3.4"\fP.
|
||||
|
||||
Encapsulated Vendor-class options may also be specified (IPv4 only) using
|
||||
\fB--dhcp-option\fP: for instance
|
||||
@@ -1403,7 +1410,7 @@ Vendor Options" as specified by RFC3925. These are denoted like this:
|
||||
The number in the vi-encap: section is the IANA enterprise number
|
||||
used to identify this option. This form of encapsulation is supported
|
||||
in IPv6.
|
||||
|
||||
|
||||
The address 0.0.0.0 is not treated specially in
|
||||
encapsulated options.
|
||||
.TP
|
||||
@@ -1429,7 +1436,7 @@ forces "simple and safe" behaviour to avoid problems in such a case.
|
||||
.B --dhcp-relay=<local address>[,<server address>[#<server port>]][,<interface]
|
||||
Configure dnsmasq to do DHCP relay. The local address is an address
|
||||
allocated to an interface on the host running dnsmasq. All DHCP
|
||||
requests arriving on that interface will we relayed to a remote DHCP
|
||||
requests arriving on that interface will be relayed to a remote DHCP
|
||||
server at the server address. It is possible to relay from a single local
|
||||
address to multiple remote servers by using multiple \fB--dhcp-relay\fP
|
||||
configs with the same local address and different server
|
||||
@@ -1529,45 +1536,47 @@ via relays. If a list of IP addresses is given, only interactions via
|
||||
relays at those addresses are affected.
|
||||
.TP
|
||||
.B --dhcp-match=set:<tag>,<option number>|option:<option name>|vi-encap:<enterprise>[,<value>]
|
||||
Without a value, set the tag if the client sends a DHCP
|
||||
option of the given number or name. When a value is given, set the tag only if
|
||||
the option is sent and matches the value. The value may be of the form
|
||||
Without \fB<value>\fP, set \fB<tag>\fP if the client sends a DHCP
|
||||
\fBoption\fP of the given number or name. With \fB<value>\fP, set \fB<tag>\fP only if
|
||||
the client sends the \fBoption\fP matching or containing \fB<value>\fP.
|
||||
|
||||
The value may be of the form
|
||||
"01:ff:*:02" in which case the value must match (apart from wildcards)
|
||||
but the option sent may have unmatched data past the end of the
|
||||
value. The value may also be of the same form as in
|
||||
.B --dhcp-option
|
||||
in which case the option sent is treated as an array, and one element
|
||||
must match, so
|
||||
must match.
|
||||
.B --dhcp-match=set:efi-ia32,option:client-arch,6
|
||||
will set the tag "efi-ia32" if the the number 6 appears in the list of
|
||||
sets the tag \fBefi-ia32\fP if the number \fB6\fP appears in the list of
|
||||
architectures sent by the client in option 93. (See RFC 4578 for
|
||||
details.) If the value is a string, substring matching is used.
|
||||
details.) If the value is a string, substring matching is used.
|
||||
|
||||
The special form with vi-encap:<enterprise number> matches against
|
||||
vendor-identifying vendor classes for the specified enterprise. Please
|
||||
see RFC 3925 for more details of these rare and interesting beasts.
|
||||
.TP
|
||||
.B --dhcp-name-match=set:<tag>,<name>[*]
|
||||
Set the tag if the given name is supplied by a DHCP client. There may be a single trailing wildcard *, which has the usual meaning. Combined with dhcp-ignore or dhcp-ignore-names this gives the ability to ignore certain clients by name, or disallow certain hostnames from being claimed by a client.
|
||||
Set a \fB<tag>\fP if the given \fB<name>\fP is supplied by a DHCP client. There may be a single trailing wildcard *, with a glob meaning. Combined with \fBdhcp-ignore\fP or \fBdhcp-ignore-names\fP this gives the ability to ignore certain clients by name, or disallow certain hostnames from being claimed by a client.
|
||||
.TP
|
||||
.B --tag-if=set:<tag>[,set:<tag>[,tag:<tag>[,tag:<tag>]]]
|
||||
Perform boolean operations on tags. Any tag appearing as set:<tag> is set if
|
||||
all the tags which appear as tag:<tag> are set, (or unset when tag:!<tag> is used)
|
||||
If no tag:<tag> appears set:<tag> tags are set unconditionally.
|
||||
Any number of set: and tag: forms may appear, in any order.
|
||||
\fB--tag-if\fP lines are executed in order, so if the tag in tag:<tag> is a
|
||||
Perform boolean operations on tags. All \fBset:<tag>\fP tags are set if
|
||||
all \fBtag:<tag>\fP are already set, (or unset when \fBtag:!<tag>\fP is used).
|
||||
If no \fBtag:<tag>\fP appears, \fBset:<tag>\fP tags are set unconditionally.
|
||||
Any number of \fBset:\fP and \fBtag:\fP forms may appear, in any order.
|
||||
\fB--tag-if\fP lines are executed in order, so if the tag in \fBtag:<tag>\fP is a
|
||||
tag set by another
|
||||
.B --tag-if,
|
||||
the line which sets the tag must precede the one which tests it.
|
||||
the line which sets the tag must precede the line which tests it.
|
||||
|
||||
As an extension, the tag:<tag> clauses support limited wildcard matching,
|
||||
similar to the matching in the \fB--interface\fP directive. This allows, for
|
||||
example, using \fB--tag-if=set:ppp,tag:ppp*\fP to set the tag 'ppp' for all requests
|
||||
received on any matching interface (ppp0, ppp1, etc). This can be used in conjunction
|
||||
with the tag:!<tag> format meaning that no tag matching the wildcard may be set.
|
||||
As an extension, the \fBtag:<tag>\fP clauses support limited wildcard matching,
|
||||
similar to the matching in the \fB--interface\fP directive. This allows the
|
||||
example \fB--tag-if=set:ppp,tag:ppp*\fP to set the tag 'ppp' for all requests
|
||||
received on any matching interface (ppp0, ppp1, etc). This can be used in conjunction
|
||||
with the \fBtag:!<tag>\fP format meaning that no tag matching the wildcard may be set.
|
||||
.TP
|
||||
.B \-J, --dhcp-ignore=tag:<tag>[,tag:<tag>]
|
||||
When all the given tags appear in the tag set ignore the host and do
|
||||
When all the given tags appear in the tag set, ignore the host and do
|
||||
not allocate it a DHCP lease.
|
||||
.TP
|
||||
.B --dhcp-ignore-names[=tag:<tag>[,tag:<tag>]]
|
||||
@@ -2416,48 +2425,67 @@ the CNAME. To work around this, add the CNAME to /etc/hosts so that
|
||||
the CNAME is shadowed too.
|
||||
|
||||
.PP
|
||||
The tag system works as follows: For each DHCP request, dnsmasq
|
||||
collects a set of valid tags from active configuration lines which
|
||||
include set:<tag>, including one from the
|
||||
.B --dhcp-range
|
||||
used to allocate the address, one from any matching
|
||||
.B --dhcp-host
|
||||
(and "known" or "known-othernet" if a \fB--dhcp-host\fP matches)
|
||||
The tag "bootp" is set for BOOTP requests, and a tag whose name is the
|
||||
name of the interface on which the request arrived is also set.
|
||||
The tag system works as follows: dnsmasq tags each DHCP request
|
||||
with tags from applicable configuration lines containing \fBset:<tag>\fP i.e.
|
||||
|
||||
Any configuration lines which include one or more tag:<tag> constructs
|
||||
will only be valid if all that tags are matched in the set derived
|
||||
above. Typically this is \fB--dhcp-option\fP.
|
||||
.B --dhcp-option
|
||||
which has tags will be used in preference to an untagged
|
||||
.B --dhcp-option,
|
||||
provided that _all_ the tags match somewhere in the
|
||||
set collected as described above. The prefix '!' on a tag means 'not'
|
||||
so \fB--dhcp-option=tag:!purple,3,1.2.3.4\fP sends the option when the
|
||||
tag purple is not in the set of valid tags. (If using this in a
|
||||
command line rather than a configuration file, be sure to escape !,
|
||||
which is a shell metacharacter)
|
||||
\fBset:<tag>\fP from the \fB--dhcp-range\fP used to allocate the address;
|
||||
|
||||
\fBset:<tag>\fP from any matching \fB--dhcp-host\fP (plus the tag \fBknown\fP or
|
||||
\fBknown-othernet\fP).
|
||||
|
||||
BOOTP requests are tagged \fBbootp\fP. Each request is also tagged with the name of
|
||||
the interface on which the request arrived.
|
||||
|
||||
Each configuration line containing one or more \fBtag:<tag>\fP constructs
|
||||
applies when all its tags exist on the request. That is:
|
||||
|
||||
Configuration tag:A applies to a request set:tagged A.
|
||||
|
||||
Configuration tag:B applies to a request set:tagged B.
|
||||
|
||||
Configuration tag:A+B doesn't apply to a request set:tagged A.
|
||||
|
||||
Configuration tag:A+B doesn't apply to a request set:tagged B.
|
||||
|
||||
Configurations tag:A+B, tag:A, tag:B apply to a request set:tagged A+B.
|
||||
|
||||
\fBset:<tag>\fP constructs in \fB--dhcp-range\fP and \fB--dhcp-host\fP tag requests.
|
||||
|
||||
Use \fBtag:<tag>\fPs in \fB--dhcp-option\fPs to match \fBset:<tag>\fP and apply configurations.
|
||||
|
||||
A \fB--dhcp-option\fP with \fBtag:<tag>\fP is preferred over an untagged
|
||||
\fB--dhcp-option\fP, provided that \fBall\fP its tags match somewhere in the
|
||||
set gathered above.
|
||||
|
||||
The tag prefix '!' means 'not'.
|
||||
\fB--dhcp-option=tag:!purple,3,1.2.3.4\fP sends the option when the request
|
||||
is not tagged with purple. (The shell metacharacter '!' must be escaped on the
|
||||
command line but not in a configuration file).
|
||||
|
||||
When selecting \fB--dhcp-option\fPs, a \fB--dhcp-range\fP tag is second class
|
||||
to other tags, to make it easy to override options for
|
||||
individual hosts, so:
|
||||
|
||||
When selecting \fB--dhcp-options\fP, a tag from \fB--dhcp-range\fP is second class
|
||||
relative to other tags, to make it easy to override options for
|
||||
individual hosts, so
|
||||
.B --dhcp-range=set:interface1,......
|
||||
|
||||
.B --dhcp-host=set:myhost,.....
|
||||
|
||||
.B --dhcp-option=tag:interface1,option:nis-domain,"domain1"
|
||||
|
||||
.B --dhcp-option=tag:myhost,option:nis-domain,"domain2"
|
||||
will set the NIS-domain to domain1 for hosts in the range, but
|
||||
override that to domain2 for a particular host.
|
||||
|
||||
sets the NIS-domain to domain1 for hosts in the range, but
|
||||
to domain2 for a particular host that may or may not fall in the range.
|
||||
|
||||
.PP
|
||||
Note that for
|
||||
.B --dhcp-range
|
||||
both tag:<tag> and set:<tag> are allowed, to both select the range in
|
||||
Note that for \fB--dhcp-range\fP both
|
||||
\fBtag:<tag>\fP and
|
||||
\fBset:<tag>\fP are possible, in order to both select the range in
|
||||
use based on (eg) \fB--dhcp-host\fP, and to affect the options sent, based on
|
||||
the range selected.
|
||||
|
||||
This system evolved from an earlier, more limited one and for backward
|
||||
compatibility "net:" may be used instead of "tag:" and "set:" may be
|
||||
The tag system evolved from an earlier, more limited system. For backward
|
||||
compatibility, "net:" may be used instead of "tag:" and "set:" may be
|
||||
omitted. (Except in
|
||||
.B --dhcp-host,
|
||||
where "net:" may be used instead of "set:".) For the same reason, '#'
|
||||
@@ -2475,7 +2503,7 @@ configuration option is present to activate the DHCP server
|
||||
on a particular network. (Setting \fB--bootp-dynamic\fP removes the need for
|
||||
static address mappings.) The filename
|
||||
parameter in a BOOTP request is used as a tag,
|
||||
as is the tag "bootp", allowing some control over the options returned to
|
||||
as is the tag \fBbootp\fP, allowing some control over the options returned to
|
||||
different classes of hosts.
|
||||
|
||||
.SH AUTHORITATIVE CONFIGURATION
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 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
|
||||
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 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
|
||||
@@ -137,7 +137,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
nameoffset = p - (unsigned char *)header;
|
||||
|
||||
/* now extract name as .-concatenated string into name */
|
||||
if (!extract_name(header, qlen, &p, name, 1, 4))
|
||||
if (!extract_name(header, qlen, &p, name, EXTR_NAME_EXTRACT, 4))
|
||||
return 0; /* bad packet */
|
||||
|
||||
GETSHORT(qtype, p);
|
||||
@@ -513,7 +513,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
nxdomain = 0;
|
||||
if ((crecp->flags & flag) && (local_query || filter_zone(zone, flag, &(crecp->addr))))
|
||||
{
|
||||
log_query(crecp->flags, name, &crecp->addr, record_source(crecp->uid), 0);
|
||||
log_query(crecp->flags & ~F_REVERSE, name, &crecp->addr, record_source(crecp->uid), 0);
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
daemon->auth_ttl, NULL, qtype, C_IN,
|
||||
qtype == T_A ? "4" : "6", &crecp->addr))
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 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
|
||||
@@ -126,112 +126,110 @@ int iface_enumerate(int family, void *parm, callback_t callback)
|
||||
|
||||
for (addrs = head; addrs; addrs = addrs->ifa_next)
|
||||
{
|
||||
if (addrs->ifa_addr->sa_family == family)
|
||||
int iface_index = if_nametoindex(addrs->ifa_name);
|
||||
|
||||
if (iface_index == 0 || !addrs->ifa_addr ||
|
||||
addrs->ifa_addr->sa_family != family ||
|
||||
(!addrs->ifa_netmask && family != AF_LINK))
|
||||
continue;
|
||||
|
||||
if (family == AF_INET)
|
||||
{
|
||||
int iface_index = if_nametoindex(addrs->ifa_name);
|
||||
|
||||
if (iface_index == 0 || !addrs->ifa_addr ||
|
||||
(!addrs->ifa_netmask && family != AF_LINK))
|
||||
struct in_addr addr, netmask, broadcast;
|
||||
addr = ((struct sockaddr_in *) addrs->ifa_addr)->sin_addr;
|
||||
#ifdef HAVE_BSD_NETWORK
|
||||
if (del_family == AF_INET && del_addr.addr4.s_addr == addr.s_addr)
|
||||
continue;
|
||||
|
||||
if (family == AF_INET)
|
||||
{
|
||||
struct in_addr addr, netmask, broadcast;
|
||||
addr = ((struct sockaddr_in *) addrs->ifa_addr)->sin_addr;
|
||||
#ifdef HAVE_BSD_NETWORK
|
||||
if (del_family == AF_INET && del_addr.addr4.s_addr == addr.s_addr)
|
||||
continue;
|
||||
#endif
|
||||
netmask = ((struct sockaddr_in *) addrs->ifa_netmask)->sin_addr;
|
||||
if (addrs->ifa_broadaddr)
|
||||
broadcast = ((struct sockaddr_in *) addrs->ifa_broadaddr)->sin_addr;
|
||||
else
|
||||
broadcast.s_addr = 0;
|
||||
if (!callback.af_inet(addr, iface_index, NULL, netmask, broadcast, parm))
|
||||
goto err;
|
||||
}
|
||||
else if (family == AF_INET6)
|
||||
{
|
||||
struct in6_addr *addr = &((struct sockaddr_in6 *) addrs->ifa_addr)->sin6_addr;
|
||||
unsigned char *netmask = (unsigned char *) &((struct sockaddr_in6 *) addrs->ifa_netmask)->sin6_addr;
|
||||
int scope_id = ((struct sockaddr_in6 *) addrs->ifa_addr)->sin6_scope_id;
|
||||
int i, j, prefix = 0;
|
||||
u32 valid = 0xffffffff, preferred = 0xffffffff;
|
||||
int flags = 0;
|
||||
netmask = ((struct sockaddr_in *) addrs->ifa_netmask)->sin_addr;
|
||||
if (addrs->ifa_broadaddr)
|
||||
broadcast = ((struct sockaddr_in *) addrs->ifa_broadaddr)->sin_addr;
|
||||
else
|
||||
broadcast.s_addr = 0;
|
||||
if (!callback.af_inet(addr, iface_index, NULL, netmask, broadcast, parm))
|
||||
goto err;
|
||||
}
|
||||
else if (family == AF_INET6)
|
||||
{
|
||||
struct in6_addr *addr = &((struct sockaddr_in6 *) addrs->ifa_addr)->sin6_addr;
|
||||
unsigned char *netmask = (unsigned char *) &((struct sockaddr_in6 *) addrs->ifa_netmask)->sin6_addr;
|
||||
int scope_id = ((struct sockaddr_in6 *) addrs->ifa_addr)->sin6_scope_id;
|
||||
int i, j, prefix = 0;
|
||||
u32 valid = 0xffffffff, preferred = 0xffffffff;
|
||||
int flags = 0;
|
||||
#ifdef HAVE_BSD_NETWORK
|
||||
if (del_family == AF_INET6 && IN6_ARE_ADDR_EQUAL(&del_addr.addr6, addr))
|
||||
continue;
|
||||
if (del_family == AF_INET6 && IN6_ARE_ADDR_EQUAL(&del_addr.addr6, addr))
|
||||
continue;
|
||||
#endif
|
||||
#if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
|
||||
struct in6_ifreq ifr6;
|
||||
|
||||
memset(&ifr6, 0, sizeof(ifr6));
|
||||
safe_strncpy(ifr6.ifr_name, addrs->ifa_name, sizeof(ifr6.ifr_name));
|
||||
struct in6_ifreq ifr6;
|
||||
|
||||
memset(&ifr6, 0, sizeof(ifr6));
|
||||
safe_strncpy(ifr6.ifr_name, addrs->ifa_name, sizeof(ifr6.ifr_name));
|
||||
|
||||
ifr6.ifr_addr = *((struct sockaddr_in6 *) addrs->ifa_addr);
|
||||
if (fd != -1 && ioctl(fd, SIOCGIFAFLAG_IN6, &ifr6) != -1)
|
||||
{
|
||||
if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_TENTATIVE)
|
||||
flags |= IFACE_TENTATIVE;
|
||||
|
||||
if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DEPRECATED)
|
||||
flags |= IFACE_DEPRECATED;
|
||||
|
||||
ifr6.ifr_addr = *((struct sockaddr_in6 *) addrs->ifa_addr);
|
||||
if (fd != -1 && ioctl(fd, SIOCGIFAFLAG_IN6, &ifr6) != -1)
|
||||
{
|
||||
if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_TENTATIVE)
|
||||
flags |= IFACE_TENTATIVE;
|
||||
|
||||
if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DEPRECATED)
|
||||
flags |= IFACE_DEPRECATED;
|
||||
|
||||
#ifdef IN6_IFF_TEMPORARY
|
||||
if (!(ifr6.ifr_ifru.ifru_flags6 & (IN6_IFF_AUTOCONF | IN6_IFF_TEMPORARY)))
|
||||
flags |= IFACE_PERMANENT;
|
||||
if (!(ifr6.ifr_ifru.ifru_flags6 & (IN6_IFF_AUTOCONF | IN6_IFF_TEMPORARY)))
|
||||
flags |= IFACE_PERMANENT;
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef IN6_IFF_PRIVACY
|
||||
if (!(ifr6.ifr_ifru.ifru_flags6 & (IN6_IFF_AUTOCONF | IN6_IFF_PRIVACY)))
|
||||
flags |= IFACE_PERMANENT;
|
||||
if (!(ifr6.ifr_ifru.ifru_flags6 & (IN6_IFF_AUTOCONF | IN6_IFF_PRIVACY)))
|
||||
flags |= IFACE_PERMANENT;
|
||||
#endif
|
||||
}
|
||||
|
||||
ifr6.ifr_addr = *((struct sockaddr_in6 *) addrs->ifa_addr);
|
||||
if (fd != -1 && ioctl(fd, SIOCGIFALIFETIME_IN6, &ifr6) != -1)
|
||||
{
|
||||
valid = ifr6.ifr_ifru.ifru_lifetime.ia6t_vltime;
|
||||
preferred = ifr6.ifr_ifru.ifru_lifetime.ia6t_pltime;
|
||||
}
|
||||
}
|
||||
|
||||
ifr6.ifr_addr = *((struct sockaddr_in6 *) addrs->ifa_addr);
|
||||
if (fd != -1 && ioctl(fd, SIOCGIFALIFETIME_IN6, &ifr6) != -1)
|
||||
{
|
||||
valid = ifr6.ifr_ifru.ifru_lifetime.ia6t_vltime;
|
||||
preferred = ifr6.ifr_ifru.ifru_lifetime.ia6t_pltime;
|
||||
}
|
||||
#endif
|
||||
|
||||
for (i = 0; i < IN6ADDRSZ; i++, prefix += 8)
|
||||
if (netmask[i] != 0xff)
|
||||
break;
|
||||
|
||||
if (i != IN6ADDRSZ && netmask[i])
|
||||
for (j = 7; j > 0; j--, prefix++)
|
||||
if ((netmask[i] & (1 << j)) == 0)
|
||||
break;
|
||||
|
||||
/* voodoo to clear interface field in address */
|
||||
if (!option_bool(OPT_NOWILD) && IN6_IS_ADDR_LINKLOCAL(addr))
|
||||
{
|
||||
addr->s6_addr[2] = 0;
|
||||
addr->s6_addr[3] = 0;
|
||||
}
|
||||
|
||||
if (!callback.af_inet6(addr, prefix, scope_id, iface_index, flags,
|
||||
(unsigned int) preferred, (unsigned int)valid, parm))
|
||||
goto err;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
else if (family == AF_LINK)
|
||||
{
|
||||
/* Assume ethernet again here */
|
||||
struct sockaddr_dl *sdl = (struct sockaddr_dl *) addrs->ifa_addr;
|
||||
if (sdl->sdl_alen != 0 &&
|
||||
!callback.af_local(iface_index, ARPHRD_ETHER, LLADDR(sdl), sdl->sdl_alen, parm))
|
||||
goto err;
|
||||
}
|
||||
#endif
|
||||
|
||||
for (i = 0; i < IN6ADDRSZ; i++, prefix += 8)
|
||||
if (netmask[i] != 0xff)
|
||||
break;
|
||||
|
||||
if (i != IN6ADDRSZ && netmask[i])
|
||||
for (j = 7; j > 0; j--, prefix++)
|
||||
if ((netmask[i] & (1 << j)) == 0)
|
||||
break;
|
||||
|
||||
/* voodoo to clear interface field in address */
|
||||
if (!option_bool(OPT_NOWILD) && IN6_IS_ADDR_LINKLOCAL(addr))
|
||||
{
|
||||
addr->s6_addr[2] = 0;
|
||||
addr->s6_addr[3] = 0;
|
||||
}
|
||||
|
||||
if (!callback.af_inet6(addr, prefix, scope_id, iface_index, flags,
|
||||
(unsigned int) preferred, (unsigned int)valid, parm))
|
||||
goto err;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
else if (family == AF_LINK)
|
||||
{
|
||||
/* Assume ethernet again here */
|
||||
struct sockaddr_dl *sdl = (struct sockaddr_dl *) addrs->ifa_addr;
|
||||
if (sdl->sdl_alen != 0 &&
|
||||
!callback.af_local(iface_index, ARPHRD_ETHER, LLADDR(sdl), sdl->sdl_alen, parm))
|
||||
goto err;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
ret = 1;
|
||||
|
||||
|
||||
err:
|
||||
errsave = errno;
|
||||
freeifaddrs(head);
|
||||
|
||||
+24
-8
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 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
|
||||
@@ -479,7 +479,7 @@ static struct crec *cache_scan_free(char *name, union all_addr *addr, unsigned s
|
||||
if ((crecp->flags & F_FORWARD) && hostname_isequal(cache_get_name(crecp), name))
|
||||
{
|
||||
int rrmatch = 0;
|
||||
if (crecp->flags & flags & F_RR)
|
||||
if (addr && (crecp->flags & flags & F_RR))
|
||||
{
|
||||
unsigned short rrc = (crecp->flags & F_KEYTAG) ? crecp->addr.rrblock.rrtype : crecp->addr.rrdata.rrtype;
|
||||
unsigned short rra = (flags & F_KEYTAG) ? addr->rrblock.rrtype : addr->rrdata.rrtype;
|
||||
@@ -1852,15 +1852,31 @@ int cache_make_stat(struct txt_record *t)
|
||||
#endif
|
||||
|
||||
/* There can be names in the cache containing control chars, don't
|
||||
mess up logging or open security holes. */
|
||||
mess up logging or open security holes. Also convert to all-LC
|
||||
so that 0x20-encoding doesn't make logs look like ransom notes
|
||||
made out of letters cut from a newspaper.
|
||||
Overwrites daemon->workspacename */
|
||||
static char *sanitise(char *name)
|
||||
{
|
||||
unsigned char *r;
|
||||
unsigned char *r = (unsigned char *)name;
|
||||
|
||||
if (name)
|
||||
for (r = (unsigned char *)name; *r; r++)
|
||||
if (!isprint((int)*r))
|
||||
return "<name unprintable>";
|
||||
|
||||
{
|
||||
char *d = name = daemon->workspacename;
|
||||
|
||||
for (; *r; r++, d++)
|
||||
if (!isprint((int)*r))
|
||||
return "<name unprintable>";
|
||||
else
|
||||
{
|
||||
unsigned char c = *r;
|
||||
|
||||
*d = (char)((c >= 'A' && c <= 'Z') ? c + 'a' - 'A' : c);
|
||||
}
|
||||
|
||||
*d = 0;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
+6
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 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
|
||||
@@ -362,6 +362,11 @@ HAVE_SOCKADDR_SA_LEN
|
||||
#define HAVE_INOTIFY
|
||||
#endif
|
||||
|
||||
/* This never compiles code, it's only used by the makefile to fingerprint builds. */
|
||||
#ifdef DNSMASQ_COMPILE_FLAGS
|
||||
static char *compile_flags = DNSMASQ_COMPILE_FLAGS;
|
||||
#endif
|
||||
|
||||
/* Define a string indicating which options are in use.
|
||||
DNSMASQ_COMPILE_OPTS is only defined in dnsmasq.c */
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 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
|
||||
|
||||
+294
-228
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 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
|
||||
@@ -32,6 +32,7 @@ static volatile int pipewrite;
|
||||
static void set_dns_listeners(void);
|
||||
static void set_tftp_listeners(void);
|
||||
static void check_dns_listeners(time_t now);
|
||||
static void do_tcp_connection(struct listener *listener, time_t now, int slot);
|
||||
static void sig_handler(int sig);
|
||||
static void async_event(int pipe, time_t now);
|
||||
static void fatal_event(struct event_desc *ev, char *msg);
|
||||
@@ -61,6 +62,7 @@ int main (int argc, char **argv)
|
||||
int need_cap_net_admin = 0;
|
||||
int need_cap_net_raw = 0;
|
||||
int need_cap_net_bind_service = 0;
|
||||
int have_cap_chown = 0;
|
||||
char *bound_device = NULL;
|
||||
int did_bind = 0;
|
||||
struct server *serv;
|
||||
@@ -556,6 +558,8 @@ int main (int argc, char **argv)
|
||||
data = safe_malloc(sizeof(*data) * capsize);
|
||||
capget(hdr, data); /* Get current values, for verification */
|
||||
|
||||
have_cap_chown = data->permitted & (1 << CAP_CHOWN);
|
||||
|
||||
if (need_cap_net_admin && !(data->permitted & (1 << CAP_NET_ADMIN)))
|
||||
fail = "NET_ADMIN";
|
||||
else if (need_cap_net_raw && !(data->permitted & (1 << CAP_NET_RAW)))
|
||||
@@ -869,7 +873,14 @@ int main (int argc, char **argv)
|
||||
my_syslog(LOG_INFO, _("compile time options: %s"), compile_opts);
|
||||
|
||||
if (chown_warn != 0)
|
||||
my_syslog(LOG_WARNING, "chown of PID file %s failed: %s", daemon->runfile, strerror(chown_warn));
|
||||
{
|
||||
#if defined(HAVE_LINUX_NETWORK)
|
||||
if (chown_warn == EPERM && !have_cap_chown)
|
||||
my_syslog(LOG_INFO, "chown of PID file %s failed: please add capability CAP_CHOWN", daemon->runfile);
|
||||
else
|
||||
#endif
|
||||
my_syslog(LOG_WARNING, "chown of PID file %s failed: %s", daemon->runfile, strerror(chown_warn));
|
||||
}
|
||||
|
||||
#ifdef HAVE_DBUS
|
||||
if (option_bool(OPT_DBUS))
|
||||
@@ -951,7 +962,14 @@ int main (int argc, char **argv)
|
||||
my_syslog(LOG_WARNING, _("warning: ignoring resolv-file flag because no-resolv is set"));
|
||||
daemon->resolv_files = NULL;
|
||||
if (!daemon->servers)
|
||||
my_syslog(LOG_WARNING, _("warning: no upstream servers configured"));
|
||||
{
|
||||
#ifdef HAVE_DBUS
|
||||
if (option_bool(OPT_DBUS))
|
||||
my_syslog(LOG_INFO, _("no upstream servers configured - please set them from DBus"));
|
||||
else
|
||||
#endif
|
||||
my_syslog(LOG_WARNING, _("warning: no upstream servers configured"));
|
||||
}
|
||||
}
|
||||
|
||||
if (daemon->max_logs != 0)
|
||||
@@ -1466,7 +1484,7 @@ static void async_event(int pipe, time_t now)
|
||||
{
|
||||
pid_t p;
|
||||
struct event_desc ev;
|
||||
int i, check = 0;
|
||||
int wstatus, i, check = 0;
|
||||
char *msg;
|
||||
|
||||
/* NOTE: the memory used to return msg is leaked: use msgs in events only
|
||||
@@ -1529,7 +1547,7 @@ static void async_event(int pipe, time_t now)
|
||||
|
||||
case EVENT_CHILD:
|
||||
/* See Stevens 5.10 */
|
||||
while ((p = waitpid(-1, NULL, WNOHANG)) != 0)
|
||||
while ((p = waitpid(-1, &wstatus, WNOHANG)) != 0)
|
||||
if (p == -1)
|
||||
{
|
||||
if (errno != EINTR)
|
||||
@@ -1540,6 +1558,20 @@ static void async_event(int pipe, time_t now)
|
||||
if (daemon->tcp_pids[i] == p)
|
||||
{
|
||||
daemon->tcp_pids[i] = 0;
|
||||
|
||||
if (!WIFEXITED(wstatus))
|
||||
{
|
||||
/* If a helper process dies, (eg with SIGSEV)
|
||||
log that and attempt to patch things up so that the
|
||||
parent can continue to function. */
|
||||
my_syslog(LOG_WARNING, _("TCP helper process %u died unexpectedly"), (unsigned int)p);
|
||||
if (daemon->tcp_pipes[i] != -1)
|
||||
{
|
||||
close(daemon->tcp_pipes[i]);
|
||||
daemon->tcp_pipes[i] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* tcp_pipes == -1 && tcp_pids == 0 required to free slot */
|
||||
if (daemon->tcp_pipes[i] == -1)
|
||||
daemon->metrics[METRIC_TCP_CONNECTIONS]--;
|
||||
@@ -1822,247 +1854,281 @@ static void check_dns_listeners(time_t now)
|
||||
struct listener *listener;
|
||||
struct randfd_list *rfl;
|
||||
int i;
|
||||
int pipefd[2];
|
||||
|
||||
/* Note that handling events here can create or destroy fds and
|
||||
render the result of the last poll() call invalid. Once
|
||||
we find an fd that needs service, do it, then return to go around the
|
||||
poll() loop again. This avoid really, really, wierd bugs. */
|
||||
|
||||
if (!option_bool(OPT_DEBUG))
|
||||
for (i = 0; i < daemon->max_procs; i++)
|
||||
if (daemon->tcp_pipes[i] != -1 &&
|
||||
poll_check(daemon->tcp_pipes[i], POLLIN | POLLHUP))
|
||||
{
|
||||
/* 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
|
||||
process, and tcp_pipes to -1 and close the FD when we read the last
|
||||
of the data - indicated by cache_recv_insert returning zero.
|
||||
The order of these events is indeterminate, and both are needed
|
||||
to free the process slot. Once the child process has gone, poll()
|
||||
returns POLLHUP, not POLLIN, so have to check for both here. */
|
||||
if (!cache_recv_insert(now, daemon->tcp_pipes[i]))
|
||||
{
|
||||
close(daemon->tcp_pipes[i]);
|
||||
daemon->tcp_pipes[i] = -1;
|
||||
/* tcp_pipes == -1 && tcp_pids == 0 required to free slot */
|
||||
if (daemon->tcp_pids[i] == 0)
|
||||
daemon->metrics[METRIC_TCP_CONNECTIONS]--;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
|
||||
if (poll_check(serverfdp->fd, POLLIN))
|
||||
reply_query(serverfdp->fd, now);
|
||||
{
|
||||
reply_query(serverfdp->fd, now);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < daemon->numrrand; i++)
|
||||
if (daemon->randomsocks[i].refcount != 0 &&
|
||||
poll_check(daemon->randomsocks[i].fd, POLLIN))
|
||||
reply_query(daemon->randomsocks[i].fd, now);
|
||||
|
||||
{
|
||||
reply_query(daemon->randomsocks[i].fd, now);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check overflow random sockets too. */
|
||||
for (rfl = daemon->rfl_poll; rfl; rfl = rfl->next)
|
||||
if (poll_check(rfl->rfd->fd, POLLIN))
|
||||
reply_query(rfl->rfd->fd, now);
|
||||
|
||||
/* Races. The child process can die before we read all of the data from the
|
||||
pipe, or vice versa. Therefore send tcp_pids to zero when we wait() the
|
||||
process, and tcp_pipes to -1 and close the FD when we read the last
|
||||
of the data - indicated by cache_recv_insert returning zero.
|
||||
The order of these events is indeterminate, and both are needed
|
||||
to free the process slot. Once the child process has gone, poll()
|
||||
returns POLLHUP, not POLLIN, so have to check for both here. */
|
||||
if (!option_bool(OPT_DEBUG))
|
||||
for (i = 0; i < daemon->max_procs; i++)
|
||||
if (daemon->tcp_pipes[i] != -1 &&
|
||||
poll_check(daemon->tcp_pipes[i], POLLIN | POLLHUP) &&
|
||||
!cache_recv_insert(now, daemon->tcp_pipes[i]))
|
||||
{
|
||||
close(daemon->tcp_pipes[i]);
|
||||
daemon->tcp_pipes[i] = -1;
|
||||
/* tcp_pipes == -1 && tcp_pids == 0 required to free slot */
|
||||
if (daemon->tcp_pids[i] == 0)
|
||||
daemon->metrics[METRIC_TCP_CONNECTIONS]--;
|
||||
}
|
||||
|
||||
{
|
||||
reply_query(rfl->rfd->fd, now);
|
||||
return;
|
||||
}
|
||||
|
||||
for (listener = daemon->listeners; listener; listener = listener->next)
|
||||
{
|
||||
if (listener->fd != -1 && poll_check(listener->fd, POLLIN))
|
||||
if (listener->fd != -1 && poll_check(listener->fd, POLLIN))
|
||||
{
|
||||
receive_query(listener, now);
|
||||
|
||||
/* 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 = daemon->max_procs - 1; i >= 0; i--)
|
||||
if (daemon->tcp_pids[i] == 0 && daemon->tcp_pipes[i] == -1)
|
||||
break;
|
||||
return;
|
||||
}
|
||||
|
||||
/* 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 = daemon->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))
|
||||
if (i >= 0)
|
||||
for (listener = daemon->listeners; listener; listener = listener->next)
|
||||
if (listener->tcpfd != -1 && poll_check(listener->tcpfd, POLLIN))
|
||||
{
|
||||
int confd, client_ok = 1;
|
||||
struct irec *iface = NULL;
|
||||
pid_t p;
|
||||
union mysockaddr tcp_addr;
|
||||
socklen_t tcp_len = sizeof(union mysockaddr);
|
||||
do_tcp_connection(listener, now, i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
while ((confd = accept(listener->tcpfd, NULL, NULL)) == -1 && errno == EINTR);
|
||||
|
||||
if (confd == -1)
|
||||
continue;
|
||||
|
||||
if (getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) == -1)
|
||||
{
|
||||
close(confd);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Make sure that the interface list is up-to-date.
|
||||
|
||||
We do this here as we may need the results below, and
|
||||
the DNS code needs them for --interface-name stuff.
|
||||
static void do_tcp_connection(struct listener *listener, time_t now, int slot)
|
||||
{
|
||||
int confd, client_ok = 1;
|
||||
struct irec *iface = NULL;
|
||||
pid_t p;
|
||||
union mysockaddr tcp_addr;
|
||||
socklen_t tcp_len = sizeof(union mysockaddr);
|
||||
unsigned char a = 0, *buff;
|
||||
struct server *s;
|
||||
int flags, auth_dns;
|
||||
struct in_addr netmask;
|
||||
int pipefd[2];
|
||||
|
||||
Multiple calls to enumerate_interfaces() per select loop are
|
||||
inhibited, so calls to it in the child process (which doesn't select())
|
||||
have no effect. This avoids two processes reading from the same
|
||||
netlink fd and screwing the pooch entirely.
|
||||
*/
|
||||
|
||||
enumerate_interfaces(0);
|
||||
while ((confd = accept(listener->tcpfd, NULL, NULL)) == -1 && errno == EINTR);
|
||||
|
||||
if (confd == -1)
|
||||
return;
|
||||
|
||||
if (getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) == -1)
|
||||
{
|
||||
closeconandreturn:
|
||||
shutdown(confd, SHUT_RDWR);
|
||||
close(confd);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Make sure that the interface list is up-to-date.
|
||||
|
||||
We do this here as we may need the results below, and
|
||||
the DNS code needs them for --interface-name stuff.
|
||||
|
||||
Multiple calls to enumerate_interfaces() per select loop are
|
||||
inhibited, so calls to it in the child process (which doesn't select())
|
||||
have no effect. This avoids two processes reading from the same
|
||||
netlink fd and screwing the pooch entirely.
|
||||
*/
|
||||
|
||||
enumerate_interfaces(0);
|
||||
|
||||
if (option_bool(OPT_NOWILD))
|
||||
iface = listener->iface; /* May be NULL */
|
||||
else
|
||||
{
|
||||
int if_index;
|
||||
char intr_name[IF_NAMESIZE];
|
||||
|
||||
/* if we can find the arrival interface, check it's one that's allowed */
|
||||
if ((if_index = tcp_interface(confd, tcp_addr.sa.sa_family)) != 0 &&
|
||||
indextoname(listener->tcpfd, if_index, intr_name))
|
||||
{
|
||||
union all_addr addr;
|
||||
|
||||
if (option_bool(OPT_NOWILD))
|
||||
iface = listener->iface; /* May be NULL */
|
||||
else
|
||||
{
|
||||
int if_index;
|
||||
char intr_name[IF_NAMESIZE];
|
||||
|
||||
/* if we can find the arrival interface, check it's one that's allowed */
|
||||
if ((if_index = tcp_interface(confd, tcp_addr.sa.sa_family)) != 0 &&
|
||||
indextoname(listener->tcpfd, if_index, intr_name))
|
||||
{
|
||||
union all_addr addr;
|
||||
|
||||
if (tcp_addr.sa.sa_family == AF_INET6)
|
||||
addr.addr6 = tcp_addr.in6.sin6_addr;
|
||||
else
|
||||
addr.addr4 = tcp_addr.in.sin_addr;
|
||||
|
||||
for (iface = daemon->interfaces; iface; iface = iface->next)
|
||||
if (iface->index == if_index &&
|
||||
iface->addr.sa.sa_family == tcp_addr.sa.sa_family)
|
||||
break;
|
||||
|
||||
if (!iface && !loopback_exception(listener->tcpfd, tcp_addr.sa.sa_family, &addr, intr_name))
|
||||
client_ok = 0;
|
||||
}
|
||||
|
||||
if (option_bool(OPT_CLEVERBIND))
|
||||
iface = listener->iface; /* May be NULL */
|
||||
else
|
||||
{
|
||||
/* Check for allowed interfaces when binding the wildcard address:
|
||||
we do this by looking for an interface with the same address as
|
||||
the local address of the TCP connection, then looking to see if that's
|
||||
an allowed interface. As a side effect, we get the netmask of the
|
||||
interface too, for localisation. */
|
||||
|
||||
for (iface = daemon->interfaces; iface; iface = iface->next)
|
||||
if (sockaddr_isequal(&iface->addr, &tcp_addr))
|
||||
break;
|
||||
|
||||
if (!iface)
|
||||
client_ok = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!client_ok)
|
||||
{
|
||||
shutdown(confd, SHUT_RDWR);
|
||||
close(confd);
|
||||
}
|
||||
else if (!option_bool(OPT_DEBUG) && pipe(pipefd) == 0 && (p = fork()) != 0)
|
||||
{
|
||||
close(pipefd[1]); /* parent needs read pipe end. */
|
||||
if (p == -1)
|
||||
close(pipefd[0]);
|
||||
else
|
||||
{
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
/* The child process inherits the netlink socket,
|
||||
which it never uses, but when the parent (us)
|
||||
uses it in the future, the answer may go to the
|
||||
child, resulting in the parent blocking
|
||||
forever awaiting the result. To avoid this
|
||||
the child closes the netlink socket, but there's
|
||||
a nasty race, since the parent may use netlink
|
||||
before the child has done the close.
|
||||
|
||||
To avoid this, the parent blocks here until a
|
||||
single byte comes back up the pipe, which
|
||||
is sent by the child after it has closed the
|
||||
netlink socket. */
|
||||
|
||||
unsigned char a;
|
||||
read_write(pipefd[0], &a, 1, RW_READ);
|
||||
#endif
|
||||
|
||||
/* i holds index of free slot */
|
||||
daemon->tcp_pids[i] = p;
|
||||
daemon->tcp_pipes[i] = pipefd[0];
|
||||
daemon->metrics[METRIC_TCP_CONNECTIONS]++;
|
||||
if (daemon->metrics[METRIC_TCP_CONNECTIONS] > daemon->max_procs_used)
|
||||
daemon->max_procs_used = daemon->metrics[METRIC_TCP_CONNECTIONS];
|
||||
}
|
||||
close(confd);
|
||||
|
||||
/* The child can use up to TCP_MAX_QUERIES ids, so skip that many. */
|
||||
daemon->log_id += TCP_MAX_QUERIES;
|
||||
#ifdef HAVE_DNSSEC
|
||||
/* It can do more if making DNSSEC queries too. */
|
||||
if (option_bool(OPT_DNSSEC_VALID))
|
||||
daemon->log_id += daemon->limit[LIMIT_WORK];
|
||||
#endif
|
||||
}
|
||||
if (tcp_addr.sa.sa_family == AF_INET6)
|
||||
addr.addr6 = tcp_addr.in6.sin6_addr;
|
||||
else
|
||||
{
|
||||
unsigned char *buff;
|
||||
struct server *s;
|
||||
int flags;
|
||||
struct in_addr netmask;
|
||||
int auth_dns;
|
||||
|
||||
if (iface)
|
||||
{
|
||||
netmask = iface->netmask;
|
||||
auth_dns = iface->dns_auth;
|
||||
}
|
||||
else
|
||||
{
|
||||
netmask.s_addr = 0;
|
||||
auth_dns = 0;
|
||||
}
|
||||
|
||||
/* Arrange for SIGALRM after CHILD_LIFETIME seconds to
|
||||
terminate the process. */
|
||||
if (!option_bool(OPT_DEBUG))
|
||||
{
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
/* See comment above re: netlink socket. */
|
||||
unsigned char a = 0;
|
||||
|
||||
close(daemon->netlinkfd);
|
||||
read_write(pipefd[1], &a, 1, RW_WRITE);
|
||||
#endif
|
||||
alarm(CHILD_LIFETIME);
|
||||
close(pipefd[0]); /* close read end in child. */
|
||||
daemon->pipe_to_parent = pipefd[1];
|
||||
}
|
||||
|
||||
/* The connected socket inherits non-blocking
|
||||
attribute from the listening socket.
|
||||
Reset that here. */
|
||||
if ((flags = fcntl(confd, F_GETFL, 0)) != -1)
|
||||
while(retry_send(fcntl(confd, F_SETFL, flags & ~O_NONBLOCK)));
|
||||
|
||||
buff = tcp_request(confd, now, &tcp_addr, netmask, auth_dns);
|
||||
|
||||
if (buff)
|
||||
free(buff);
|
||||
|
||||
for (s = daemon->servers; s; s = s->next)
|
||||
if (s->tcpfd != -1)
|
||||
{
|
||||
shutdown(s->tcpfd, SHUT_RDWR);
|
||||
close(s->tcpfd);
|
||||
s->tcpfd = -1;
|
||||
}
|
||||
|
||||
if (!option_bool(OPT_DEBUG))
|
||||
{
|
||||
close(daemon->pipe_to_parent);
|
||||
flush_log();
|
||||
_exit(0);
|
||||
}
|
||||
}
|
||||
addr.addr4 = tcp_addr.in.sin_addr;
|
||||
|
||||
for (iface = daemon->interfaces; iface; iface = iface->next)
|
||||
if (iface->index == if_index &&
|
||||
iface->addr.sa.sa_family == tcp_addr.sa.sa_family)
|
||||
break;
|
||||
|
||||
if (!iface && !loopback_exception(listener->tcpfd, tcp_addr.sa.sa_family, &addr, intr_name))
|
||||
client_ok = 0;
|
||||
}
|
||||
|
||||
if (option_bool(OPT_CLEVERBIND))
|
||||
iface = listener->iface; /* May be NULL */
|
||||
else
|
||||
{
|
||||
/* Check for allowed interfaces when binding the wildcard address:
|
||||
we do this by looking for an interface with the same address as
|
||||
the local address of the TCP connection, then looking to see if that's
|
||||
an allowed interface. As a side effect, we get the netmask of the
|
||||
interface too, for localisation. */
|
||||
|
||||
for (iface = daemon->interfaces; iface; iface = iface->next)
|
||||
if (sockaddr_isequal(&iface->addr, &tcp_addr))
|
||||
break;
|
||||
|
||||
if (!iface)
|
||||
client_ok = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!client_ok)
|
||||
goto closeconandreturn;
|
||||
|
||||
if (!option_bool(OPT_DEBUG))
|
||||
{
|
||||
if (pipe(pipefd) == -1)
|
||||
goto closeconandreturn; /* pipe failed */
|
||||
|
||||
if ((p = fork()) == -1)
|
||||
{
|
||||
/* fork failed */
|
||||
close(pipefd[0]);
|
||||
close(pipefd[1]);
|
||||
goto closeconandreturn;
|
||||
}
|
||||
|
||||
if (p != 0)
|
||||
{
|
||||
/* fork() done: parent side */
|
||||
close(pipefd[1]); /* parent needs read pipe end. */
|
||||
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
/* The child process inherits the netlink socket,
|
||||
which it never uses, but when the parent (us)
|
||||
uses it in the future, the answer may go to the
|
||||
child, resulting in the parent blocking
|
||||
forever awaiting the result. To avoid this
|
||||
the child closes the netlink socket, but there's
|
||||
a nasty race, since the parent may use netlink
|
||||
before the child has done the close.
|
||||
|
||||
To avoid this, the parent blocks here until a
|
||||
single byte comes back up the pipe, which
|
||||
is sent by the child after it has closed the
|
||||
netlink socket. */
|
||||
|
||||
read_write(pipefd[0], &a, 1, RW_READ);
|
||||
#endif
|
||||
|
||||
|
||||
daemon->tcp_pids[slot] = p;
|
||||
daemon->tcp_pipes[slot] = pipefd[0];
|
||||
daemon->metrics[METRIC_TCP_CONNECTIONS]++;
|
||||
if (daemon->metrics[METRIC_TCP_CONNECTIONS] > daemon->max_procs_used)
|
||||
daemon->max_procs_used = daemon->metrics[METRIC_TCP_CONNECTIONS];
|
||||
|
||||
close(confd);
|
||||
|
||||
/* The child can use up to TCP_MAX_QUERIES ids, so skip that many. */
|
||||
daemon->log_id += TCP_MAX_QUERIES;
|
||||
#ifdef HAVE_DNSSEC
|
||||
/* It can do more if making DNSSEC queries too. */
|
||||
if (option_bool(OPT_DNSSEC_VALID))
|
||||
daemon->log_id += daemon->limit[LIMIT_WORK];
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (iface)
|
||||
{
|
||||
netmask = iface->netmask;
|
||||
auth_dns = iface->dns_auth;
|
||||
}
|
||||
else
|
||||
{
|
||||
netmask.s_addr = 0;
|
||||
auth_dns = 0;
|
||||
}
|
||||
|
||||
/* Arrange for SIGALRM after CHILD_LIFETIME seconds to
|
||||
terminate the process. */
|
||||
if (!option_bool(OPT_DEBUG))
|
||||
{
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
/* See comment above re: netlink socket. */
|
||||
close(daemon->netlinkfd);
|
||||
read_write(pipefd[1], &a, 1, RW_WRITE);
|
||||
#endif
|
||||
alarm(CHILD_LIFETIME);
|
||||
close(pipefd[0]); /* close read end in child. */
|
||||
daemon->pipe_to_parent = pipefd[1];
|
||||
}
|
||||
|
||||
/* The connected socket inherits non-blocking
|
||||
attribute from the listening socket.
|
||||
Reset that here. */
|
||||
if ((flags = fcntl(confd, F_GETFL, 0)) != -1)
|
||||
while(retry_send(fcntl(confd, F_SETFL, flags & ~O_NONBLOCK)));
|
||||
|
||||
buff = tcp_request(confd, now, &tcp_addr, netmask, auth_dns);
|
||||
|
||||
if (buff)
|
||||
free(buff);
|
||||
|
||||
for (s = daemon->servers; s; s = s->next)
|
||||
if (s->tcpfd != -1)
|
||||
{
|
||||
shutdown(s->tcpfd, SHUT_RDWR);
|
||||
close(s->tcpfd);
|
||||
s->tcpfd = -1;
|
||||
}
|
||||
|
||||
if (!option_bool(OPT_DEBUG))
|
||||
{
|
||||
close(daemon->pipe_to_parent);
|
||||
flush_log();
|
||||
_exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
/* If a DNSSEC query over UDP returns a truncated answer,
|
||||
we swap to the TCP path. This routine is responsible for forking
|
||||
@@ -2091,11 +2157,11 @@ int swap_to_tcp(struct frec *forward, time_t now, int status, struct dns_header
|
||||
if (daemon->tcp_pids[i] == 0 && daemon->tcp_pipes[i] == -1)
|
||||
break;
|
||||
|
||||
/* No slots */
|
||||
if (i < 0)
|
||||
/* No slots or no pipe */
|
||||
if (i < 0 || pipe(pipefd) != 0)
|
||||
return STAT_ABANDONED;
|
||||
|
||||
if (pipe(pipefd) == 0 && (p = fork()) != 0)
|
||||
|
||||
if ((p = fork()) != 0)
|
||||
{
|
||||
close(pipefd[1]); /* parent needs read pipe end. */
|
||||
if (p == -1)
|
||||
|
||||
+16
-9
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -14,7 +14,7 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define COPYRIGHT "Copyright (c) 2000-2024 Simon Kelley"
|
||||
#define COPYRIGHT "Copyright (c) 2000-2025 Simon Kelley"
|
||||
|
||||
/* We do defines that influence behavior of stdio.h, so complain
|
||||
if included too early. */
|
||||
@@ -279,7 +279,9 @@ struct event_desc {
|
||||
#define OPT_CACHE_RR 71
|
||||
#define OPT_LOCALHOST_SERVICE 72
|
||||
#define OPT_LOG_PROTO 73
|
||||
#define OPT_LAST 74
|
||||
#define OPT_NO_0x20 74
|
||||
#define OPT_DO_0x20 75
|
||||
#define OPT_LAST 76
|
||||
|
||||
#define OPTION_BITS (sizeof(unsigned int)*8)
|
||||
#define OPTION_SIZE ( (OPT_LAST/OPTION_BITS)+((OPT_LAST%OPTION_BITS)!=0) )
|
||||
@@ -772,15 +774,15 @@ struct dyndir {
|
||||
#define FREC_DS_QUERY 16
|
||||
#define FREC_AD_QUESTION 32
|
||||
#define FREC_DO_QUESTION 64
|
||||
#define FREC_ADDED_PHEADER 128
|
||||
#define FREC_HAS_PHEADER 256
|
||||
#define FREC_GONE_TO_TCP 512
|
||||
#define FREC_HAS_PHEADER 128
|
||||
#define FREC_GONE_TO_TCP 256
|
||||
#define FREC_ANSWER 512
|
||||
|
||||
struct frec {
|
||||
struct frec_src {
|
||||
union mysockaddr source;
|
||||
union all_addr dest;
|
||||
unsigned int iface, log_id;
|
||||
unsigned int iface, log_id, encode_bitmap, *encode_bigmap;
|
||||
int fd;
|
||||
unsigned short orig_id, udp_pkt_size;
|
||||
struct frec_src *next;
|
||||
@@ -1387,7 +1389,7 @@ int is_rev_synth(int flag, union all_addr *addr, char *name);
|
||||
/* rfc1035.c */
|
||||
int do_doctor(struct dns_header *header, size_t qlen, char *namebuff);
|
||||
int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
|
||||
char *name, int isExtract, int extrabytes);
|
||||
char *name, int func, unsigned int parm);
|
||||
unsigned char *skip_name(unsigned char *ansp, struct dns_header *header, size_t plen, int extrabytes);
|
||||
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);
|
||||
@@ -1402,7 +1404,7 @@ void report_addresses(struct dns_header *header, size_t len, u32 mark);
|
||||
#endif
|
||||
size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
struct in_addr local_addr, struct in_addr local_netmask,
|
||||
time_t now, int ad_reqd, int do_bit, int *stale, int *filtered);
|
||||
time_t now, int ad_reqd, int do_bit, int no_cache, int *stale, int *filtered);
|
||||
int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
|
||||
time_t now);
|
||||
int check_for_ignored_address(struct dns_header *header, size_t qlen);
|
||||
@@ -1414,6 +1416,11 @@ int add_resource_record(struct dns_header *header, char *limit, int *truncp,
|
||||
int *offset, unsigned short type, unsigned short class, char *format, ...);
|
||||
int in_arpa_name_2_addr(char *namein, union all_addr *addrp);
|
||||
int private_net(struct in_addr addr, int ban_localhost);
|
||||
/* extract_name ops */
|
||||
#define EXTR_NAME_EXTRACT 1
|
||||
#define EXTR_NAME_COMPARE 2
|
||||
#define EXTR_NAME_NOCASE 3
|
||||
#define EXTR_NAME_FLIP 4
|
||||
|
||||
/* auth.c */
|
||||
#ifdef HAVE_AUTH
|
||||
|
||||
+24
-24
@@ -1,5 +1,5 @@
|
||||
/* dnssec.c is Copyright (c) 2012 Giovanni Bajo <rasky@develer.com>
|
||||
and Copyright (c) 2012-2023 Simon Kelley
|
||||
and Copyright (c) 2012-2025 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
|
||||
@@ -191,7 +191,7 @@ static int get_rdata(struct dns_header *header, size_t plen, struct rdata_state
|
||||
/* domain-name, canonicalise */
|
||||
int len;
|
||||
|
||||
if (!extract_name(header, plen, &state->ip, state->buff, 1, 0) ||
|
||||
if (!extract_name(header, plen, &state->ip, state->buff, EXTR_NAME_EXTRACT, 0) ||
|
||||
(len = to_wire(state->buff)) == 0)
|
||||
continue;
|
||||
|
||||
@@ -339,7 +339,7 @@ static int explore_rrset(struct dns_header *header, size_t plen, int class, int
|
||||
|
||||
pstart = p;
|
||||
|
||||
if (!(res = extract_name(header, plen, &p, name, 0, 10)))
|
||||
if (!(res = extract_name(header, plen, &p, name, EXTR_NAME_COMPARE, 10)))
|
||||
return 0; /* bad packet */
|
||||
|
||||
GETSHORT(stype, p);
|
||||
@@ -374,14 +374,14 @@ static int explore_rrset(struct dns_header *header, size_t plen, int class, int
|
||||
if (gotkey)
|
||||
{
|
||||
/* If there's more than one SIG, ensure they all have same keyname */
|
||||
if (extract_name(header, plen, &p, keyname, 0, 0) != 1)
|
||||
if (extract_name(header, plen, &p, keyname, EXTR_NAME_COMPARE, 0) != 1)
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
gotkey = 1;
|
||||
|
||||
if (!extract_name(header, plen, &p, keyname, 1, 0))
|
||||
if (!extract_name(header, plen, &p, keyname, EXTR_NAME_EXTRACT, 0))
|
||||
return 0;
|
||||
|
||||
/* RFC 4035 5.3.1 says that the Signer's Name field MUST equal
|
||||
@@ -503,7 +503,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
|
||||
GETLONG(sig_inception, p);
|
||||
GETSHORT(key_tag, p);
|
||||
|
||||
if (!extract_name(header, plen, &p, keyname, 1, 0))
|
||||
if (!extract_name(header, plen, &p, keyname, EXTR_NAME_EXTRACT, 0))
|
||||
return STAT_BOGUS;
|
||||
|
||||
if (!time_check)
|
||||
@@ -568,7 +568,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
|
||||
|
||||
p = rrset[i];
|
||||
|
||||
if (!extract_name(header, plen, &p, name, 1, 10))
|
||||
if (!extract_name(header, plen, &p, name, EXTR_NAME_EXTRACT, 10))
|
||||
return STAT_BOGUS;
|
||||
|
||||
name_start = name;
|
||||
@@ -661,7 +661,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
|
||||
|
||||
/* namebuff used for workspace above, restore to leave unchanged on exit */
|
||||
p = (unsigned char*)(rrset[0]);
|
||||
if (!extract_name(header, plen, &p, name, 1, 0))
|
||||
if (!extract_name(header, plen, &p, name, EXTR_NAME_EXTRACT, 0))
|
||||
return STAT_BOGUS;
|
||||
|
||||
if (key)
|
||||
@@ -727,7 +727,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
|
||||
static unsigned char **cached_digest;
|
||||
static size_t cached_digest_size = 0;
|
||||
|
||||
if (ntohs(header->qdcount) != 1 || RCODE(header) != NOERROR || !extract_name(header, plen, &p, name, 1, 4))
|
||||
if (ntohs(header->qdcount) != 1 || RCODE(header) != NOERROR || !extract_name(header, plen, &p, name, EXTR_NAME_EXTRACT, 4))
|
||||
return STAT_BOGUS | DNSSEC_FAIL_NOKEY;
|
||||
|
||||
GETSHORT(qtype, p);
|
||||
@@ -752,7 +752,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
|
||||
for (j = ntohs(header->ancount); j != 0; j--)
|
||||
{
|
||||
/* Ensure we have type, class TTL and length */
|
||||
if (!(rc = extract_name(header, plen, &p, name, 0, 10)))
|
||||
if (!(rc = extract_name(header, plen, &p, name, EXTR_NAME_COMPARE, 10)))
|
||||
return STAT_BOGUS; /* bad packet */
|
||||
|
||||
GETSHORT(qtype, p);
|
||||
@@ -904,7 +904,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
|
||||
for (j = ntohs(header->ancount); j != 0; j--)
|
||||
{
|
||||
/* Ensure we have type, class TTL and length */
|
||||
if (!(rc = extract_name(header, plen, &p, name, 0, 10)))
|
||||
if (!(rc = extract_name(header, plen, &p, name, EXTR_NAME_COMPARE, 10)))
|
||||
return STAT_BOGUS; /* bad packet */
|
||||
|
||||
GETSHORT(qtype, p);
|
||||
@@ -1024,7 +1024,7 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
|
||||
}
|
||||
|
||||
p = (unsigned char *)(header+1);
|
||||
if (!extract_name(header, plen, &p, name, 1, 4))
|
||||
if (!extract_name(header, plen, &p, name, EXTR_NAME_EXTRACT, 4))
|
||||
return STAT_BOGUS;
|
||||
|
||||
p += 4; /* qtype, qclass */
|
||||
@@ -1050,7 +1050,7 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
|
||||
{
|
||||
unsigned char *psave;
|
||||
|
||||
if (!(rc = extract_name(header, plen, &p, name, 0, 10)))
|
||||
if (!(rc = extract_name(header, plen, &p, name, EXTR_NAME_COMPARE, 10)))
|
||||
return STAT_BOGUS; /* bad packet */
|
||||
|
||||
GETSHORT(atype, p);
|
||||
@@ -1231,12 +1231,12 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
|
||||
int sig_labels, name_labels;
|
||||
|
||||
p = nsecs[i];
|
||||
if (!extract_name(header, plen, &p, workspace1, 1, 10))
|
||||
if (!extract_name(header, plen, &p, workspace1, EXTR_NAME_EXTRACT, 10))
|
||||
return DNSSEC_FAIL_BADPACKET;
|
||||
p += 8; /* class, type, TTL */
|
||||
GETSHORT(rdlen, p);
|
||||
psave = p;
|
||||
if (!extract_name(header, plen, &p, workspace2, 1, 0))
|
||||
if (!extract_name(header, plen, &p, workspace2, EXTR_NAME_EXTRACT, 0))
|
||||
return DNSSEC_FAIL_BADPACKET;
|
||||
|
||||
/* If NSEC comes from wildcard expansion, use original wildcard
|
||||
@@ -1400,7 +1400,7 @@ static int check_nsec3_coverage(struct dns_header *header, size_t plen, int dige
|
||||
for (i = 0; i < nsec_count; i++)
|
||||
if ((p = nsecs[i]))
|
||||
{
|
||||
if (!extract_name(header, plen, &p, workspace1, 1, 10) ||
|
||||
if (!extract_name(header, plen, &p, workspace1, EXTR_NAME_EXTRACT, 10) ||
|
||||
!(base32_len = base32_decode(workspace1, (unsigned char *)workspace2)))
|
||||
return 0;
|
||||
|
||||
@@ -1609,7 +1609,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
|
||||
for (i = 0; i < nsec_count; i++)
|
||||
if ((p = nsecs[i]))
|
||||
{
|
||||
if (!extract_name(header, plen, &p, workspace1, 1, 0))
|
||||
if (!extract_name(header, plen, &p, workspace1, EXTR_NAME_EXTRACT, 0))
|
||||
return DNSSEC_FAIL_BADPACKET;
|
||||
|
||||
if (!(base32_len = base32_decode(workspace1, (unsigned char *)workspace2)))
|
||||
@@ -1684,7 +1684,7 @@ static int prove_non_existence(struct dns_header *header, size_t plen, char *key
|
||||
{
|
||||
unsigned char *pstart = p;
|
||||
|
||||
if (!extract_name(header, plen, &p, daemon->workspacename, 1, 10))
|
||||
if (!extract_name(header, plen, &p, daemon->workspacename, EXTR_NAME_EXTRACT, 10))
|
||||
return DNSSEC_FAIL_BADPACKET;
|
||||
|
||||
GETSHORT(type, p);
|
||||
@@ -1735,7 +1735,7 @@ static int prove_non_existence(struct dns_header *header, size_t plen, char *key
|
||||
{
|
||||
unsigned char *psav;
|
||||
|
||||
if (!(res = extract_name(header, plen, &p1, daemon->workspacename, 0, 10)))
|
||||
if (!(res = extract_name(header, plen, &p1, daemon->workspacename, EXTR_NAME_COMPARE, 10)))
|
||||
return DNSSEC_FAIL_BADPACKET;
|
||||
|
||||
GETSHORT(type1, p1);
|
||||
@@ -1965,7 +1965,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
||||
targets[0] = p1;
|
||||
targetidx = 1;
|
||||
|
||||
if (!extract_name(header, plen, &p1, name, 1, 4))
|
||||
if (!extract_name(header, plen, &p1, name, EXTR_NAME_EXTRACT, 4))
|
||||
return STAT_BOGUS;
|
||||
|
||||
GETSHORT(qtype, p1);
|
||||
@@ -2003,7 +2003,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
||||
if (i != 0 && !ADD_RDLEN(header, p1, plen, rdlen1))
|
||||
return STAT_BOGUS;
|
||||
|
||||
if (!extract_name(header, plen, &p1, name, 1, 10))
|
||||
if (!extract_name(header, plen, &p1, name, EXTR_NAME_EXTRACT, 10))
|
||||
return STAT_BOGUS; /* bad packet */
|
||||
|
||||
GETSHORT(type1, p1);
|
||||
@@ -2018,7 +2018,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
||||
/* Check if we've done this RRset already */
|
||||
for (p2 = ans_start, j = 0; j < i; j++)
|
||||
{
|
||||
if (!(rc = extract_name(header, plen, &p2, name, 0, 10)))
|
||||
if (!(rc = extract_name(header, plen, &p2, name, EXTR_NAME_COMPARE, 10)))
|
||||
return STAT_BOGUS; /* bad packet */
|
||||
|
||||
GETSHORT(type2, p2);
|
||||
@@ -2115,7 +2115,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
||||
if ((p2 = targets[j]))
|
||||
{
|
||||
int rc1;
|
||||
if (!(rc1 = extract_name(header, plen, &p2, name, 0, 10)))
|
||||
if (!(rc1 = extract_name(header, plen, &p2, name, EXTR_NAME_COMPARE, 10)))
|
||||
return STAT_BOGUS; /* bad packet */
|
||||
|
||||
if (class1 == qclass && rc1 == 1 && (type1 == T_CNAME || type1 == qtype || qtype == T_ANY ))
|
||||
@@ -2149,7 +2149,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
||||
if (neganswer)
|
||||
*neganswer = 1;
|
||||
|
||||
if (!extract_name(header, plen, &p2, name, 1, 10))
|
||||
if (!extract_name(header, plen, &p2, name, EXTR_NAME_EXTRACT, 10))
|
||||
return STAT_BOGUS; /* bad packet */
|
||||
|
||||
/* NXDOMAIN or NODATA reply, unanswered question is (name, qclass, qtype) */
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
+30
-12
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 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
|
||||
@@ -417,21 +417,34 @@ static size_t add_source_addr(struct dns_header *header, size_t plen, unsigned c
|
||||
else if (option_bool(OPT_STRIP_ECS))
|
||||
replace = 2;
|
||||
else
|
||||
return plen;
|
||||
|
||||
{
|
||||
unsigned char *pheader;
|
||||
/* If we still think the data is cacheable, and we're not
|
||||
messing with EDNS client subnet ourselves, see if the client
|
||||
sent a client subnet. If so, mark the data as uncacheable */
|
||||
if (*cacheable &&
|
||||
(pheader = find_pseudoheader(header, plen, NULL, NULL, NULL, NULL)) &&
|
||||
!check_source(header, plen, pheader, NULL))
|
||||
*cacheable = 0;
|
||||
|
||||
return plen;
|
||||
}
|
||||
|
||||
return add_pseudoheader(header, plen, (unsigned char *)limit, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0, replace);
|
||||
}
|
||||
|
||||
int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer)
|
||||
{
|
||||
/* Section 9.2, Check that subnet option in reply matches. */
|
||||
/* Section 9.2, Check that subnet option (if any) in reply matches.
|
||||
if peer == NULL, this degrades to a check for the existence of and EDNS0 client-subnet option. */
|
||||
|
||||
int len, calc_len;
|
||||
struct subnet_opt opt;
|
||||
unsigned char *p;
|
||||
int code, i, rdlen;
|
||||
|
||||
calc_len = calc_subnet_opt(&opt, peer, NULL);
|
||||
if (peer)
|
||||
calc_len = calc_subnet_opt(&opt, peer, NULL);
|
||||
|
||||
if (!(p = skip_name(pseudoheader, header, plen, 10)))
|
||||
return 1;
|
||||
@@ -443,21 +456,26 @@ int check_source(struct dns_header *header, size_t plen, unsigned char *pseudohe
|
||||
return 1; /* bad packet */
|
||||
|
||||
/* check if option there */
|
||||
for (i = 0; i + 4 < rdlen; i += len + 4)
|
||||
for (i = 0; i + 4 < rdlen; i += len + 4)
|
||||
{
|
||||
GETSHORT(code, p);
|
||||
GETSHORT(len, p);
|
||||
if (code == EDNS0_OPTION_CLIENT_SUBNET)
|
||||
{
|
||||
/* make sure this doesn't mismatch. */
|
||||
opt.scope_netmask = p[3];
|
||||
if (len != calc_len || memcmp(p, &opt, len) != 0)
|
||||
if (peer)
|
||||
{
|
||||
/* make sure this doesn't mismatch. */
|
||||
opt.scope_netmask = p[3];
|
||||
if (len != calc_len || memcmp(p, &opt, len) != 0)
|
||||
return 0;
|
||||
}
|
||||
else if (((struct subnet_opt *)p)->source_netmask != 0)
|
||||
return 0;
|
||||
}
|
||||
p += len;
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* See https://docs.umbrella.com/umbrella-api/docs/identifying-dns-traffic for
|
||||
@@ -541,6 +559,6 @@ size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *l
|
||||
plen = add_umbrella_opt(header, plen, limit, source, cacheable);
|
||||
|
||||
plen = add_source_addr(header, plen, limit, source, cacheable);
|
||||
|
||||
|
||||
return plen;
|
||||
}
|
||||
+359
-161
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 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
|
||||
@@ -163,37 +163,22 @@ static int domain_no_rebind(char *domain)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
union all_addr *dst_addr, unsigned int dst_iface,
|
||||
struct dns_header *header, size_t plen, size_t replylimit, time_t now,
|
||||
struct frec *forward, int ad_reqd, int do_bit, int fast_retry)
|
||||
static void forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
union all_addr *dst_addr, unsigned int dst_iface,
|
||||
struct dns_header *header, size_t plen, size_t replylimit, time_t now,
|
||||
struct frec *forward, unsigned int fwd_flags, int fast_retry)
|
||||
{
|
||||
unsigned int flags = 0;
|
||||
unsigned int fwd_flags = 0;
|
||||
int is_dnssec = forward && (forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY));
|
||||
struct server *master;
|
||||
unsigned int gotname;
|
||||
int old_src = 0, old_reply = 0;
|
||||
int first, last, start = 0;
|
||||
int cacheable, forwarded = 0;
|
||||
unsigned char *oph;
|
||||
int forwarded = 0;
|
||||
int ede = EDE_UNSET;
|
||||
(void)do_bit;
|
||||
unsigned short rrtype;
|
||||
|
||||
gotname = extract_request(header, plen, daemon->namebuff, &rrtype);
|
||||
oph = find_pseudoheader(header, plen, NULL, NULL, NULL, NULL);
|
||||
|
||||
if (header->hb4 & HB4_CD)
|
||||
fwd_flags |= FREC_CHECKING_DISABLED;
|
||||
if (ad_reqd)
|
||||
fwd_flags |= FREC_AD_QUESTION;
|
||||
if (oph)
|
||||
fwd_flags |= FREC_HAS_PHEADER;
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (do_bit)
|
||||
fwd_flags |= FREC_DO_QUESTION;
|
||||
#endif
|
||||
|
||||
/* Check for retry on existing query.
|
||||
FREC_DNSKEY and FREC_DS_QUERY are never set in flags, so the test below
|
||||
@@ -205,15 +190,25 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
{
|
||||
old_src = 1;
|
||||
old_reply = 1;
|
||||
fwd_flags = forward->flags;
|
||||
}
|
||||
else if (gotname && (forward = lookup_frec(daemon->namebuff, C_IN, (int)rrtype, -1, fwd_flags,
|
||||
FREC_CHECKING_DISABLED | FREC_AD_QUESTION | FREC_DO_QUESTION |
|
||||
FREC_HAS_PHEADER | FREC_DNSKEY_QUERY | FREC_DS_QUERY | FREC_NO_CACHE)))
|
||||
{
|
||||
struct frec_src *src;
|
||||
unsigned int casediff = 0;
|
||||
unsigned int *bitvector = NULL;
|
||||
unsigned short id = ntohs(header->id); /* Retrieve the id from the new query before we overwrite it. */
|
||||
|
||||
/* Get the case-scambled version of the query to resend. This is important because we
|
||||
may fall through below and forward the query in the packet buffer again and we
|
||||
want to use the same case scrambling as the first time. */
|
||||
blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
|
||||
plen = forward->stash_len;
|
||||
|
||||
for (src = &forward->frec_src; src; src = src->next)
|
||||
if (src->orig_id == ntohs(header->id) &&
|
||||
if (src->orig_id == id &&
|
||||
sockaddr_isequal(&src->source, udpaddr))
|
||||
break;
|
||||
|
||||
@@ -235,6 +230,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
{
|
||||
daemon->frec_src_count++;
|
||||
daemon->free_frec_src->next = NULL;
|
||||
daemon->free_frec_src->encode_bigmap = NULL;
|
||||
}
|
||||
|
||||
/* If we've been spammed with many duplicates, return REFUSED. */
|
||||
@@ -251,24 +247,79 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
free_frec(forward);
|
||||
goto reply;
|
||||
}
|
||||
|
||||
/* Find a bitmap of case differences between the query sent upstream and this one,
|
||||
so we can reply to each query with the correct case pattern.
|
||||
Since we need this to get back the exact case pattern of each query when doing
|
||||
query combining, we have to handle the (rare) case that there are case differences
|
||||
beyond the first 32 letters.
|
||||
If that happens we have to allocate memory to save it, and the casediff variable
|
||||
holds the length of that array.
|
||||
Mismatches beyond 32 letters are rare because most queries are all lowercase and
|
||||
we only scramble the first 32 letters for security reasons.
|
||||
|
||||
Note the two names are guaranteed to be the same length and differ only in the case
|
||||
of letters at this point.
|
||||
|
||||
The original query we sent is now in packet buffer and the query name in the
|
||||
new instance is on daemon->namebuff. */
|
||||
|
||||
if (extract_request(header, forward->stash_len, daemon->workspacename, NULL))
|
||||
{
|
||||
unsigned int i, gobig = 0;
|
||||
char *s1, *s2;
|
||||
|
||||
#define BITS_IN_INT (sizeof(unsigned int) * 8)
|
||||
|
||||
big_redo:
|
||||
for (s1 = daemon->namebuff, s2 = daemon->workspacename, i = 0; *s1; s1++, s2++)
|
||||
{
|
||||
char c1 = *s1, c2 = *s2;
|
||||
|
||||
if ((c1 >= 'a' && c1 <= 'z') || (c1 >= 'A' && c1 <= 'Z'))
|
||||
{
|
||||
if ((c1 & 0x20) ^ (c2 & 0x20))
|
||||
{
|
||||
if (bitvector)
|
||||
bitvector[i/BITS_IN_INT] |= 1<<(i%BITS_IN_INT);
|
||||
else if (i >= BITS_IN_INT)
|
||||
gobig = 1; /* More than 32 */
|
||||
else
|
||||
casediff |= 1<<i;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
if (gobig && !bitvector)
|
||||
{
|
||||
casediff = ((i - 1)/BITS_IN_INT) + 1; /* length of array */
|
||||
/* whine_malloc() zeros memory */
|
||||
if ((bitvector = whine_malloc(casediff * sizeof(unsigned int))))
|
||||
goto big_redo;
|
||||
}
|
||||
}
|
||||
|
||||
src = daemon->free_frec_src;
|
||||
daemon->free_frec_src = src->next;
|
||||
src->next = forward->frec_src.next;
|
||||
forward->frec_src.next = src;
|
||||
src->orig_id = ntohs(header->id);
|
||||
src->orig_id = id;
|
||||
src->source = *udpaddr;
|
||||
src->dest = *dst_addr;
|
||||
src->log_id = daemon->log_id;
|
||||
src->iface = dst_iface;
|
||||
src->fd = udpfd;
|
||||
src->encode_bitmap = casediff;
|
||||
src->encode_bigmap = bitvector;
|
||||
|
||||
src->udp_pkt_size = (unsigned short)replylimit;
|
||||
|
||||
/* closely spaced identical queries cannot be a try and a retry, so
|
||||
it's safe to wait for the reply from the first without
|
||||
forwarding the second. */
|
||||
if (difftime(now, forward->time) < 2)
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* use our id when resending */
|
||||
@@ -278,6 +329,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
/* new query */
|
||||
if (!forward)
|
||||
{
|
||||
unsigned char *p;
|
||||
|
||||
if (OPCODE(header) != QUERY)
|
||||
{
|
||||
flags = F_RCODE;
|
||||
@@ -301,8 +354,6 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
flags = 0;
|
||||
}
|
||||
|
||||
master = daemon->serverarray[first];
|
||||
|
||||
/* don't forward A or AAAA queries for simple names, except the empty name */
|
||||
if (!flags &&
|
||||
option_bool(OPT_NODOTS_LOCAL) &&
|
||||
@@ -314,18 +365,15 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
/* Configured answer. */
|
||||
if (flags || ede == EDE_NOT_READY)
|
||||
goto reply;
|
||||
|
||||
|
||||
master = daemon->serverarray[first];
|
||||
|
||||
if (!(forward = get_new_frec(now, master, 0)))
|
||||
goto reply;
|
||||
/* table full - flags == 0, return REFUSED */
|
||||
|
||||
forward->flags = fwd_flags;
|
||||
|
||||
plen = add_edns0_config(header, plen, ((unsigned char *)header) + daemon->edns_pktsz, &forward->frec_src.source, now, &cacheable);
|
||||
|
||||
if (!cacheable)
|
||||
forward->flags |= FREC_NO_CACHE;
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID) && (master->flags & SERV_DO_DNSSEC))
|
||||
{
|
||||
@@ -338,15 +386,17 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
}
|
||||
#endif
|
||||
|
||||
/* If there wasn't a PH before, and there is now, we added it. */
|
||||
if (!oph && find_pseudoheader(header, plen, NULL, NULL, NULL, NULL))
|
||||
forward->flags |= FREC_ADDED_PHEADER;
|
||||
|
||||
/* Do these before saving query. */
|
||||
forward->frec_src.orig_id = ntohs(header->id);
|
||||
forward->new_id = get_id();
|
||||
header->id = ntohs(forward->new_id);
|
||||
|
||||
|
||||
forward->frec_src.encode_bitmap = (!option_bool(OPT_NO_0x20) && option_bool(OPT_DO_0x20)) ? rand32() : 0;
|
||||
forward->frec_src.encode_bigmap = NULL;
|
||||
p = (unsigned char *)(header+1);
|
||||
if (!extract_name(header, plen, &p, (char *)&forward->frec_src.encode_bitmap, EXTR_NAME_FLIP, 1))
|
||||
goto reply;
|
||||
|
||||
/* Keep copy of query for retries and move to TCP */
|
||||
if (!(forward->stash = blockdata_alloc((char *)header, plen)))
|
||||
{
|
||||
@@ -365,15 +415,9 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
forward->forwardall = 0;
|
||||
if (domain_no_rebind(daemon->namebuff))
|
||||
forward->flags |= FREC_NOREBIND;
|
||||
if (header->hb4 & HB4_CD)
|
||||
forward->flags |= FREC_CHECKING_DISABLED;
|
||||
if (ad_reqd)
|
||||
forward->flags |= FREC_AD_QUESTION;
|
||||
#ifdef HAVE_DNSSEC
|
||||
forward->work_counter = daemon->limit[LIMIT_WORK];
|
||||
forward->validate_counter = daemon->limit[LIMIT_CRYPTO];
|
||||
if (do_bit)
|
||||
forward->flags |= FREC_DO_QUESTION;
|
||||
#endif
|
||||
|
||||
start = first;
|
||||
@@ -403,6 +447,10 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
while (forward->blocking_query)
|
||||
forward = forward->blocking_query;
|
||||
|
||||
/* Don't retry if we've already sent it via TCP. */
|
||||
if (forward->flags & FREC_GONE_TO_TCP)
|
||||
return;
|
||||
|
||||
if (forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY))
|
||||
{
|
||||
/* log_id should match previous DNSSEC query. */
|
||||
@@ -415,8 +463,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
|
||||
/* Find suitable servers: should never fail. */
|
||||
if (!filter_servers(forward->sentto->arrayposn, F_DNSSECOK, &first, &last))
|
||||
return 0;
|
||||
|
||||
return;
|
||||
|
||||
is_dnssec = 1;
|
||||
forward->forwardall = 1;
|
||||
}
|
||||
@@ -531,8 +579,9 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
|
||||
if (forwarded || is_dnssec)
|
||||
{
|
||||
daemon->metrics[METRIC_DNS_QUERIES_FORWARDED]++;
|
||||
forward->forward_timestamp = dnsmasq_milliseconds();
|
||||
return 1;
|
||||
return;
|
||||
}
|
||||
|
||||
/* could not send on, prepare to return */
|
||||
@@ -544,16 +593,16 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
if (udpfd != -1)
|
||||
{
|
||||
if (!(plen = make_local_answer(flags, gotname, plen, header, daemon->namebuff, (char *)(header + replylimit), first, last, ede)))
|
||||
return 0;
|
||||
return;
|
||||
|
||||
if (oph)
|
||||
if (fwd_flags & FREC_HAS_PHEADER)
|
||||
{
|
||||
u16 swap = htons((u16)ede);
|
||||
|
||||
if (ede != EDE_UNSET)
|
||||
plen = add_pseudoheader(header, plen, (unsigned char *)(header + replylimit), EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0);
|
||||
plen = add_pseudoheader(header, plen, (unsigned char *)(header + replylimit), EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, 0, 0);
|
||||
else
|
||||
plen = add_pseudoheader(header, plen, (unsigned char *)(header + replylimit), 0, NULL, 0, do_bit, 0);
|
||||
plen = add_pseudoheader(header, plen, (unsigned char *)(header + replylimit), 0, NULL, 0, 0, 0);
|
||||
}
|
||||
|
||||
#if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS)
|
||||
@@ -572,7 +621,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
send_from(udpfd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND), (char *)header, plen, udpaddr, dst_addr, dst_iface);
|
||||
}
|
||||
|
||||
return 0;
|
||||
daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]++;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check if any frecs need to do a retry, and action that if so.
|
||||
@@ -611,8 +661,7 @@ int fast_retry(time_t now)
|
||||
daemon->log_display_id = f->frec_src.log_id;
|
||||
daemon->log_source_addr = NULL;
|
||||
|
||||
forward_query(-1, NULL, NULL, 0, header, f->stash_len, 0, now, f,
|
||||
f->flags & FREC_AD_QUESTION, f->flags & FREC_DO_QUESTION, 1);
|
||||
forward_query(-1, NULL, NULL, 0, header, f->stash_len, 0, now, f, 0, 1);
|
||||
|
||||
to_run = f->forward_delay = 2 * f->forward_delay;
|
||||
}
|
||||
@@ -810,10 +859,9 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
||||
header->ancount = htons(0);
|
||||
header->nscount = htons(0);
|
||||
header->arcount = htons(0);
|
||||
ede = EDE_DNSSEC_BOGUS;
|
||||
}
|
||||
}
|
||||
else if (!(header->hb4 & HB4_CD) && ad_reqd && cache_secure)
|
||||
else if (ad_reqd && cache_secure)
|
||||
header->hb4 |= HB4_AD;
|
||||
|
||||
/* If the requestor didn't set the DO bit, don't return DNSSEC info. */
|
||||
@@ -847,10 +895,6 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
|
||||
|
||||
daemon->log_display_id = forward->frec_src.log_id;
|
||||
|
||||
/* We've had a reply already, which we're validating. Ignore this duplicate */
|
||||
if (forward->blocking_query || (forward->flags & FREC_GONE_TO_TCP))
|
||||
return;
|
||||
|
||||
/* Find the original query that started it all.... */
|
||||
for (orig = forward; orig->dependent; orig = orig->dependent);
|
||||
|
||||
@@ -997,7 +1041,9 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
|
||||
new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY);
|
||||
new->flags |= flags;
|
||||
new->forwardall = 0;
|
||||
|
||||
new->frec_src.encode_bitmap = 0;
|
||||
new->frec_src.encode_bigmap = NULL;
|
||||
|
||||
forward->next_dependent = NULL;
|
||||
new->dependent = forward; /* to find query awaiting new one. */
|
||||
|
||||
@@ -1114,12 +1160,12 @@ void reply_query(int fd, time_t now)
|
||||
return;
|
||||
|
||||
p = (unsigned char *)(header+1);
|
||||
if (!extract_name(header, n, &p, daemon->namebuff, 1, 4))
|
||||
if (!extract_name(header, n, &p, daemon->namebuff, EXTR_NAME_EXTRACT, 4))
|
||||
return; /* bad packet */
|
||||
GETSHORT(rrtype, p);
|
||||
GETSHORT(class, p);
|
||||
|
||||
if (!(forward = lookup_frec(daemon->namebuff, class, rrtype, ntohs(header->id), 0, 0)))
|
||||
if (!(forward = lookup_frec(daemon->namebuff, class, rrtype, ntohs(header->id), FREC_ANSWER, 0)))
|
||||
return;
|
||||
|
||||
filter_servers(forward->sentto->arrayposn, F_SERVER, &first, &last);
|
||||
@@ -1177,23 +1223,24 @@ void reply_query(int fd, time_t now)
|
||||
if (daemon->ignore_addr && RCODE(header) == NOERROR &&
|
||||
check_for_ignored_address(header, n))
|
||||
return;
|
||||
|
||||
if ((RCODE(header) == REFUSED || RCODE(header) == SERVFAIL) && forward->forwardall == 0)
|
||||
/* for broken servers, attempt to send to another one. */
|
||||
{
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
/* The query MAY have got a good answer, and be awaiting
|
||||
the results of further queries, in which case
|
||||
The Stash contains something else and we don't need to retry anyway. */
|
||||
the stash contains something else and we don't need to retry anyway.
|
||||
We may also have already got a truncated reply, and be in the process
|
||||
of doing the query by TCP so can ignore further, probably truncated, UDP answers. */
|
||||
if (forward->blocking_query || (forward->flags & FREC_GONE_TO_TCP))
|
||||
return;
|
||||
#endif
|
||||
|
||||
if ((RCODE(header) == REFUSED || RCODE(header) == SERVFAIL) && forward->forwardall == 0)
|
||||
/* for broken servers, attempt to send to another one. */
|
||||
{
|
||||
/* Get the saved query back. */
|
||||
blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
|
||||
|
||||
forward_query(-1, NULL, NULL, 0, header, forward->stash_len, 0, now, forward,
|
||||
forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION, 0);
|
||||
forward_query(-1, NULL, NULL, 0, header, forward->stash_len, 0, now, forward, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1223,7 +1270,11 @@ void reply_query(int fd, time_t now)
|
||||
/* denominator controls how many queries we average over. */
|
||||
server->query_latency = server->mma_latency/128;
|
||||
|
||||
|
||||
/* Flip the bits back in the query name. */
|
||||
p = (unsigned char *)(header+1);
|
||||
if (!extract_name(header, n, &p, (char *)&forward->frec_src.encode_bitmap, EXTR_NAME_FLIP, 1))
|
||||
return;
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID))
|
||||
{
|
||||
@@ -1243,6 +1294,51 @@ void reply_query(int fd, time_t now)
|
||||
return_reply(now, forward, header, n, STAT_OK);
|
||||
}
|
||||
|
||||
static void xor_array(unsigned int *arg1, unsigned int *arg2, unsigned int len)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
arg1[i] ^= arg2[i];
|
||||
}
|
||||
|
||||
/* Call extract_name() to flip case of query in packet according to the XOR of the bit maps help in arg1 and arg2 */
|
||||
static void flip_queryname(struct dns_header *header, ssize_t len, struct frec_src *arg1, struct frec_src *arg2)
|
||||
{
|
||||
unsigned char *p = (unsigned char *)(header+1);
|
||||
unsigned int *arg1p, *arg2p, arg1len, arg2len, *swapp, swap;
|
||||
|
||||
/* Two cases: bitmap is single 32 bit int, or it's arbitrary-length array of 32bit ints.
|
||||
The two args may be different and of different lengths.
|
||||
The shorter gets notionally extended with zeros. */
|
||||
|
||||
if (arg1->encode_bigmap)
|
||||
arg1p = arg1->encode_bigmap, arg1len = arg1->encode_bitmap;
|
||||
else
|
||||
arg1p = &arg1->encode_bitmap, arg1len = 1;
|
||||
|
||||
if (arg2->encode_bigmap)
|
||||
arg2p = arg2->encode_bigmap, arg2len = arg2->encode_bitmap;
|
||||
else
|
||||
arg2p = &arg2->encode_bitmap, arg2len = 1;
|
||||
|
||||
/* make arg1 the longer, if they differ. */
|
||||
if (arg2len > arg1len)
|
||||
{
|
||||
swap = arg1len;
|
||||
swapp = arg1p;
|
||||
arg1len = arg2len;
|
||||
arg1p = arg2p;
|
||||
arg2len = swap;
|
||||
arg2p = swapp;
|
||||
}
|
||||
|
||||
/* XOR on shorter length, flip on longer, operate on longer */
|
||||
xor_array(arg1p, arg2p, arg2len);
|
||||
extract_name(header, len, &p, (char *)arg1p, EXTR_NAME_FLIP, arg1len);
|
||||
xor_array(arg1p, arg2p, arg2len); /* restore */
|
||||
}
|
||||
|
||||
void return_reply(time_t now, struct frec *forward, struct dns_header *header, ssize_t n, int status)
|
||||
{
|
||||
int check_rebind = 0, no_cache_dnssec = 0, cache_secure = 0, bogusanswer = 0;
|
||||
@@ -1275,20 +1371,25 @@ void return_reply(time_t now, struct frec *forward, struct dns_header *header, s
|
||||
char *result, *domain = "result";
|
||||
union all_addr a;
|
||||
|
||||
a.log.ede = ede = errflags_to_ede(status);
|
||||
ede = errflags_to_ede(status);
|
||||
|
||||
if (STAT_ISEQUAL(status, STAT_ABANDONED))
|
||||
{
|
||||
result = "ABANDONED";
|
||||
status = STAT_BOGUS;
|
||||
if (ede == EDE_UNSET)
|
||||
ede = EDE_OTHER;
|
||||
}
|
||||
else
|
||||
result = (STAT_ISEQUAL(status, STAT_SECURE) ? "SECURE" : (STAT_ISEQUAL(status, STAT_INSECURE) ? "INSECURE" : "BOGUS"));
|
||||
|
||||
|
||||
if (STAT_ISEQUAL(status, STAT_SECURE))
|
||||
cache_secure = 1;
|
||||
else if (STAT_ISEQUAL(status, STAT_BOGUS))
|
||||
{
|
||||
if (ede == EDE_UNSET)
|
||||
ede = EDE_DNSSEC_BOGUS;
|
||||
no_cache_dnssec = 1;
|
||||
bogusanswer = 1;
|
||||
|
||||
@@ -1296,6 +1397,7 @@ void return_reply(time_t now, struct frec *forward, struct dns_header *header, s
|
||||
domain = daemon->namebuff;
|
||||
}
|
||||
|
||||
a.log.ede = ede;
|
||||
log_query(F_SECSTAT, domain, &a, result, 0);
|
||||
}
|
||||
}
|
||||
@@ -1315,7 +1417,7 @@ void return_reply(time_t now, struct frec *forward, struct dns_header *header, s
|
||||
header->hb4 |= HB4_CD;
|
||||
else
|
||||
header->hb4 &= ~HB4_CD;
|
||||
|
||||
|
||||
/* Never cache answers which are contingent on the source or MAC address EDSN0 option,
|
||||
since the cache is ignorant of such things. */
|
||||
if (forward->flags & FREC_NO_CACHE)
|
||||
@@ -1323,13 +1425,13 @@ void return_reply(time_t now, struct frec *forward, struct dns_header *header, s
|
||||
|
||||
if ((nn = process_reply(header, now, forward->sentto, (size_t)n, check_rebind, no_cache_dnssec, cache_secure, bogusanswer,
|
||||
forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION,
|
||||
forward->flags & FREC_ADDED_PHEADER, &forward->frec_src.source,
|
||||
!(forward->flags & FREC_HAS_PHEADER), &forward->frec_src.source,
|
||||
((unsigned char *)header) + daemon->edns_pktsz, ede)))
|
||||
{
|
||||
struct frec_src *src;
|
||||
struct frec_src *src, *prev;
|
||||
int do_trunc;
|
||||
|
||||
for (do_trunc = 0, src = &forward->frec_src; src; src = src->next)
|
||||
|
||||
for (do_trunc = 0, prev = NULL, src = &forward->frec_src; src; prev = src, src = src->next)
|
||||
{
|
||||
header->id = htons(src->orig_id);
|
||||
|
||||
@@ -1343,6 +1445,13 @@ void return_reply(time_t now, struct frec *forward, struct dns_header *header, s
|
||||
}
|
||||
#endif
|
||||
|
||||
/* You will have to draw diagrams and scratch your head to convince yourself
|
||||
that this works. Bear in mind that the flip to upstream state has already been undone,
|
||||
for the original query so nothing needs to be done, but subsequent queries' flips
|
||||
were recorded relative to the flipped name sent upstream. */
|
||||
if (prev)
|
||||
flip_queryname(header, nn, prev, src);
|
||||
|
||||
if (src->fd != -1)
|
||||
{
|
||||
/* Only send packets that fit what the requestor allows.
|
||||
@@ -1366,7 +1475,7 @@ void return_reply(time_t now, struct frec *forward, struct dns_header *header, s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* The packet is too big for one or more requestors, send them a truncated answer. */
|
||||
if (do_trunc)
|
||||
{
|
||||
@@ -1378,22 +1487,31 @@ void return_reply(time_t now, struct frec *forward, struct dns_header *header, s
|
||||
header->arcount = htons(0);
|
||||
header->hb3 |= HB3_TC;
|
||||
new = resize_packet(header, nn, pheader, hlen);
|
||||
|
||||
|
||||
daemon->log_display_id = forward->frec_src.log_id;
|
||||
daemon->log_source_addr = &forward->frec_src.source;
|
||||
log_query(F_UPSTREAM, NULL, NULL, "truncated", 0);
|
||||
|
||||
for (src = &forward->frec_src; src; src = src->next)
|
||||
if (src->fd != -1 && nn > src->udp_pkt_size)
|
||||
{
|
||||
header->id = htons(src->orig_id);
|
||||
send_from(src->fd, option_bool(OPT_NOWILD) || option_bool (OPT_CLEVERBIND), daemon->packet, new,
|
||||
&src->source, &src->dest, src->iface);
|
||||
|
||||
/* This gets the name back to the state it was in when we started. */
|
||||
flip_queryname(header, new, prev, &forward->frec_src);
|
||||
|
||||
for (src = &forward->frec_src, prev = NULL; src; prev = src, src = src->next)
|
||||
{
|
||||
/* If you didn't undertand this above, you won't understand it here either. */
|
||||
if (prev)
|
||||
flip_queryname(header, new, prev, src);
|
||||
|
||||
if (src->fd != -1 && nn > src->udp_pkt_size)
|
||||
{
|
||||
header->id = htons(src->orig_id);
|
||||
send_from(src->fd, option_bool(OPT_NOWILD) || option_bool (OPT_CLEVERBIND), daemon->packet, new,
|
||||
&src->source, &src->dest, src->iface);
|
||||
|
||||
#ifdef HAVE_DUMPFILE
|
||||
dump_packet_udp(DUMP_REPLY, daemon->packet, (size_t)nn, NULL, &src->source, src->fd);
|
||||
dump_packet_udp(DUMP_REPLY, daemon->packet, (size_t)new, NULL, &src->source, src->fd);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1454,9 +1572,10 @@ void receive_query(struct listener *listen, time_t now)
|
||||
struct in_addr netmask, dst_addr_4;
|
||||
size_t m;
|
||||
ssize_t n;
|
||||
int if_index = 0, auth_dns = 0, do_bit = 0, have_pseudoheader = 0;
|
||||
int if_index = 0, auth_dns = 0, do_bit = 0;
|
||||
unsigned int fwd_flags = 0;
|
||||
int stale = 0, filtered = 0, ede = EDE_UNSET, do_forward = 0;
|
||||
int metric, ad_reqd, fd;
|
||||
int metric, fd;
|
||||
struct blockdata *saved_question = NULL;
|
||||
#ifdef HAVE_CONNTRACK
|
||||
unsigned int mark = 0;
|
||||
@@ -1712,15 +1831,27 @@ void receive_query(struct listener *listen, time_t now)
|
||||
&source_addr, auth_dns ? "auth" : "query", type);
|
||||
|
||||
#ifdef HAVE_AUTH
|
||||
/* find queries for zones we're authoritative for, and answer them directly */
|
||||
/* Find queries for zones we're authoritative for, and answer them directly.
|
||||
The exception to this is DS queries for the zone route. They
|
||||
have to come from the parent zone. Since dnsmasq's auth server
|
||||
can't do DNSSEC, the zone will be unsigned, and anything using
|
||||
dnsmasq as a forwarder and doing validation will be expecting to
|
||||
see the proof of non-existence from the parent. */
|
||||
if (!auth_dns && !option_bool(OPT_LOCALISE))
|
||||
for (zone = daemon->auth_zones; zone; zone = zone->next)
|
||||
if (in_zone(zone, daemon->namebuff, NULL))
|
||||
{
|
||||
auth_dns = 1;
|
||||
local_auth = 1;
|
||||
break;
|
||||
}
|
||||
{
|
||||
char *cut;
|
||||
|
||||
if (in_zone(zone, daemon->namebuff, &cut))
|
||||
{
|
||||
if (type != T_DS || cut)
|
||||
{
|
||||
auth_dns = 1;
|
||||
local_auth = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LOOP
|
||||
@@ -1734,7 +1865,8 @@ void receive_query(struct listener *listen, time_t now)
|
||||
{
|
||||
unsigned short flags;
|
||||
|
||||
have_pseudoheader = 1;
|
||||
fwd_flags |= FREC_HAS_PHEADER;
|
||||
|
||||
GETSHORT(udp_size, pheader);
|
||||
pheader += 2; /* ext_rcode */
|
||||
GETSHORT(flags, pheader);
|
||||
@@ -1752,10 +1884,15 @@ void receive_query(struct listener *listen, time_t now)
|
||||
udp_size = PACKETSZ; /* Sanity check - can't reduce below default. RFC 6891 6.2.3 */
|
||||
}
|
||||
|
||||
ad_reqd = do_bit;
|
||||
/* RFC 6840 5.7 */
|
||||
if (header->hb4 & HB4_AD)
|
||||
ad_reqd = 1;
|
||||
if (do_bit || (header->hb4 & HB4_AD))
|
||||
fwd_flags |= FREC_AD_QUESTION;
|
||||
|
||||
if (do_bit)
|
||||
fwd_flags |= FREC_DO_QUESTION;
|
||||
|
||||
if (header->hb4 & HB4_CD)
|
||||
fwd_flags |= FREC_CHECKING_DISABLED;
|
||||
|
||||
fd = listen->fd;
|
||||
|
||||
@@ -1790,10 +1927,16 @@ void receive_query(struct listener *listen, time_t now)
|
||||
#endif
|
||||
else
|
||||
{
|
||||
int cacheable;
|
||||
|
||||
n = add_edns0_config(header, n, ((unsigned char *)header) + daemon->edns_pktsz, &source_addr, now, &cacheable);
|
||||
saved_question = blockdata_alloc((char *) header, (size_t)n);
|
||||
|
||||
|
||||
if (!cacheable)
|
||||
fwd_flags |= FREC_NO_CACHE;
|
||||
|
||||
m = answer_request(header, ((char *) header) + udp_size, (size_t)n,
|
||||
dst_addr_4, netmask, now, ad_reqd, do_bit, &stale, &filtered);
|
||||
dst_addr_4, netmask, now, fwd_flags & FREC_AD_QUESTION, do_bit, !cacheable, &stale, &filtered);
|
||||
|
||||
metric = stale ? METRIC_DNS_STALE_ANSWERED : METRIC_DNS_LOCAL_ANSWERED;
|
||||
|
||||
@@ -1814,7 +1957,7 @@ void receive_query(struct listener *listen, time_t now)
|
||||
|
||||
if (m != 0)
|
||||
{
|
||||
if (have_pseudoheader)
|
||||
if (fwd_flags & FREC_HAS_PHEADER)
|
||||
{
|
||||
if (ede != EDE_UNSET)
|
||||
{
|
||||
@@ -1863,12 +2006,9 @@ void receive_query(struct listener *listen, time_t now)
|
||||
blockdata_retrieve(saved_question, (size_t)n, (void *)header);
|
||||
blockdata_free(saved_question);
|
||||
saved_question = NULL;
|
||||
|
||||
if (forward_query(fd, &source_addr, &dst_addr, if_index, header, (size_t)n,
|
||||
udp_size, now, NULL, ad_reqd, do_bit, 0))
|
||||
daemon->metrics[METRIC_DNS_QUERIES_FORWARDED]++;
|
||||
else
|
||||
daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]++;
|
||||
|
||||
forward_query(fd, &source_addr, &dst_addr, if_index, header, (size_t)n,
|
||||
udp_size, now, NULL, fwd_flags, 0);
|
||||
}
|
||||
|
||||
blockdata_free(saved_question);
|
||||
@@ -1895,7 +2035,7 @@ static ssize_t tcp_talk(int first, int last, int start, unsigned char *packet,
|
||||
|
||||
/* Save the query to make sure we get the answer we expect. */
|
||||
p = (unsigned char *)(header+1);
|
||||
if (!extract_name(header, qsize, &p, daemon->namebuff, 1, 4))
|
||||
if (!extract_name(header, qsize, &p, daemon->namebuff, EXTR_NAME_EXTRACT, 4))
|
||||
return 0;
|
||||
GETSHORT(type, p);
|
||||
GETSHORT(class, p);
|
||||
@@ -1922,7 +2062,7 @@ static ssize_t tcp_talk(int first, int last, int start, unsigned char *packet,
|
||||
break;
|
||||
}
|
||||
|
||||
serv = daemon->serverarray[start];
|
||||
*servp = serv = daemon->serverarray[start];
|
||||
|
||||
retry:
|
||||
blockdata_retrieve(saved_question, qsize, header);
|
||||
@@ -1973,6 +2113,12 @@ static ssize_t tcp_talk(int first, int last, int start, unsigned char *packet,
|
||||
trying again in non-FASTOPEN mode. */
|
||||
if (fatal || (!data_sent && connect(serv->tcpfd, &serv->addr.sa, sa_len(&serv->addr)) == -1))
|
||||
{
|
||||
int port;
|
||||
|
||||
failed:
|
||||
port = prettyprint_addr(&serv->addr, daemon->addrbuff);
|
||||
my_syslog(LOG_DEBUG|MS_DEBUG, _("TCP connection failed to %s#%d"), daemon->addrbuff, port);
|
||||
|
||||
close(serv->tcpfd);
|
||||
serv->tcpfd = -1;
|
||||
continue;
|
||||
@@ -1988,23 +2134,25 @@ static ssize_t tcp_talk(int first, int last, int start, unsigned char *packet,
|
||||
!read_write(serv->tcpfd, (unsigned char *)length, sizeof (*length), RW_READ_ONCE) ||
|
||||
!read_write(serv->tcpfd, payload, (rsize = ntohs(*length)), RW_READ_ONCE))
|
||||
{
|
||||
close(serv->tcpfd);
|
||||
serv->tcpfd = -1;
|
||||
/* We get data then EOF, reopen connection to same server,
|
||||
else try next. This avoids DoS from a server which accepts
|
||||
connections and then closes them. */
|
||||
if (serv->flags & SERV_GOT_TCP)
|
||||
goto retry;
|
||||
{
|
||||
close(serv->tcpfd);
|
||||
serv->tcpfd = -1;
|
||||
goto retry;
|
||||
}
|
||||
else
|
||||
continue;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* If the question section of the reply doesn't match the crc we sent, then
|
||||
/* If the question section of the reply doesn't match the question we sent, then
|
||||
someone might be attempting to insert bogus values into the cache by
|
||||
sending replies containing questions and bogus answers.
|
||||
sending replies containing questions and bogus answers.
|
||||
Try another server, or give up */
|
||||
p = (unsigned char *)(header+1);
|
||||
if (extract_name(header, rsize, &p, daemon->namebuff, 0, 4) != 1)
|
||||
if (extract_name(header, rsize, &p, daemon->namebuff, EXTR_NAME_COMPARE, 4) != 1)
|
||||
continue;
|
||||
GETSHORT(rtype, p);
|
||||
GETSHORT(rclass, p);
|
||||
@@ -2212,7 +2360,7 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
#ifdef HAVE_AUTH
|
||||
int local_auth = 0;
|
||||
#endif
|
||||
int checking_disabled, do_bit = 0, ad_reqd = 0, added_pheader = 0, have_pseudoheader = 0;
|
||||
int checking_disabled, do_bit = 0, ad_reqd = 0, have_pseudoheader = 0;
|
||||
struct blockdata *saved_question = NULL;
|
||||
unsigned short qtype;
|
||||
unsigned int gotname = 0;
|
||||
@@ -2284,7 +2432,7 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
|
||||
while (1)
|
||||
{
|
||||
int stale = 0, ede = EDE_UNSET;
|
||||
int cacheable = 1, stale = 0, ede = EDE_UNSET;
|
||||
size_t m = 0;
|
||||
unsigned int flags = 0;
|
||||
#ifdef HAVE_AUTH
|
||||
@@ -2329,6 +2477,7 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
if (saved_question)
|
||||
blockdata_free(saved_question);
|
||||
|
||||
size = add_edns0_config(header, size, ((unsigned char *) header) + 65536, &peer_addr, now, &cacheable);
|
||||
saved_question = blockdata_alloc((char *)header, (size_t)size);
|
||||
saved_size = size;
|
||||
|
||||
@@ -2336,15 +2485,27 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
&peer_addr, auth_dns ? "auth" : "query", qtype);
|
||||
|
||||
#ifdef HAVE_AUTH
|
||||
/* find queries for zones we're authoritative for, and answer them directly */
|
||||
/* Find queries for zones we're authoritative for, and answer them directly.
|
||||
The exception to this is DS queries for the zone route. They
|
||||
have to come from the parent zone. Since dnsmasq's auth server
|
||||
can't do DNSSEC, the zone will be unsigned, and anything using
|
||||
dnsmasq as a forwarder and doing validation will be expecting to
|
||||
see the proof of non-existence from the parent. */
|
||||
if (!auth_dns && !option_bool(OPT_LOCALISE))
|
||||
for (zone = daemon->auth_zones; zone; zone = zone->next)
|
||||
if (in_zone(zone, daemon->namebuff, NULL))
|
||||
{
|
||||
auth_dns = 1;
|
||||
local_auth = 1;
|
||||
break;
|
||||
}
|
||||
{
|
||||
char *cut;
|
||||
|
||||
if (in_zone(zone, daemon->namebuff, &cut))
|
||||
{
|
||||
if (qtype != T_DS || cut)
|
||||
{
|
||||
auth_dns = 1;
|
||||
local_auth = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
norebind = domain_no_rebind(daemon->namebuff);
|
||||
@@ -2395,7 +2556,7 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
#endif
|
||||
else
|
||||
m = answer_request(header, ((char *) header) + 65536, (size_t)size,
|
||||
dst_addr_4, netmask, now, ad_reqd, do_bit, &stale, &filtered);
|
||||
dst_addr_4, netmask, now, ad_reqd, do_bit, !cacheable, &stale, &filtered);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2406,15 +2567,14 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
{
|
||||
struct server *master;
|
||||
int start;
|
||||
int cacheable, no_cache_dnssec = 0, cache_secure = 0, bogusanswer = 0;
|
||||
int no_cache_dnssec = 0, cache_secure = 0, bogusanswer = 0;
|
||||
|
||||
blockdata_retrieve(saved_question, (size_t)saved_size, header);
|
||||
size = saved_size;
|
||||
|
||||
/* save state of "cd" flag in query */
|
||||
if ((checking_disabled = header->hb4 & HB4_CD))
|
||||
no_cache_dnssec = 1;
|
||||
|
||||
checking_disabled = header->hb4 & HB4_CD;
|
||||
|
||||
if (lookup_domain(daemon->namebuff, gotname, &first, &last))
|
||||
flags = is_local_answer(now, first, daemon->namebuff);
|
||||
else
|
||||
@@ -2437,8 +2597,6 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
else
|
||||
start = master->last_server;
|
||||
|
||||
size = add_edns0_config(header, size, ((unsigned char *) header) + 65536, &peer_addr, now, &cacheable);
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID) && (master->flags & SERV_DO_DNSSEC))
|
||||
{
|
||||
@@ -2451,11 +2609,6 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Check if we added a pheader on forwarding - may need to
|
||||
strip it from the reply. */
|
||||
if (!have_pseudoheader && find_pseudoheader(header, size, NULL, NULL, NULL, NULL))
|
||||
added_pheader = 1;
|
||||
|
||||
/* Loop round available servers until we succeed in connecting to one. */
|
||||
if ((m = tcp_talk(first, last, start, packet, size, have_mark, mark, &serv)) == 0)
|
||||
ede = EDE_NETERR;
|
||||
@@ -2472,7 +2625,9 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
/* Clear this in case we don't call tcp_key_recurse() below */
|
||||
memset(daemon->rr_status, 0, sizeof(*daemon->rr_status) * daemon->rr_status_sz);
|
||||
|
||||
if (!checking_disabled && (master->flags & SERV_DO_DNSSEC))
|
||||
if (checking_disabled || (header->hb4 & HB4_CD))
|
||||
no_cache_dnssec = 1;
|
||||
else if (master->flags & SERV_DO_DNSSEC)
|
||||
{
|
||||
int keycount = daemon->limit[LIMIT_WORK]; /* Limit to number of DNSSEC questions, to catch loops and avoid filling cache. */
|
||||
int validatecount = daemon->limit[LIMIT_CRYPTO];
|
||||
@@ -2481,12 +2636,14 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
char *result, *domain = "result";
|
||||
|
||||
union all_addr a;
|
||||
a.log.ede = ede = errflags_to_ede(status);
|
||||
ede = errflags_to_ede(status);
|
||||
|
||||
if (STAT_ISEQUAL(status, STAT_ABANDONED))
|
||||
{
|
||||
result = "ABANDONED";
|
||||
status = STAT_BOGUS;
|
||||
if (ede == EDE_UNSET)
|
||||
ede = EDE_OTHER;
|
||||
}
|
||||
else
|
||||
result = (STAT_ISEQUAL(status, STAT_SECURE) ? "SECURE" : (STAT_ISEQUAL(status, STAT_INSECURE) ? "INSECURE" : "BOGUS"));
|
||||
@@ -2495,6 +2652,8 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
cache_secure = 1;
|
||||
else if (STAT_ISEQUAL(status, STAT_BOGUS))
|
||||
{
|
||||
if (ede == EDE_UNSET)
|
||||
ede = EDE_DNSSEC_BOGUS;
|
||||
no_cache_dnssec = 1;
|
||||
bogusanswer = 1;
|
||||
|
||||
@@ -2502,6 +2661,7 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
domain = daemon->namebuff;
|
||||
}
|
||||
|
||||
a.log.ede = ede;
|
||||
log_query(F_SECSTAT, domain, &a, result, 0);
|
||||
|
||||
if ((daemon->limit[LIMIT_CRYPTO] - validatecount) > (int)daemon->metrics[METRIC_CRYPTO_HWM])
|
||||
@@ -2530,7 +2690,7 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
|
||||
m = process_reply(header, now, serv, (unsigned int)m,
|
||||
option_bool(OPT_NO_REBIND) && !norebind, no_cache_dnssec, cache_secure, bogusanswer,
|
||||
ad_reqd, do_bit, added_pheader, &peer_addr, ((unsigned char *)header) + 65536, ede);
|
||||
ad_reqd, do_bit, !have_pseudoheader, &peer_addr, ((unsigned char *)header) + 65536, ede);
|
||||
|
||||
/* process_reply() adds pheader itself */
|
||||
have_pseudoheader = 0;
|
||||
@@ -2604,7 +2764,8 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
}
|
||||
|
||||
blockdata_free(saved_question);
|
||||
|
||||
check_log_writer(1);
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
@@ -2883,10 +3044,23 @@ static void free_frec(struct frec *f)
|
||||
{
|
||||
struct frec_src *last;
|
||||
|
||||
/* add back to freelist if not the record builtin to every frec. */
|
||||
for (last = f->frec_src.next; last && last->next; last = last->next) ;
|
||||
/* add back to freelist if not the record builtin to every frec,
|
||||
also free any bigmaps they've been decorated with. */
|
||||
for (last = f->frec_src.next; last && last->next; last = last->next)
|
||||
if (last->encode_bigmap)
|
||||
{
|
||||
free(last->encode_bigmap);
|
||||
last->encode_bigmap = NULL;
|
||||
}
|
||||
|
||||
if (last)
|
||||
{
|
||||
/* final link in the chain loses bigmap too. */
|
||||
if (last->encode_bigmap)
|
||||
{
|
||||
free(last->encode_bigmap);
|
||||
last->encode_bigmap = NULL;
|
||||
}
|
||||
last->next = daemon->free_frec_src;
|
||||
daemon->free_frec_src = f->frec_src.next;
|
||||
}
|
||||
@@ -2901,7 +3075,7 @@ static void free_frec(struct frec *f)
|
||||
blockdata_free(f->stash);
|
||||
f->stash = NULL;
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
/* Anything we're waiting on is pointless now, too */
|
||||
if (f->blocking_query)
|
||||
@@ -3024,6 +3198,16 @@ static struct frec *lookup_frec(char *target, int class, int rrtype, int id, int
|
||||
{
|
||||
struct frec *f;
|
||||
struct dns_header *header;
|
||||
int compare_mode = EXTR_NAME_COMPARE;
|
||||
|
||||
/* Only compare case-sensitive when matching frec to a received answer,
|
||||
NOT when looking for a duplicated question. */
|
||||
if (flags & FREC_ANSWER)
|
||||
{
|
||||
flags &= ~FREC_ANSWER;
|
||||
if (!option_bool(OPT_NO_0x20) && option_bool(OPT_DO_0x20))
|
||||
compare_mode = EXTR_NAME_NOCASE;
|
||||
}
|
||||
|
||||
for (f = daemon->frec_list; f; f = f->next)
|
||||
if (f->sentto &&
|
||||
@@ -3032,21 +3216,35 @@ static struct frec *lookup_frec(char *target, int class, int rrtype, int id, int
|
||||
(header = blockdata_retrieve(f->stash, f->stash_len, NULL)))
|
||||
{
|
||||
unsigned char *p = (unsigned char *)(header+1);
|
||||
int hclass, hrrtype;
|
||||
|
||||
if (extract_name(header, f->stash_len, &p, target, 0, 4) != 1)
|
||||
continue;
|
||||
|
||||
GETSHORT(hrrtype, p);
|
||||
GETSHORT(hclass, p);
|
||||
|
||||
/* type checked by flags for DNSSEC queries. */
|
||||
if (rrtype != -1 && rrtype != hrrtype)
|
||||
continue;
|
||||
|
||||
if (class != hclass)
|
||||
continue;
|
||||
|
||||
int hclass, hrrtype, rc;
|
||||
|
||||
/* Case sensitive compare for DNS-0x20 encoding. */
|
||||
if ((rc = extract_name(header, f->stash_len, &p, target, compare_mode, 4)))
|
||||
{
|
||||
GETSHORT(hrrtype, p);
|
||||
GETSHORT(hclass, p);
|
||||
|
||||
/* type checked by flags for DNSSEC queries. */
|
||||
if (rrtype != -1 && rrtype != hrrtype)
|
||||
continue;
|
||||
|
||||
if (class != hclass)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rc != 1)
|
||||
{
|
||||
static int warned = 0;
|
||||
|
||||
if (rc == 3 && !warned)
|
||||
{
|
||||
my_syslog(LOG_WARNING, _("Case mismatch in DNS reply - check bit 0x20 encoding."));
|
||||
warned = 1;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 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
|
||||
|
||||
+11
-5
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 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
|
||||
@@ -193,6 +193,8 @@ struct myoption {
|
||||
#define LOPT_MAX_PROCS 384
|
||||
#define LOPT_DNSSEC_LIMITS 385
|
||||
#define LOPT_PXE_OPT 386
|
||||
#define LOPT_NO_ENCODE 387
|
||||
#define LOPT_DO_ENCODE 388
|
||||
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
static const struct option opts[] =
|
||||
@@ -247,6 +249,8 @@ static const struct myoption opts[] =
|
||||
{ "local-ttl", 1, 0, 'T' },
|
||||
{ "no-negcache", 0, 0, 'N' },
|
||||
{ "no-round-robin", 0, 0, LOPT_NORR },
|
||||
{ "no-0x20-encode", 0, 0, LOPT_NO_ENCODE },
|
||||
{ "do-0x20-encode", 0, 0, LOPT_DO_ENCODE },
|
||||
{ "cache-rr", 1, 0, LOPT_CACHE_RR },
|
||||
{ "addn-hosts", 1, 0, 'H' },
|
||||
{ "hostsdir", 1, 0, LOPT_HOST_INOTIFY },
|
||||
@@ -568,7 +572,7 @@ static struct {
|
||||
{ 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_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 },
|
||||
{ LOPT_DNSSEC_DEBUG, OPT_DNSSEC_DEBUG, NULL, gettext_noop("Disable upstream checking for DNSSEC debugging."), NULL },
|
||||
{ LOPT_DNSSEC_CHECK, ARG_DUP, NULL, gettext_noop("Ensure answers without DNSSEC are in unsigned zones."), NULL },
|
||||
{ LOPT_DNSSEC_TIME, OPT_DNSSEC_TIME, NULL, gettext_noop("Don't check DNSSEC signature timestamps until first cache-reload"), NULL },
|
||||
@@ -591,6 +595,8 @@ static struct {
|
||||
{ LOPT_UMBRELLA, ARG_ONE, "[=<optspec>]", gettext_noop("Send Cisco Umbrella identifiers including remote IP."), NULL },
|
||||
{ LOPT_QUIET_TFTP, OPT_QUIET_TFTP, NULL, gettext_noop("Do not log routine TFTP."), NULL },
|
||||
{ LOPT_NORR, OPT_NORR, NULL, gettext_noop("Suppress round-robin ordering of DNS records."), NULL },
|
||||
{ LOPT_NO_ENCODE, OPT_NO_0x20, NULL, gettext_noop("Suppress DNS bit 0x20 encoding."), NULL },
|
||||
{ LOPT_DO_ENCODE, OPT_DO_0x20, NULL, gettext_noop("Enable DNS bit 0x20 encoding."), NULL },
|
||||
{ LOPT_NO_IDENT, OPT_NO_IDENT, NULL, gettext_noop("Do not add CHAOS TXT records."), NULL },
|
||||
{ LOPT_CACHE_RR, ARG_DUP, "<RR-type>", gettext_noop("Cache this DNS resource record type."), NULL },
|
||||
{ LOPT_MAX_PROCS, ARG_ONE, "<integer>", gettext_noop("Maximum number of concurrent tcp connections."), NULL },
|
||||
@@ -4043,10 +4049,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
}
|
||||
|
||||
new_addr = opt_malloc(sizeof(struct addrlist));
|
||||
new_addr->next = new->addr6;
|
||||
new_addr->flags = 0;
|
||||
new_addr->addr.addr6 = in6;
|
||||
new->addr6 = new_addr;
|
||||
|
||||
if (pref)
|
||||
{
|
||||
@@ -4057,7 +4061,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
((((u64)1<<(128-new_addr->prefixlen))-1) & addrpart) != 0)
|
||||
{
|
||||
dhcp_config_free(new);
|
||||
ret_err(_("bad IPv6 prefix"));
|
||||
ret_err_free(_("bad IPv6 prefix"), new_addr);
|
||||
}
|
||||
|
||||
new_addr->flags |= ADDRLIST_PREFIX;
|
||||
@@ -4071,6 +4075,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
if (i == 8)
|
||||
new_addr->flags |= ADDRLIST_WILDCARD;
|
||||
|
||||
new_addr->next = new->addr6;
|
||||
new->addr6 = new_addr;
|
||||
new->flags |= CONFIG_ADDR6;
|
||||
}
|
||||
#endif
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 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
|
||||
|
||||
+99
-41
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -16,16 +16,39 @@
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
/* EXTR_NAME_EXTRACT -> extract name
|
||||
EXTR_NAME_COMPARE -> compare name, case insensitive
|
||||
EXTR_NAME_NOCASE -> compare name, case sensitive
|
||||
EXTR_NAME_FLIP -> flip 0x20 bits in packet.
|
||||
|
||||
For flip, name is an array of ints, whose size
|
||||
is given in parm, which forms the bitmap. Bits beyond the size
|
||||
are assumed to be zero.
|
||||
|
||||
return = 0 -> error
|
||||
return = 1 -> extract OK, compare OK, flip OK
|
||||
return = 2 -> extract OK, compare failed.
|
||||
return = 3 -> extract OK, compare failed but only on case.
|
||||
*/
|
||||
int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
|
||||
char *name, int isExtract, int extrabytes)
|
||||
char *name, int func, unsigned int parm)
|
||||
{
|
||||
unsigned char *cp = (unsigned char *)name, *p = *pp, *p1 = NULL;
|
||||
unsigned int j, l, namelen = 0, hops = 0;
|
||||
int retvalue = 1;
|
||||
|
||||
if (isExtract)
|
||||
*cp = 0;
|
||||
unsigned int bigmap_counter = 0, bigmap_posn = 0, bigmap_size = parm, bitmap = 0;
|
||||
int retvalue = 1, case_insens = 1, isExtract = 0, flip = 0, extrabytes = (int)parm;
|
||||
unsigned int *bigmap = (unsigned int *)name;
|
||||
|
||||
if (func == EXTR_NAME_EXTRACT)
|
||||
isExtract = 1, *cp = 0;
|
||||
else if (func == EXTR_NAME_NOCASE)
|
||||
case_insens = 0;
|
||||
else if (func == EXTR_NAME_FLIP)
|
||||
{
|
||||
flip = 1, extrabytes = 0;
|
||||
name = NULL;
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
unsigned int label_type;
|
||||
@@ -46,7 +69,7 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
|
||||
cp--;
|
||||
*cp = 0; /* terminate: lose final period */
|
||||
}
|
||||
else if (*cp != 0)
|
||||
else if (!flip && *cp != 0)
|
||||
retvalue = 2;
|
||||
|
||||
if (p1) /* we jumped via compression */
|
||||
@@ -85,7 +108,7 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
|
||||
if (!CHECK_LEN(header, p, plen, l))
|
||||
return 0;
|
||||
|
||||
for(j=0; j<l; j++, p++)
|
||||
for (j=0; j<l; j++, p++)
|
||||
if (isExtract)
|
||||
{
|
||||
unsigned char c = *p;
|
||||
@@ -98,6 +121,24 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
|
||||
else
|
||||
*cp++ = c;
|
||||
}
|
||||
else if (flip)
|
||||
{
|
||||
unsigned char c = *p;
|
||||
|
||||
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
|
||||
{
|
||||
/* Get the next int of the bitmap */
|
||||
if (bigmap_posn < bigmap_size && bigmap_counter-- == 0)
|
||||
{
|
||||
bitmap = bigmap[bigmap_posn++];
|
||||
bigmap_counter = (sizeof(unsigned int) * 8) - 1;
|
||||
}
|
||||
|
||||
if (bitmap & 1)
|
||||
*p ^= 0x20;
|
||||
bitmap >>= 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned char c1 = *cp, c2 = *p;
|
||||
@@ -107,23 +148,35 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
|
||||
else
|
||||
{
|
||||
cp++;
|
||||
if (c1 >= 'A' && c1 <= 'Z')
|
||||
c1 += 'a' - 'A';
|
||||
|
||||
if (c1 == NAME_ESCAPE)
|
||||
c1 = (*cp++)-1;
|
||||
else if (case_insens && c1 >= 'A' && c1 <= 'Z')
|
||||
c1 += 'a' - 'A';
|
||||
|
||||
if (c2 >= 'A' && c2 <= 'Z')
|
||||
if (case_insens && c2 >= 'A' && c2 <= 'Z')
|
||||
c2 += 'a' - 'A';
|
||||
|
||||
|
||||
if (!case_insens && retvalue != 2 && c1 != c2)
|
||||
{
|
||||
if (c1 >= 'A' && c1 <= 'Z')
|
||||
c1 += 'a' - 'A';
|
||||
|
||||
if (c2 >= 'A' && c2 <= 'Z')
|
||||
c2 += 'a' - 'A';
|
||||
|
||||
if (c1 == c2)
|
||||
retvalue = 3;
|
||||
}
|
||||
|
||||
if (c1 != c2)
|
||||
retvalue = 2;
|
||||
retvalue = 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (isExtract)
|
||||
*cp++ = '.';
|
||||
else if (*cp != 0 && *cp++ != '.')
|
||||
else if (!flip && *cp != 0 && *cp++ != '.')
|
||||
retvalue = 2;
|
||||
}
|
||||
else
|
||||
@@ -399,7 +452,7 @@ int do_doctor(struct dns_header *header, size_t qlen, char *namebuff)
|
||||
if (i == ntohs(header->ancount) && !(p = skip_section(p, ntohs(header->nscount), header, qlen)))
|
||||
return done;
|
||||
|
||||
if (!extract_name(header, qlen, &p, namebuff, 1, 10))
|
||||
if (!extract_name(header, qlen, &p, namebuff, EXTR_NAME_EXTRACT, 10))
|
||||
return done; /* bad packet */
|
||||
|
||||
GETSHORT(qtype, p);
|
||||
@@ -480,7 +533,7 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *sub
|
||||
|
||||
for (i = 0; i < ntohs(header->nscount); i++)
|
||||
{
|
||||
if (!extract_name(header, qlen, &p, daemon->workspacename, 1, 0))
|
||||
if (!extract_name(header, qlen, &p, daemon->workspacename, EXTR_NAME_EXTRACT, 0))
|
||||
return 0; /* bad packet */
|
||||
|
||||
GETSHORT(qtype, p);
|
||||
@@ -509,7 +562,7 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *sub
|
||||
|
||||
for (j = 0; j < 2; j++) /* MNAME, RNAME */
|
||||
{
|
||||
if (!extract_name(header, qlen, &p, daemon->workspacename, 1, 0))
|
||||
if (!extract_name(header, qlen, &p, daemon->workspacename, EXTR_NAME_EXTRACT, 0))
|
||||
{
|
||||
if (!no_cache)
|
||||
blockdata_free(addr.rrblock.rrdata);
|
||||
@@ -660,7 +713,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
|
||||
namep = p = (unsigned char *)(header+1);
|
||||
|
||||
if (ntohs(header->qdcount) != 1 || !extract_name(header, qlen, &p, name, 1, 4))
|
||||
if (ntohs(header->qdcount) != 1 || !extract_name(header, qlen, &p, name, EXTR_NAME_EXTRACT, 4))
|
||||
return 2; /* bad packet */
|
||||
|
||||
GETSHORT(qtype, p);
|
||||
@@ -684,7 +737,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
for (j = 0; j < ntohs(header->ancount); j++)
|
||||
{
|
||||
int secflag = 0;
|
||||
if (!(res = extract_name(header, qlen, &p1, name, 0, 10)))
|
||||
if (!(res = extract_name(header, qlen, &p1, name, EXTR_NAME_COMPARE, 10)))
|
||||
return 2; /* bad packet */
|
||||
|
||||
GETSHORT(aqtype, p1);
|
||||
@@ -722,7 +775,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
if (aqtype == T_CNAME)
|
||||
log_query(secflag | F_CNAME | F_FORWARD | F_UPSTREAM, name, NULL, NULL, 0);
|
||||
|
||||
if (!extract_name(header, qlen, &p1, name, 1, 0))
|
||||
if (!extract_name(header, qlen, &p1, name, EXTR_NAME_EXTRACT, 0))
|
||||
return 2;
|
||||
|
||||
if (aqtype == T_CNAME)
|
||||
@@ -800,7 +853,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
{
|
||||
int secflag = 0;
|
||||
|
||||
if (!(res = extract_name(header, qlen, &p1, name, 0, 10)))
|
||||
if (!(res = extract_name(header, qlen, &p1, name, EXTR_NAME_COMPARE, 10)))
|
||||
return 2; /* bad packet */
|
||||
|
||||
GETSHORT(aqtype, p1);
|
||||
@@ -862,7 +915,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
}
|
||||
|
||||
namep = p1;
|
||||
if (!extract_name(header, qlen, &p1, name, 1, 0))
|
||||
if (!extract_name(header, qlen, &p1, name, EXTR_NAME_EXTRACT, 0))
|
||||
return 2;
|
||||
|
||||
if (qtype != T_CNAME)
|
||||
@@ -932,7 +985,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
/* Name, extract it then re-encode. */
|
||||
int len;
|
||||
|
||||
if (!extract_name(header, qlen, &p1, name, 1, 0))
|
||||
if (!extract_name(header, qlen, &p1, name, EXTR_NAME_EXTRACT, 0))
|
||||
{
|
||||
blockdata_free(addr.rrblock.rrdata);
|
||||
return 2;
|
||||
@@ -965,7 +1018,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
} while (desc != -1);
|
||||
|
||||
/* we overwrote the original name, so get it back here. */
|
||||
if (!extract_name(header, qlen, &tmp, name, 1, 0))
|
||||
if (!extract_name(header, qlen, &tmp, name, EXTR_NAME_EXTRACT, 0))
|
||||
{
|
||||
blockdata_free(addr.rrblock.rrdata);
|
||||
return 2;
|
||||
@@ -1127,7 +1180,7 @@ void report_addresses(struct dns_header *header, size_t len, u32 mark)
|
||||
{
|
||||
int aqtype, aqclass, ardlen;
|
||||
|
||||
if (!extract_name(header, len, &p, daemon->namebuff, 1, 10))
|
||||
if (!extract_name(header, len, &p, daemon->namebuff, EXTR_NAME_EXTRACT, 10))
|
||||
return;
|
||||
|
||||
if (!CHECK_LEN(header, p, len, 10))
|
||||
@@ -1145,7 +1198,7 @@ void report_addresses(struct dns_header *header, size_t len, u32 mark)
|
||||
{
|
||||
if (aqtype == T_CNAME)
|
||||
{
|
||||
if (!extract_name(header, len, &p, daemon->workspacename, 1, 0))
|
||||
if (!extract_name(header, len, &p, daemon->workspacename, EXTR_NAME_EXTRACT, 0))
|
||||
return;
|
||||
if (safe_name(daemon->namebuff) && safe_name(daemon->workspacename))
|
||||
ubus_event_bcast_connmark_allowlist_resolved(mark, daemon->namebuff, daemon->workspacename, attl);
|
||||
@@ -1195,7 +1248,7 @@ unsigned int extract_request(struct dns_header *header, size_t qlen, char *name,
|
||||
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, EXTR_NAME_EXTRACT, 4))
|
||||
return 0; /* bad packet */
|
||||
|
||||
GETSHORT(qtype, p);
|
||||
@@ -1309,7 +1362,7 @@ static int check_bad_address(struct dns_header *header, size_t qlen, struct bogu
|
||||
|
||||
for (i = ntohs(header->ancount); i != 0; i--)
|
||||
{
|
||||
if (name && !extract_name(header, qlen, &p, name, 1, 10))
|
||||
if (name && !extract_name(header, qlen, &p, name, EXTR_NAME_EXTRACT, 10))
|
||||
return 0; /* bad packet */
|
||||
|
||||
if (!name && !(p = skip_name(p, header, qlen, 10)))
|
||||
@@ -1559,7 +1612,7 @@ static unsigned long crec_ttl(struct crec *crecp, time_t now)
|
||||
return daemon->max_ttl;
|
||||
}
|
||||
|
||||
static int cache_validated(const struct crec *crecp)
|
||||
static int cache_not_validated(const struct crec *crecp)
|
||||
{
|
||||
return (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK));
|
||||
}
|
||||
@@ -1567,7 +1620,7 @@ static int cache_validated(const struct crec *crecp)
|
||||
/* return zero if we can't answer from cache, or packet size if we can */
|
||||
size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
struct in_addr local_addr, struct in_addr local_netmask,
|
||||
time_t now, int ad_reqd, int do_bit, int *stale, int *filtered)
|
||||
time_t now, int ad_reqd, int do_bit, int no_cache, int *stale, int *filtered)
|
||||
{
|
||||
char *name = daemon->namebuff;
|
||||
unsigned char *p, *ansp;
|
||||
@@ -1582,6 +1635,10 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
size_t len;
|
||||
int rd_bit = (header->hb3 & HB3_RD);
|
||||
int count = 255; /* catch loops */
|
||||
|
||||
/* Suppress cached answers of no_cache set. */
|
||||
if (no_cache)
|
||||
rd_bit = 0;
|
||||
|
||||
if (stale)
|
||||
*stale = 0;
|
||||
@@ -1615,7 +1672,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
nameoffset = p - (unsigned char *)header;
|
||||
|
||||
/* now extract name as .-concatenated string into name */
|
||||
if (!extract_name(header, qlen, &p, name, 1, 4))
|
||||
if (!extract_name(header, qlen, &p, name, EXTR_NAME_EXTRACT, 4))
|
||||
return 0; /* bad packet */
|
||||
|
||||
GETSHORT(qtype, p);
|
||||
@@ -1656,7 +1713,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
|
||||
/* If the client asked for DNSSEC don't use cached data. */
|
||||
if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) ||
|
||||
(rd_bit && (!do_bit || cache_validated(crecp))))
|
||||
(rd_bit && (!do_bit || cache_not_validated(crecp))))
|
||||
{
|
||||
if (crecp->flags & F_CONFIG || qtype == T_CNAME)
|
||||
ans = 1;
|
||||
@@ -1815,7 +1872,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
the zone is unsigned, which implies that we're doing
|
||||
validation. */
|
||||
if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) ||
|
||||
(rd_bit && (!do_bit || cache_validated(crecp)) ))
|
||||
(rd_bit && (!do_bit || cache_not_validated(crecp)) ))
|
||||
{
|
||||
do
|
||||
{
|
||||
@@ -1971,7 +2028,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
|
||||
/* If the client asked for DNSSEC don't use cached data. */
|
||||
if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) ||
|
||||
(rd_bit && (!do_bit || cache_validated(crecp)) ))
|
||||
(rd_bit && (!do_bit || cache_not_validated(crecp)) ))
|
||||
do
|
||||
{
|
||||
int stale_flag = 0;
|
||||
@@ -2153,19 +2210,19 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
|
||||
if (!ans)
|
||||
{
|
||||
if ((crecp = cache_find_by_name(NULL, name, now, F_RR | F_NXDOMAIN)) &&
|
||||
rd_bit && (!do_bit || cache_validated(crecp)))
|
||||
if ((crecp = cache_find_by_name(NULL, name, now, F_RR | F_NXDOMAIN)) && rd_bit)
|
||||
do
|
||||
{
|
||||
int flags = crecp->flags;
|
||||
unsigned short rrtype;
|
||||
|
||||
|
||||
if (flags & F_KEYTAG)
|
||||
rrtype = crecp->addr.rrblock.rrtype;
|
||||
else
|
||||
rrtype = crecp->addr.rrdata.rrtype;
|
||||
|
||||
if ((flags & F_NXDOMAIN) || rrtype == qtype)
|
||||
if (((flags & F_NXDOMAIN) || rrtype == qtype) &&
|
||||
(!do_bit || cache_not_validated(crecp)))
|
||||
{
|
||||
char *rrdata = NULL;
|
||||
unsigned short rrlen = 0;
|
||||
@@ -2228,14 +2285,15 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
}
|
||||
|
||||
|
||||
if (qtype != T_ANY && !ans && rr_on_list(daemon->filter_rr, qtype))
|
||||
if (qtype != T_ANY && !ans && rr_on_list(daemon->filter_rr, qtype) && !do_bit)
|
||||
{
|
||||
/* We don't have a cached answer and when we get an answer from upstream we're going to
|
||||
filter it anyway. If we have a cached answer for the domain for another RRtype then
|
||||
that may be enough to tell us if the answer should be NODATA and save the round trip.
|
||||
Cached NXDOMAIN has already been handled, so here we look for any record for the domain,
|
||||
since its existence allows us to return a NODATA answer. Note that we never set the AD flag,
|
||||
since we didn't authenticate the record. */
|
||||
since we didn't authenticate the record; this doesn't work if we want auth data, so
|
||||
don't use this shortcut in that case. */
|
||||
|
||||
if (cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_RR | F_CNAME))
|
||||
{
|
||||
@@ -2334,7 +2392,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
if (!(ansp = skip_questions(header, qlen)))
|
||||
return 0; /* bad packet */
|
||||
anscount = nscount = addncount = 0;
|
||||
log_query(F_CONFIG, "reply", NULL, "truncated", 0);
|
||||
log_query(0, "reply", NULL, "truncated", 0);
|
||||
}
|
||||
|
||||
if (nxdomain)
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-4
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 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
|
||||
@@ -345,9 +345,6 @@ int expand_workspace(unsigned char ***wkspc, int *szp, int new)
|
||||
if (old >= new+1)
|
||||
return 1;
|
||||
|
||||
if (new >= 100)
|
||||
return 0;
|
||||
|
||||
new += 5;
|
||||
|
||||
if (!(p = whine_realloc(*wkspc, new * sizeof(unsigned char *))))
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 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
|
||||
|
||||
+9
-8
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 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
|
||||
@@ -742,15 +742,16 @@ static void free_transfer(struct tftp_transfer *transfer)
|
||||
|
||||
static char *next(char **p, char *end)
|
||||
{
|
||||
char *ret = *p;
|
||||
size_t len;
|
||||
char *n, *ret = *p;
|
||||
|
||||
/* Look for end of string, without running off the end of the packet. */
|
||||
for (n = *p; n < end && *n != 0; n++);
|
||||
|
||||
if (*(end-1) != 0 ||
|
||||
*p == end ||
|
||||
(len = strlen(ret)) == 0)
|
||||
/* ran off the end or zero length string - failed */
|
||||
if (n == end || n == ret)
|
||||
return NULL;
|
||||
|
||||
*p += len + 1;
|
||||
|
||||
*p = n + 1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 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
|
||||
|
||||
+29
-24
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2025 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
|
||||
@@ -767,10 +767,10 @@ int retry_send(ssize_t rc)
|
||||
|
||||
/* rw = 0 -> write
|
||||
rw = 1 -> read
|
||||
rw = 2 -> read once
|
||||
rw = 3 -> write once
|
||||
rw = 2 -> write once
|
||||
rw = 3 -> read once
|
||||
|
||||
"once" fail if all the data doesn't arrive/go in a single read/write.
|
||||
"once" fails on EAGAIN, as this a timeout.
|
||||
This indicates a timeout of a TCP socket.
|
||||
*/
|
||||
int read_write(int fd, unsigned char *packet, int size, int rw)
|
||||
@@ -779,29 +779,34 @@ int read_write(int fd, unsigned char *packet, int size, int rw)
|
||||
|
||||
for (done = 0; done < size; done += n)
|
||||
{
|
||||
do {
|
||||
if (rw & 1)
|
||||
n = read(fd, &packet[done], (size_t)(size - done));
|
||||
else
|
||||
n = write(fd, &packet[done], (size_t)(size - done));
|
||||
|
||||
if (n == 0)
|
||||
return 0;
|
||||
|
||||
if (n == -1 && errno == EINTR)
|
||||
continue;
|
||||
|
||||
/* "once" variant */
|
||||
if ((rw & 2) && n != size)
|
||||
return 0;
|
||||
|
||||
} while (n == -1 && (errno == EINTR || errno == ENOMEM || errno == ENOBUFS ||
|
||||
errno == EAGAIN || errno == EWOULDBLOCK));
|
||||
if (rw & 1)
|
||||
n = read(fd, &packet[done], (size_t)(size - done));
|
||||
else
|
||||
n = write(fd, &packet[done], (size_t)(size - done));
|
||||
|
||||
if (n == -1)
|
||||
if (n == 0)
|
||||
return 0;
|
||||
|
||||
if (n == -1)
|
||||
{
|
||||
n = 0; /* don't mess with counter when we loop. */
|
||||
|
||||
if (errno == EINTR || errno == ENOMEM || errno == ENOBUFS)
|
||||
continue;
|
||||
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK)
|
||||
{
|
||||
/* "once" variant */
|
||||
if (rw & 2)
|
||||
return 0;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
Submodule
+1
Submodule submodules/dnsmasq-debian added at 9fe6b08a52.
Reference in new issue
Block a user