mirror of
http://thekelleys.org.uk/git/dnsmasq.git
synced 2026-11-04 13:48:30 +00:00
Compare commits
126
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
14e81b6976 | ||
|
|
6ce7f2d55a | ||
|
|
287d6bc88d | ||
|
|
c378d2c1de | ||
|
|
5846f749e5 | ||
|
|
c9a4240ec4 | ||
|
|
e7b87dee85 | ||
|
|
90b248582c | ||
|
|
ebef27f321 | ||
|
|
1861a881eb | ||
|
|
96bdb42d40 | ||
|
|
c7a909ad65 | ||
|
|
baf3c57af5 | ||
|
|
e48a2af4f5 | ||
|
|
91b800cc62 | ||
|
|
075e4a56b7 | ||
|
|
48658ebc54 | ||
|
|
692ed0dd32 | ||
|
|
43805c1859 | ||
|
|
4fbe1add95 | ||
|
|
57c7ae8fc0 | ||
|
|
d1008215dc | ||
|
|
b0aa604fcc | ||
|
|
8ddabd11bc | ||
|
|
98189ff988 | ||
|
|
e86d53c438 | ||
|
|
e127a972d1 | ||
|
|
a458c2bfb0 | ||
|
|
9e67099ce7 | ||
|
|
cfa1313e1f | ||
|
|
e3a2c8dadf | ||
|
|
95b74a7acf | ||
|
|
ae57f84061 | ||
|
|
0620309b73 | ||
|
|
942a35f517 | ||
|
|
83658efbf4 | ||
|
|
b0b4d90b6a | ||
|
|
bdce03f928 | ||
|
|
d390dc0338 | ||
|
|
105c25e561 | ||
|
|
67e07b7fe8 | ||
|
|
f5659b406b | ||
|
|
484fea238a | ||
|
|
1e587bec57 | ||
|
|
581c201aa8 | ||
|
|
5487f6979e | ||
|
|
99f12e3541 | ||
|
|
7c1212e3d1 | ||
|
|
0ccbdf8087 | ||
|
|
57f0489f38 | ||
|
|
3e659bd4ec | ||
|
|
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 | ||
|
|
5ef6c8c24f | ||
|
|
7c348a0b73 | ||
|
|
d578da0665 | ||
|
|
8949ef44b4 | ||
|
|
3ac11cdd98 | ||
|
|
5d49fa112d | ||
|
|
d29d19e654 | ||
|
|
49ea7db74e | ||
|
|
fcb40ee73d | ||
|
|
0f437b3b5e | ||
|
|
8a5fe8ce6b | ||
|
|
2a664c03db | ||
|
|
4902807879 | ||
|
|
742af6e4b9 | ||
|
|
3eb008c36d | ||
|
|
32248ebd5b |
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
|
||||
@@ -1,3 +1,67 @@
|
||||
version 2.92
|
||||
Redesign the interaction between DNSSEC validation and per-domain
|
||||
servers, specified as --server=/<domain>/<ip-address>. This should
|
||||
just work in all cases now. If the normal chain-of-trust exists into
|
||||
the delegated domain then whether the domain is signed or not, DNSSEC
|
||||
validation will function normally. In the case the delegated domain
|
||||
is an "overlay" on top of the global DNS and no NS and/or DS records
|
||||
exist connecting it to the global dns, then if the domain is
|
||||
unsigned the situation will be handled by synthesising a
|
||||
proof-of-non-existence-of-DS for the domain and queries will be
|
||||
answered unvalidated; this action will be logged. A signed domain
|
||||
without chain-of-trust can be validated if a suitable trust-anchor
|
||||
is provided using --trust-anchor. This change should be backwards
|
||||
compatible for all existing working configurations; it extends the
|
||||
space of possible configurations which are functional.
|
||||
|
||||
Fix a couple of problems with DNSSEC validation and DNAME. One
|
||||
could cause validation failure on correct domains, and the other
|
||||
would fail to spot an invalid domain. Thanks to Graham Clinch
|
||||
for spotting the problem.
|
||||
|
||||
Add --log-queries=auth option to only log replies from the auth DNS
|
||||
facility.
|
||||
|
||||
Fix some edge-cases with domains and --address and --server. There
|
||||
has been some regressions with this in previous releases. This change
|
||||
fixes the priority order from loqwer to highest as:
|
||||
--address with a IPv4 or IPv6 address (as long as the query matches the type)
|
||||
--address with # for all-zeros, as long as the query is A or AAAA)
|
||||
--address with no address, which returns NXDOMAIN or NOERROR for all types.
|
||||
--server with address set to # to use the unqualified servers.
|
||||
--server with matching domain.
|
||||
--server without domain or from /etc/resolv.conf.
|
||||
|
||||
Fix problems with ipset or nftset and TCP DNS transport. Previously
|
||||
this was racy, and insertion of addresses could fail on a busy server
|
||||
when DNS-over-TCP transport was involved.
|
||||
|
||||
DNSSEC validation change for reverse lookups in RFC-1918 ranges and friends.
|
||||
The large public DNS services seem not to return proof-of-nonexistence
|
||||
for DS records at the start of RFC-1918 in-addr.arpa domains and the their
|
||||
IPv6 equivalents. 10.in-addr.arpa, 168.192.in-addr.arpa etc.
|
||||
Since dnsmasq already has an option which instructs it not bother
|
||||
upstream servers with pointless queries about these address ranges,
|
||||
namely --bogus-priv, we extend that to enable behaviour which allows
|
||||
dnsmasq to assume that insecure NXDOMAIN replies for these domains
|
||||
are expected and to assume that the domains are legitimately unsigned.
|
||||
This behaviour only matters when some address range is directed to
|
||||
another upstream server using --rev-server. In that case it allows
|
||||
replies from that server to pass DNSSEC validation. Without such a
|
||||
server configured, queries are never sent upstream so they are never
|
||||
validated and the new behaviour is moot.
|
||||
|
||||
Add support for leasequery to the dnsmasq DHCPv4 server.
|
||||
This has to be specifically enabled with the --leasequery option.
|
||||
Many thanks to JAXPORT, Jacksonville Port Authority for sponsoring
|
||||
this enhancement to dnsmasq.
|
||||
|
||||
Fix failure to cache PTR RRs when a reply contains more than one answer.
|
||||
Thanks to Dmitry for spotting this.
|
||||
|
||||
Add TFTP options windowsize (RFC 7440) and timeout (RFC 2349).
|
||||
|
||||
|
||||
version 2.91
|
||||
Fix spurious "resource limit exceeded messages". Thanks to
|
||||
Dominik Derigs for the bug report.
|
||||
@@ -7,9 +71,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 +81,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 +100,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 +139,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 respect _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
|
||||
@@ -86,7 +201,7 @@ version 2.90
|
||||
for a particular domain. Thanks to Daniel Danzberger for
|
||||
spotting this bug.
|
||||
|
||||
Set the default maximum DNS UDP packet sice to 1232. This
|
||||
Set the default maximum DNS UDP packet size to 1232. This
|
||||
has been the recommended value since 2020 because it's the
|
||||
largest value that avoid fragmentation, and fragmentation
|
||||
is just not reliable on the modern internet, especially
|
||||
@@ -94,14 +209,14 @@ version 2.90
|
||||
--edns-packet-max for special circumstances.
|
||||
|
||||
Add --no-dhcpv4-interface and --no-dhcpv6-interface for
|
||||
better control over which inetrfaces are providing DHCP service.
|
||||
better control over which interfaces are providing DHCP service.
|
||||
|
||||
Fix issue with stale caching: After replying with stale data,
|
||||
dnsmasq sends the query upstream to refresh the cache asynchronously
|
||||
and sometimes sends the wrong packet: packet length can be wrong,
|
||||
and if an EDE marking stale data is added to the answer that can
|
||||
end up in the query also. This bug only seems to cause problems
|
||||
when the usptream server is a DOH/DOT proxy. Thanks to Justin He
|
||||
when the upstream server is a DOH/DOT proxy. Thanks to Justin He
|
||||
for the bug report.
|
||||
|
||||
Add configurable caching for arbitrary RR-types.
|
||||
@@ -139,7 +254,7 @@ version 2.90
|
||||
Applied Cybersecurity ATHENE for finding this vulnerability.
|
||||
|
||||
CVE 2023-50387 and CVE 2023-50868 apply.
|
||||
Note that the is a security vulnerablity only when DNSSEC validation
|
||||
Note that this a security vulnerability only when DNSSEC validation
|
||||
is enabled.
|
||||
|
||||
Fix memory-leak when attempting to cache SRV records with zero TTL.
|
||||
@@ -225,7 +340,7 @@ version 2.88
|
||||
upstream servers from /etc/resolv.conf or other sources that
|
||||
can change dnsmasq tries to avoid memory fragmentation by re-using
|
||||
existing records that are being re-read unchanged. This involves
|
||||
seaching all the server records for each new one installed.
|
||||
searching all the server records for each new one installed.
|
||||
During startup this search is pointless, and can cause long
|
||||
start times with thousands of --server options because the work
|
||||
needed is O(n^2). Handle this case more intelligently.
|
||||
@@ -288,7 +403,7 @@ version 2.87
|
||||
|
||||
Enhance --domain to accept, for instance,
|
||||
--domain=net2.thekelleys.org.uk,eth2 so that hosts get a domain
|
||||
which relects the interface they are attached to in a way which
|
||||
which reflects the interface they are attached to in a way which
|
||||
doesn't require hard-coding addresses. Thanks to Sten Spans for
|
||||
the idea.
|
||||
|
||||
@@ -662,22 +777,22 @@ version 2.80
|
||||
but those which used the default of no checking will need to be
|
||||
altered to explicitly select no checking. The new default is
|
||||
because switching off checking for unsigned replies is
|
||||
inherently dangerous. Not only does it open the possiblity of forged
|
||||
inherently dangerous. Not only does it open the possibility of forged
|
||||
replies, but it allows everything to appear to be working even
|
||||
when the upstream namesevers do not support DNSSEC, and in this
|
||||
case no DNSSEC validation at all is occuring.
|
||||
case no DNSSEC validation at all is occurring.
|
||||
|
||||
Fix DHCP broken-ness when --no-ping AND --dhcp-sequential-ip
|
||||
are set. Thanks to Daniel Miess for help with this.
|
||||
|
||||
Add a facilty to store DNS packets sent/recieved in a
|
||||
Add a facility to store DNS packets sent/received in a
|
||||
pcap-format file for later debugging. The file location
|
||||
is given by the --dumpfile option, and a bitmap controlling
|
||||
which packets should be dumped is given by the --dumpmask
|
||||
option.
|
||||
|
||||
Handle the case of both standard and constructed dhcp-ranges on the
|
||||
same interface better. We don't now contruct a dhcp-range if there's
|
||||
same interface better. We don't now construct a dhcp-range if there's
|
||||
already one specified. This allows the specified interface to
|
||||
have different parameters and avoids advertising the same
|
||||
prefix twice. Thanks to Luis Marsano for spotting this case.
|
||||
@@ -1147,7 +1262,7 @@ version 2.73
|
||||
|
||||
Use inotify for checking on updates to /etc/resolv.conf and
|
||||
friends under Linux. This fixes race conditions when the files are
|
||||
updated rapidly and saves CPU by noy polling. To build
|
||||
updated rapidly and saves CPU by not polling. To build
|
||||
a binary that runs on old Linux kernels without inotify,
|
||||
use make COPTS=-DNO_INOTIFY
|
||||
|
||||
@@ -1487,7 +1602,7 @@ version 2.68
|
||||
are dynamic and works much better than the previous
|
||||
work-around which exempted constructed DHCP ranges from the
|
||||
IP address filtering. As a consequence, that work-around
|
||||
is removed. Under certain circumstances, this change wil
|
||||
is removed. Under certain circumstances, this change will
|
||||
break existing configuration: if you're relying on the
|
||||
constructed-range exception, you need to change --auth-zone
|
||||
to specify the same interface as is used to construct your
|
||||
@@ -1944,7 +2059,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 \
|
||||
@@ -102,9 +102,7 @@ clean : mostly_clean
|
||||
rm -f core */core
|
||||
rm -f *~ contrib/*/*~ */*~
|
||||
|
||||
install : all install-common
|
||||
|
||||
install-common :
|
||||
install : all
|
||||
$(INSTALL) -d $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL) -d $(DESTDIR)$(MANDIR)/man8
|
||||
$(INSTALL) -m 644 $(MAN)/dnsmasq.8 $(DESTDIR)$(MANDIR)/man8
|
||||
@@ -121,7 +119,11 @@ all-i18n : $(BUILDDIR)
|
||||
cd $(top) && cd $(BUILDDIR) && $(MAKE) top="$(top)" -f $(top)/Makefile $${f%.po}.mo; \
|
||||
done
|
||||
|
||||
install-i18n : all-i18n install-common
|
||||
install-i18n : all-i18n
|
||||
$(INSTALL) -d $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL) -d $(DESTDIR)$(MANDIR)/man8
|
||||
$(INSTALL) -m 644 $(MAN)/dnsmasq.8 $(DESTDIR)$(MANDIR)/man8
|
||||
$(INSTALL) -m 755 $(BUILDDIR)/dnsmasq $(DESTDIR)$(BINDIR)
|
||||
cd $(BUILDDIR); $(top)/bld/install-mo $(DESTDIR)$(LOCALEDIR) $(INSTALL)
|
||||
cd $(MAN); ../bld/install-man $(DESTDIR)$(MANDIR) $(INSTALL)
|
||||
|
||||
|
||||
@@ -0,0 +1,695 @@
|
||||
/* leasequery is Copyright (c) 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
|
||||
the Free Software Foundation; version 2 dated June, 1991, or
|
||||
(at your option) version 3 dated 29 June, 2007.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Author's email: simon@thekelleys.org.uk */
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/stat.h>
|
||||
#include <limits.h>
|
||||
#include <net/if.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
#include <netdb.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/capability.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <ctype.h>
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned long long u64;
|
||||
|
||||
#define INADDRSZ 4
|
||||
#define ADDRSTRLEN 46
|
||||
|
||||
#define option_len(opt) ((int)(((unsigned char *)(opt))[1]))
|
||||
#define option_ptr(opt, i) ((void *)&(((unsigned char *)(opt))[2u+(unsigned int)(i)]))
|
||||
|
||||
#define DHCP_CHADDR_MAX 16
|
||||
#define DHCP_SERVER_PORT 67
|
||||
#define DHCP_CLIENT_PORT 68
|
||||
#define BOOTREQUEST 1
|
||||
#define BOOTREPLY 2
|
||||
|
||||
#define DHCP_COOKIE 0x63825363
|
||||
|
||||
#define DHCPLEASEQUERY 10
|
||||
#define DHCPLEASEUNASSIGNED 11
|
||||
#define DHCPLEASEUNKNOWN 12
|
||||
#define DHCPLEASEACTIVE 13
|
||||
|
||||
#define OPTION_END 255
|
||||
#define OPTION_ROUTER 3
|
||||
#define OPTION_VENDOR_CLASS_OPT 43
|
||||
#define OPTION_LEASE_TIME 51
|
||||
#define OPTION_MESSAGE_TYPE 53
|
||||
#define OPTION_SERVER_IDENTIFIER 54
|
||||
#define OPTION_REQUESTED_OPTIONS 55
|
||||
#define OPTION_VENDOR_ID 60
|
||||
#define OPTION_CLIENT_ID 61
|
||||
|
||||
/* flags in top of length field for DHCP-option tables */
|
||||
#define OT_ADDR_LIST 0x8000
|
||||
#define OT_RFC1035_NAME 0x4000
|
||||
#define OT_INTERNAL 0x2000
|
||||
#define OT_NAME 0x1000
|
||||
#define OT_CSTRING 0x0800
|
||||
#define OT_DEC 0x0400
|
||||
#define OT_TIME 0x0200
|
||||
|
||||
#define GETSHORT(s, cp) do { \
|
||||
unsigned char *t_cp = (unsigned char *)(cp); \
|
||||
(s) = ((u16)t_cp[0] << 8) \
|
||||
| ((u16)t_cp[1]) \
|
||||
; \
|
||||
(cp) += 2; \
|
||||
} while(0)
|
||||
|
||||
#define GETLONG(l, cp) do { \
|
||||
unsigned char *t_cp = (unsigned char *)(cp); \
|
||||
(l) = ((u32)t_cp[0] << 24) \
|
||||
| ((u32)t_cp[1] << 16) \
|
||||
| ((u32)t_cp[2] << 8) \
|
||||
| ((u32)t_cp[3]) \
|
||||
; \
|
||||
(cp) += 4; \
|
||||
} while (0)
|
||||
|
||||
#define PUTSHORT(s, cp) do { \
|
||||
u16 t_s = (u16)(s); \
|
||||
unsigned char *t_cp = (unsigned char *)(cp); \
|
||||
*t_cp++ = t_s >> 8; \
|
||||
*t_cp = t_s; \
|
||||
(cp) += 2; \
|
||||
} while(0)
|
||||
|
||||
#define PUTLONG(l, cp) do { \
|
||||
u32 t_l = (u32)(l); \
|
||||
unsigned char *t_cp = (unsigned char *)(cp); \
|
||||
*t_cp++ = t_l >> 24; \
|
||||
*t_cp++ = t_l >> 16; \
|
||||
*t_cp++ = t_l >> 8; \
|
||||
*t_cp = t_l; \
|
||||
(cp) += 4; \
|
||||
} while (0)
|
||||
|
||||
union all_addr {
|
||||
struct in_addr addr4;
|
||||
struct in6_addr addr6;
|
||||
};
|
||||
|
||||
struct dhcp_packet_with_opts{
|
||||
struct dhcp_packet {
|
||||
unsigned char op, htype, hlen, hops;
|
||||
unsigned int xid;
|
||||
unsigned short secs, flags;
|
||||
struct in_addr ciaddr, yiaddr, siaddr, giaddr;
|
||||
unsigned char chaddr[DHCP_CHADDR_MAX], sname[64], file[128];
|
||||
} header;
|
||||
unsigned char options[312];
|
||||
};
|
||||
|
||||
static const struct opttab_t {
|
||||
char *name;
|
||||
u16 val, size;
|
||||
} opttab[] = {
|
||||
{ "netmask", 1, OT_ADDR_LIST },
|
||||
{ "time-offset", 2, 4 },
|
||||
{ "router", 3, OT_ADDR_LIST },
|
||||
{ "dns-server", 6, OT_ADDR_LIST },
|
||||
{ "log-server", 7, OT_ADDR_LIST },
|
||||
{ "lpr-server", 9, OT_ADDR_LIST },
|
||||
{ "hostname", 12, OT_INTERNAL | OT_NAME },
|
||||
{ "boot-file-size", 13, 2 | OT_DEC },
|
||||
{ "domain-name", 15, OT_NAME },
|
||||
{ "swap-server", 16, OT_ADDR_LIST },
|
||||
{ "root-path", 17, OT_NAME },
|
||||
{ "extension-path", 18, OT_NAME },
|
||||
{ "ip-forward-enable", 19, 1 },
|
||||
{ "non-local-source-routing", 20, 1 },
|
||||
{ "policy-filter", 21, OT_ADDR_LIST },
|
||||
{ "max-datagram-reassembly", 22, 2 | OT_DEC },
|
||||
{ "default-ttl", 23, 1 | OT_DEC },
|
||||
{ "mtu", 26, 2 | OT_DEC },
|
||||
{ "all-subnets-local", 27, 1 },
|
||||
{ "broadcast", 28, OT_INTERNAL | OT_ADDR_LIST },
|
||||
{ "router-discovery", 31, 1 },
|
||||
{ "router-solicitation", 32, OT_ADDR_LIST },
|
||||
{ "static-route", 33, OT_ADDR_LIST },
|
||||
{ "trailer-encapsulation", 34, 1 },
|
||||
{ "arp-timeout", 35, 4 | OT_DEC },
|
||||
{ "ethernet-encap", 36, 1 },
|
||||
{ "tcp-ttl", 37, 1 },
|
||||
{ "tcp-keepalive", 38, 4 | OT_DEC },
|
||||
{ "nis-domain", 40, OT_NAME },
|
||||
{ "nis-server", 41, OT_ADDR_LIST },
|
||||
{ "ntp-server", 42, OT_ADDR_LIST },
|
||||
{ "vendor-encap", 43, OT_INTERNAL },
|
||||
{ "netbios-ns", 44, OT_ADDR_LIST },
|
||||
{ "netbios-dd", 45, OT_ADDR_LIST },
|
||||
{ "netbios-nodetype", 46, 1 },
|
||||
{ "netbios-scope", 47, 0 },
|
||||
{ "x-windows-fs", 48, OT_ADDR_LIST },
|
||||
{ "x-windows-dm", 49, OT_ADDR_LIST },
|
||||
{ "requested-address", 50, OT_INTERNAL | OT_ADDR_LIST },
|
||||
{ "lease-time", 51, OT_INTERNAL | OT_TIME },
|
||||
{ "option-overload", 52, OT_INTERNAL },
|
||||
{ "message-type", 53, OT_INTERNAL | OT_DEC },
|
||||
{ "server-identifier", 54, OT_INTERNAL | OT_ADDR_LIST },
|
||||
{ "parameter-request", 55, OT_INTERNAL },
|
||||
{ "message", 56, OT_INTERNAL },
|
||||
{ "max-message-size", 57, OT_INTERNAL },
|
||||
{ "T1", 58, OT_TIME},
|
||||
{ "T2", 59, OT_TIME},
|
||||
{ "vendor-class", 60, OT_NAME },
|
||||
{ "client-id", 61, OT_INTERNAL },
|
||||
{ "nis+-domain", 64, OT_NAME },
|
||||
{ "nis+-server", 65, OT_ADDR_LIST },
|
||||
{ "tftp-server", 66, OT_NAME },
|
||||
{ "bootfile-name", 67, OT_NAME },
|
||||
{ "mobile-ip-home", 68, OT_ADDR_LIST },
|
||||
{ "smtp-server", 69, OT_ADDR_LIST },
|
||||
{ "pop3-server", 70, OT_ADDR_LIST },
|
||||
{ "nntp-server", 71, OT_ADDR_LIST },
|
||||
{ "irc-server", 74, OT_ADDR_LIST },
|
||||
{ "user-class", 77, 0 },
|
||||
{ "rapid-commit", 80, 0 },
|
||||
{ "FQDN", 81, OT_INTERNAL },
|
||||
{ "agent-info", 82, OT_INTERNAL },
|
||||
{ "last-transaction", 91, 4 | OT_TIME },
|
||||
{ "associated-ip", 92, OT_ADDR_LIST },
|
||||
{ "client-arch", 93, 2 | OT_DEC },
|
||||
{ "client-interface-id", 94, 0 },
|
||||
{ "client-machine-id", 97, 0 },
|
||||
{ "posix-timezone", 100, OT_NAME }, /* RFC 4833, Sec. 2 */
|
||||
{ "tzdb-timezone", 101, OT_NAME }, /* RFC 4833, Sec. 2 */
|
||||
{ "ipv6-only", 108, 4 | OT_DEC }, /* RFC 8925 */
|
||||
{ "subnet-select", 118, OT_INTERNAL },
|
||||
{ "domain-search", 119, OT_RFC1035_NAME },
|
||||
{ "sip-server", 120, 0 },
|
||||
{ "classless-static-route", 121, 0 },
|
||||
{ "vendor-id-encap", 125, 0 },
|
||||
{ "tftp-server-address", 150, OT_ADDR_LIST },
|
||||
{ "server-ip-address", 255, OT_ADDR_LIST }, /* special, internal only, sets siaddr */
|
||||
{ NULL, 0, 0 }
|
||||
};
|
||||
|
||||
static void prettyprint_time(char *buf, unsigned int t);
|
||||
static char *print_mac(char *buff, unsigned char *mac, int len);
|
||||
|
||||
int lookup_dhcp_opt(char *name)
|
||||
{
|
||||
const struct opttab_t *t = opttab;
|
||||
int i;
|
||||
|
||||
for (i = 0; t[i].name; i++)
|
||||
if (strcasecmp(t[i].name, name) == 0)
|
||||
return t[i].val;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *option_string(unsigned int opt, unsigned char *val, int opt_len, char *buf, int buf_len)
|
||||
{
|
||||
int o, i, j, nodecode = 0;
|
||||
const struct opttab_t *ot = opttab;
|
||||
char addrbuff[ADDRSTRLEN];
|
||||
|
||||
for (o = 0; ot[o].name; o++)
|
||||
if (ot[o].val == opt)
|
||||
{
|
||||
if (buf)
|
||||
{
|
||||
memset(buf, 0, buf_len);
|
||||
|
||||
if (ot[o].size & OT_ADDR_LIST)
|
||||
{
|
||||
union all_addr addr;
|
||||
int addr_len = INADDRSZ;
|
||||
|
||||
for (buf[0]= 0, i = 0; i <= opt_len - addr_len; i += addr_len)
|
||||
{
|
||||
if (i != 0)
|
||||
strncat(buf, ", ", buf_len - strlen(buf));
|
||||
/* align */
|
||||
memcpy(&addr, &val[i], addr_len);
|
||||
inet_ntop(AF_INET, &val[i], addrbuff, ADDRSTRLEN);
|
||||
strncat(buf, addrbuff, buf_len - strlen(buf));
|
||||
}
|
||||
}
|
||||
else if (ot[o].size & OT_NAME)
|
||||
for (i = 0, j = 0; i < opt_len && j < buf_len ; i++)
|
||||
{
|
||||
char c = val[i];
|
||||
if (isprint((unsigned char)c))
|
||||
buf[j++] = c;
|
||||
}
|
||||
else if ((ot[o].size & (OT_DEC | OT_TIME)) && opt_len != 0)
|
||||
{
|
||||
unsigned int dec = 0;
|
||||
|
||||
for (i = 0; i < opt_len; i++)
|
||||
dec = (dec << 8) | val[i];
|
||||
|
||||
if (ot[o].size & OT_TIME)
|
||||
prettyprint_time(buf, dec);
|
||||
else
|
||||
sprintf(buf, "%u", dec);
|
||||
}
|
||||
else
|
||||
nodecode = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (opt_len != 0 && buf && (!ot[o].name || nodecode))
|
||||
{
|
||||
int trunc = 0;
|
||||
if (opt_len > 14)
|
||||
{
|
||||
trunc = 1;
|
||||
opt_len = 14;
|
||||
}
|
||||
print_mac(buf, val, opt_len);
|
||||
if (trunc)
|
||||
strncat(buf, "...", buf_len - strlen(buf));
|
||||
|
||||
|
||||
}
|
||||
|
||||
return ot[o].name ? ot[o].name : "";
|
||||
|
||||
}
|
||||
|
||||
static void prettyprint_time(char *buf, unsigned int t)
|
||||
{
|
||||
if (t == 0xffffffff)
|
||||
sprintf(buf, "infinite");
|
||||
else
|
||||
{
|
||||
unsigned int x, p = 0;
|
||||
if ((x = t/86400))
|
||||
p += sprintf(&buf[p], "%ud", x);
|
||||
if ((x = (t/3600)%24))
|
||||
p += sprintf(&buf[p], "%uh", x);
|
||||
if ((x = (t/60)%60))
|
||||
p += sprintf(&buf[p], "%um", x);
|
||||
if ((x = t%60))
|
||||
sprintf(&buf[p], "%us", x);
|
||||
}
|
||||
}
|
||||
|
||||
static char *print_mac(char *buff, unsigned char *mac, int len)
|
||||
{
|
||||
char *p = buff;
|
||||
int i;
|
||||
|
||||
if (len == 0)
|
||||
sprintf(p, "<null>");
|
||||
else
|
||||
for (i = 0; i < len; i++)
|
||||
p += sprintf(p, "%.2x%s", mac[i], (i == len - 1) ? "" : ":");
|
||||
|
||||
return buff;
|
||||
}
|
||||
|
||||
static unsigned char *dhcp_skip_opts(struct dhcp_packet_with_opts *pktp)
|
||||
{
|
||||
unsigned char *start = (unsigned char *)(((unsigned int *)&pktp->options[0]) + 1);
|
||||
while (*start != 0)
|
||||
start += start[1] + 2;
|
||||
return start;
|
||||
}
|
||||
|
||||
static unsigned char *dhcp_find_opt(struct dhcp_packet_with_opts *pktp, int optno)
|
||||
{
|
||||
unsigned char *start = (unsigned char *)(((unsigned int *)&pktp->options[0]) + 1);
|
||||
while (*start != OPTION_END)
|
||||
{
|
||||
if (*start == optno)
|
||||
return start;
|
||||
start += start[1] + 2;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void option_put(struct dhcp_packet_with_opts *pktp, int opt, int len, unsigned int val)
|
||||
{
|
||||
int i;
|
||||
unsigned char *p = dhcp_skip_opts(pktp);
|
||||
|
||||
if (p)
|
||||
{
|
||||
*p++ = opt;
|
||||
*p++ = len;
|
||||
for (i = 0; i < len; i++)
|
||||
*(p++) = val >> (8 * (len - (i + 1)));
|
||||
}
|
||||
}
|
||||
|
||||
static void option_put_string(struct dhcp_packet_with_opts *pktp, int opt,
|
||||
const char *string, int null_term)
|
||||
{
|
||||
unsigned char *p;
|
||||
size_t len = strlen(string);
|
||||
|
||||
if (null_term && len != 255)
|
||||
len++;
|
||||
|
||||
if ((p = dhcp_skip_opts(pktp)))
|
||||
{
|
||||
*p++ = opt;
|
||||
*p++ = len;
|
||||
memcpy(p, string, len);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* in may equal out, when maxlen may be -1 (No max len).
|
||||
Return -1 for extraneous no-hex chars found. */
|
||||
int parse_hex(char *in, unsigned char *out, int maxlen,
|
||||
unsigned int *wildcard_mask, int *mac_type)
|
||||
{
|
||||
int done = 0, mask = 0, i = 0;
|
||||
char *r;
|
||||
|
||||
if (mac_type)
|
||||
*mac_type = 0;
|
||||
|
||||
while (!done && (maxlen == -1 || i < maxlen))
|
||||
{
|
||||
for (r = in; *r != 0 && *r != ':' && *r != '-' && *r != ' '; r++)
|
||||
if (*r != '*' && !isxdigit((unsigned char)*r))
|
||||
return -1;
|
||||
|
||||
if (*r == 0)
|
||||
done = 1;
|
||||
|
||||
if (r != in )
|
||||
{
|
||||
if (*r == '-' && i == 0 && mac_type)
|
||||
{
|
||||
*r = 0;
|
||||
*mac_type = strtol(in, NULL, 16);
|
||||
mac_type = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
*r = 0;
|
||||
if (strcmp(in, "*") == 0)
|
||||
{
|
||||
mask = (mask << 1) | 1;
|
||||
i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
int j, bytes = (1 + (r - in))/2;
|
||||
for (j = 0; j < bytes; j++)
|
||||
{
|
||||
char sav;
|
||||
if (j < bytes - 1)
|
||||
{
|
||||
sav = in[(j+1)*2];
|
||||
in[(j+1)*2] = 0;
|
||||
}
|
||||
/* checks above allow mix of hexdigit and *, which
|
||||
is illegal. */
|
||||
if (strchr(&in[j*2], '*'))
|
||||
return -1;
|
||||
out[i] = strtol(&in[j*2], NULL, 16);
|
||||
mask = mask << 1;
|
||||
if (++i == maxlen)
|
||||
break;
|
||||
if (j < bytes - 1)
|
||||
in[(j+1)*2] = sav;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
in = r+1;
|
||||
}
|
||||
|
||||
if (wildcard_mask)
|
||||
*wildcard_mask = mask;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static char *split_chr(char *s, char c)
|
||||
{
|
||||
char *comma, *p;
|
||||
|
||||
if (!s || !(comma = strchr(s, c)))
|
||||
return NULL;
|
||||
|
||||
p = comma;
|
||||
*comma = ' ';
|
||||
|
||||
for (; *comma == ' '; comma++);
|
||||
|
||||
for (; (p >= s) && *p == ' '; p--)
|
||||
*p = 0;
|
||||
|
||||
return comma;
|
||||
}
|
||||
|
||||
static char *split(char *s)
|
||||
{
|
||||
return split_chr(s, ',');
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int fd;
|
||||
struct ifreq ifr;
|
||||
struct in_addr iface_addr, server_addr, lease_addr;
|
||||
struct dhcp_packet_with_opts pkt;
|
||||
struct sockaddr_in saddr;
|
||||
unsigned char *p;
|
||||
ssize_t sz;
|
||||
unsigned char mac[DHCP_CHADDR_MAX], clid[256], req_options[256];
|
||||
char buff[500];
|
||||
int clid_len = 0, mac_len = 0, mac_type = 0, opts_len = 0;
|
||||
unsigned short port = DHCP_SERVER_PORT;
|
||||
unsigned int xid;
|
||||
struct timeval tv;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
srand(tv.tv_usec);
|
||||
xid = rand();
|
||||
server_addr.s_addr = lease_addr.s_addr = iface_addr.s_addr = 0;
|
||||
|
||||
if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
|
||||
{
|
||||
perror("leasequery: cannot create socket");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
int option = getopt(argc, argv, "i:s:l:m:c:p:r:");
|
||||
|
||||
if (option == -1)
|
||||
break;
|
||||
|
||||
switch (option)
|
||||
{
|
||||
default:
|
||||
fprintf(stderr,
|
||||
"-p <port> port on DHCP server.\n"
|
||||
"-i <interface> exit interface to DHCP server.\n"
|
||||
"-s <inet-addr> DHCP server address.\n"
|
||||
"-l <inet-addr> Query lease by IP address.\n"
|
||||
"-m <MAC-addr> Query lease by MAC address.\n"
|
||||
"-c <hex> Query lease by client-id.\n"
|
||||
"-r <name>|<int>[,...] List of options to return.\n");
|
||||
exit(1);
|
||||
|
||||
case 'p':
|
||||
port = atoi(optarg);
|
||||
break;
|
||||
|
||||
case 'i':
|
||||
strncpy(ifr.ifr_name, optarg, IF_NAMESIZE);
|
||||
ifr.ifr_addr.sa_family = AF_INET;
|
||||
if (ioctl(fd, SIOCGIFADDR, &ifr) == -1)
|
||||
{
|
||||
perror("leasequery: cannot get interface address");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
|
||||
break;
|
||||
|
||||
case 's':
|
||||
if (inet_pton(AF_INET, optarg, &server_addr) <= 0)
|
||||
{
|
||||
fprintf(stderr, "leasequery: bad server address\n");
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
if (inet_pton(AF_INET, optarg, &lease_addr) <= 0)
|
||||
{
|
||||
fprintf(stderr, "leasequery: bad lease address\n");
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'm':
|
||||
mac_type = 1; /* default ethernet */
|
||||
mac_len = parse_hex(optarg, mac, DHCP_CHADDR_MAX, NULL, &mac_type);
|
||||
if (!mac_type)
|
||||
mac_type = 1; /* default ethernet */
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
clid_len = parse_hex(optarg, clid, 256, NULL, NULL);
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
{
|
||||
char *comma;
|
||||
int opt;
|
||||
|
||||
while (optarg)
|
||||
{
|
||||
comma = split(optarg);
|
||||
|
||||
if ((opt = lookup_dhcp_opt(optarg)) != -1 || (opt = atoi(optarg)) != 0)
|
||||
req_options[opts_len++] = opt;
|
||||
|
||||
optarg = comma;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!server_addr.s_addr)
|
||||
{
|
||||
fprintf(stderr, "leasequery: no server address\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
memset(&pkt, 0, sizeof pkt);
|
||||
pkt.header.op = BOOTREQUEST;
|
||||
pkt.header.xid = xid;
|
||||
pkt.header.ciaddr = lease_addr;
|
||||
pkt.header.hlen = mac_len;
|
||||
pkt.header.htype = mac_type;
|
||||
memcpy(pkt.header.chaddr, mac, mac_len);
|
||||
*((unsigned int *)&pkt.options[0]) = htonl(DHCP_COOKIE);
|
||||
|
||||
/* Dnsmasq extension. */
|
||||
pkt.header.giaddr.s_addr = iface_addr.s_addr ? iface_addr.s_addr : INADDR_BROADCAST;
|
||||
|
||||
option_put(&pkt, OPTION_MESSAGE_TYPE, 1, DHCPLEASEQUERY);
|
||||
|
||||
if (clid_len != 0)
|
||||
{
|
||||
p = dhcp_skip_opts(&pkt);
|
||||
*p++ = OPTION_CLIENT_ID;
|
||||
*p++ = clid_len;
|
||||
memcpy(p, clid, clid_len);
|
||||
}
|
||||
|
||||
if (opts_len != 0)
|
||||
{
|
||||
p = dhcp_skip_opts(&pkt);
|
||||
*p++ = OPTION_REQUESTED_OPTIONS;
|
||||
*p++ = opts_len;
|
||||
memcpy(p, req_options, opts_len);
|
||||
}
|
||||
|
||||
if (iface_addr.s_addr)
|
||||
{
|
||||
saddr.sin_family = AF_INET;
|
||||
saddr.sin_port = htons(port);
|
||||
saddr.sin_addr.s_addr = iface_addr.s_addr;
|
||||
|
||||
if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in)))
|
||||
{
|
||||
perror("leasequery: cannot bind DHCP server socket");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
saddr.sin_family = AF_INET;
|
||||
saddr.sin_port = htons(port);
|
||||
saddr.sin_addr = server_addr;
|
||||
|
||||
while((sz = sendto(fd, &pkt, dhcp_skip_opts(&pkt) - ((unsigned char *)&pkt), 0, (struct sockaddr *)&saddr, sizeof(saddr))) == -1 &&
|
||||
errno == EINTR);
|
||||
|
||||
if (sz == -1)
|
||||
{
|
||||
perror("leasequery: sendto()");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
sz = recv(fd, &pkt, sizeof(pkt), 0);
|
||||
|
||||
if (sz == -1)
|
||||
{
|
||||
perror("leasequery: recv()");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (sz >= sizeof(pkt.header) &&
|
||||
pkt.header.op == BOOTREPLY &&
|
||||
(p = dhcp_find_opt(&pkt, OPTION_MESSAGE_TYPE)) &&
|
||||
pkt.header.xid == xid)
|
||||
{
|
||||
if (p[2] == DHCPLEASEUNASSIGNED)
|
||||
printf("UNASSIGNED\n");
|
||||
else if (p[2] == DHCPLEASEUNKNOWN)
|
||||
printf("UNKNOWN\n");
|
||||
else if (p[2] == DHCPLEASEACTIVE)
|
||||
{
|
||||
print_mac(buff, pkt.header.chaddr, pkt.header.hlen);
|
||||
printf("ACTIVE %s %s\n", inet_ntoa(pkt.header.ciaddr), buff);
|
||||
|
||||
p = (unsigned char *)(((unsigned int *)&pkt.options[0]) + 1);
|
||||
|
||||
while (*p != OPTION_END)
|
||||
{
|
||||
if (*p != OPTION_MESSAGE_TYPE)
|
||||
{
|
||||
char *optname = option_string(p[0], option_ptr(p, 0), option_len(p), buff, 500);
|
||||
|
||||
printf("size:%3d option:%3d %s %s\n", option_len(p), p[0], optname, buff);
|
||||
}
|
||||
p += p[1] + 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-1
Submodule debian deleted from 83e05da879.
+153
-108
@@ -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
|
||||
@@ -134,7 +134,8 @@ only, to stop dnsmasq daemonising in production, use
|
||||
Log the results of DNS queries handled by dnsmasq. Enable a full cache dump on receipt of SIGUSR1. If the argument "extra" is supplied, ie
|
||||
.B --log-queries=extra
|
||||
then the log has extra information at the start of each line.
|
||||
This consists of a serial number which ties together the log lines associated with an individual query, and the IP address of the requestor. If the argument "proto" is supplied, this shows everything that "extra" does and also the network protocol used to communicate the queries.
|
||||
This consists of a serial number which ties together the log lines associated with an individual query, and the IP address of the requestor. If the argument "proto" is supplied, this shows everything that "extra" does and also the network protocol used to communicate the queries. Logging of only queries to the authoritative server can be configured with
|
||||
.B --log-queries=auth
|
||||
.TP
|
||||
.B \-8, --log-facility=<facility>
|
||||
Set the facility to which dnsmasq will send syslog entries, this
|
||||
@@ -345,6 +346,10 @@ Bogus private reverse lookups. All reverse lookups for private IP ranges (ie 192
|
||||
which are not found in /etc/hosts or the DHCP leases file are answered
|
||||
with "no such domain" rather than being forwarded upstream. The
|
||||
set of prefixes affected is the list given in RFC6303, for IPv4 and IPv6.
|
||||
Enabling this also subtly alters DNSSEC validation for reverse lookups in the
|
||||
private ranges such that a non-secure DS record is accepted as proof that
|
||||
the range is not signed. This works around behaviour by the public DNS services
|
||||
which seem not to return validated proof-of-non-existence for DS records in these domains.
|
||||
.TP
|
||||
.B \-V, --alias=[<old-ip>]|[<start-ip>-<end-ip>],<new-ip>[,<mask>]
|
||||
Modify IPv4 addresses returned from upstream nameservers; old-ip is
|
||||
@@ -498,10 +503,7 @@ xxx.internal.thekelleys.org.uk at 192.168.1.1 then giving the flag
|
||||
.B --server=/internal.thekelleys.org.uk/192.168.1.1
|
||||
will send all queries for
|
||||
internal machines to that nameserver, everything else will go to the
|
||||
servers in /etc/resolv.conf. DNSSEC validation is turned off for such
|
||||
private nameservers, UNLESS a
|
||||
.B --trust-anchor
|
||||
is specified for the domain in question. An empty domain specification,
|
||||
servers in /etc/resolv.conf. An empty domain specification,
|
||||
.B //
|
||||
has the special meaning of "unqualified names only" ie names without any
|
||||
dots in them. A non-standard port may be specified as
|
||||
@@ -852,6 +854,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
|
||||
@@ -864,7 +878,7 @@ Set the maximum number of concurrent DNS queries. The default value is
|
||||
150, which should be fine for most setups. The only known situation
|
||||
where this needs to be increased is when using web-server log file
|
||||
resolvers, which can generate large numbers of concurrent queries. This
|
||||
parameter actually controls the number of concurrent queries per server group, where a server group is the set of server(s) associated with a single domain. So if a domain has it's own server via --server=/example.com/1.2.3.4 and 1.2.3.4 is not responding, but queries for *.example.com cannot go elsewhere, then other queries will not be affected. On configurations with many such server groups and tight resources, this value may need to be reduced.
|
||||
parameter actually controls the number of concurrent queries per server group, where a server group is the set of server(s) associated with a single domain. So if a domain has its own server via --server=/example.com/1.2.3.4 and 1.2.3.4 is not responding, but queries for *.example.com cannot go elsewhere, then other queries will not be affected. On configurations with many such server groups and tight resources, this value may need to be reduced.
|
||||
.TP
|
||||
.B --dnssec
|
||||
Validate DNS replies and cache DNSSEC data. When forwarding DNS queries, dnsmasq requests the
|
||||
@@ -882,12 +896,15 @@ 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
|
||||
validation. The class defaults to IN. Typically these will be the DS record(s) for Key Signing
|
||||
key(s) (KSK) of the root zone,
|
||||
but trust anchors for limited domains are also possible. The current
|
||||
root-zone trust anchors may be downloaded from https://data.iana.org/root-anchors/root-anchors.xml
|
||||
but trust anchors for limited domains are also possible.
|
||||
A negative trust anchor (ie. proof that a DS record doesn't exist) may be configured be specifying
|
||||
only the name or only the name and class. This can be useful for forcing dnsmasq to treat zones delegated
|
||||
using \fB--server=/<domain>/<ip-address>\fP as unsigned. The current
|
||||
root-zone trust anchors may be downloaded from https://data.iana.org/root-anchors/root-anchors.xml
|
||||
.TP
|
||||
.B --dnssec-check-unsigned[=no]
|
||||
As a default, dnsmasq checks that unsigned DNS replies are
|
||||
@@ -1188,7 +1205,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 +1233,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 +1274,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 +1325,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 +1364,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 +1415,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
|
||||
@@ -1426,10 +1438,22 @@ DHCP options. This make extra space available in the DHCP packet for
|
||||
options but can, rarely, confuse old or broken clients. This flag
|
||||
forces "simple and safe" behaviour to avoid problems in such a case.
|
||||
.TP
|
||||
.B --leasequery[=<addr>[/<prefix>]]
|
||||
Enable RFC 4388 leasequery. The dnsmasq DHCP server will answer LEASEQUERY messages from DHCP relays
|
||||
when this option is given. If an address or subnet is given, only queries from a relay at that source are allowed. Repeat
|
||||
the --leasequery option to specify multiple allowed sources.
|
||||
To correctly answer lease queries it is necessary to store extra data in
|
||||
the lease database, and this is also enabled by the \fB--leasequery\fP option. The extra fields
|
||||
(agent-info and vendorclass) are stored in the leases file in a somewhat backwards compatible manner.
|
||||
Enabling and then disabling leasequery will not cause problems; the extra information will be aged
|
||||
out of the database. However, enabling leasequery in release 2.92 or later, storing leases
|
||||
which come via DHCP relays which add option-82 agent-info data, and then moving back to a pre-2.92 dnsmasq
|
||||
binary may cause problems reading the leases file. As of release 2.92, leasequery is only supported for DHCPv4.
|
||||
.TP
|
||||
.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 +1553,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 +2442,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 +2520,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
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# This revised version is (C) Copyright by
|
||||
# Matthias Andree <matthias.andree@gmx.de>, 2010 - 2021.
|
||||
# Conrad Kostecki <conrad@kostecki.com>, 2014 - 2022.
|
||||
# Conrad Kostecki <conrad@kostecki.com>, 2014 - 2024.
|
||||
# It is subject to the GNU General Public License v2,
|
||||
# or at your option, any later version.
|
||||
#
|
||||
@@ -10,10 +10,10 @@
|
||||
# Simon Kelley <simon@thekelleys.org.uk>, 2005.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: dnsmasq 2.88\n"
|
||||
"Project-Id-Version: dnsmasq 2.91\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-03-20 00:00+0000\n"
|
||||
"PO-Revision-Date: 2022-11-17 20:08+0100\n"
|
||||
"PO-Revision-Date: 2024-12-23 22:36+0100\n"
|
||||
"Last-Translator: Conrad Kostecki <conrad@kostecki.com>\n"
|
||||
"Language-Team: German <de@li.org>\n"
|
||||
"Language: de\n"
|
||||
@@ -21,7 +21,7 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"X-Generator: Poedit 3.2\n"
|
||||
"X-Generator: Poedit 3.5\n"
|
||||
|
||||
#: cache.c:652
|
||||
msgid "Internal error in cache."
|
||||
@@ -43,9 +43,9 @@ msgid "bad name at %s line %d"
|
||||
msgstr "Fehlerhafter Name in %s Zeile %d"
|
||||
|
||||
#: cache.c:1265
|
||||
#, fuzzy, c-format
|
||||
#, c-format
|
||||
msgid "read %s - %d names"
|
||||
msgstr "%s gelesen - %d Adressen"
|
||||
msgstr "%s gelesen - %d Namen"
|
||||
|
||||
#: cache.c:1381
|
||||
msgid "cleared cache"
|
||||
@@ -84,7 +84,7 @@ msgstr "weitergeleitete Anfragen %u, lokal beantwortete Anfragen %u"
|
||||
#: cache.c:1766
|
||||
#, c-format
|
||||
msgid "queries answered from stale cache %u"
|
||||
msgstr ""
|
||||
msgstr "Anfragen beantwortet vom veralteten Cache %u"
|
||||
|
||||
#: cache.c:1768
|
||||
#, c-format
|
||||
@@ -92,9 +92,9 @@ msgid "queries for authoritative zones %u"
|
||||
msgstr "Anfragen nach autoritativen Zonen %u"
|
||||
|
||||
#: cache.c:1796
|
||||
#, fuzzy, c-format
|
||||
#, c-format
|
||||
msgid "server %s#%d: queries sent %u, retried %u, failed %u, nxdomain replies %u, avg. latency %ums"
|
||||
msgstr "Server %s#%d: Anfragen gesendet %u, erneut versucht oder fehlgeschlagen %u"
|
||||
msgstr "Server %s#%d: Anfragen gesendet %u, erneut versucht %u, fehlgeschlagen %u, nxdomain-Antworten %u, durchschnittliche Latenz %ums"
|
||||
|
||||
#: util.c:51
|
||||
#, c-format
|
||||
@@ -120,14 +120,14 @@ msgid "failed to allocate %d bytes"
|
||||
msgstr "Konnte %d Bytes nicht belegen"
|
||||
|
||||
#: util.c:344
|
||||
#, fuzzy, c-format
|
||||
#, c-format
|
||||
msgid "failed to reallocate %d bytes"
|
||||
msgstr "Konnte %d Bytes nicht belegen"
|
||||
msgstr "Konnte %d Bytes nicht allozieren"
|
||||
|
||||
#: util.c:465
|
||||
#, fuzzy, c-format
|
||||
#, c-format
|
||||
msgid "cannot read monotonic clock: %s"
|
||||
msgstr "kann Netlink-Socket nicht erzeugen: %s"
|
||||
msgstr "monotone Uhr kann nicht gelesen werden: %s"
|
||||
|
||||
# @Simon: not perfect but I cannot get nearer right now.
|
||||
#: util.c:579
|
||||
@@ -188,11 +188,11 @@ msgstr "Unberechtigte DNS-Anfragen von Windows-Rechnern nicht weiterleiten."
|
||||
|
||||
#: option.c:404
|
||||
msgid "Don't include IPv4 addresses in DNS answers."
|
||||
msgstr ""
|
||||
msgstr "Keine IPv4-Adressen in DNS-Antworten inkludieren."
|
||||
|
||||
#: option.c:405
|
||||
msgid "Don't include IPv6 addresses in DNS answers."
|
||||
msgstr ""
|
||||
msgstr "Keine IPv6-Adressen in DNS-Antworten inkludieren."
|
||||
|
||||
#: option.c:406
|
||||
msgid "Enable DHCP in the range given with lease duration."
|
||||
@@ -313,7 +313,7 @@ msgstr "Fehlerhafte Suchergebnisse NICHT zwischenspeichern."
|
||||
|
||||
#: option.c:434
|
||||
msgid "Use expired cache data for faster reply."
|
||||
msgstr ""
|
||||
msgstr "Verwende abgelaufene Cachedaten für schnellere Antwort."
|
||||
|
||||
#: option.c:435
|
||||
#, c-format
|
||||
@@ -347,7 +347,7 @@ msgstr "Ausgehenden Port für DNS-Anfragen an vorgelagerte Server erzwingen."
|
||||
|
||||
#: option.c:442
|
||||
msgid "Set maximum number of random originating ports for a query."
|
||||
msgstr ""
|
||||
msgstr "Setze die maximale Anzahl zufälliger Ursprungsports für eine Abfrage."
|
||||
|
||||
#: option.c:443
|
||||
msgid "Do NOT read resolv.conf."
|
||||
@@ -404,7 +404,7 @@ msgstr "Spezifiziere untere Gültigkeitsdauergrenze für Zwischenspeicher."
|
||||
|
||||
#: option.c:456
|
||||
msgid "Retry DNS queries after this many milliseconds."
|
||||
msgstr ""
|
||||
msgstr "DNS-Abfragen nach so vielen Millisekunden wiederholen."
|
||||
|
||||
#: option.c:457
|
||||
#, c-format
|
||||
@@ -520,7 +520,7 @@ msgstr "Konfiguration aus allen Dateien in diesem Verzeichnis lesen."
|
||||
|
||||
#: option.c:484
|
||||
msgid "Execute file and read configuration from stdin."
|
||||
msgstr ""
|
||||
msgstr "Führe Datei aus und lese die Konfiguration aus stdin."
|
||||
|
||||
#: option.c:485
|
||||
msgid "Log to this syslog facility or file. (defaults to DAEMON)"
|
||||
@@ -680,7 +680,7 @@ msgstr "Anfragende MAC-Adresse in die weiterleitende DNS-Anfrage einfügen."
|
||||
|
||||
#: option.c:523
|
||||
msgid "Strip MAC information from queries."
|
||||
msgstr ""
|
||||
msgstr "Entferne die MAC-Information von Abfragen."
|
||||
|
||||
#: option.c:524
|
||||
msgid "Add specified IP subnet to forwarded DNS queries."
|
||||
@@ -688,7 +688,7 @@ msgstr "Füge spezifiziertes IP-Subnetz an weitergeleiteten DNS-Anfragen hinzu."
|
||||
|
||||
#: option.c:525
|
||||
msgid "Strip ECS information from queries."
|
||||
msgstr ""
|
||||
msgstr "Entferne die ECS-Information von Abfragen."
|
||||
|
||||
#: option.c:526
|
||||
msgid "Add client identification to forwarded DNS queries."
|
||||
@@ -773,9 +773,8 @@ msgid "Specify ipsets to which matching domains should be added"
|
||||
msgstr "Spezifiziere IPSets, zu denen passende Domains hinzugefügt werden sollen"
|
||||
|
||||
#: option.c:546
|
||||
#, fuzzy
|
||||
msgid "Specify nftables sets to which matching domains should be added"
|
||||
msgstr "Spezifiziere IPSets, zu denen passende Domains hinzugefügt werden sollen"
|
||||
msgstr "Geben Sie nftables sets an, zu denen passende Domänen hinzugefügt werden sollen"
|
||||
|
||||
#: option.c:547
|
||||
msgid "Enable filtering of DNS queries with connection-track marks."
|
||||
@@ -880,7 +879,7 @@ msgstr "Protokolliere kein Routine-TFTP."
|
||||
|
||||
#: option.c:572
|
||||
msgid "Suppress round-robin ordering of DNS records."
|
||||
msgstr ""
|
||||
msgstr "Unterdrückt die Round-Robin-Sortierung von DNS-Einträgen."
|
||||
|
||||
#: option.c:802
|
||||
#, c-format
|
||||
@@ -915,11 +914,11 @@ msgstr "Schnittstellenbindung nicht unterstützt"
|
||||
|
||||
#: option.c:955
|
||||
msgid "Cannot resolve server name"
|
||||
msgstr ""
|
||||
msgstr "Servername kann nicht aufgelöst werden"
|
||||
|
||||
#: option.c:991
|
||||
msgid "cannot use IPv4 server address with IPv6 source address"
|
||||
msgstr ""
|
||||
msgstr "IPv4-Serveradresse kann nicht mit IPv6-Quelladresse verwendet werden"
|
||||
|
||||
#: option.c:997 option.c:1043
|
||||
msgid "interface can only be specified once"
|
||||
@@ -931,21 +930,19 @@ msgstr "Fehlerhafter Schnittestellenname"
|
||||
|
||||
#: option.c:1037
|
||||
msgid "cannot use IPv6 server address with IPv4 source address"
|
||||
msgstr ""
|
||||
msgstr "IPv6-Serveradresse kann nicht mit IPv4-Quelladresse verwendet werden"
|
||||
|
||||
#: option.c:1124
|
||||
#, fuzzy
|
||||
msgid "bad IPv4 prefix length"
|
||||
msgstr "fehlerhafte Präfixlänge"
|
||||
msgstr "ungültige IPv4-Präfixlänge"
|
||||
|
||||
#: option.c:1155 option.c:1165 option.c:1240 option.c:1250 option.c:5360
|
||||
msgid "error"
|
||||
msgstr "Fehler"
|
||||
|
||||
#: option.c:1207
|
||||
#, fuzzy
|
||||
msgid "bad IPv6 prefix length"
|
||||
msgstr "fehlerhafte Präfixlänge"
|
||||
msgstr "ungültige IPv6-Präfixlänge"
|
||||
|
||||
#: option.c:1467
|
||||
msgid "inappropriate vendor:"
|
||||
@@ -1034,7 +1031,6 @@ msgid "recompile with HAVE_LUASCRIPT defined to enable Lua scripts"
|
||||
msgstr "Neuübersetzung mit HAVE_LUASCRIPT nötig, um benutzerdefinierte Lua-Skripte auszuführen"
|
||||
|
||||
#: option.c:2447
|
||||
#, fuzzy
|
||||
msgid "invalid auth-zone"
|
||||
msgstr "unzulässiger Alias-Bereich"
|
||||
|
||||
@@ -1059,9 +1055,8 @@ msgid "recompile with HAVE_IPSET defined to enable ipset directives"
|
||||
msgstr "Neuübersetzung mit HAVE_IPSET nötig, um IPSet-Direktiven zu aktivieren"
|
||||
|
||||
#: option.c:3117
|
||||
#, fuzzy
|
||||
msgid "recompile with HAVE_NFTSET defined to enable nftset directives"
|
||||
msgstr "Neuübersetzung mit HAVE_IPSET nötig, um IPSet-Direktiven zu aktivieren"
|
||||
msgstr "Neukompilieren mit definiertem HAVE_NFTSET, um NFTSET-Direktiven zu aktivieren"
|
||||
|
||||
#: option.c:3192 option.c:3210
|
||||
msgid "recompile with HAVE_CONNTRACK defined to enable connmark-allowlist directives"
|
||||
@@ -1259,7 +1254,7 @@ msgstr "unzulässige Option"
|
||||
#: option.c:5363
|
||||
#, c-format
|
||||
msgid " in output from %s"
|
||||
msgstr ""
|
||||
msgstr " in der Ausgabe von %s"
|
||||
|
||||
#: option.c:5365
|
||||
#, c-format
|
||||
@@ -1272,9 +1267,9 @@ msgid "read %s"
|
||||
msgstr "%s gelesen"
|
||||
|
||||
#: option.c:5446
|
||||
#, fuzzy, c-format
|
||||
#, c-format
|
||||
msgid "cannot execute %s: %s"
|
||||
msgstr "kann %s nicht erstellen: %s"
|
||||
msgstr "kann %s nicht ausführen: %s"
|
||||
|
||||
#: option.c:5454 option.c:5615 tftp.c:790
|
||||
#, c-format
|
||||
@@ -1282,14 +1277,14 @@ msgid "cannot read %s: %s"
|
||||
msgstr "kann %s nicht lesen: %s"
|
||||
|
||||
#: option.c:5473
|
||||
#, fuzzy, c-format
|
||||
#, c-format
|
||||
msgid "error executing %s: %s"
|
||||
msgstr "konnte %s nicht ausführen: %s"
|
||||
msgstr "Fehler bei der Ausführung von %s: %s"
|
||||
|
||||
#: option.c:5476
|
||||
#, c-format
|
||||
msgid "%s returns non-zero error code"
|
||||
msgstr ""
|
||||
msgstr "%s gibt einen Fehlercode ungleich Null zurück"
|
||||
|
||||
#: option.c:5775
|
||||
msgid "junk found in command line"
|
||||
@@ -1399,14 +1394,14 @@ msgid "reducing DNS packet size for nameserver %s to %d"
|
||||
msgstr "Reduziere die DNS-Paketgröße für Nameserver %s auf %d"
|
||||
|
||||
#: forward.c:1565
|
||||
#, fuzzy, c-format
|
||||
#, c-format
|
||||
msgid "ignoring query from non-local network %s (logged only once)"
|
||||
msgstr "Ignoriere Anfrage von auswärtigem Netzwerk"
|
||||
msgstr "Ignoriere Abfrage von nicht-lokalen Netzwerk %s (Nur einmal protokolliert)"
|
||||
|
||||
#: forward.c:2139
|
||||
#, fuzzy, c-format
|
||||
#, c-format
|
||||
msgid "ignoring query from non-local network %s"
|
||||
msgstr "Ignoriere Anfrage von auswärtigem Netzwerk"
|
||||
msgstr "Ignoriere Abfrage von nicht-lokalen Netzwerk %s"
|
||||
|
||||
#: forward.c:2501
|
||||
#, c-format
|
||||
@@ -1488,7 +1483,7 @@ msgstr "ignoriere Namensserver %s - kann Socket nicht erzeugen/binden: %s"
|
||||
|
||||
#: network.c:1643
|
||||
msgid "more servers are defined but not logged"
|
||||
msgstr ""
|
||||
msgstr "mehr Server sind definiert aber nicht protokolliert"
|
||||
|
||||
#: network.c:1654
|
||||
msgid "(no DNSSEC)"
|
||||
@@ -1605,7 +1600,7 @@ msgstr "max_port darf nicht kleiner als min_port sein"
|
||||
|
||||
#: dnsmasq.c:271
|
||||
msgid "port_limit must not be larger than available port range"
|
||||
msgstr ""
|
||||
msgstr "port_limit darf nicht größer als der verfügbare Portbereich sein"
|
||||
|
||||
#: dnsmasq.c:278
|
||||
msgid "--auth-server required when an auth zone is defined."
|
||||
@@ -1810,9 +1805,9 @@ msgid "restricting maximum simultaneous TFTP transfers to %d"
|
||||
msgstr "Begrenze gleichzeitige TFTP-Übertragungen auf maximal %d"
|
||||
|
||||
#: dnsmasq.c:1095
|
||||
#, fuzzy, c-format
|
||||
#, c-format
|
||||
msgid "error binding DHCP socket to device %s"
|
||||
msgstr "Fehler beim Senden des DHCP-Pakets an %s: %s"
|
||||
msgstr "Fehler beim Binden des DHCP-Sockets an Gerät %s"
|
||||
|
||||
#: dnsmasq.c:1229
|
||||
msgid "connected to system DBus"
|
||||
@@ -1981,19 +1976,19 @@ msgid "read %s - %d addresses"
|
||||
msgstr "%s gelesen - %d Adressen"
|
||||
|
||||
#: dhcp.c:1136
|
||||
#, fuzzy, c-format
|
||||
#, c-format
|
||||
msgid "Cannot broadcast DHCP relay via interface %s"
|
||||
msgstr "Kann nicht zum DHCPv6 Server multicasten ohne korrekte Schnittstelle"
|
||||
msgstr "DHCP-Relay kann nicht über die Schnittstelle %s gesendet werden"
|
||||
|
||||
#: dhcp.c:1160
|
||||
#, c-format
|
||||
msgid "broadcast via %s"
|
||||
msgstr ""
|
||||
msgstr "broadcast via %s"
|
||||
|
||||
#: dhcp.c:1163 rfc3315.c:2219
|
||||
#, fuzzy, c-format
|
||||
#, c-format
|
||||
msgid "DHCP relay at %s -> %s"
|
||||
msgstr "DHCP Relais %s -> %s"
|
||||
msgstr "DHCP-Relay bei %s -> %s"
|
||||
|
||||
#: lease.c:64
|
||||
#, c-format
|
||||
@@ -2405,14 +2400,14 @@ msgid "release received"
|
||||
msgstr "Freigabe empfangen"
|
||||
|
||||
#: rfc3315.c:2200
|
||||
#, fuzzy, c-format
|
||||
#, c-format
|
||||
msgid "Cannot multicast DHCP relay via interface %s"
|
||||
msgstr "Kann nicht zum DHCPv6 Server multicasten ohne korrekte Schnittstelle"
|
||||
msgstr "Multicast-DHCP-Relay über Schnittstelle %s nicht möglich"
|
||||
|
||||
#: rfc3315.c:2216
|
||||
#, c-format
|
||||
msgid "multicast via %s"
|
||||
msgstr ""
|
||||
msgstr "multicast via %s"
|
||||
|
||||
#: dhcp-common.c:187
|
||||
#, c-format
|
||||
@@ -2484,9 +2479,9 @@ msgid "router advertisement on %s%s"
|
||||
msgstr "Router-Advertisment auf %s%s"
|
||||
|
||||
#: dhcp-common.c:1043
|
||||
#, fuzzy, c-format
|
||||
#, c-format
|
||||
msgid "DHCP relay from %s via %s"
|
||||
msgstr "DHCP Weiterleitung von %s nach %s"
|
||||
msgstr "DHCP-Relay von %s über %s"
|
||||
|
||||
#: dhcp-common.c:1045
|
||||
#, c-format
|
||||
@@ -2621,9 +2616,9 @@ msgid "Insecure DS reply received for %s, check domain configuration and upstrea
|
||||
msgstr "Unsichere DS-Antwort für %s, bitte Domainkonfiguration und Upstream DNS-Server für DNSSEC-Unterstützung überprüfen"
|
||||
|
||||
#: blockdata.c:55
|
||||
#, fuzzy, c-format
|
||||
#, c-format
|
||||
msgid "pool memory in use %zu, max %zu, allocated %zu"
|
||||
msgstr "Speicherpool in Benutzung %u, Max %u, zugewiesen %u"
|
||||
msgstr "Speicherpool in Benutzung %zu, Max %zu, zugewiesen %zu"
|
||||
|
||||
#: tables.c:61
|
||||
#, c-format
|
||||
@@ -2695,24 +2690,23 @@ msgid "bad dynamic directory %s: %s"
|
||||
msgstr "fehlerhaftes dynamisches Verzeichnis %s: %s"
|
||||
|
||||
#: inotify.c:186
|
||||
#, fuzzy
|
||||
msgid "not a directory"
|
||||
msgstr "Kann auf Verzeichnis %s nicht zugreifen: %s"
|
||||
msgstr "ist kein Verzeichnis"
|
||||
|
||||
#: inotify.c:299
|
||||
#, c-format
|
||||
msgid "inotify: %s removed"
|
||||
msgstr ""
|
||||
msgstr "inotify: %s entfernt"
|
||||
|
||||
#: inotify.c:301
|
||||
#, fuzzy, c-format
|
||||
#, c-format
|
||||
msgid "inotify: %s new or modified"
|
||||
msgstr "inotify, neue oder geänderte Datei %s"
|
||||
msgstr "inotify: %s neu oder modifiziert"
|
||||
|
||||
#: inotify.c:309
|
||||
#, c-format
|
||||
msgid "inotify: flushed %u names read from %s"
|
||||
msgstr ""
|
||||
msgstr "inotify: %u Namen gelöscht, aus %s gelesen"
|
||||
|
||||
#: dump.c:68
|
||||
#, c-format
|
||||
@@ -2729,14 +2723,14 @@ msgid "failed to write packet dump"
|
||||
msgstr "schreiben des Paketmitschnitts fehlgeschlagen"
|
||||
|
||||
#: dump.c:289
|
||||
#, fuzzy, c-format
|
||||
#, c-format
|
||||
msgid "%u dumping packet %u mask 0x%04x"
|
||||
msgstr "Lade UDP Paket %u Maske 0x%04x ab"
|
||||
msgstr "%u dumpe Paket %u Maske 0x%04x"
|
||||
|
||||
#: dump.c:291
|
||||
#, fuzzy, c-format
|
||||
#, c-format
|
||||
msgid "dumping packet %u mask 0x%04x"
|
||||
msgstr "Lade UDP Paket %u Maske 0x%04x ab"
|
||||
msgstr "Dumpe Packet %u Make 0x%04x"
|
||||
|
||||
#: ubus.c:79
|
||||
#, c-format
|
||||
@@ -2770,9 +2764,8 @@ msgid "Failed to create SHA-256 hash object"
|
||||
msgstr "Kann SHA-256-Hash-Objekt nicht erstellen"
|
||||
|
||||
#: nftset.c:35
|
||||
#, fuzzy
|
||||
msgid "failed to create nftset context"
|
||||
msgstr "konnte IPset-Kontroll-Socket nicht erzeugen: %s"
|
||||
msgstr "Fehler beim Erstellen des NFTSET-Kontexts"
|
||||
|
||||
#~ msgid "bad IPv4 prefix"
|
||||
#~ msgstr "fehlerhaftes IPv4-Präfix"
|
||||
|
||||
@@ -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
|
||||
@@ -35,7 +35,7 @@ struct arp_record {
|
||||
static struct arp_record *arps = NULL, *old = NULL, *freelist = NULL;
|
||||
static time_t last = 0;
|
||||
|
||||
static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv)
|
||||
static int filter_mac(int family, void *addrp, char *mac, size_t maclen, void *parmv)
|
||||
{
|
||||
struct arp_record *arp;
|
||||
|
||||
|
||||
+49
-36
@@ -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
|
||||
@@ -103,9 +103,9 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
unsigned char *p, *ansp;
|
||||
int qtype, qclass, rc;
|
||||
int nameoffset, axfroffset = 0;
|
||||
int q, anscount = 0, authcount = 0;
|
||||
int anscount = 0, authcount = 0;
|
||||
struct crec *crecp;
|
||||
int auth = !local_query, trunc = 0, nxdomain = 1, soa = 0, ns = 0, axfr = 0, out_of_zone = 0;
|
||||
int auth = !local_query, trunc = 0, nxdomain = 1, soa = 0, ns = 0, axfr = 0, out_of_zone = 0, notimp = 0;
|
||||
struct auth_zone *zone = NULL;
|
||||
struct addrlist *subnet = NULL;
|
||||
char *cut;
|
||||
@@ -116,18 +116,20 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
union all_addr addr;
|
||||
struct cname *a, *candidate;
|
||||
unsigned int wclen;
|
||||
unsigned int log_flags = local_query ? 0 : F_NOERR;
|
||||
|
||||
if (ntohs(header->qdcount) == 0 || OPCODE(header) != QUERY )
|
||||
if (ntohs(header->qdcount) != 1)
|
||||
return 0;
|
||||
|
||||
/* determine end of question section (we put answers there) */
|
||||
if (!(ansp = skip_questions(header, qlen)))
|
||||
return 0; /* bad packet */
|
||||
|
||||
/* now process each question, answers go in RRs after the question */
|
||||
p = (unsigned char *)(header+1);
|
||||
|
||||
for (q = ntohs(header->qdcount); q != 0; q--)
|
||||
if (OPCODE(header) != QUERY)
|
||||
notimp = 1;
|
||||
else
|
||||
{
|
||||
unsigned int flag = 0;
|
||||
int found = 0;
|
||||
@@ -137,7 +139,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);
|
||||
@@ -147,7 +149,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
{
|
||||
auth = 0;
|
||||
out_of_zone = 1;
|
||||
continue;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if ((qtype == T_PTR || qtype == T_SOA || qtype == T_NS) &&
|
||||
@@ -162,7 +164,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
{
|
||||
out_of_zone = 1;
|
||||
auth = 0;
|
||||
continue;
|
||||
goto done;
|
||||
}
|
||||
else if (qtype == T_SOA)
|
||||
soa = 1, found = 1;
|
||||
@@ -210,7 +212,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
if (local_query || in_zone(zone, intr->name, NULL))
|
||||
{
|
||||
found = 1;
|
||||
log_query(flag | F_REVERSE | F_CONFIG, intr->name, &addr, NULL, 0);
|
||||
log_query(log_flags | flag | F_REVERSE | F_CONFIG, intr->name, &addr, NULL, 0);
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
daemon->auth_ttl, NULL,
|
||||
T_PTR, C_IN, "d", intr->name))
|
||||
@@ -234,7 +236,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
strcat(name, ".");
|
||||
strcat(name, zone->domain);
|
||||
}
|
||||
log_query(flag | F_DHCP | F_REVERSE, name, &addr, record_source(crecp->uid), 0);
|
||||
log_query(log_flags | flag | F_DHCP | F_REVERSE, name, &addr, record_source(crecp->uid), 0);
|
||||
found = 1;
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
daemon->auth_ttl, NULL,
|
||||
@@ -243,7 +245,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
}
|
||||
else if (crecp->flags & (F_DHCP | F_HOSTS) && (local_query || in_zone(zone, name, NULL)))
|
||||
{
|
||||
log_query(crecp->flags & ~F_FORWARD, name, &addr, record_source(crecp->uid), 0);
|
||||
log_query(log_flags | (crecp->flags & ~F_FORWARD), name, &addr, record_source(crecp->uid), 0);
|
||||
found = 1;
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
daemon->auth_ttl, NULL,
|
||||
@@ -257,7 +259,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
|
||||
if (!found && is_rev_synth(flag, &addr, name) && (local_query || in_zone(zone, name, NULL)))
|
||||
{
|
||||
log_query(F_CONFIG | F_REVERSE | flag, name, &addr, NULL, 0);
|
||||
log_query(log_flags | F_CONFIG | F_REVERSE | flag, name, &addr, NULL, 0);
|
||||
found = 1;
|
||||
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
@@ -269,9 +271,9 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
if (found)
|
||||
nxdomain = 0;
|
||||
else
|
||||
log_query(flag | F_NEG | F_NXDOMAIN | F_REVERSE | (auth ? F_AUTH : 0), NULL, &addr, NULL, 0);
|
||||
log_query(log_flags | flag | F_NEG | F_NXDOMAIN | F_REVERSE | (auth ? F_AUTH : 0), NULL, &addr, NULL, 0);
|
||||
|
||||
continue;
|
||||
goto done;
|
||||
}
|
||||
|
||||
cname_restart:
|
||||
@@ -288,7 +290,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
{
|
||||
out_of_zone = 1;
|
||||
auth = 0;
|
||||
continue;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -300,7 +302,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
if (rc == 2 && qtype == T_MX)
|
||||
{
|
||||
found = 1;
|
||||
log_query(F_CONFIG | F_RRNAME, name, NULL, "<MX>", 0);
|
||||
log_query(log_flags | F_CONFIG | F_RRNAME, name, NULL, "<MX>", 0);
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
|
||||
NULL, T_MX, C_IN, "sd", rec->weight, rec->target))
|
||||
anscount++;
|
||||
@@ -315,7 +317,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
if (rc == 2 && qtype == T_SRV)
|
||||
{
|
||||
found = 1;
|
||||
log_query(F_CONFIG | F_RRNAME, name, NULL, "<SRV>", 0);
|
||||
log_query(log_flags | F_CONFIG | F_RRNAME, name, NULL, "<SRV>", 0);
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
|
||||
NULL, T_SRV, C_IN, "sssd",
|
||||
rec->priority, rec->weight, rec->srvport, rec->target))
|
||||
@@ -349,7 +351,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
if (rc == 2 && txt->class == qtype)
|
||||
{
|
||||
found = 1;
|
||||
log_query(F_CONFIG | F_RRNAME, name, NULL, NULL, txt->class);
|
||||
log_query(log_flags | F_CONFIG | F_RRNAME, name, NULL, NULL, txt->class);
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
|
||||
NULL, txt->class, C_IN, "t", txt->len, txt->txt))
|
||||
anscount++;
|
||||
@@ -363,7 +365,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
if (rc == 2 && qtype == T_TXT)
|
||||
{
|
||||
found = 1;
|
||||
log_query(F_CONFIG | F_RRNAME, name, NULL, "<TXT>", 0);
|
||||
log_query(log_flags | F_CONFIG | F_RRNAME, name, NULL, "<TXT>", 0);
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
|
||||
NULL, T_TXT, C_IN, "t", txt->len, txt->txt))
|
||||
anscount++;
|
||||
@@ -377,7 +379,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
if (rc == 2 && qtype == T_NAPTR)
|
||||
{
|
||||
found = 1;
|
||||
log_query(F_CONFIG | F_RRNAME, name, NULL, "<NAPTR>", 0);
|
||||
log_query(log_flags | F_CONFIG | F_RRNAME, name, NULL, "<NAPTR>", 0);
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
|
||||
NULL, T_NAPTR, C_IN, "sszzzd",
|
||||
na->order, na->pref, na->flags, na->services, na->regexp, na->replace))
|
||||
@@ -407,7 +409,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
continue;
|
||||
|
||||
found = 1;
|
||||
log_query(F_FORWARD | F_CONFIG | flag, name, &addrlist->addr, NULL, 0);
|
||||
log_query(log_flags | F_FORWARD | F_CONFIG | flag, name, &addrlist->addr, NULL, 0);
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
daemon->auth_ttl, NULL, qtype, C_IN,
|
||||
qtype == T_A ? "4" : "6", &addrlist->addr))
|
||||
@@ -419,7 +421,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
{
|
||||
nxdomain = 0;
|
||||
|
||||
log_query(F_FORWARD | F_CONFIG | flag, name, &addr, NULL, 0);
|
||||
log_query(log_flags | F_FORWARD | F_CONFIG | flag, name, &addr, NULL, 0);
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
daemon->auth_ttl, NULL, qtype, C_IN, qtype == T_A ? "4" : "6", &addr))
|
||||
anscount++;
|
||||
@@ -432,7 +434,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
if (qtype == T_SOA)
|
||||
{
|
||||
auth = soa = 1; /* inhibits auth section */
|
||||
log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<SOA>", 0);
|
||||
log_query(log_flags | F_RRNAME | F_AUTH, zone->domain, NULL, "<SOA>", 0);
|
||||
}
|
||||
else if (qtype == T_AXFR)
|
||||
{
|
||||
@@ -468,13 +470,13 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
ns = 1; /* ensure we include NS records! */
|
||||
axfr = 1;
|
||||
axfroffset = nameoffset;
|
||||
log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<AXFR>", 0);
|
||||
log_query(log_flags | F_RRNAME | F_AUTH, zone->domain, NULL, "<AXFR>", 0);
|
||||
}
|
||||
else if (qtype == T_NS)
|
||||
{
|
||||
auth = 1;
|
||||
ns = 1; /* inhibits auth section */
|
||||
log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<NS>", 0);
|
||||
log_query(log_flags | F_RRNAME | F_AUTH, zone->domain, NULL, "<NS>", 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -492,7 +494,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
(local_query || filter_zone(zone, flag, &(crecp->addr))))
|
||||
{
|
||||
*cut = '.'; /* restore domain part */
|
||||
log_query(crecp->flags, name, &crecp->addr, record_source(crecp->uid), 0);
|
||||
log_query(log_flags | crecp->flags, name, &crecp->addr, record_source(crecp->uid), 0);
|
||||
*cut = 0; /* remove domain part */
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
daemon->auth_ttl, NULL, qtype, C_IN,
|
||||
@@ -513,7 +515,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(log_flags | (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))
|
||||
@@ -560,7 +562,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
|
||||
if (candidate)
|
||||
{
|
||||
log_query(F_CONFIG | F_CNAME, name, NULL, NULL, 0);
|
||||
log_query(log_flags | F_CONFIG | F_CNAME, name, NULL, NULL, 0);
|
||||
strcpy(name, candidate->target);
|
||||
if (!strchr(name, '.'))
|
||||
{
|
||||
@@ -578,10 +580,12 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
else if (cache_find_non_terminal(name, now))
|
||||
nxdomain = 0;
|
||||
|
||||
log_query(flag | F_NEG | (nxdomain ? F_NXDOMAIN : 0) | F_FORWARD | F_AUTH, name, NULL, NULL, 0);
|
||||
log_query(log_flags | flag | F_NEG | (nxdomain ? F_NXDOMAIN : 0) | F_FORWARD | F_AUTH, name, NULL, NULL, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
done:
|
||||
|
||||
/* Add auth section */
|
||||
if (auth && zone)
|
||||
@@ -873,7 +877,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
if (!(ansp = skip_questions(header, qlen)))
|
||||
return 0; /* bad packet */
|
||||
anscount = authcount = 0;
|
||||
log_query(F_AUTH, "reply", NULL, "truncated", 0);
|
||||
log_query(log_flags | F_AUTH, "reply", NULL, "truncated", 0);
|
||||
}
|
||||
|
||||
if ((auth || local_query) && nxdomain)
|
||||
@@ -885,14 +889,23 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
header->nscount = htons(authcount);
|
||||
header->arcount = htons(0);
|
||||
|
||||
if (!local_query && out_of_zone)
|
||||
if ((!local_query && out_of_zone) || notimp)
|
||||
{
|
||||
SET_RCODE(header, REFUSED);
|
||||
if (out_of_zone)
|
||||
{
|
||||
addr.log.rcode = REFUSED;
|
||||
addr.log.ede = EDE_NOT_AUTH;
|
||||
}
|
||||
else
|
||||
{
|
||||
addr.log.rcode = NOTIMP;
|
||||
addr.log.ede = EDE_UNSET;
|
||||
}
|
||||
|
||||
SET_RCODE(header, addr.log.rcode);
|
||||
header->ancount = htons(0);
|
||||
header->nscount = htons(0);
|
||||
addr.log.rcode = REFUSED;
|
||||
addr.log.ede = EDE_NOT_AUTH;
|
||||
log_query(F_UPSTREAM | F_RCODE, "error", &addr, NULL, 0);
|
||||
log_query(log_flags | F_UPSTREAM | F_RCODE, "error", &addr, NULL, 0);
|
||||
return resize_packet(header, ansp - (unsigned char *)header, NULL, 0);
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -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,7 +193,7 @@ void *blockdata_retrieve(struct blockdata *block, size_t len, void *data)
|
||||
{
|
||||
size_t blen;
|
||||
struct blockdata *b;
|
||||
void *new, *d;
|
||||
uint8_t *new, *d;
|
||||
|
||||
static unsigned int buff_len = 0;
|
||||
static unsigned char *buff = NULL;
|
||||
|
||||
@@ -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
|
||||
@@ -100,14 +100,14 @@ int arp_enumerate(void *parm, callback_t callback)
|
||||
#endif /* defined(HAVE_BSD_NETWORK) && !defined(__APPLE__) */
|
||||
|
||||
|
||||
int iface_enumerate(int family, void *parm, int (*callback)())
|
||||
int iface_enumerate(int family, void *parm, callback_t callback)
|
||||
{
|
||||
struct ifaddrs *head, *addrs;
|
||||
int errsave, fd = -1, ret = 0;
|
||||
|
||||
if (family == AF_UNSPEC)
|
||||
#if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
|
||||
return arp_enumerate(parm, callback.af_unspec);
|
||||
return arp_enumerate(parm, callback);
|
||||
#else
|
||||
return 0; /* need code for Solaris and MacOS*/
|
||||
#endif
|
||||
@@ -126,112 +126,110 @@ int iface_enumerate(int family, void *parm, int (*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);
|
||||
|
||||
+295
-170
@@ -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
|
||||
@@ -101,6 +101,9 @@ static const struct {
|
||||
{ 63, "ZONEMD" }, /* Message Digest Over Zone Data [RFC8976] ZONEMD/zonemd-completed-template 2018-12-12*/
|
||||
{ 64, "SVCB" }, /* Service Binding [draft-ietf-dnsop-svcb-https-00] SVCB/svcb-completed-template 2020-06-30*/
|
||||
{ 65, "HTTPS" }, /* HTTPS Binding [draft-ietf-dnsop-svcb-https-00] HTTPS/https-completed-template 2020-06-30*/
|
||||
{ 66, "DSYNC" }, /* Endpoint discovery for delegation synchronization [draft-ietf-dnsop-generalized-notify-03] DSYNC/dsync-completed-template 2024-12-10 */
|
||||
{ 67, "HHIT" }, /* [draft-ietf-drip-registries-28] */
|
||||
{ 68, "BRID" }, /* [draft-ietf-drip-registries-28] */
|
||||
{ 99, "SPF" }, /* [RFC7208] */
|
||||
{ 100, "UINFO" }, /* [IANA-Reserved] */
|
||||
{ 101, "UID" }, /* [IANA-Reserved] */
|
||||
@@ -112,6 +115,7 @@ static const struct {
|
||||
{ 107, "LP" }, /* [RFC6742] ILNP/lp-completed-template */
|
||||
{ 108, "EUI48" }, /* an EUI-48 address [RFC7043] EUI48/eui48-completed-template 2013-03-27*/
|
||||
{ 109, "EUI64" }, /* an EUI-64 address [RFC7043] EUI64/eui64-completed-template 2013-03-27*/
|
||||
{ 128, "NXNAME" }, /* NXDOMAIN indicator for Compact Denial of Existence https://www.iana.org/go/draft-ietf-dnsop-compact-denial-of-existence-04 */
|
||||
{ 249, "TKEY" }, /* Transaction Key [RFC2930] */
|
||||
{ 250, "TSIG" }, /* Transaction Signature [RFC8945] */
|
||||
{ 251, "IXFR" }, /* incremental transfer [RFC1995] */
|
||||
@@ -125,6 +129,9 @@ static const struct {
|
||||
{ 259, "DOA" }, /* Digital Object Architecture [draft-durand-doa-over-dns] DOA/doa-completed-template 2017-08-30*/
|
||||
{ 260, "AMTRELAY" }, /* Automatic Multicast Tunneling Relay [RFC8777] AMTRELAY/amtrelay-completed-template 2019-02-06*/
|
||||
{ 261, "RESINFO" }, /* Resolver Information as Key/Value Pairs https://datatracker.ietf.org/doc/draft-ietf-add-resolver-info/06/ */
|
||||
{ 262, "WALLET" }, /* Public wallet address https://www.iana.org/assignments/dns-parameters/WALLET/wallet-completed-template */
|
||||
{ 263, "CLA" }, /* BP Convergence Layer Adapter https://www.iana.org/go/draft-johnson-dns-ipn-cla-07 */
|
||||
{ 264, "IPN" }, /* BP Node Number https://www.iana.org/go/draft-johnson-dns-ipn-cla-07 */
|
||||
{ 32768, "TA" }, /* DNSSEC Trust Authorities [Sam_Weiler][http://cameo.library.cmu.edu/][ Deploying DNSSEC Without a Signed Root. Technical Report 1999-19, Information Networking Institute, Carnegie Mellon University, April 2004.] 2005-12-13*/
|
||||
{ 32769, "DLV" }, /* DNSSEC Lookaside Validation (OBSOLETE) [RFC8749][RFC4431] */
|
||||
};
|
||||
@@ -474,7 +481,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;
|
||||
@@ -774,7 +781,14 @@ void cache_end_insert(void)
|
||||
{
|
||||
if (insert_error)
|
||||
return;
|
||||
|
||||
|
||||
/* signal start of cache insert transaction to master process */
|
||||
if (daemon->pipe_to_parent != -1)
|
||||
{
|
||||
unsigned char op = PIPE_OP_INSERT;
|
||||
read_write(daemon->pipe_to_parent, &op, sizeof(op), RW_WRITE);
|
||||
}
|
||||
|
||||
while (new_chain)
|
||||
{
|
||||
struct crec *tmp = new_chain->next;
|
||||
@@ -801,7 +815,7 @@ void cache_end_insert(void)
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), RW_WRITE);
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)name, m, RW_WRITE);
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->ttd, sizeof(new_chain->ttd), RW_WRITE);
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&flags, sizeof(flags), RW_WRITE);
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&flags, sizeof(flags), RW_WRITE);
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->addr, sizeof(new_chain->addr), RW_WRITE);
|
||||
|
||||
if (flags & F_RR)
|
||||
@@ -811,7 +825,7 @@ void cache_end_insert(void)
|
||||
blockdata_write(new_chain->addr.rrblock.rrdata, new_chain->addr.rrblock.datalen, daemon->pipe_to_parent);
|
||||
}
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (flags & F_DNSKEY)
|
||||
else if (flags & F_DNSKEY)
|
||||
{
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&class, sizeof(class), RW_WRITE);
|
||||
blockdata_write(new_chain->addr.key.keydata, new_chain->addr.key.keylen, daemon->pipe_to_parent);
|
||||
@@ -834,166 +848,237 @@ void cache_end_insert(void)
|
||||
if (daemon->pipe_to_parent != -1)
|
||||
{
|
||||
ssize_t m = -1;
|
||||
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), RW_WRITE);
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
/* Sneak out possibly updated crypto HWM values. */
|
||||
m = daemon->metrics[METRIC_CRYPTO_HWM];
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), RW_WRITE);
|
||||
m = daemon->metrics[METRIC_SIG_FAIL_HWM];
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), RW_WRITE);
|
||||
m = daemon->metrics[METRIC_WORK_HWM];
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), RW_WRITE);
|
||||
#endif
|
||||
}
|
||||
|
||||
new_chain = NULL;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
void cache_update_hwm(void)
|
||||
{
|
||||
/* Sneak out possibly updated crypto HWM values. */
|
||||
unsigned char op = PIPE_OP_STATS;
|
||||
|
||||
/* A marshalled cache entry arrives on fd, read, unmarshall and insert into cache of master process. */
|
||||
read_write(daemon->pipe_to_parent, &op, sizeof(op), RW_WRITE);
|
||||
read_write(daemon->pipe_to_parent,
|
||||
(unsigned char *)&daemon->metrics[METRIC_CRYPTO_HWM],
|
||||
sizeof(daemon->metrics[METRIC_CRYPTO_HWM]), RW_WRITE);
|
||||
read_write(daemon->pipe_to_parent,
|
||||
(unsigned char *)&daemon->metrics[METRIC_SIG_FAIL_HWM],
|
||||
sizeof(daemon->metrics[METRIC_SIG_FAIL_HWM]), RW_WRITE);
|
||||
read_write(daemon->pipe_to_parent,
|
||||
(unsigned char *)&daemon->metrics[METRIC_WORK_HWM],
|
||||
sizeof(daemon->metrics[METRIC_WORK_HWM]), RW_WRITE);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_IPSET) || defined(HAVE_NFTSET)
|
||||
void cache_send_ipset(unsigned char op, struct ipsets *sets, int flags, union all_addr *addr)
|
||||
{
|
||||
read_write(daemon->pipe_to_parent, &op, sizeof(op), RW_WRITE);
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&sets, sizeof(sets), RW_WRITE);
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&flags, sizeof(flags), RW_WRITE);
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)addr, sizeof(*addr), RW_WRITE);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Retrieve and handle a result from child TCP-handler.
|
||||
Return 0 when pipe is closed by far end. */
|
||||
int cache_recv_insert(time_t now, int fd)
|
||||
{
|
||||
ssize_t m;
|
||||
union all_addr addr;
|
||||
unsigned long ttl;
|
||||
time_t ttd;
|
||||
unsigned int flags;
|
||||
struct crec *crecp = NULL;
|
||||
unsigned char op;
|
||||
|
||||
cache_start_insert();
|
||||
if (!read_write(fd, &op, sizeof(op), RW_READ))
|
||||
return 0;
|
||||
|
||||
while (1)
|
||||
switch (op)
|
||||
{
|
||||
|
||||
if (!read_write(fd, (unsigned char *)&m, sizeof(m), RW_READ))
|
||||
return 0;
|
||||
case PIPE_OP_INSERT:
|
||||
{
|
||||
/* A marshalled set if cache entries arrives on fd, read, unmarshall and insert into cache of master process. */
|
||||
ssize_t m;
|
||||
union all_addr addr;
|
||||
unsigned long ttl;
|
||||
time_t ttd;
|
||||
unsigned int flags;
|
||||
struct crec *crecp = NULL;
|
||||
|
||||
cache_start_insert();
|
||||
|
||||
/* loop reading RRs, since we don't want to go back to the poll() loop
|
||||
and start processing other queries which might pollute the insertion
|
||||
chain. The child will never block between the first OP_RR and the
|
||||
minus-one length marking the end. */
|
||||
while (1)
|
||||
{
|
||||
if (!read_write(fd, (unsigned char *)&m, sizeof(m), RW_READ))
|
||||
return 0;
|
||||
|
||||
if (m == -1)
|
||||
{
|
||||
cache_end_insert();
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!read_write(fd, (unsigned char *)daemon->namebuff, m, RW_READ) ||
|
||||
!read_write(fd, (unsigned char *)&ttd, sizeof(ttd), RW_READ) ||
|
||||
!read_write(fd, (unsigned char *)&flags, sizeof(flags), RW_READ) ||
|
||||
!read_write(fd, (unsigned char *)&addr, sizeof(addr), RW_READ))
|
||||
return 0;
|
||||
|
||||
daemon->namebuff[m] = 0;
|
||||
|
||||
ttl = difftime(ttd, now);
|
||||
|
||||
if (flags & F_CNAME)
|
||||
{
|
||||
struct crec *newc = really_insert(daemon->namebuff, NULL, C_IN, now, ttl, flags);
|
||||
/* This relies on the fact that the target of a CNAME immediately precedes
|
||||
it because of the order of extraction in extract_addresses, and
|
||||
the order reversal on the new_chain. */
|
||||
if (newc)
|
||||
{
|
||||
newc->addr.cname.is_name_ptr = 0;
|
||||
|
||||
if (!crecp)
|
||||
newc->addr.cname.target.cache = NULL;
|
||||
else
|
||||
{
|
||||
next_uid(crecp);
|
||||
newc->addr.cname.target.cache = crecp;
|
||||
newc->addr.cname.uid = crecp->uid;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned short class = C_IN;
|
||||
struct blockdata *block = NULL;
|
||||
|
||||
if ((flags & F_RR) && !(flags & F_NEG) && (flags & F_KEYTAG)
|
||||
&& !(block = addr.rrblock.rrdata = blockdata_read(fd, addr.rrblock.datalen)))
|
||||
continue;
|
||||
#ifdef HAVE_DNSSEC
|
||||
else if (flags & F_DNSKEY)
|
||||
{
|
||||
if (!read_write(fd, (unsigned char *)&class, sizeof(class), RW_READ))
|
||||
return 0;
|
||||
if (!(block = addr.key.keydata = blockdata_read(fd, addr.key.keylen)))
|
||||
continue;
|
||||
}
|
||||
else if (flags & F_DS)
|
||||
{
|
||||
if (!read_write(fd, (unsigned char *)&class, sizeof(class), RW_READ))
|
||||
return 0;
|
||||
if (!(flags & F_NEG) && !(block = addr.ds.keydata = blockdata_read(fd, addr.ds.keylen)))
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
if (!(crecp = really_insert(daemon->namebuff, &addr, class, now, ttl, flags)))
|
||||
blockdata_free(block);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m == -1)
|
||||
{
|
||||
#ifdef HAVE_DNSSEC
|
||||
/* Sneak in possibly updated crypto HWM. */
|
||||
if (!read_write(fd, (unsigned char *)&m, sizeof(m), RW_READ))
|
||||
return 0;
|
||||
if (m > daemon->metrics[METRIC_CRYPTO_HWM])
|
||||
daemon->metrics[METRIC_CRYPTO_HWM] = m;
|
||||
if (!read_write(fd, (unsigned char *)&m, sizeof(m), RW_READ))
|
||||
return 0;
|
||||
if (m > daemon->metrics[METRIC_SIG_FAIL_HWM])
|
||||
daemon->metrics[METRIC_SIG_FAIL_HWM] = m;
|
||||
if (!read_write(fd, (unsigned char *)&m, sizeof(m), RW_READ))
|
||||
return 0;
|
||||
if (m > daemon->metrics[METRIC_WORK_HWM])
|
||||
daemon->metrics[METRIC_WORK_HWM] = m;
|
||||
#endif
|
||||
cache_end_insert();
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
/* UDP validation moved to TCP to avoid truncation.
|
||||
Restart UDP validation process with the returned result. */
|
||||
if (m == -2)
|
||||
{
|
||||
int status, uid, keycount, validatecount;
|
||||
int *keycountp, *validatecountp;
|
||||
size_t ret_len;
|
||||
|
||||
struct frec *forward;
|
||||
|
||||
if (!read_write(fd, (unsigned char *)&status, sizeof(status), RW_READ))
|
||||
return 0;
|
||||
if (!read_write(fd, (unsigned char *)&ret_len, sizeof(ret_len), RW_READ))
|
||||
return 0;
|
||||
if (!read_write(fd, (unsigned char *)daemon->packet, ret_len, RW_READ))
|
||||
return 0;
|
||||
if (!read_write(fd, (unsigned char *)&forward, sizeof(forward), RW_READ))
|
||||
return 0;
|
||||
if (!read_write(fd, (unsigned char *)&uid, sizeof(uid), RW_READ))
|
||||
return 0;
|
||||
if (!read_write(fd, (unsigned char *)&keycount, sizeof(keycount), RW_READ))
|
||||
return 0;
|
||||
if (!read_write(fd, (unsigned char *)&keycountp, sizeof(keycountp), RW_READ))
|
||||
return 0;
|
||||
if (!read_write(fd, (unsigned char *)&validatecount, sizeof(validatecount), RW_READ))
|
||||
return 0;
|
||||
if (!read_write(fd, (unsigned char *)&validatecountp, sizeof(validatecountp), RW_READ))
|
||||
return 0;
|
||||
|
||||
/* There's a tiny chance that the frec may have been freed
|
||||
and reused before the TCP process returns. Detect that with
|
||||
the uid field which is unique modulo 2^32 for each use. */
|
||||
if (uid == forward->uid)
|
||||
{
|
||||
/* repatriate the work counters from the child process. */
|
||||
*keycountp = keycount;
|
||||
*validatecountp = validatecount;
|
||||
|
||||
if (!forward->dependent)
|
||||
return_reply(now, forward, (struct dns_header *)daemon->packet, ret_len, status);
|
||||
else
|
||||
pop_and_retry_query(forward, status, now);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!read_write(fd, (unsigned char *)daemon->namebuff, m, RW_READ) ||
|
||||
!read_write(fd, (unsigned char *)&ttd, sizeof(ttd), RW_READ) ||
|
||||
!read_write(fd, (unsigned char *)&flags, sizeof(flags), RW_READ) ||
|
||||
!read_write(fd, (unsigned char *)&addr, sizeof(addr), RW_READ))
|
||||
return 0;
|
||||
|
||||
daemon->namebuff[m] = 0;
|
||||
|
||||
ttl = difftime(ttd, now);
|
||||
case PIPE_OP_STATS:
|
||||
{
|
||||
/* Sneak in possibly updated crypto HWM. */
|
||||
unsigned int val;
|
||||
|
||||
if (!read_write(fd, (unsigned char *)&val, sizeof(val), RW_READ))
|
||||
return 0;
|
||||
if (val > daemon->metrics[METRIC_CRYPTO_HWM])
|
||||
daemon->metrics[METRIC_CRYPTO_HWM] = val;
|
||||
if (!read_write(fd, (unsigned char *)&val, sizeof(val), RW_READ))
|
||||
return 0;
|
||||
if (val > daemon->metrics[METRIC_SIG_FAIL_HWM])
|
||||
daemon->metrics[METRIC_SIG_FAIL_HWM] = val;
|
||||
if (!read_write(fd, (unsigned char *)&val, sizeof(val), RW_READ))
|
||||
return 0;
|
||||
if (val > daemon->metrics[METRIC_WORK_HWM])
|
||||
daemon->metrics[METRIC_WORK_HWM] = val;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (flags & F_CNAME)
|
||||
{
|
||||
struct crec *newc = really_insert(daemon->namebuff, NULL, C_IN, now, ttl, flags);
|
||||
/* This relies on the fact that the target of a CNAME immediately precedes
|
||||
it because of the order of extraction in extract_addresses, and
|
||||
the order reversal on the new_chain. */
|
||||
if (newc)
|
||||
{
|
||||
newc->addr.cname.is_name_ptr = 0;
|
||||
|
||||
if (!crecp)
|
||||
newc->addr.cname.target.cache = NULL;
|
||||
else
|
||||
{
|
||||
next_uid(crecp);
|
||||
newc->addr.cname.target.cache = crecp;
|
||||
newc->addr.cname.uid = crecp->uid;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned short class = C_IN;
|
||||
|
||||
if ((flags & F_RR) && !(flags & F_NEG) && (flags & F_KEYTAG)
|
||||
&& !(addr.rrblock.rrdata = blockdata_read(fd, addr.rrblock.datalen)))
|
||||
return 0;
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (flags & F_DNSKEY)
|
||||
{
|
||||
if (!read_write(fd, (unsigned char *)&class, sizeof(class), RW_READ) ||
|
||||
!(addr.key.keydata = blockdata_read(fd, addr.key.keylen)))
|
||||
return 0;
|
||||
}
|
||||
else if (flags & F_DS)
|
||||
{
|
||||
if (!read_write(fd, (unsigned char *)&class, sizeof(class), RW_READ) ||
|
||||
(!(flags & F_NEG) && !(addr.key.keydata = blockdata_read(fd, addr.key.keylen))))
|
||||
return 0;
|
||||
}
|
||||
case PIPE_OP_RESULT:
|
||||
{
|
||||
/* UDP validation moved to TCP to avoid truncation.
|
||||
Restart UDP validation process with the returned result. */
|
||||
int status, uid, keycount, validatecount;
|
||||
int *keycountp, *validatecountp;
|
||||
size_t ret_len;
|
||||
|
||||
struct frec *forward;
|
||||
|
||||
if (!read_write(fd, (unsigned char *)&status, sizeof(status), RW_READ) ||
|
||||
!read_write(fd, (unsigned char *)&ret_len, sizeof(ret_len), RW_READ) ||
|
||||
!read_write(fd, (unsigned char *)daemon->packet, ret_len, RW_READ) ||
|
||||
!read_write(fd, (unsigned char *)&forward, sizeof(forward), RW_READ) ||
|
||||
!read_write(fd, (unsigned char *)&uid, sizeof(uid), RW_READ) ||
|
||||
!read_write(fd, (unsigned char *)&keycount, sizeof(keycount), RW_READ) ||
|
||||
!read_write(fd, (unsigned char *)&keycountp, sizeof(keycountp), RW_READ) ||
|
||||
!read_write(fd, (unsigned char *)&validatecount, sizeof(validatecount), RW_READ) ||
|
||||
!read_write(fd, (unsigned char *)&validatecountp, sizeof(validatecountp), RW_READ))
|
||||
return 0;
|
||||
|
||||
/* There's a tiny chance that the frec may have been freed
|
||||
and reused before the TCP process returns. Detect that with
|
||||
the uid field which is unique modulo 2^32 for each use. */
|
||||
if (uid == forward->uid)
|
||||
{
|
||||
/* repatriate the work counters from the child process. */
|
||||
*keycountp = keycount;
|
||||
*validatecountp = validatecount;
|
||||
|
||||
if (!forward->dependent)
|
||||
return_reply(now, forward, (struct dns_header *)daemon->packet, ret_len, status);
|
||||
else
|
||||
pop_and_retry_query(forward, status, now);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
crecp = really_insert(daemon->namebuff, &addr, class, now, ttl, flags);
|
||||
}
|
||||
|
||||
#if defined(HAVE_IPSET) || defined(HAVE_NFTSET)
|
||||
case PIPE_OP_IPSET:
|
||||
case PIPE_OP_NFTSET:
|
||||
{
|
||||
struct ipsets *sets;
|
||||
char **sets_cur;
|
||||
unsigned int flags;
|
||||
union all_addr addr;
|
||||
|
||||
if (!read_write(fd, (unsigned char *)&sets, sizeof(sets), RW_READ) ||
|
||||
!read_write(fd, (unsigned char *)&flags, sizeof(flags), RW_READ) ||
|
||||
!read_write(fd, (unsigned char *)&addr, sizeof(addr), RW_READ))
|
||||
return 0;
|
||||
|
||||
for (sets_cur = sets->sets; *sets_cur; sets_cur++)
|
||||
{
|
||||
int rc = -1;
|
||||
|
||||
#ifdef HAVE_IPSET
|
||||
if (op == PIPE_OP_IPSET)
|
||||
rc = add_to_ipset(*sets_cur, &addr, flags, 0);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_NFTSET
|
||||
if (op == PIPE_OP_NFTSET)
|
||||
rc = add_to_nftset(*sets_cur, &addr, flags, 0);
|
||||
#endif
|
||||
|
||||
if (rc == 0)
|
||||
log_query((flags & (F_IPV4 | F_IPV6)) | F_IPSET, sets->domain, &addr, *sets_cur, op == PIPE_OP_IPSET);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cache_find_non_terminal(char *name, time_t now)
|
||||
@@ -1443,11 +1528,17 @@ void cache_reload(void)
|
||||
cache->flags = F_FORWARD | F_IMMORTAL | F_DS | F_CONFIG | F_NAMEP;
|
||||
cache->ttd = daemon->local_ttl;
|
||||
cache->name.namep = ds->name;
|
||||
cache->addr.ds.keylen = ds->digestlen;
|
||||
cache->addr.ds.algo = ds->algo;
|
||||
cache->addr.ds.keytag = ds->keytag;
|
||||
cache->addr.ds.digest = ds->digest_type;
|
||||
cache->uid = ds->class;
|
||||
if (ds->digestlen != 0)
|
||||
{
|
||||
cache->addr.ds.keylen = ds->digestlen;
|
||||
cache->addr.ds.algo = ds->algo;
|
||||
cache->addr.ds.keytag = ds->keytag;
|
||||
cache->addr.ds.digest = ds->digest_type;
|
||||
}
|
||||
else
|
||||
cache->flags |= F_NEG | F_DNSSECOK | F_NO_RR;
|
||||
|
||||
cache_hash(cache);
|
||||
make_non_terminals(cache);
|
||||
}
|
||||
@@ -1847,15 +1938,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;
|
||||
}
|
||||
|
||||
@@ -2148,12 +2255,17 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg,
|
||||
char *extra = "";
|
||||
char *gap = " ";
|
||||
char portstring[7]; /* space for #<portnum> */
|
||||
|
||||
char opcodestring[3]; /* maximum is 15 */
|
||||
|
||||
if (!option_bool(OPT_LOG))
|
||||
return;
|
||||
|
||||
/* F_NOERR is reused here to indicate logs arrising from auth queries */
|
||||
if (!(flags & F_NOERR) && option_bool(OPT_AUTH_LOG))
|
||||
return;
|
||||
|
||||
/* build query type string if requested */
|
||||
if (!(flags & (F_SERVER | F_IPSET)) && type > 0)
|
||||
if (!(flags & (F_SERVER | F_IPSET | F_QUERY)) && type > 0)
|
||||
arg = querystr(arg, type);
|
||||
|
||||
dest = arg;
|
||||
@@ -2186,6 +2298,8 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg,
|
||||
dest = "SERVFAIL";
|
||||
else if (rcode == REFUSED)
|
||||
dest = "REFUSED";
|
||||
else if (rcode == FORMERR)
|
||||
dest = "FORMERR";
|
||||
else if (rcode == NOTIMP)
|
||||
dest = "not implemented";
|
||||
else
|
||||
@@ -2244,6 +2358,10 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg,
|
||||
source = arg;
|
||||
else if (flags & F_UPSTREAM)
|
||||
source = "reply";
|
||||
else if (flags & F_AUTH)
|
||||
source = "auth";
|
||||
else if (flags & F_QUERY)
|
||||
source = "query";
|
||||
else if (flags & F_SECSTAT)
|
||||
{
|
||||
if (addr && addr->log.ede != EDE_UNSET && option_bool(OPT_EXTRALOG))
|
||||
@@ -2254,8 +2372,6 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg,
|
||||
source = "validation";
|
||||
dest = arg;
|
||||
}
|
||||
else if (flags & F_AUTH)
|
||||
source = "auth";
|
||||
else if (flags & F_DNSSEC)
|
||||
{
|
||||
source = arg;
|
||||
@@ -2266,11 +2382,6 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg,
|
||||
source = "forwarded";
|
||||
verb = "to";
|
||||
}
|
||||
else if (flags & F_QUERY)
|
||||
{
|
||||
source = arg;
|
||||
verb = "from";
|
||||
}
|
||||
else if (flags & F_IPSET)
|
||||
{
|
||||
source = type ? "ipset add" : "nftset add";
|
||||
@@ -2282,7 +2393,21 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg,
|
||||
source = "cached-stale";
|
||||
else
|
||||
source = "cached";
|
||||
|
||||
|
||||
if (flags & F_QUERY)
|
||||
{
|
||||
if (flags & F_CONFIG)
|
||||
{
|
||||
sprintf(opcodestring, "%u", type & 0xf);
|
||||
source = "non-query opcode";
|
||||
name = opcodestring;
|
||||
}
|
||||
else if (type > 0)
|
||||
source = querystr(source, type);
|
||||
|
||||
verb = "from";
|
||||
}
|
||||
|
||||
if (!name)
|
||||
gap = name = "";
|
||||
else if (!name[0])
|
||||
|
||||
+16
-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
|
||||
@@ -26,6 +26,7 @@
|
||||
#define DNSSEC_LIMIT_SIG_FAIL 20 /* Number of signature that can fail to validate in one answer */
|
||||
#define DNSSEC_LIMIT_CRYPTO 200 /* max no. of crypto operations to validate one query. */
|
||||
#define DNSSEC_LIMIT_NSEC3_ITERS 150 /* Max. number if iterations allowed in NSEC3 record. */
|
||||
#define DNSSEC_ASSUMED_DS_TTL 3600 /* TTL for negative DS records implied by server=/domain/ */
|
||||
#define TIMEOUT 10 /* drop UDP queries after TIMEOUT seconds */
|
||||
#define SMALL_PORT_RANGE 30 /* If DNS port range is smaller than this, use different allocation. */
|
||||
#define FORWARD_TEST 50 /* try all servers every 50 queries */
|
||||
@@ -51,6 +52,8 @@
|
||||
#define CHUSER "nobody"
|
||||
#define CHGRP "dip"
|
||||
#define TFTP_MAX_CONNECTIONS 50 /* max simultaneous connections */
|
||||
#define TFTP_MAX_WINDOW 32 /* max window size to negotiate */
|
||||
#define TFTP_TRANSFER_TIME 120 /* Abandon TFTP transfers after this long. Two mins. */
|
||||
#define LOG_MAX 5 /* log-queue length */
|
||||
#define RANDFILE "/dev/urandom"
|
||||
#define DNSMASQ_SERVICE "uk.org.thekelleys.dnsmasq" /* Default - may be overridden by config */
|
||||
@@ -152,6 +155,7 @@ NO_AUTH
|
||||
NO_DUMPFILE
|
||||
NO_LOOP
|
||||
NO_INOTIFY
|
||||
NO_IPSET
|
||||
these are available to explicitly disable compile time options which would
|
||||
otherwise be enabled automatically or which are enabled by default
|
||||
in the distributed source tree. Building dnsmasq
|
||||
@@ -286,7 +290,6 @@ HAVE_SOCKADDR_SA_LEN
|
||||
#define HAVE_BSD_NETWORK
|
||||
#define HAVE_GETOPT_LONG
|
||||
#define HAVE_SOCKADDR_SA_LEN
|
||||
#define NO_IPSET
|
||||
/* Define before sys/socket.h is included so we get socklen_t */
|
||||
#define _BSD_SOCKLEN_T_
|
||||
/* Select the RFC_3542 version of the IPv6 socket API.
|
||||
@@ -296,7 +299,6 @@ HAVE_SOCKADDR_SA_LEN
|
||||
#ifndef SOL_TCP
|
||||
# define SOL_TCP IPPROTO_TCP
|
||||
#endif
|
||||
#define NO_IPSET
|
||||
|
||||
#elif defined(__NetBSD__)
|
||||
#define HAVE_BSD_NETWORK
|
||||
@@ -346,6 +348,11 @@ HAVE_SOCKADDR_SA_LEN
|
||||
#undef HAVE_AUTH
|
||||
#endif
|
||||
|
||||
#if !defined(HAVE_LINUX_NETWORK)
|
||||
#undef HAVE_IPSET
|
||||
#undef HAVE_NFTSET
|
||||
#endif
|
||||
|
||||
#if defined(NO_IPSET)
|
||||
#undef HAVE_IPSET
|
||||
#endif
|
||||
@@ -362,6 +369,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 */
|
||||
|
||||
@@ -454,4 +466,4 @@ static char *compile_opts =
|
||||
#endif
|
||||
"dumpfile";
|
||||
|
||||
#endif /* defined(HAVE_DHCP) */
|
||||
#endif /* defined(DNSMASQ_COMPILE_OPTS) */
|
||||
+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
|
||||
|
||||
+5
-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
|
||||
@@ -768,10 +768,10 @@ static DBusMessage *dbus_get_server_metrics(DBusMessage* message)
|
||||
add_dict_entry(&dict_array, "address", daemon->namebuff);
|
||||
|
||||
add_dict_int(&dict_array, "port", port);
|
||||
add_dict_int(&dict_array, "queries", serv->queries);
|
||||
add_dict_int(&dict_array, "failed_queries", serv->failed_queries);
|
||||
add_dict_int(&dict_array, "nxdomain", serv->nxdomain_replies);
|
||||
add_dict_int(&dict_array, "retries", serv->retrys);
|
||||
add_dict_int(&dict_array, "queries", queries);
|
||||
add_dict_int(&dict_array, "failed_queries", failed_queries);
|
||||
add_dict_int(&dict_array, "nxdomain", nxdomain_replies);
|
||||
add_dict_int(&dict_array, "retries", retrys);
|
||||
add_dict_int(&dict_array, "latency", sigma_latency/count_latency);
|
||||
|
||||
dbus_message_iter_close_container(&server_array, &dict_array);
|
||||
|
||||
+4
-2
@@ -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
|
||||
@@ -701,7 +701,9 @@ static const struct opttab_t {
|
||||
{ "user-class", 77, 0 },
|
||||
{ "rapid-commit", 80, 0 },
|
||||
{ "FQDN", 81, OT_INTERNAL },
|
||||
{ "agent-id", 82, OT_INTERNAL },
|
||||
{ "agent-info", 82, OT_INTERNAL },
|
||||
{ "last-transaction", 91, 4 | OT_TIME },
|
||||
{ "associated-ip", 92, OT_ADDR_LIST },
|
||||
{ "client-arch", 93, 2 | OT_DEC },
|
||||
{ "client-interface-id", 94, 0 },
|
||||
{ "client-machine-id", 97, 0 },
|
||||
|
||||
+8
-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
|
||||
@@ -57,6 +57,8 @@
|
||||
#define OPTION_RAPID_COMMIT 80
|
||||
#define OPTION_CLIENT_FQDN 81
|
||||
#define OPTION_AGENT_ID 82
|
||||
#define OPTION_LAST_TRANSACTION 91
|
||||
#define OPTION_ASSOCIATED_IP 92
|
||||
#define OPTION_ARCH 93
|
||||
#define OPTION_PXE_UUID 97
|
||||
#define OPTION_SUBNET_SELECT 118
|
||||
@@ -87,6 +89,11 @@
|
||||
#define DHCPNAK 6
|
||||
#define DHCPRELEASE 7
|
||||
#define DHCPINFORM 8
|
||||
#define DHCPFORCERENEW 9
|
||||
#define DHCPLEASEQUERY 10
|
||||
#define DHCPLEASEUNASSIGNED 11
|
||||
#define DHCPLEASEUNKNOWN 12
|
||||
#define DHCPLEASEACTIVE 13
|
||||
|
||||
#define BRDBAND_FORUM_IANA 3561 /* Broadband forum IANA enterprise */
|
||||
|
||||
|
||||
+29
-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
|
||||
@@ -135,7 +135,7 @@ void dhcp_packet(time_t now, int pxe_fd)
|
||||
struct dhcp_packet *mess;
|
||||
struct dhcp_context *context;
|
||||
struct dhcp_relay *relay;
|
||||
int is_relay_reply = 0;
|
||||
int is_relay_reply = 0, is_relay_use_source = 0;
|
||||
struct iname *tmp;
|
||||
struct ifreq ifr;
|
||||
struct msghdr msg;
|
||||
@@ -230,6 +230,20 @@ void dhcp_packet(time_t now, int pxe_fd)
|
||||
return;
|
||||
|
||||
mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
|
||||
|
||||
/* Non-standard extension:
|
||||
If giaddr == 255.255.255.255 we reply to the source
|
||||
address in the request packet header. This makes
|
||||
stand-alone leasequery clients easier, as they
|
||||
can leave source address determination to the kernel.
|
||||
In this case, set a flag and clear giaddr here,
|
||||
to avoid massive relay confusion. */
|
||||
if (mess->giaddr.s_addr == INADDR_BROADCAST)
|
||||
{
|
||||
mess->giaddr.s_addr = 0;
|
||||
is_relay_use_source = 1;
|
||||
}
|
||||
|
||||
loopback = !mess->giaddr.s_addr && (ifr.ifr_flags & IFF_LOOPBACK);
|
||||
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
@@ -337,8 +351,9 @@ void dhcp_packet(time_t now, int pxe_fd)
|
||||
return;
|
||||
|
||||
lease_prune(NULL, now); /* lose any expired leases */
|
||||
iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz,
|
||||
now, unicast_dest, loopback, &is_inform, pxe_fd, iface_addr, recvtime);
|
||||
iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz, now, unicast_dest,
|
||||
loopback, &is_inform, pxe_fd, iface_addr, recvtime,
|
||||
is_relay_use_source ? dest.sin_addr : mess->giaddr);
|
||||
lease_update_file(now);
|
||||
lease_update_dns(0);
|
||||
|
||||
@@ -365,11 +380,17 @@ void dhcp_packet(time_t now, int pxe_fd)
|
||||
if (mess->ciaddr.s_addr != 0)
|
||||
dest.sin_addr = mess->ciaddr;
|
||||
}
|
||||
else if (mess->giaddr.s_addr && !is_relay_reply)
|
||||
if ((is_relay_use_source || mess->giaddr.s_addr) && !is_relay_reply)
|
||||
{
|
||||
/* Send to BOOTP relay */
|
||||
dest.sin_port = htons(daemon->dhcp_server_port);
|
||||
dest.sin_addr = mess->giaddr;
|
||||
/* Send to BOOTP relay. */
|
||||
if (is_relay_use_source)
|
||||
/* restore as-received value */
|
||||
mess->giaddr.s_addr = INADDR_BROADCAST;
|
||||
else
|
||||
{
|
||||
dest.sin_addr = mess->giaddr;
|
||||
dest.sin_port = htons(daemon->dhcp_server_port);
|
||||
}
|
||||
}
|
||||
else if (mess->ciaddr.s_addr)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
+2
-2
@@ -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
|
||||
@@ -812,7 +812,7 @@ void dhcp_construct_contexts(time_t now)
|
||||
{
|
||||
if ((context->flags & CONTEXT_RA) || option_bool(OPT_RA))
|
||||
{
|
||||
/* previously constructed context has gone. advertise it's demise */
|
||||
/* previously constructed context has gone; advertise its demise */
|
||||
context->flags |= CONTEXT_OLD;
|
||||
context->address_lost_time = now;
|
||||
/* Apply same ceiling of configured lease time as in radv.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
|
||||
|
||||
+325
-245
@@ -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,14 +30,16 @@ static volatile pid_t pid = 0;
|
||||
static volatile int pipewrite;
|
||||
|
||||
static void set_dns_listeners(void);
|
||||
#ifdef HAVE_TFTP
|
||||
static void set_tftp_listeners(void);
|
||||
#endif
|
||||
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);
|
||||
static int read_event(int fd, struct event_desc *evp, char **msg);
|
||||
static void poll_resolv(int force, int do_reload, time_t now);
|
||||
static void tcp_init(void);
|
||||
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
@@ -61,14 +63,17 @@ 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;
|
||||
# ifdef HAVE_DHCP
|
||||
char *bound_device = NULL;
|
||||
int did_bind = 0;
|
||||
# endif
|
||||
struct server *serv;
|
||||
char *netlink_warn;
|
||||
#else
|
||||
int bind_fallback = 0;
|
||||
#endif
|
||||
#if defined(HAVE_DHCP) || defined(HAVE_DHCP6)
|
||||
#if defined(HAVE_DHCP)
|
||||
struct dhcp_context *context;
|
||||
struct dhcp_relay *relay;
|
||||
#endif
|
||||
@@ -76,6 +81,10 @@ int main (int argc, char **argv)
|
||||
int tftp_prefix_missing = 0;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
(void)netlink_warn;
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_IDN) || defined(HAVE_LIBIDN2) || defined(LOCALEDIR)
|
||||
setlocale(LC_ALL, "");
|
||||
#endif
|
||||
@@ -131,6 +140,7 @@ int main (int argc, char **argv)
|
||||
'.' or NAME_ESCAPE then all would have to be escaped, so the
|
||||
presentation format would be twice as long as the spec. */
|
||||
daemon->keyname = safe_malloc((MAXDNAME * 2) + 1);
|
||||
daemon->cname = safe_malloc((MAXDNAME * 2) + 1);
|
||||
/* one char flag per possible RR in answer section (may get extended). */
|
||||
daemon->rr_status_sz = 64;
|
||||
daemon->rr_status = safe_malloc(sizeof(*daemon->rr_status) * daemon->rr_status_sz);
|
||||
@@ -419,7 +429,11 @@ int main (int argc, char **argv)
|
||||
/* safe_malloc returns zero'd memory */
|
||||
daemon->randomsocks = safe_malloc(daemon->numrrand * sizeof(struct randfd));
|
||||
|
||||
tcp_init();
|
||||
daemon->tcp_pids = safe_malloc(daemon->max_procs*sizeof(pid_t));
|
||||
daemon->tcp_pipes = safe_malloc(daemon->max_procs*sizeof(int));
|
||||
|
||||
for (i = 0; i < daemon->max_procs; i++)
|
||||
daemon->tcp_pipes[i] = -1;
|
||||
}
|
||||
|
||||
#ifdef HAVE_INOTIFY
|
||||
@@ -556,6 +570,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 +885,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))
|
||||
@@ -919,7 +942,8 @@ int main (int argc, char **argv)
|
||||
my_syslog(LOG_INFO, _("DNSSEC signature timestamps not checked until system time valid"));
|
||||
|
||||
for (ds = daemon->ds; ds; ds = ds->next)
|
||||
my_syslog(LOG_INFO, _("configured with trust anchor for %s keytag %u"),
|
||||
my_syslog(LOG_INFO,
|
||||
ds->digestlen == 0 ? _("configured with negative trust anchor for %s") : _("configured with trust anchor for %s keytag %u"),
|
||||
ds->name[0] == 0 ? "<root>" : ds->name, ds->keytag);
|
||||
}
|
||||
#endif
|
||||
@@ -951,7 +975,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)
|
||||
@@ -1051,10 +1082,6 @@ int main (int argc, char **argv)
|
||||
|
||||
daemon->pipe_to_parent = -1;
|
||||
|
||||
if (daemon->port != 0)
|
||||
for (i = 0; i < daemon->max_procs; i++)
|
||||
daemon->tcp_pipes[i] = -1;
|
||||
|
||||
#ifdef HAVE_INOTIFY
|
||||
/* Using inotify, have to select a resolv file at startup */
|
||||
poll_resolv(1, 0, now);
|
||||
@@ -1466,7 +1493,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 +1556,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 +1567,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 +1863,288 @@ 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.
|
||||
|
||||
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 (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
|
||||
{
|
||||
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 *buff;
|
||||
struct server *s;
|
||||
int flags, auth_dns;
|
||||
struct in_addr netmask;
|
||||
int pipefd[2];
|
||||
#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);
|
||||
unsigned char a = 0;
|
||||
#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
|
||||
}
|
||||
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 (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))
|
||||
{
|
||||
#ifdef HAVE_DNSSEC
|
||||
cache_update_hwm(); /* Sneak out possibly updated crypto HWM values. */
|
||||
#endif
|
||||
|
||||
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
|
||||
@@ -2074,7 +2156,7 @@ static void check_dns_listeners(time_t now)
|
||||
cache_recv_insert() calls pop_and_retry_query() after the result
|
||||
arrives via the pipe to the parent. */
|
||||
int swap_to_tcp(struct frec *forward, time_t now, int status, struct dns_header *header,
|
||||
ssize_t *plen, int class, struct server *server, int *keycount, int *validatecount)
|
||||
ssize_t *plen, char *name, int class, struct server *server, int *keycount, int *validatecount)
|
||||
{
|
||||
struct server *s;
|
||||
|
||||
@@ -2091,11 +2173,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)
|
||||
@@ -2148,8 +2230,7 @@ int swap_to_tcp(struct frec *forward, time_t now, int status, struct dns_header
|
||||
}
|
||||
}
|
||||
|
||||
status = tcp_from_udp(now, status, header, plen, class, daemon->namebuff, daemon->keyname,
|
||||
server, keycount, validatecount);
|
||||
status = tcp_from_udp(now, status, header, plen, class, name, server, keycount, validatecount);
|
||||
|
||||
/* close upstream connections. */
|
||||
for (s = daemon->servers; s; s = s->next)
|
||||
@@ -2162,10 +2243,10 @@ int swap_to_tcp(struct frec *forward, time_t now, int status, struct dns_header
|
||||
|
||||
if (!option_bool(OPT_DEBUG))
|
||||
{
|
||||
ssize_t m = -2;
|
||||
unsigned char op = PIPE_OP_RESULT;
|
||||
|
||||
/* tell our parent we're done, and what the result was then exit. */
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), RW_WRITE);
|
||||
read_write(daemon->pipe_to_parent, &op, sizeof(op), RW_WRITE);
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&status, sizeof(status), RW_WRITE);
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)plen, sizeof(*plen), RW_WRITE);
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)header, *plen, RW_WRITE);
|
||||
@@ -2175,6 +2256,9 @@ int swap_to_tcp(struct frec *forward, time_t now, int status, struct dns_header
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&keycount, sizeof(keycount), RW_WRITE);
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)validatecount, sizeof(*validatecount), RW_WRITE);
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&validatecount, sizeof(validatecount), RW_WRITE);
|
||||
|
||||
cache_update_hwm(); /* Sneak out possibly updated crypto HWM values. */
|
||||
|
||||
close(daemon->pipe_to_parent);
|
||||
|
||||
flush_log();
|
||||
@@ -2345,8 +2429,4 @@ int delay_dhcp(time_t start, int sec, int fd, uint32_t addr, unsigned short id)
|
||||
}
|
||||
#endif /* HAVE_DHCP */
|
||||
|
||||
void tcp_init(void)
|
||||
{
|
||||
daemon->tcp_pids = safe_malloc(daemon->max_procs*sizeof(pid_t));
|
||||
daemon->tcp_pipes = safe_malloc(daemon->max_procs*sizeof(int));
|
||||
}
|
||||
|
||||
+63
-28
@@ -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. */
|
||||
@@ -48,6 +48,12 @@
|
||||
#define ATTRIBUTE_NORETURN
|
||||
#endif
|
||||
|
||||
#if __GNUC__ + 0 >= 8 // clang 20.1.0 does not yet support this
|
||||
#define ATTRIBUTE_NONSTRING __attribute__ ((nonstring))
|
||||
#else
|
||||
#define ATTRIBUTE_NONSTRING
|
||||
#endif
|
||||
|
||||
/* get these before config.h for IPv6 stuff... */
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
@@ -279,7 +285,11 @@ 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_AUTH_LOG 76
|
||||
#define OPT_LEASEQUERY 77
|
||||
#define OPT_LAST 78
|
||||
|
||||
#define OPTION_BITS (sizeof(unsigned int)*8)
|
||||
#define OPTION_SIZE ( (OPT_LAST/OPTION_BITS)+((OPT_LAST%OPTION_BITS)!=0) )
|
||||
@@ -339,7 +349,7 @@ union all_addr {
|
||||
struct datablock {
|
||||
unsigned short rrtype;
|
||||
unsigned char datalen; /* also length of SOA in negative records. */
|
||||
char data[];
|
||||
char data[1];
|
||||
} rrdata;
|
||||
};
|
||||
|
||||
@@ -534,6 +544,11 @@ struct crec {
|
||||
#define SRC_HOSTS 2
|
||||
#define SRC_AH 3
|
||||
|
||||
#define PIPE_OP_INSERT 1 /* Cache entry */
|
||||
#define PIPE_OP_RESULT 2 /* Validation result */
|
||||
#define PIPE_OP_STATS 3 /* Update parent's stats */
|
||||
#define PIPE_OP_IPSET 4 /* Update IPset */
|
||||
#define PIPE_OP_NFTSET 5 /* Update NFTset */
|
||||
|
||||
/* struct sockaddr is not large enough to hold any address,
|
||||
and specifically not big enough to hold an IPv6 address.
|
||||
@@ -551,9 +566,9 @@ union mysockaddr {
|
||||
|
||||
|
||||
/* The actual values here matter, since we sort on them to get records in the order
|
||||
IPv6 addr, IPv4 addr, all zero return, resolvconf servers, upstream server, no-data return */
|
||||
#define SERV_LITERAL_ADDRESS 1 /* addr is the answer, or NoDATA is the answer, depending on the next four flags */
|
||||
#define SERV_USE_RESOLV 2 /* forward this domain in the normal way */
|
||||
IPv6 addr, IPv4 addr, all zero return, no-data return, resolvconf servers, upstream server */
|
||||
#define SERV_USE_RESOLV 1 /* forward this domain in the normal way */
|
||||
#define SERV_LITERAL_ADDRESS 2 /* addr is the answer, or NoDATA is the answer, depending on the next four flags */
|
||||
#define SERV_ALL_ZEROS 4 /* return all zeros for A and AAAA */
|
||||
#define SERV_4ADDR 8 /* addr is IPv4 */
|
||||
#define SERV_6ADDR 16 /* addr is IPv6 */
|
||||
@@ -772,15 +787,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;
|
||||
@@ -856,6 +871,8 @@ struct dhcp_lease {
|
||||
int last_interface;
|
||||
int new_interface; /* save possible originated interface */
|
||||
int new_prefixlen; /* and its prefix length */
|
||||
unsigned char *agent_id, *vendorclass;
|
||||
int agent_id_len, vendorclass_len;
|
||||
#ifdef HAVE_DHCP6
|
||||
struct in6_addr addr6;
|
||||
unsigned int iaid;
|
||||
@@ -1100,14 +1117,13 @@ struct tftp_file {
|
||||
|
||||
struct tftp_transfer {
|
||||
int sockfd;
|
||||
time_t timeout;
|
||||
int backoff;
|
||||
unsigned int block, blocksize, expansion;
|
||||
time_t retransmit, start;
|
||||
unsigned int lastack, block, blocksize, windowsize, timeout, expansion;
|
||||
off_t offset;
|
||||
union mysockaddr peer;
|
||||
union all_addr source;
|
||||
int if_index;
|
||||
char opt_blocksize, opt_transize, netascii, carrylf;
|
||||
unsigned char opt_blocksize, opt_transize, opt_windowsize, opt_timeout, netascii, carrylf, backoff;
|
||||
struct tftp_file *file;
|
||||
struct tftp_transfer *next;
|
||||
};
|
||||
@@ -1125,7 +1141,10 @@ struct tftp_prefix {
|
||||
};
|
||||
|
||||
struct dhcp_relay {
|
||||
union all_addr local, server;
|
||||
union {
|
||||
struct in_addr addr4;
|
||||
struct in6_addr addr6;
|
||||
} local, server;
|
||||
char *interface; /* Allowable interface for replies from server, and dest for IPv6 multicast */
|
||||
int iface_index; /* working - interface in which requests arrived, for return */
|
||||
int port; /* Port of relay we forward to. */
|
||||
@@ -1172,7 +1191,7 @@ extern struct daemon {
|
||||
char *runfile;
|
||||
char *lease_change_command;
|
||||
struct iname *if_names, *if_addrs, *if_except, *dhcp_except, *auth_peers, *tftp_interfaces;
|
||||
struct bogus_addr *bogus_addr, *ignore_addr;
|
||||
struct bogus_addr *bogus_addr, *ignore_addr, *leasequery_addr;
|
||||
struct server *servers, *servers_tail, *local_domains, **serverarray;
|
||||
struct rebind_domain *no_rebind;
|
||||
int server_has_wildcard;
|
||||
@@ -1243,7 +1262,7 @@ extern struct daemon {
|
||||
char *namebuff; /* MAXDNAME size buffer */
|
||||
char *workspacename;
|
||||
#ifdef HAVE_DNSSEC
|
||||
char *keyname; /* MAXDNAME size buffer */
|
||||
char *keyname, *cname; /* MAXDNAME size buffer */
|
||||
unsigned long *rr_status; /* ceiling in TTL from DNSSEC or zero for insecure */
|
||||
int rr_status_sz;
|
||||
int dnssec_no_time_check;
|
||||
@@ -1348,6 +1367,13 @@ void cache_end_insert(void);
|
||||
void cache_start_insert(void);
|
||||
unsigned int cache_remove_uid(const unsigned int uid);
|
||||
int cache_recv_insert(time_t now, int fd);
|
||||
#ifdef HAVE_DNSSEC
|
||||
void cache_update_hwm(void);
|
||||
#endif
|
||||
#if defined(HAVE_IPSET) || defined(HAVE_NFTSET)
|
||||
void cache_send_ipset(unsigned char op, struct ipsets *sets,
|
||||
int flags, union all_addr *addr);
|
||||
#endif
|
||||
struct crec *cache_insert(char *name, union all_addr *addr, unsigned short class,
|
||||
time_t now, unsigned long ttl, unsigned int flags);
|
||||
void cache_reload(void);
|
||||
@@ -1384,22 +1410,22 @@ 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);
|
||||
unsigned int extract_request(struct dns_header *header, size_t qlen,
|
||||
char *name, unsigned short *typep);
|
||||
unsigned int extract_request(struct dns_header *header, size_t qlen, char *name,
|
||||
unsigned short *typep, unsigned short *classp);
|
||||
void setup_reply(struct dns_header *header, unsigned int flags, int ede);
|
||||
int extract_addresses(struct dns_header *header, size_t qlen, char *name,
|
||||
time_t now, struct ipsets *ipsets, struct ipsets *nftsets, int is_sign,
|
||||
time_t now, struct ipsets *ipsets, struct ipsets *nftsets,
|
||||
int check_rebind, int no_cache_dnssec, int secure);
|
||||
#if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS)
|
||||
void report_addresses(struct dns_header *header, size_t len, u32 mark);
|
||||
#endif
|
||||
size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
struct in_addr local_addr, struct in_addr local_netmask,
|
||||
time_t now, int ad_reqd, int do_bit, int *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);
|
||||
@@ -1411,6 +1437,12 @@ 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);
|
||||
int private_net6(struct in6_addr *a, 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
|
||||
@@ -1520,7 +1552,7 @@ void return_reply(time_t now, struct frec *forward, struct dns_header *header, s
|
||||
#ifdef HAVE_DNSSEC
|
||||
void pop_and_retry_query(struct frec *forward, int status, time_t now);
|
||||
int tcp_from_udp(time_t now, int status, struct dns_header *header, ssize_t *n,
|
||||
int class, char *name, char *keyname, struct server *server,
|
||||
int class, char *name, struct server *server,
|
||||
int *keycount, int *validatecount);
|
||||
#endif
|
||||
unsigned char *tcp_request(int confd, time_t now,
|
||||
@@ -1595,6 +1627,7 @@ void lease6_reset(void);
|
||||
struct dhcp_lease *lease6_find_by_client(struct dhcp_lease *first, int lease_type,
|
||||
unsigned char *clid, int clid_len, unsigned int iaid);
|
||||
struct dhcp_lease *lease6_find_by_addr(struct in6_addr *net, int prefix, u64 addr);
|
||||
struct dhcp_lease *lease6_find_by_plain_addr(struct in6_addr *addr);
|
||||
u64 lease_find_max_addr6(struct dhcp_context *context);
|
||||
void lease_ping_reply(struct in6_addr *sender, unsigned char *packet, char *interface);
|
||||
void lease_update_slaac(time_t now);
|
||||
@@ -1607,6 +1640,8 @@ void lease_set_hwaddr(struct dhcp_lease *lease, const unsigned char *hwaddr,
|
||||
void lease_set_hostname(struct dhcp_lease *lease, const char *name, int auth, char *domain, char *config_domain);
|
||||
void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now);
|
||||
void lease_set_interface(struct dhcp_lease *lease, int interface, time_t now);
|
||||
void lease_set_agent_id(struct dhcp_lease *lease, unsigned char *new, int len);
|
||||
void lease_set_vendorclass(struct dhcp_lease *lease, unsigned char *new, int len);
|
||||
struct dhcp_lease *lease_find_by_client(unsigned char *hwaddr, int hw_len, int hw_type,
|
||||
unsigned char *clid, int clid_len);
|
||||
struct dhcp_lease *lease_find_by_addr(struct in_addr addr);
|
||||
@@ -1627,7 +1662,8 @@ void lease_add_extradata(struct dhcp_lease *lease, unsigned char *data,
|
||||
#ifdef HAVE_DHCP
|
||||
size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
size_t sz, time_t now, int unicast_dest, int loopback,
|
||||
int *is_inform, int pxe, struct in_addr fallback, time_t recvtime);
|
||||
int *is_inform, int pxe, struct in_addr fallback,
|
||||
time_t recvtime, struct in_addr leasequery_source);
|
||||
unsigned char *extended_hwaddr(int hwtype, int hwlen, unsigned char *hwaddr,
|
||||
int clid_len, unsigned char *clid, int *len_out);
|
||||
#endif
|
||||
@@ -1644,7 +1680,7 @@ void send_event(int fd, int event, int data, char *msg);
|
||||
void clear_cache_and_reload(time_t now);
|
||||
#ifdef HAVE_DNSSEC
|
||||
int swap_to_tcp(struct frec *forward, time_t now, int status, struct dns_header *header,
|
||||
ssize_t *plen, int class, struct server *server, int *keycount, int *validatecount);
|
||||
ssize_t *plen, char *name, int class, struct server *server, int *keycount, int *validatecount);
|
||||
#endif
|
||||
|
||||
/* netlink.c */
|
||||
@@ -1664,7 +1700,7 @@ void route_sock(void);
|
||||
|
||||
/* bpf.c or netlink.c */
|
||||
typedef union {
|
||||
int (*af_unspec)(int family, char *addrp, char *mac, size_t maclen, void *parmv);
|
||||
int (*af_unspec)(int family, void *addrp, char *mac, size_t maclen, void *parmv);
|
||||
int (*af_inet)(struct in_addr local, int if_index, char *label, struct in_addr netmask, struct in_addr broadcast, void *vparam);
|
||||
int (*af_inet6)(struct in6_addr *local, int prefix, int scope, int if_index, int flags, unsigned int preferred, unsigned int valid, void *vparam);
|
||||
int (*af_local)(int index, unsigned int type, char *mac, size_t maclen, void *parm);
|
||||
@@ -1731,7 +1767,6 @@ void queue_relay_snoop(struct in6_addr *client, int if_index, struct in6_addr *p
|
||||
|
||||
/* tftp.c */
|
||||
#ifdef HAVE_TFTP
|
||||
void tftp_request(struct listener *listen, time_t now);
|
||||
void check_tftp_listeners(time_t now);
|
||||
int do_tftp_script_run(void);
|
||||
#endif
|
||||
|
||||
+236
-90
@@ -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);
|
||||
@@ -997,49 +997,66 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
|
||||
unsigned long ttl;
|
||||
union all_addr a;
|
||||
|
||||
if (ntohs(header->qdcount) != 1 ||
|
||||
!(p = skip_name(p, header, plen, 4)))
|
||||
return STAT_BOGUS;
|
||||
|
||||
GETSHORT(qtype, p);
|
||||
GETSHORT(qclass, p);
|
||||
|
||||
if (qtype != T_DS || qclass != class)
|
||||
return STAT_BOGUS;
|
||||
|
||||
/* A SERVFAIL answer has been seen to a DS query not at start of authority,
|
||||
/* A SERVFAIL answer has been seen to a DS query not at start of authority,
|
||||
so treat it as such and continue to search for a DS or proof of no existence
|
||||
further down the tree. */
|
||||
if (RCODE(header) == SERVFAIL)
|
||||
servfail = neganswer = nons = 1;
|
||||
else
|
||||
{
|
||||
rc = dnssec_validate_reply(now, header, plen, name, keyname, NULL, 0, &neganswer, &nons, &neg_ttl, validate_counter);
|
||||
rc = dnssec_validate_reply(now, header, plen, name, keyname, NULL, 0, &neganswer, &nons, &neg_ttl, validate_counter);
|
||||
|
||||
p = (unsigned char *)(header+1);
|
||||
if (ntohs(header->qdcount) != 1 ||
|
||||
!extract_name(header, plen, &p, name, EXTR_NAME_EXTRACT, 4))
|
||||
return STAT_BOGUS;
|
||||
|
||||
GETSHORT(qtype, p);
|
||||
GETSHORT(qclass, p);
|
||||
|
||||
if (qtype != T_DS || qclass != class)
|
||||
return STAT_BOGUS;
|
||||
|
||||
if (!servfail)
|
||||
{
|
||||
if (STAT_ISEQUAL(rc, STAT_INSECURE))
|
||||
{
|
||||
my_syslog(LOG_WARNING, _("Insecure DS reply received for %s, check domain configuration and upstream DNS server DNSSEC support"), name);
|
||||
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS - not secure", 0);
|
||||
return STAT_BOGUS | DNSSEC_FAIL_INDET;
|
||||
if (option_bool(OPT_BOGUSPRIV) &&
|
||||
(flags = in_arpa_name_2_addr(name, &a)) &&
|
||||
((flags == F_IPV6 && private_net6(&a.addr6, 0)) || (flags == F_IPV4 && private_net(a.addr4, 0))))
|
||||
{
|
||||
my_syslog(LOG_INFO, _("Insecure reply received for DS %s, assuming that's OK for a RFC-1918 address."), name);
|
||||
neganswer = 1;
|
||||
nons = 0; /* If we're faking a DS, fake one with an NS. */
|
||||
neg_ttl = DNSSEC_ASSUMED_DS_TTL;
|
||||
}
|
||||
else if (lookup_domain(name, F_DOMAINSRV, NULL, NULL))
|
||||
{
|
||||
my_syslog(LOG_INFO, _("Insecure reply received for DS %s, assuming non-DNSSEC domain-specific server."), name);
|
||||
neganswer = 1;
|
||||
nons = 0; /* If we're faking a DS, fake one with an NS. */
|
||||
neg_ttl = DNSSEC_ASSUMED_DS_TTL;
|
||||
}
|
||||
else
|
||||
{
|
||||
my_syslog(LOG_WARNING, _("Insecure DS reply received for %s, check domain configuration and upstream DNS server DNSSEC support"), name);
|
||||
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS - not secure", 0);
|
||||
return STAT_BOGUS | DNSSEC_FAIL_INDET;
|
||||
}
|
||||
}
|
||||
|
||||
p = (unsigned char *)(header+1);
|
||||
if (!extract_name(header, plen, &p, name, 1, 4))
|
||||
return STAT_BOGUS;
|
||||
|
||||
p += 4; /* qtype, qclass */
|
||||
|
||||
/* If the key needed to validate the DS is on the same domain as the DS, we'll
|
||||
loop getting nowhere. Stop that now. This can happen of the DS answer comes
|
||||
from the DS's zone, and not the parent zone. */
|
||||
if (STAT_ISEQUAL(rc, STAT_NEED_KEY) && hostname_isequal(name, keyname))
|
||||
else
|
||||
{
|
||||
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS", 0);
|
||||
return STAT_BOGUS;
|
||||
if (STAT_ISEQUAL(rc, STAT_NEED_KEY) && hostname_isequal(name, keyname))
|
||||
{
|
||||
/* If the key needed to validate the DS is on the same domain as the DS, we'll
|
||||
loop getting nowhere. Stop that now. This can happen of the DS answer comes
|
||||
from the DS's zone, and not the parent zone. */
|
||||
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS", 0);
|
||||
return STAT_BOGUS;
|
||||
}
|
||||
|
||||
if (!STAT_ISEQUAL(rc, STAT_SECURE))
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (!STAT_ISEQUAL(rc, STAT_SECURE))
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (!neganswer)
|
||||
@@ -1050,7 +1067,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);
|
||||
@@ -1129,9 +1146,19 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
|
||||
/* We only cache validated DS records, DNSSECOK flag hijacked
|
||||
to store presence/absence of NS. */
|
||||
if (nons)
|
||||
flags &= ~F_DNSSECOK;
|
||||
{
|
||||
if (lookup_domain(name, F_DOMAINSRV, NULL, NULL))
|
||||
{
|
||||
my_syslog(LOG_WARNING, _("Negative DS reply without NS record received for %s, assuming non-DNSSEC domain-specific server."), name);
|
||||
nons = 0;
|
||||
}
|
||||
else
|
||||
/* We only cache validated DS records, DNSSECOK flag hijacked
|
||||
to store presence/absence of NS. */
|
||||
flags &= ~F_DNSSECOK;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
cache_start_insert();
|
||||
|
||||
/* Use TTL from NSEC for negative cache entries */
|
||||
@@ -1231,12 +1258,13 @@ 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
|
||||
@@ -1258,7 +1286,22 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
|
||||
workspace1--;
|
||||
*workspace1 = '*';
|
||||
}
|
||||
|
||||
|
||||
rdlen -= p - psave;
|
||||
/* rdlen is now length of type map, and p points to it
|
||||
packet checked to be as long as rdlen implies in prove_non_existence() */
|
||||
|
||||
/* check that the first typemap is complete. */
|
||||
if (rdlen < 2 || rdlen < p[1] + 2)
|
||||
return DNSSEC_FAIL_BADPACKET;
|
||||
|
||||
/* RFC 6672 5.3.4.1. */
|
||||
#define DNAME_OFFSET (T_DNAME >> 3)
|
||||
#define DNAME_MASK (0x80 >> (T_DNAME & 0x07))
|
||||
if (p[0] == 0 && (p[1] >= DNAME_OFFSET + 1) && (p[2 + DNAME_OFFSET] & DNAME_MASK) != 0 &&
|
||||
hostname_issubdomain(name, workspace1) == 1)
|
||||
return DNSSEC_FAIL_NONSEC;
|
||||
|
||||
rc = hostname_cmp(workspace1, name);
|
||||
|
||||
if (rc == 0)
|
||||
@@ -1269,16 +1312,12 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
|
||||
|
||||
/* NSEC with the same name as the RR we're testing, check
|
||||
that the type in question doesn't appear in the type map */
|
||||
rdlen -= p - psave;
|
||||
/* rdlen is now length of type map, and p points to it
|
||||
packet checked to be as long as rdlen implies in prove_non_existence() */
|
||||
|
||||
/* If we can prove that there's no NS record, return that information. */
|
||||
if (nons && rdlen >= 2 && p[0] == 0 && (p[2] & (0x80 >> T_NS)) != 0)
|
||||
*nons = 0;
|
||||
|
||||
if (rdlen >= 2 && p[0] == 0)
|
||||
if (p[0] == 0 && p[1] >= 1)
|
||||
{
|
||||
/* If we can prove that there's no NS record, return that information. */
|
||||
if (nons && (p[2] & (0x80 >> T_NS)) != 0)
|
||||
*nons = 0;
|
||||
|
||||
/* A CNAME answer would also be valid, so if there's a CNAME is should
|
||||
have been returned. */
|
||||
if ((p[2] & (0x80 >> T_CNAME)) != 0)
|
||||
@@ -1290,10 +1329,10 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
|
||||
if (name_labels != 0 && type == T_DS && (p[2] & (0x80 >> T_SOA)) != 0)
|
||||
return DNSSEC_FAIL_NONSEC;
|
||||
}
|
||||
|
||||
while (rdlen >= 2)
|
||||
|
||||
while (rdlen > 0)
|
||||
{
|
||||
if (!CHECK_LEN(header, p, plen, rdlen))
|
||||
if (rdlen < 2 || rdlen < p[1] + 2)
|
||||
return DNSSEC_FAIL_BADPACKET;
|
||||
|
||||
if (p[0] == type >> 8)
|
||||
@@ -1400,7 +1439,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;
|
||||
|
||||
@@ -1433,7 +1472,11 @@ static int check_nsec3_coverage(struct dns_header *header, size_t plen, int dige
|
||||
p += hash_len; /* skip next-domain hash */
|
||||
rdlen -= p - psave;
|
||||
|
||||
if (rdlen >= 2 && p[0] == 0)
|
||||
/* check that the first typemap is complete. */
|
||||
if (rdlen < 2 || rdlen < p[1] + 2)
|
||||
return DNSSEC_FAIL_BADPACKET;
|
||||
|
||||
if (p[0] == 0 && p[1] >= 1)
|
||||
{
|
||||
/* If we can prove that there's no NS record, return that information. */
|
||||
if (nons && (p[2] & (0x80 >> T_NS)) != 0)
|
||||
@@ -1451,8 +1494,11 @@ static int check_nsec3_coverage(struct dns_header *header, size_t plen, int dige
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (rdlen >= 2)
|
||||
while (rdlen > 0)
|
||||
{
|
||||
if (rdlen < 2 || rdlen < p[1] + 2)
|
||||
return DNSSEC_FAIL_BADPACKET;
|
||||
|
||||
if (p[0] == type >> 8)
|
||||
{
|
||||
/* Does the NSEC3 say our type exists? */
|
||||
@@ -1609,7 +1655,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 +1730,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 +1781,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);
|
||||
@@ -1924,11 +1970,13 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
||||
static unsigned char **targets = NULL;
|
||||
static int target_sz = 0;
|
||||
|
||||
unsigned char *ans_start, *p1, *p2;
|
||||
int type1, class1, rdlen1 = 0, type2, class2, rdlen2, qclass, qtype, targetidx;
|
||||
int i, j, rc = STAT_INSECURE;
|
||||
unsigned char *ans_start, *p1, *p2, *p3;
|
||||
int type1, class1, rdlen1 = 0, type2, class2, rdlen2, qclass, qtype, targetidx, gotdname;
|
||||
int i, j, k, rc = STAT_INSECURE;
|
||||
int secure = STAT_SECURE;
|
||||
int rc_nsec;
|
||||
unsigned long ttl;
|
||||
|
||||
/* extend rr_status if necessary */
|
||||
if (daemon->rr_status_sz < ntohs(header->ancount) + ntohs(header->nscount))
|
||||
{
|
||||
@@ -1965,7 +2013,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);
|
||||
@@ -1975,27 +2023,119 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
||||
/* Can't validate an RRSIG query */
|
||||
if (qtype == T_RRSIG)
|
||||
return STAT_INSECURE;
|
||||
|
||||
/* Find CNAME targets. */
|
||||
for (gotdname = i = 0; i < ntohs(header->ancount); i++)
|
||||
{
|
||||
if (!(p1 = skip_name(p1, header, plen, 10)))
|
||||
return STAT_BOGUS; /* bad packet */
|
||||
|
||||
GETSHORT(type1, p1);
|
||||
GETSHORT(class1, p1);
|
||||
p1 += 4; /* TTL */
|
||||
GETSHORT(rdlen1, p1);
|
||||
|
||||
if (type1 == T_DNAME)
|
||||
gotdname = 1;
|
||||
|
||||
if (qtype != T_CNAME && qtype != T_ANY && type1 == T_CNAME && class1 == qclass)
|
||||
{
|
||||
if (!expand_workspace(&targets, &target_sz, targetidx))
|
||||
return STAT_BOGUS;
|
||||
|
||||
targets[targetidx++] = p1; /* pointer to target name */
|
||||
}
|
||||
|
||||
if (!ADD_RDLEN(header, p1, plen, rdlen1))
|
||||
return STAT_BOGUS;
|
||||
}
|
||||
|
||||
if (qtype != T_CNAME && qtype != T_ANY)
|
||||
for (j = ntohs(header->ancount); j != 0; j--)
|
||||
/* A DNAME capable of sythesising a CNAME means we don't need to validate the CNAME,
|
||||
we can just assume that it's valid. RFC 4035 3.2.3 */
|
||||
if (gotdname)
|
||||
for (p1 = ans_start, i = 0; i < ntohs(header->ancount); i++)
|
||||
{
|
||||
if (!(p1 = skip_name(p1, header, plen, 10)))
|
||||
if (!extract_name(header, plen, &p1, name, EXTR_NAME_EXTRACT, 10))
|
||||
return STAT_BOGUS; /* bad packet */
|
||||
|
||||
GETSHORT(type2, p1);
|
||||
p1 += 6; /* class, TTL */
|
||||
GETSHORT(rdlen2, p1);
|
||||
GETSHORT(type1, p1);
|
||||
GETSHORT(class1, p1);
|
||||
p1 += 4; /* TTL */
|
||||
GETSHORT(rdlen1, p1);
|
||||
|
||||
if (type2 == T_CNAME)
|
||||
if (type1 != T_DNAME)
|
||||
{
|
||||
if (!expand_workspace(&targets, &target_sz, targetidx))
|
||||
if (!ADD_RDLEN(header, p1, plen, rdlen1))
|
||||
return STAT_BOGUS;
|
||||
|
||||
targets[targetidx++] = p1; /* pointer to target name */
|
||||
}
|
||||
|
||||
if (!ADD_RDLEN(header, p1, plen, rdlen2))
|
||||
return STAT_BOGUS;
|
||||
else
|
||||
{
|
||||
if (!extract_name(header, plen, &p1, keyname, EXTR_NAME_EXTRACT, 0))
|
||||
return STAT_BOGUS; /* bad packet */
|
||||
|
||||
/* We now have the name of the DNAME in name, and the target in keyname.
|
||||
Look for any CNAMEs which could have been synthesised from this DNAME
|
||||
and pre-qualify them. */
|
||||
for (p2 = ans_start, j = 0; j < ntohs(header->ancount); j++)
|
||||
{
|
||||
if (!extract_name(header, plen, &p2, daemon->cname, EXTR_NAME_EXTRACT, 10))
|
||||
return STAT_BOGUS; /* bad packet */
|
||||
|
||||
GETSHORT(type2, p2);
|
||||
GETSHORT(class2, p2);
|
||||
GETLONG(ttl, p2);
|
||||
GETSHORT(rdlen2, p2);
|
||||
|
||||
if (type2 != T_CNAME || class2 != class1)
|
||||
{
|
||||
if (!ADD_RDLEN(header, p2, plen, rdlen2))
|
||||
return STAT_BOGUS;
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t name_prefix_len = strlen(daemon->cname) - strlen(name);
|
||||
|
||||
if (!extract_name(header, plen, &p2, daemon->workspacename, EXTR_NAME_EXTRACT, 0))
|
||||
return STAT_BOGUS; /* bad packet */
|
||||
|
||||
/* We have the name of the CNAME in daemon->cname, and the target in daemon->workspacename.
|
||||
See if the CNAME was sythesised from the DNAME.
|
||||
CNAME must be <subdomain>.<dname>
|
||||
CNAME target must be <subdomain>.<dname_target>
|
||||
<subdomain>s must match for name and target. */
|
||||
if (hostname_issubdomain(name, daemon->cname) == 1 &&
|
||||
hostname_issubdomain(keyname, daemon->workspacename) == 1 &&
|
||||
name_prefix_len == strlen(daemon->workspacename) - strlen(keyname))
|
||||
{
|
||||
char save = daemon->cname[name_prefix_len];
|
||||
daemon->cname[name_prefix_len] = 0;
|
||||
daemon->workspacename[name_prefix_len] = 0;
|
||||
|
||||
if (hostname_isequal(daemon->cname, daemon->workspacename))
|
||||
{
|
||||
/* pre-qualify this as validated */
|
||||
daemon->rr_status[j] = ttl > 0 ? ttl : 1;
|
||||
|
||||
/* and remove it from the targets we need to have validated answers to. */
|
||||
if (class2 == qclass)
|
||||
{
|
||||
daemon->cname[name_prefix_len] = save;
|
||||
for (k = 0; k <targetidx; k++)
|
||||
if ((p3 = targets[k]))
|
||||
{
|
||||
int rc1;
|
||||
if (!(rc1 = extract_name(header, plen, &p3, daemon->cname, EXTR_NAME_COMPARE, 0)))
|
||||
return STAT_BOGUS; /* bad packet */
|
||||
|
||||
if (rc1 == 1)
|
||||
targets[k] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (p1 = ans_start, i = 0; i < ntohs(header->ancount) + ntohs(header->nscount); i++)
|
||||
@@ -2003,7 +2143,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);
|
||||
@@ -2014,11 +2154,15 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
||||
/* Don't try and validate RRSIGs! */
|
||||
if (type1 == T_RRSIG)
|
||||
continue;
|
||||
|
||||
/* Pre-validated by DNAME above don't validate. */
|
||||
if (daemon->rr_status[i] != 0)
|
||||
continue;
|
||||
|
||||
/* 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 +2259,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,13 +2293,15 @@ 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) */
|
||||
|
||||
/* For anything other than a DS record, this situation is OK if either
|
||||
the answer is in an unsigned zone, or there's a NSEC records. */
|
||||
the answer is in an unsigned zone, or there's NSEC records.
|
||||
For a DS record, we return INSECURE, which almost always turns
|
||||
into BOGUS in the caller. */
|
||||
if ((rc_nsec = prove_non_existence(header, plen, keyname, name, qtype, qclass, NULL, nons, nsec_ttl, validate_counter)) != 0)
|
||||
{
|
||||
if (rc_nsec & DNSSEC_FAIL_WORK)
|
||||
@@ -2163,7 +2309,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
||||
|
||||
/* Empty DS without NSECS */
|
||||
if (qtype == T_DS)
|
||||
return STAT_BOGUS | rc_nsec;
|
||||
return STAT_INSECURE;
|
||||
|
||||
if ((rc_nsec & (DNSSEC_FAIL_NONSEC | DNSSEC_FAIL_NSEC3_ITERS)) &&
|
||||
!STAT_ISEQUAL((rc = zone_status(name, qclass, keyname, now)), STAT_SECURE))
|
||||
|
||||
+29
-33
@@ -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
|
||||
@@ -20,7 +20,7 @@ static int order(char *qdomain, size_t qlen, struct server *serv);
|
||||
static int order_qsort(const void *a, const void *b);
|
||||
static int order_servers(struct server *s, struct server *s2);
|
||||
|
||||
/* If the server is USE_RESOLV or LITERAL_ADDRES, it lives on the local_domains chain. */
|
||||
/* If the server is USE_RESOLV or LITERAL_ADDRESS, it lives on the local_domains chain. */
|
||||
#define SERV_IS_LOCAL (SERV_USE_RESOLV | SERV_LITERAL_ADDRESS)
|
||||
|
||||
void build_server_array(void)
|
||||
@@ -94,8 +94,7 @@ void build_server_array(void)
|
||||
server=/.example.com/ works.
|
||||
|
||||
A flag of F_SERVER returns an upstream server only.
|
||||
A flag of F_DNSSECOK returns a DNSSEC capable server only and
|
||||
also disables NODOTS servers from consideration.
|
||||
A flag of F_DNSSECOK disables NODOTS servers from consideration.
|
||||
A flag of F_DOMAINSRV returns a domain-specific server only.
|
||||
A flag of F_CONFIG returns anything that generates a local
|
||||
reply of IPv4 or IPV6.
|
||||
@@ -260,7 +259,6 @@ int lookup_domain(char *domain, int flags, int *lowout, int *highout)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Return first server in group of equivalent servers; this is the "master" record. */
|
||||
int server_samegroup(struct server *a, struct server *b)
|
||||
{
|
||||
return order_servers(a, b) == 0;
|
||||
@@ -297,11 +295,13 @@ int filter_servers(int seed, int flags, int *lowout, int *highout)
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Now the servers are on order between low and high, in the order
|
||||
IPv6 addr, IPv4 addr, return zero for both, resolvconf servers, send upstream, no-data return.
|
||||
/* Now the matching server records are all between low and high.
|
||||
order_qsort() ensures that they are in the order
|
||||
IPv6 addr, IPv4 addr, return zero for both, no-data return,
|
||||
"use resolvconf" servers, domain-specific upstream servers.
|
||||
|
||||
See which of those match our query in that priority order and narrow (low, high) */
|
||||
|
||||
|
||||
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_6ADDR); i++);
|
||||
|
||||
if (!(flags & F_SERVER) && i != nlow && (flags & F_IPV6))
|
||||
@@ -326,33 +326,25 @@ int filter_servers(int seed, int flags, int *lowout, int *highout)
|
||||
{
|
||||
nlow = i;
|
||||
|
||||
/* Short to resolv.conf servers */
|
||||
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_USE_RESOLV); i++);
|
||||
/* now look for a NXDOMAIN answer --local=/domain/ */
|
||||
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_LITERAL_ADDRESS); i++);
|
||||
|
||||
if (i != nlow)
|
||||
if (!(flags & (F_DOMAINSRV | F_SERVER)) && i != nlow)
|
||||
nhigh = i;
|
||||
else
|
||||
{
|
||||
/* now look for a server */
|
||||
for (i = nlow; i < nhigh && !(daemon->serverarray[i]->flags & SERV_LITERAL_ADDRESS); i++);
|
||||
nlow = i;
|
||||
|
||||
/* return "use resolv.conf servers" if they exist */
|
||||
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_USE_RESOLV); i++);
|
||||
|
||||
if (i != nlow)
|
||||
{
|
||||
/* If we want a server that can do DNSSEC, and this one can't,
|
||||
return nothing, similarly if were looking only for a server
|
||||
for a particular domain. */
|
||||
if ((flags & F_DNSSECOK) && !(daemon->serverarray[nlow]->flags & SERV_DO_DNSSEC))
|
||||
nlow = nhigh;
|
||||
else if ((flags & F_DOMAINSRV) && daemon->serverarray[nlow]->domain_len == 0)
|
||||
nlow = nhigh;
|
||||
else
|
||||
nhigh = i;
|
||||
}
|
||||
nhigh = i;
|
||||
else
|
||||
{
|
||||
/* --local=/domain/, only return if we don't need a server. */
|
||||
if (flags & (F_DNSSECOK | F_DOMAINSRV | F_SERVER))
|
||||
nhigh = i;
|
||||
/* If we want a server for a particular domain, and this one isn't, return nothing. */
|
||||
if (nlow < daemon->serverarraysz && nlow != nhigh && (flags & F_DOMAINSRV) && daemon->serverarray[nlow]->domain_len == 0)
|
||||
nlow = nhigh;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -407,6 +399,8 @@ size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header
|
||||
|
||||
setup_reply(header, flags, ede);
|
||||
|
||||
gotname &= ~F_QUERY;
|
||||
|
||||
if (flags & (F_NXDOMAIN | F_NOERR))
|
||||
log_query(flags | gotname | F_NEG | F_CONFIG | F_FORWARD, name, NULL, NULL, 0);
|
||||
|
||||
@@ -472,7 +466,7 @@ int dnssec_server(struct server *server, char *keyname, int *firstp, int *lastp)
|
||||
/* Find server to send DNSSEC query to. This will normally be the
|
||||
same as for the original query, but may be another if
|
||||
servers for domains are involved. */
|
||||
if (!lookup_domain(keyname, F_DNSSECOK, &first, &last))
|
||||
if (!lookup_domain(keyname, F_SERVER | F_DNSSECOK, &first, &last))
|
||||
return -1;
|
||||
|
||||
for (index = first; index != last; index++)
|
||||
@@ -546,12 +540,14 @@ static int order_qsort(const void *a, const void *b)
|
||||
rc = order_servers(s1, s2);
|
||||
|
||||
/* Sort all literal NODATA and local IPV4 or IPV6 responses together,
|
||||
in a very specific order. We flip the SERV_LITERAL_ADDRESS bit
|
||||
so the order is IPv6 literal, IPv4 literal, all-zero literal,
|
||||
unqualified servers, upstream server, NXDOMAIN literal. */
|
||||
in a very specific order IPv6 literal, IPv4 literal, all-zero literal,
|
||||
NXDOMAIN literal. We also include SERV_USE_RESOLV in this, so that
|
||||
use-standard servers sort before ordinary servers. (SERV_USR_RESOLV set
|
||||
implies that none of SERV_LITERAL_ADDRESS,SERV_4ADDR,SERV_6ADDR,SERV_ALL_ZEROS
|
||||
are set) */
|
||||
if (rc == 0)
|
||||
rc = ((s2->flags & (SERV_LITERAL_ADDRESS | SERV_4ADDR | SERV_6ADDR | SERV_USE_RESOLV | SERV_ALL_ZEROS)) ^ SERV_LITERAL_ADDRESS) -
|
||||
((s1->flags & (SERV_LITERAL_ADDRESS | SERV_4ADDR | SERV_6ADDR | SERV_USE_RESOLV | SERV_ALL_ZEROS)) ^ SERV_LITERAL_ADDRESS);
|
||||
rc = ((s2->flags & (SERV_LITERAL_ADDRESS | SERV_4ADDR | SERV_6ADDR | SERV_ALL_ZEROS | SERV_USE_RESOLV))) -
|
||||
((s1->flags & (SERV_LITERAL_ADDRESS | SERV_4ADDR | SERV_6ADDR | SERV_ALL_ZEROS | SERV_USE_RESOLV)));
|
||||
|
||||
/* Finally, order by appearance in /etc/resolv.conf etc, for --strict-order */
|
||||
if (rc == 0)
|
||||
|
||||
+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
|
||||
|
||||
+32
-14
@@ -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
|
||||
@@ -476,7 +494,7 @@ int check_source(struct dns_header *header, size_t plen, unsigned char *pseudohe
|
||||
#define UMBRELLA_DEVICESZ sizeof(daemon->umbrella_device)
|
||||
|
||||
struct umbrella_opt {
|
||||
u8 magic[4];
|
||||
u8 magic[4] ATTRIBUTE_NONSTRING;
|
||||
u8 version;
|
||||
u8 flags;
|
||||
/* We have 4 possible fields since we'll never send both IPv4 and
|
||||
@@ -491,7 +509,7 @@ static size_t add_umbrella_opt(struct dns_header *header, size_t plen, unsigned
|
||||
{
|
||||
*cacheable = 0;
|
||||
|
||||
struct umbrella_opt opt = {{"ODNS"}, UMBRELLA_VERSION, 0, {}};
|
||||
struct umbrella_opt opt = {{"ODNS"}, UMBRELLA_VERSION, 0, {0}};
|
||||
u8 *u = &opt.fields[0];
|
||||
int family = source->sa.sa_family;
|
||||
int size = family == AF_INET ? INADDRSZ : IN6ADDRSZ;
|
||||
@@ -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;
|
||||
}
|
||||
+664
-508
File diff suppressed because it is too large.
Load diff
+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
|
||||
|
||||
+107
-86
@@ -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
|
||||
@@ -133,29 +133,44 @@ void inotify_dnsmasq_init()
|
||||
}
|
||||
}
|
||||
|
||||
static struct hostsfile *dyndir_addhosts(struct dyndir *dd, char *path)
|
||||
static struct hostsfile *dyndir_addhosts(struct dyndir *dd, char *file)
|
||||
{
|
||||
/* Check if this file is already known in dd->files */
|
||||
struct hostsfile *ah = NULL;
|
||||
for(ah = dd->files; ah; ah = ah->next)
|
||||
if(ah && ah->fname && strcmp(path, ah->fname) == 0)
|
||||
struct hostsfile *ah;
|
||||
size_t dirlen = strlen(dd->dname);
|
||||
|
||||
/* ah->fname always starts with the string in dd->dname */
|
||||
for (ah = dd->files; ah; ah = ah->next)
|
||||
if (ah->fname[dirlen] == '/' &&
|
||||
strcmp(&ah->fname[dirlen+1], file) == 0)
|
||||
return ah;
|
||||
|
||||
|
||||
/* Not known, create new hostsfile record for this dyndir */
|
||||
struct hostsfile *newah = NULL;
|
||||
if(!(newah = whine_malloc(sizeof(struct hostsfile))))
|
||||
return NULL;
|
||||
if ((ah = whine_malloc(sizeof(struct hostsfile))))
|
||||
{
|
||||
char *path;
|
||||
|
||||
/* Add this file to the tip of the linked list */
|
||||
newah->next = dd->files;
|
||||
dd->files = newah;
|
||||
|
||||
/* Copy flags, set index and the full file path */
|
||||
newah->flags = dd->flags;
|
||||
newah->index = daemon->host_index++;
|
||||
newah->fname = path;
|
||||
|
||||
return newah;
|
||||
if (!(path = whine_malloc(dirlen + strlen(file) + 2)))
|
||||
{
|
||||
free(ah);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
strcpy(path, dd->dname);
|
||||
strcat(path, "/");
|
||||
strcat(path, file);
|
||||
|
||||
/* Add this file to the tip of the linked list */
|
||||
ah->next = dd->files;
|
||||
dd->files = ah;
|
||||
|
||||
/* Copy flags, set index and the full file path */
|
||||
ah->flags = dd->flags;
|
||||
ah->index = daemon->host_index++;
|
||||
ah->fname = path;
|
||||
}
|
||||
|
||||
return ah;
|
||||
}
|
||||
|
||||
|
||||
@@ -204,10 +219,8 @@ void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revh
|
||||
|
||||
while ((ent = readdir(dir_stream)))
|
||||
{
|
||||
size_t lendir = strlen(dd->dname);
|
||||
size_t lenfile = strlen(ent->d_name);
|
||||
char *path;
|
||||
|
||||
|
||||
/* ignore emacs backups and dotfiles */
|
||||
if (lenfile == 0 ||
|
||||
ent->d_name[lenfile - 1] == '~' ||
|
||||
@@ -215,33 +228,36 @@ void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revh
|
||||
ent->d_name[0] == '.')
|
||||
continue;
|
||||
|
||||
if ((path = whine_malloc(lendir + lenfile + 2)))
|
||||
if (dd->flags & AH_HOSTS)
|
||||
{
|
||||
struct hostsfile *ah;
|
||||
|
||||
strcpy(path, dd->dname);
|
||||
strcat(path, "/");
|
||||
strcat(path, ent->d_name);
|
||||
|
||||
if (!(ah = dyndir_addhosts(dd, path)))
|
||||
{
|
||||
free(path);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* ignore non-regular files */
|
||||
if (stat(path, &buf) != -1 && S_ISREG(buf.st_mode))
|
||||
{
|
||||
if (dd->flags & AH_HOSTS)
|
||||
total_size = read_hostsfile(path, ah->index, total_size, rhash, revhashsz);
|
||||
if ((ah = dyndir_addhosts(dd, ent->d_name)) &&
|
||||
stat(ah->fname, &buf) != -1 && S_ISREG(buf.st_mode))
|
||||
total_size = read_hostsfile(ah->fname, ah->index, total_size, rhash, revhashsz);
|
||||
}
|
||||
#ifdef HAVE_DHCP
|
||||
else if (dd->flags & (AH_DHCP_HST | AH_DHCP_OPT))
|
||||
else if (dd->flags & (AH_DHCP_HST | AH_DHCP_OPT))
|
||||
{
|
||||
char *path;
|
||||
|
||||
if ((path = whine_malloc(strlen(dd->dname) + lenfile + 2)))
|
||||
{
|
||||
strcpy(path, dd->dname);
|
||||
strcat(path, "/");
|
||||
strcat(path, ent->d_name);
|
||||
|
||||
/* ignore non-regular files */
|
||||
if (stat(path, &buf) != -1 && S_ISREG(buf.st_mode))
|
||||
option_read_dynfile(path, dd->flags);
|
||||
#endif
|
||||
|
||||
free(path);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
closedir(dir_stream);
|
||||
}
|
||||
}
|
||||
@@ -251,6 +267,8 @@ int inotify_check(time_t now)
|
||||
int hit = 0;
|
||||
struct dyndir *dd;
|
||||
|
||||
(void)now;
|
||||
|
||||
while (1)
|
||||
{
|
||||
int rc;
|
||||
@@ -283,50 +301,27 @@ int inotify_check(time_t now)
|
||||
for (dd = daemon->dynamic_dirs; dd; dd = dd->next)
|
||||
if (dd->wd == in->wd)
|
||||
{
|
||||
size_t lendir = strlen(dd->dname);
|
||||
char *path;
|
||||
|
||||
if ((path = whine_malloc(lendir + in->len + 2)))
|
||||
if (dd->flags & AH_HOSTS)
|
||||
{
|
||||
struct hostsfile *ah = NULL;
|
||||
|
||||
strcpy(path, dd->dname);
|
||||
strcat(path, "/");
|
||||
strcat(path, in->name);
|
||||
|
||||
/* Is this is a deletion event? */
|
||||
if (in->mask & IN_DELETE)
|
||||
my_syslog(LOG_INFO, _("inotify: %s removed"), path);
|
||||
else
|
||||
my_syslog(LOG_INFO, _("inotify: %s new or modified"), path);
|
||||
|
||||
if (dd->flags & AH_HOSTS)
|
||||
struct hostsfile *ah;
|
||||
if ((ah = dyndir_addhosts(dd, in->name)))
|
||||
{
|
||||
if ((ah = dyndir_addhosts(dd, path)))
|
||||
{
|
||||
const unsigned int removed = cache_remove_uid(ah->index);
|
||||
if (removed > 0)
|
||||
my_syslog(LOG_INFO, _("inotify: flushed %u names read from %s"), removed, path);
|
||||
const unsigned int removed = cache_remove_uid(ah->index);
|
||||
|
||||
/* (Re-)load hostsfile only if this event isn't triggered by deletion */
|
||||
if (!(in->mask & IN_DELETE))
|
||||
read_hostsfile(path, ah->index, 0, NULL, 0);
|
||||
/* Is this is a deletion event? */
|
||||
if (in->mask & IN_DELETE)
|
||||
my_syslog(LOG_INFO, _("inotify: %s removed"), ah->fname);
|
||||
else
|
||||
my_syslog(LOG_INFO, _("inotify: %s new or modified"), ah->fname);
|
||||
|
||||
if (removed > 0)
|
||||
my_syslog(LOG_INFO, _("inotify: flushed %u names read from %s"), removed, ah->fname);
|
||||
|
||||
/* (Re-)load hostsfile only if this event isn't triggered by deletion */
|
||||
if (!(in->mask & IN_DELETE))
|
||||
read_hostsfile(ah->fname, ah->index, 0, NULL, 0);
|
||||
#ifdef HAVE_DHCP
|
||||
if (daemon->dhcp || daemon->doing_dhcp6)
|
||||
{
|
||||
/* Propagate the consequences of loading a new dhcp-host */
|
||||
dhcp_update_configs(daemon->dhcp_conf);
|
||||
lease_update_from_configs();
|
||||
lease_update_file(now);
|
||||
lease_update_dns(1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#ifdef HAVE_DHCP
|
||||
else if (dd->flags & AH_DHCP_HST)
|
||||
{
|
||||
if (option_read_dynfile(path, AH_DHCP_HST))
|
||||
if (daemon->dhcp || daemon->doing_dhcp6)
|
||||
{
|
||||
/* Propagate the consequences of loading a new dhcp-host */
|
||||
dhcp_update_configs(daemon->dhcp_conf);
|
||||
@@ -334,17 +329,43 @@ int inotify_check(time_t now)
|
||||
lease_update_file(now);
|
||||
lease_update_dns(1);
|
||||
}
|
||||
}
|
||||
else if (dd->flags & AH_DHCP_OPT)
|
||||
option_read_dynfile(path, AH_DHCP_OPT);
|
||||
#endif
|
||||
|
||||
if (!ah)
|
||||
free(path);
|
||||
}
|
||||
}
|
||||
#ifdef HAVE_DHCP
|
||||
else if (!(in->mask & IN_DELETE))
|
||||
{
|
||||
char *path;
|
||||
|
||||
if ((path = whine_malloc(strlen(dd->dname) + in->len + 2)))
|
||||
{
|
||||
strcpy(path, dd->dname);
|
||||
strcat(path, "/");
|
||||
strcat(path, in->name);
|
||||
|
||||
my_syslog(LOG_INFO, _("inotify: %s new or modified"), path);
|
||||
|
||||
if ((dd->flags & AH_DHCP_HST) && option_read_dynfile(path, AH_DHCP_HST))
|
||||
{
|
||||
/* Propagate the consequences of loading a new dhcp-host */
|
||||
dhcp_update_configs(daemon->dhcp_conf);
|
||||
lease_update_from_configs();
|
||||
lease_update_file(now);
|
||||
lease_update_dns(1);
|
||||
}
|
||||
|
||||
if (dd->flags & AH_DHCP_OPT)
|
||||
option_read_dynfile(path, AH_DHCP_OPT);
|
||||
|
||||
free(path);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hit;
|
||||
}
|
||||
|
||||
|
||||
+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
|
||||
|
||||
+5
-5
@@ -16,7 +16,7 @@
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
#if defined(HAVE_IPSET) && defined(HAVE_LINUX_NETWORK)
|
||||
#if defined(HAVE_IPSET)
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
@@ -75,11 +75,11 @@ static char *buffer;
|
||||
|
||||
static inline void add_attr(struct nlmsghdr *nlh, uint16_t type, size_t len, const void *data)
|
||||
{
|
||||
struct my_nlattr *attr = (void *)nlh + NL_ALIGN(nlh->nlmsg_len);
|
||||
struct my_nlattr *attr = (struct my_nlattr *)((u8 *)nlh + NL_ALIGN(nlh->nlmsg_len));
|
||||
uint16_t payload_len = NL_ALIGN(sizeof(struct my_nlattr)) + len;
|
||||
attr->nla_type = type;
|
||||
attr->nla_len = payload_len;
|
||||
memcpy((void *)attr + NL_ALIGN(sizeof(struct my_nlattr)), data, len);
|
||||
memcpy((u8 *)attr + NL_ALIGN(sizeof(struct my_nlattr)), data, len);
|
||||
nlh->nlmsg_len += NL_ALIGN(payload_len);
|
||||
}
|
||||
|
||||
@@ -138,8 +138,8 @@ static int new_add_to_ipset(const char *setname, const union all_addr *ipaddr, i
|
||||
add_attr(nlh,
|
||||
(af == AF_INET ? IPSET_ATTR_IPADDR_IPV4 : IPSET_ATTR_IPADDR_IPV6) | NLA_F_NET_BYTEORDER,
|
||||
addrsz, ipaddr);
|
||||
nested[1]->nla_len = (void *)buffer + NL_ALIGN(nlh->nlmsg_len) - (void *)nested[1];
|
||||
nested[0]->nla_len = (void *)buffer + NL_ALIGN(nlh->nlmsg_len) - (void *)nested[0];
|
||||
nested[1]->nla_len = (u8 *)buffer + NL_ALIGN(nlh->nlmsg_len) - (u8 *)nested[1];
|
||||
nested[0]->nla_len = (u8 *)buffer + NL_ALIGN(nlh->nlmsg_len) - (u8 *)nested[0];
|
||||
|
||||
while (retry_send(sendto(ipset_sock, buffer, nlh->nlmsg_len, 0,
|
||||
(struct sockaddr *)&snl, sizeof(snl))));
|
||||
|
||||
+126
-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
|
||||
@@ -25,7 +25,7 @@ static int read_leases(time_t now, FILE *leasestream)
|
||||
unsigned long ei;
|
||||
union all_addr addr;
|
||||
struct dhcp_lease *lease;
|
||||
int clid_len, hw_len, hw_type;
|
||||
int opt_len, clid_len, hw_len, hw_type;
|
||||
int items;
|
||||
|
||||
*daemon->dhcp_buff3 = *daemon->dhcp_buff2 = '\0';
|
||||
@@ -55,6 +55,34 @@ static int read_leases(time_t now, FILE *leasestream)
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Weird backwards compatible way of adding extra fields to leases */
|
||||
if ((strcmp(daemon->dhcp_buff3, "vendorclass") == 0 || strcmp(daemon->dhcp_buff3, "agent-info") == 0))
|
||||
{
|
||||
if (fscanf(leasestream, " %764s", daemon->packet) == 1)
|
||||
{
|
||||
if (inet_pton(AF_INET, daemon->dhcp_buff2, &addr.addr4))
|
||||
lease = lease_find_by_addr(addr.addr4);
|
||||
#ifdef HAVE_DHCP6
|
||||
else if (inet_pton(AF_INET6, daemon->dhcp_buff2, &addr.addr6))
|
||||
lease = lease6_find_by_plain_addr(&addr.addr6);
|
||||
#endif
|
||||
else
|
||||
continue;
|
||||
|
||||
if (lease)
|
||||
{
|
||||
opt_len = parse_hex(daemon->packet, (unsigned char *)daemon->packet, 255, NULL, NULL);
|
||||
|
||||
if (strcmp(daemon->dhcp_buff3, "vendorclass") == 0)
|
||||
lease_set_vendorclass(lease, (unsigned char *)daemon->packet, opt_len);
|
||||
else if (strcmp(daemon->dhcp_buff3, "agent-info") == 0)
|
||||
lease_set_agent_id(lease, (unsigned char *)daemon->packet, opt_len);
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fscanf(leasestream, " %64s %255s %764s",
|
||||
daemon->namebuff, daemon->dhcp_buff, daemon->packet) != 3)
|
||||
@@ -251,8 +279,8 @@ void lease_update_file(time_t now)
|
||||
{
|
||||
struct dhcp_lease *lease;
|
||||
time_t next_event;
|
||||
int i, err = 0;
|
||||
|
||||
int i, err = 0, extras;
|
||||
|
||||
if (file_dirty != 0 && daemon->lease_stream)
|
||||
{
|
||||
errno = 0;
|
||||
@@ -260,9 +288,11 @@ void lease_update_file(time_t now)
|
||||
if (errno != 0 || ftruncate(fileno(daemon->lease_stream), 0) != 0)
|
||||
err = errno;
|
||||
|
||||
for (lease = leases; lease; lease = lease->next)
|
||||
for (extras = 0, lease = leases; lease; lease = lease->next)
|
||||
{
|
||||
|
||||
if (lease->agent_id || lease->vendorclass)
|
||||
extras = 1;
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
if (lease->flags & (LEASE_TA | LEASE_NA))
|
||||
continue;
|
||||
@@ -335,7 +365,37 @@ void lease_update_file(time_t now)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
if (extras)
|
||||
{
|
||||
/* Dump this at the end for least confusion with older parsing code. */
|
||||
for (lease = leases; lease; lease = lease->next)
|
||||
{
|
||||
#ifdef HAVE_DHCP6
|
||||
if (lease->flags & (LEASE_TA | LEASE_NA))
|
||||
inet_ntop(AF_INET6, &lease->addr6, daemon->addrbuff, ADDRSTRLEN);
|
||||
else
|
||||
#endif
|
||||
inet_ntop(AF_INET, &lease->addr, daemon->addrbuff, ADDRSTRLEN);
|
||||
|
||||
if (lease->agent_id)
|
||||
{
|
||||
ourprintf(&err, "agent-info %s ", daemon->addrbuff);
|
||||
for (i = 0; i < lease->agent_id_len - 1; i++)
|
||||
ourprintf(&err, "%.2x:", lease->agent_id[i]);
|
||||
ourprintf(&err, "%.2x\n", lease->agent_id[i]);
|
||||
}
|
||||
|
||||
if (lease->vendorclass)
|
||||
{
|
||||
ourprintf(&err, "vendorclass %s ", daemon->addrbuff);
|
||||
for (i = 0; i < lease->vendorclass_len - 1; i++)
|
||||
ourprintf(&err, "%.2x:", lease->vendorclass[i]);
|
||||
ourprintf(&err, "%.2x\n", lease->vendorclass[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fflush(daemon->lease_stream) != 0 ||
|
||||
fsync(fileno(daemon->lease_stream)) < 0)
|
||||
err = errno;
|
||||
@@ -713,6 +773,22 @@ struct dhcp_lease *lease6_find_by_addr(struct in6_addr *net, int prefix, u64 add
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct dhcp_lease *lease6_find_by_plain_addr(struct in6_addr *addr)
|
||||
{
|
||||
struct dhcp_lease *lease;
|
||||
|
||||
for (lease = leases; lease; lease = lease->next)
|
||||
{
|
||||
if (!(lease->flags & (LEASE_TA | LEASE_NA)))
|
||||
continue;
|
||||
|
||||
if (IN6_ARE_ADDR_EQUAL(&lease->addr6, addr))
|
||||
return lease;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Find largest assigned address in context */
|
||||
u64 lease_find_max_addr6(struct dhcp_context *context)
|
||||
{
|
||||
@@ -1084,6 +1160,46 @@ void lease_set_interface(struct dhcp_lease *lease, int interface, time_t now)
|
||||
#endif
|
||||
}
|
||||
|
||||
void lease_set_agent_id(struct dhcp_lease *lease, unsigned char *new, int len)
|
||||
{
|
||||
|
||||
if (!lease->agent_id && !new)
|
||||
return;
|
||||
|
||||
if (lease->agent_id && new && lease->agent_id_len == len && memcmp(lease->agent_id, new, len) == 0)
|
||||
return;
|
||||
|
||||
file_dirty = 1;
|
||||
free(lease->agent_id);
|
||||
lease->agent_id = NULL;
|
||||
|
||||
if (new && (lease->agent_id = whine_malloc(len)))
|
||||
{
|
||||
memcpy(lease->agent_id, new, len);
|
||||
lease->agent_id_len = len;
|
||||
}
|
||||
}
|
||||
|
||||
void lease_set_vendorclass(struct dhcp_lease *lease, unsigned char *new, int len)
|
||||
{
|
||||
if (!lease->vendorclass && !new)
|
||||
return;
|
||||
|
||||
if (lease->vendorclass && new && lease->vendorclass_len == len && memcmp(lease->vendorclass, new, len) == 0)
|
||||
return;
|
||||
|
||||
file_dirty = 1;
|
||||
free(lease->vendorclass);
|
||||
lease->vendorclass = NULL;
|
||||
|
||||
if (new && (lease->vendorclass = whine_malloc(len)))
|
||||
{
|
||||
memcpy(lease->vendorclass, new, len);
|
||||
lease->vendorclass_len = len;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void rerun_scripts(void)
|
||||
{
|
||||
struct dhcp_lease *lease;
|
||||
@@ -1143,9 +1259,11 @@ int do_script_run(time_t now)
|
||||
#endif
|
||||
old_leases = lease->next;
|
||||
|
||||
free(lease->old_hostname);
|
||||
free(lease->hostname);
|
||||
free(lease->clid);
|
||||
free(lease->extradata);
|
||||
free(lease->agent_id);
|
||||
free(lease->vendorclass);
|
||||
free(lease);
|
||||
|
||||
return 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
|
||||
|
||||
+2
-2
@@ -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
|
||||
@@ -19,7 +19,7 @@
|
||||
#ifdef HAVE_LOOP
|
||||
static ssize_t loop_make_probe(u32 uid);
|
||||
|
||||
void loop_send_probes()
|
||||
void loop_send_probes(void)
|
||||
{
|
||||
struct server *serv;
|
||||
struct randfd_list *rfds = NULL;
|
||||
|
||||
+5
-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
|
||||
@@ -43,6 +43,10 @@ const char * metric_names[] = {
|
||||
"leases_allocated_6",
|
||||
"leases_pruned_6",
|
||||
"tcp_connections",
|
||||
"dhcp_leasequery",
|
||||
"dhcp_lease_unassigned",
|
||||
"dhcp_lease_actve",
|
||||
"dhcp_lease_unknown"
|
||||
};
|
||||
|
||||
const char* get_metric_name(int i) {
|
||||
|
||||
+5
-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
|
||||
@@ -42,6 +42,10 @@ enum {
|
||||
METRIC_LEASES_ALLOCATED_6,
|
||||
METRIC_LEASES_PRUNED_6,
|
||||
METRIC_TCP_CONNECTIONS,
|
||||
METRIC_DHCPLEASEQUERY,
|
||||
METRIC_DHCPLEASEUNASSIGNED,
|
||||
METRIC_DHCPLEASEACTIVE,
|
||||
METRIC_DHCPLEASEUNKNOWN,
|
||||
|
||||
__METRIC_MAX,
|
||||
};
|
||||
|
||||
+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
-36
@@ -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
|
||||
@@ -647,7 +647,7 @@ static int iface_allowed_v4(struct in_addr local, int if_index, char *label,
|
||||
/*
|
||||
* Clean old interfaces no longer found.
|
||||
*/
|
||||
static void clean_interfaces()
|
||||
static void clean_interfaces(void)
|
||||
{
|
||||
struct irec *iface;
|
||||
struct irec **up = &daemon->interfaces;
|
||||
@@ -1379,7 +1379,7 @@ int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifind
|
||||
/* cannot set source _port_ for TCP connections. */
|
||||
if (is_tcp)
|
||||
port = 0;
|
||||
else if (port == 0 && daemon->max_port != 0)
|
||||
else if (port == 0 && daemon->max_port != 0 && daemon->max_port >= daemon->min_port)
|
||||
{
|
||||
/* Bind a random port within the range given by min-port and max-port if either
|
||||
or both are set. Otherwise use the OS's random ephemeral port allocation by
|
||||
@@ -1568,6 +1568,8 @@ void check_servers(int no_loop_check)
|
||||
struct serverfd *sfd, *tmp, **up;
|
||||
int port = 0, count;
|
||||
int locals = 0;
|
||||
|
||||
(void)no_loop_check;
|
||||
|
||||
#ifdef HAVE_LOOP
|
||||
if (!no_loop_check)
|
||||
@@ -1587,33 +1589,6 @@ void check_servers(int no_loop_check)
|
||||
|
||||
for (count = 0, serv = daemon->servers; serv; serv = serv->next)
|
||||
{
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID))
|
||||
{
|
||||
if (!(serv->flags & SERV_FOR_NODOTS))
|
||||
serv->flags |= SERV_DO_DNSSEC;
|
||||
|
||||
/* Disable DNSSEC validation when using server=/domain/.... servers
|
||||
unless there's a configured trust anchor. */
|
||||
if (strlen(serv->domain) != 0)
|
||||
{
|
||||
struct ds_config *ds;
|
||||
char *domain = serv->domain;
|
||||
|
||||
/* .example.com is valid */
|
||||
while (*domain == '.')
|
||||
domain++;
|
||||
|
||||
for (ds = daemon->ds; ds; ds = ds->next)
|
||||
if (ds->name[0] != 0 && hostname_isequal(domain, ds->name))
|
||||
break;
|
||||
|
||||
if (!ds)
|
||||
serv->flags &= ~SERV_DO_DNSSEC;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
port = prettyprint_addr(&serv->addr, daemon->namebuff);
|
||||
|
||||
/* 0.0.0.0 is nothing, the stack treats it like 127.0.0.1 */
|
||||
@@ -1659,10 +1634,6 @@ void check_servers(int no_loop_check)
|
||||
{
|
||||
char *s1, *s2, *s3 = "", *s4 = "";
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID) && !(serv->flags & SERV_DO_DNSSEC))
|
||||
s3 = _("(no DNSSEC)");
|
||||
#endif
|
||||
if (serv->flags & SERV_FOR_NODOTS)
|
||||
s1 = _("unqualified"), s2 = _("names");
|
||||
else if (strlen(serv->domain) == 0)
|
||||
@@ -1696,7 +1667,7 @@ void check_servers(int no_loop_check)
|
||||
if (++locals <= LOCALS_LOGGED)
|
||||
my_syslog(LOG_INFO, _("using only locally-known addresses for %s"), serv->domain);
|
||||
}
|
||||
else if (serv->flags & SERV_USE_RESOLV)
|
||||
else if (serv->flags & SERV_USE_RESOLV && serv->domain_len != 0)
|
||||
my_syslog(LOG_INFO, _("using standard nameservers for %s"), serv->domain);
|
||||
}
|
||||
|
||||
@@ -1806,8 +1777,10 @@ int reload_servers(char *fname)
|
||||
/* Called when addresses are added or deleted from an interface */
|
||||
void newaddress(time_t now)
|
||||
{
|
||||
#ifdef HAVE_DHCP
|
||||
struct dhcp_relay *relay;
|
||||
|
||||
#endif
|
||||
|
||||
(void)now;
|
||||
|
||||
if (option_bool(OPT_CLEVERBIND) || option_bool(OPT_LOCAL_SERVICE) ||
|
||||
|
||||
+2
-2
@@ -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
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
#if defined (HAVE_NFTSET) && defined (HAVE_LINUX_NETWORK)
|
||||
#if defined (HAVE_NFTSET)
|
||||
|
||||
#include <nftables/libnftables.h>
|
||||
|
||||
|
||||
+75
-36
@@ -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,9 @@ 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
|
||||
#define LOPT_LEASEQUERY 389
|
||||
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
static const struct option opts[] =
|
||||
@@ -247,6 +250,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 },
|
||||
@@ -390,6 +395,7 @@ static const struct myoption opts[] =
|
||||
{ "use-stale-cache", 2, 0 , LOPT_STALE_CACHE },
|
||||
{ "no-ident", 0, 0, LOPT_NO_IDENT },
|
||||
{ "max-tcp-connections", 1, 0, LOPT_MAX_PROCS },
|
||||
{ "leasequery", 2, 0, LOPT_LEASEQUERY },
|
||||
{ NULL, 0, 0, 0 }
|
||||
};
|
||||
|
||||
@@ -495,6 +501,7 @@ static struct {
|
||||
{ '4', ARG_DUP, "set:<tag>,<mac address>", gettext_noop("Map MAC address (with wildcards) to option set."), NULL },
|
||||
{ LOPT_BRIDGE, ARG_DUP, "<iface>,<alias>..", gettext_noop("Treat DHCP requests on aliases as arriving from interface."), NULL },
|
||||
{ LOPT_SHARED_NET, ARG_DUP, "<iface>|<addr>,<addr>", gettext_noop("Specify extra networks sharing a broadcast domain for DHCP"), NULL},
|
||||
{ LOPT_LEASEQUERY, ARG_DUP, "[<addr>[/prefix>]]", gettext_noop("Enable RFC 4388 leasequery functions for DHCPv4"), NULL },
|
||||
{ '5', OPT_NO_PING, NULL, gettext_noop("Disable ICMP echo address checking in the DHCP server."), NULL },
|
||||
{ '6', ARG_ONE, "<path>", gettext_noop("Shell script to run on DHCP lease creation and destruction."), NULL },
|
||||
{ LOPT_LUASCRIPT, ARG_DUP, "path", gettext_noop("Lua script to run on DHCP lease creation and destruction."), NULL },
|
||||
@@ -568,7 +575,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 +598,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 },
|
||||
@@ -955,7 +964,7 @@ char *parse_server(char *arg, struct server_details *sdetails)
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
|
||||
/* Get addresses suitable for sending datagrams. We assume that we can use the
|
||||
same addresses for TCP connections. Settting this to zero gets each address
|
||||
same addresses for TCP connections. Setting this to zero gets each address
|
||||
threes times, for SOCK_STREAM, SOCK_RAW and SOCK_DGRAM, which is not useful. */
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
|
||||
@@ -2669,15 +2678,15 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
if (msize > 128)
|
||||
ret_err_free(_("bad prefix length"), new);
|
||||
|
||||
mask = (1LLU << (128 - msize)) - 1LLU;
|
||||
/* prefix==64 overflows the mask calculation */
|
||||
if (msize <= 64)
|
||||
mask = (u64)-1LL;
|
||||
else
|
||||
mask = (1LLU << (128 - msize)) - 1LLU;
|
||||
|
||||
new->is6 = 1;
|
||||
new->prefixlen = msize;
|
||||
|
||||
/* prefix==64 overflows the mask calculation above */
|
||||
if (msize <= 64)
|
||||
mask = (u64)-1LL;
|
||||
|
||||
new->end6 = new->start6;
|
||||
setaddr6part(&new->start6, addrpart & ~mask);
|
||||
setaddr6part(&new->end6, addrpart | mask);
|
||||
@@ -2906,7 +2915,16 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
arg = comma;
|
||||
} while (arg);
|
||||
break;
|
||||
|
||||
|
||||
#ifdef HAVE_DHCP
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
|
||||
case LOPT_LEASEQUERY:
|
||||
set_option_bool(OPT_LEASEQUERY);
|
||||
if (!arg)
|
||||
break;
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
case 'B': /* --bogus-nxdomain */
|
||||
case LOPT_IGNORE_ADDR: /* --ignore-address */
|
||||
{
|
||||
@@ -2942,6 +2960,13 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
baddr->next = daemon->bogus_addr;
|
||||
daemon->bogus_addr = baddr;
|
||||
}
|
||||
#ifdef HAVE_DHCP
|
||||
else if (option == LOPT_LEASEQUERY)
|
||||
{
|
||||
baddr->next = daemon->leasequery_addr;
|
||||
daemon->leasequery_addr = baddr;
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
baddr->next = daemon->ignore_addr;
|
||||
@@ -3429,6 +3454,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
set_option_bool(OPT_EXTRALOG);
|
||||
set_option_bool(OPT_LOG_PROTO);
|
||||
}
|
||||
else if (strcmp(arg, "auth") == 0)
|
||||
set_option_bool(OPT_AUTH_LOG);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -3981,7 +4008,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
while (arg)
|
||||
{
|
||||
comma = split(arg);
|
||||
if (strchr(arg, ':')) /* ethernet address, netid or binary CLID */
|
||||
if (strchr(arg, ':')) /* Ethernet address, netid or binary CLID */
|
||||
{
|
||||
if ((arg[0] == 'i' || arg[0] == 'I') &&
|
||||
(arg[1] == 'd' || arg[1] == 'D') &&
|
||||
@@ -4043,10 +4070,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 +4082,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 +4096,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
|
||||
@@ -4161,7 +4188,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
}
|
||||
else if (strcmp(arg, "ignore") == 0)
|
||||
new->flags |= CONFIG_DISABLE;
|
||||
if (new->hostname)
|
||||
else if (new->hostname)
|
||||
{
|
||||
dhcp_config_free(new);
|
||||
ret_err(_("DHCP host has multiple names"));
|
||||
@@ -5331,7 +5358,8 @@ err:
|
||||
|
||||
new->class = C_IN;
|
||||
new->name = NULL;
|
||||
|
||||
new->digestlen = 0;
|
||||
|
||||
if ((comma = split(arg)) && (algo = split(comma)))
|
||||
{
|
||||
int class = 0;
|
||||
@@ -5349,29 +5377,37 @@ err:
|
||||
algo = split(comma);
|
||||
}
|
||||
}
|
||||
|
||||
if (!comma || !algo || !(digest = split(algo)) || !(keyhex = split(digest)) ||
|
||||
!atoi_check16(comma, &new->keytag) ||
|
||||
!atoi_check8(algo, &new->algo) ||
|
||||
!atoi_check8(digest, &new->digest_type) ||
|
||||
!(new->name = canonicalise_opt(arg)))
|
||||
|
||||
if (!(new->name = canonicalise_opt(arg)))
|
||||
ret_err_free(_("bad trust anchor"), new);
|
||||
|
||||
/* Upper bound on length */
|
||||
len = (2*strlen(keyhex))+1;
|
||||
new->digest = opt_malloc(len);
|
||||
unhide_metas(keyhex);
|
||||
/* 4034: "Whitespace is allowed within digits" */
|
||||
for (cp = keyhex; *cp; )
|
||||
if (isspace((unsigned char)*cp))
|
||||
for (cp1 = cp; *cp1; cp1++)
|
||||
*cp1 = *(cp1+1);
|
||||
else
|
||||
cp++;
|
||||
if ((new->digestlen = parse_hex(keyhex, (unsigned char *)new->digest, len, NULL, NULL)) == -1)
|
||||
|
||||
if (comma)
|
||||
{
|
||||
free(new->name);
|
||||
ret_err_free(_("bad HEX in trust anchor"), new);
|
||||
if (!algo || !(digest = split(algo)) || !(keyhex = split(digest)) ||
|
||||
!atoi_check16(comma, &new->keytag) ||
|
||||
!atoi_check8(algo, &new->algo) ||
|
||||
!atoi_check8(digest, &new->digest_type))
|
||||
{
|
||||
free(new->name);
|
||||
ret_err_free(_("bad trust anchor"), new);
|
||||
}
|
||||
|
||||
/* Upper bound on length */
|
||||
len = (2*strlen(keyhex))+1;
|
||||
new->digest = opt_malloc(len);
|
||||
unhide_metas(keyhex);
|
||||
/* 4034: "Whitespace is allowed within digits" */
|
||||
for (cp = keyhex; *cp; )
|
||||
if (isspace((unsigned char)*cp))
|
||||
for (cp1 = cp; *cp1; cp1++)
|
||||
*cp1 = *(cp1+1);
|
||||
else
|
||||
cp++;
|
||||
if ((new->digestlen = parse_hex(keyhex, (unsigned char *)new->digest, len, NULL, NULL)) == -1)
|
||||
{
|
||||
free(new->name);
|
||||
ret_err_free(_("bad HEX in trust anchor"), new);
|
||||
}
|
||||
}
|
||||
|
||||
new->next = daemon->ds;
|
||||
@@ -5920,6 +5956,9 @@ void read_opts(int argc, char **argv, char *compile_opts)
|
||||
daemon->randport_limit = 1;
|
||||
daemon->host_index = SRC_AH;
|
||||
daemon->max_procs = MAX_PROCS;
|
||||
#ifdef HAVE_DUMPFILE
|
||||
daemon->dump_mask = 0xffffffff;
|
||||
#endif
|
||||
#ifdef HAVE_DNSSEC
|
||||
daemon->limit[LIMIT_SIG_FAIL] = DNSSEC_LIMIT_SIG_FAIL;
|
||||
daemon->limit[LIMIT_CRYPTO] = DNSSEC_LIMIT_CRYPTO;
|
||||
|
||||
+7
-7
@@ -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
|
||||
@@ -23,7 +23,7 @@ static size_t outpacket_counter;
|
||||
|
||||
void end_opt6(int container)
|
||||
{
|
||||
void *p = daemon->outpacket.iov_base + container + 2;
|
||||
uint8_t *p = (uint8_t *)daemon->outpacket.iov_base + container + 2;
|
||||
u16 len = outpacket_counter - container - 4 ;
|
||||
|
||||
PUTSHORT(len, p);
|
||||
@@ -50,11 +50,11 @@ int save_counter(int newval)
|
||||
|
||||
void *expand(size_t headroom)
|
||||
{
|
||||
void *ret;
|
||||
uint8_t *ret;
|
||||
|
||||
if (expand_buf(&daemon->outpacket, outpacket_counter + headroom))
|
||||
{
|
||||
ret = daemon->outpacket.iov_base + outpacket_counter;
|
||||
ret = (uint8_t *)daemon->outpacket.iov_base + outpacket_counter;
|
||||
outpacket_counter += headroom;
|
||||
return ret;
|
||||
}
|
||||
@@ -65,7 +65,7 @@ void *expand(size_t headroom)
|
||||
int new_opt6(int opt)
|
||||
{
|
||||
int ret = outpacket_counter;
|
||||
void *p;
|
||||
unsigned char *p;
|
||||
|
||||
if ((p = expand(4)))
|
||||
{
|
||||
@@ -88,7 +88,7 @@ void *put_opt6(void *data, size_t len)
|
||||
|
||||
void put_opt6_long(unsigned int val)
|
||||
{
|
||||
void *p;
|
||||
unsigned char *p;
|
||||
|
||||
if ((p = expand(4)))
|
||||
PUTLONG(val, p);
|
||||
@@ -96,7 +96,7 @@ void put_opt6_long(unsigned int val)
|
||||
|
||||
void put_opt6_short(unsigned int val)
|
||||
{
|
||||
void *p;
|
||||
uint8_t *p;
|
||||
|
||||
if ((p = expand(2)))
|
||||
PUTSHORT(val, p);
|
||||
|
||||
+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
|
||||
|
||||
+2
-2
@@ -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
|
||||
@@ -98,7 +98,7 @@ void poll_listen(int fd, short event)
|
||||
{
|
||||
if (arrsize == nfds)
|
||||
{
|
||||
/* Array too small, extend. */
|
||||
/* Array too small. Extend. */
|
||||
struct pollfd *new;
|
||||
|
||||
arrsize = (arrsize == 0) ? 64 : arrsize * 2;
|
||||
|
||||
+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
|
||||
|
||||
+4
-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
|
||||
@@ -411,7 +411,7 @@ static void send_ra_alias(time_t now, int iface, char *iface_name, struct in6_ad
|
||||
if (!old_prefix && !parm.found_context)
|
||||
return;
|
||||
|
||||
/* If we're sending router address instead of prefix in at least on prefix,
|
||||
/* If we're sending router address instead of prefix in at least one prefix,
|
||||
include the advertisement interval option. */
|
||||
if (parm.adv_router)
|
||||
{
|
||||
@@ -825,10 +825,10 @@ time_t periodic_ra(time_t now)
|
||||
}
|
||||
else if (iface_enumerate(AF_INET6, ¶m, (callback_t){.af_inet6=iface_search}))
|
||||
/* There's a context overdue, but we can't find an interface
|
||||
associated with it, because it's for a subnet we dont
|
||||
associated with it, because it's for a subnet we don't
|
||||
have an interface on. Probably we're doing DHCP on
|
||||
a remote subnet via a relay. Zero the timer, since we won't
|
||||
ever be able to send ra's and satisfy it. */
|
||||
ever be able to send RAs to satisfy it. */
|
||||
context->ra_time = 0;
|
||||
|
||||
if (param.iface != 0 &&
|
||||
|
||||
+251
-223
@@ -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,42 @@
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
|
||||
char *name, int isExtract, int extrabytes)
|
||||
{
|
||||
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;
|
||||
/* 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.
|
||||
|
||||
If pp == NULL, operate on the query name in the packet.
|
||||
*/
|
||||
int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
|
||||
char *name, int func, unsigned int parm)
|
||||
{
|
||||
unsigned char *cp = (unsigned char *)name, *p1 = NULL;
|
||||
unsigned int j, l, namelen = 0, hops = 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;
|
||||
unsigned char *p = pp ? *pp : (unsigned char *)(header+1);
|
||||
|
||||
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,13 +72,16 @@ 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 */
|
||||
*pp = p1;
|
||||
else
|
||||
*pp = p;
|
||||
|
||||
if (pp)
|
||||
{
|
||||
if (p1) /* we jumped via compression */
|
||||
*pp = p1;
|
||||
else
|
||||
*pp = p;
|
||||
}
|
||||
|
||||
return retvalue;
|
||||
}
|
||||
@@ -85,7 +114,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 +127,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 +154,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
|
||||
@@ -365,7 +424,7 @@ int private_net(struct in_addr addr, int ban_localhost)
|
||||
((ip_addr & 0xFFFFFFFF) == 0xFFFFFFFF) /* 255.255.255.255/32 (broadcast)*/ ;
|
||||
}
|
||||
|
||||
static int private_net6(struct in6_addr *a, int ban_localhost)
|
||||
int private_net6(struct in6_addr *a, int ban_localhost)
|
||||
{
|
||||
/* Block IPv4-mapped IPv6 addresses in private IPv4 address space */
|
||||
if (IN6_IS_ADDR_V4MAPPED(a))
|
||||
@@ -399,7 +458,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);
|
||||
@@ -475,12 +534,9 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *sub
|
||||
if (substring)
|
||||
*substring = name_len;
|
||||
|
||||
if (ttlp)
|
||||
*ttlp = daemon->neg_ttl;
|
||||
|
||||
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 +565,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);
|
||||
@@ -537,18 +593,18 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *sub
|
||||
}
|
||||
|
||||
/* rest of RR */
|
||||
if (!no_cache && !blockdata_expand(addr.rrblock.rrdata, addr.rrblock.datalen, (char *)p, 20))
|
||||
{
|
||||
blockdata_free(addr.rrblock.rrdata);
|
||||
return 0;
|
||||
}
|
||||
|
||||
addr.rrblock.datalen += 20;
|
||||
|
||||
if (!no_cache)
|
||||
{
|
||||
int secflag = 0;
|
||||
|
||||
if (!blockdata_expand(addr.rrblock.rrdata, addr.rrblock.datalen, (char *)p, 20))
|
||||
{
|
||||
blockdata_free(addr.rrblock.rrdata);
|
||||
return 0;
|
||||
}
|
||||
|
||||
addr.rrblock.datalen += 20;
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID) && daemon->rr_status[i + ntohs(header->ancount)] != 0)
|
||||
{
|
||||
@@ -631,12 +687,12 @@ static int log_txt(char *name, unsigned char *p, const int ardlen, int flag)
|
||||
Return 2 if the packet is malformed.
|
||||
*/
|
||||
int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t now,
|
||||
struct ipsets *ipsets, struct ipsets *nftsets, int is_sign, int check_rebind,
|
||||
struct ipsets *ipsets, struct ipsets *nftsets, int check_rebind,
|
||||
int no_cache_dnssec, int secure)
|
||||
{
|
||||
unsigned char *p, *p1, *endrr, *namep;
|
||||
int j, qtype, qclass, aqtype, aqclass, ardlen, res;
|
||||
unsigned long ttl = 0;
|
||||
unsigned long ttl;
|
||||
union all_addr addr;
|
||||
#ifdef HAVE_IPSET
|
||||
char **ipsets_cur;
|
||||
@@ -648,19 +704,14 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
#else
|
||||
(void)nftsets; /* unused */
|
||||
#endif
|
||||
int found = 0, cname_count = CNAME_CHAIN;
|
||||
struct crec *cpp = NULL;
|
||||
int name_encoding, found = 0, ptr = 0;
|
||||
int flags = RCODE(header) == NXDOMAIN ? F_NXDOMAIN : 0;
|
||||
#ifdef HAVE_DNSSEC
|
||||
int cname_short = 0;
|
||||
#endif
|
||||
unsigned long cttl = ULONG_MAX, attl;
|
||||
|
||||
cache_start_insert();
|
||||
|
||||
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);
|
||||
@@ -668,114 +719,73 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
|
||||
if (qclass != C_IN)
|
||||
return 0;
|
||||
|
||||
/* PTRs: we chase CNAMEs here, since we have no way to
|
||||
represent them in the cache. */
|
||||
if (qtype == T_PTR)
|
||||
|
||||
/* If the PTR record encodes an address, store using a name/address record with F_REVERSE set.
|
||||
Otherwise, it gets stored as an arbitrary RR below. If the query is answerable with
|
||||
a CNAME, also take the arbitrary-RR route, since the cache can't represent a CNAME
|
||||
whose target is stored in a F_REVERSE record. */
|
||||
if (qtype == T_PTR && !(flags & F_NXDOMAIN) && (name_encoding = in_arpa_name_2_addr(name, &addr)))
|
||||
{
|
||||
int insert = 1, name_encoding = in_arpa_name_2_addr(name, &addr);
|
||||
ptr = 1;
|
||||
|
||||
if (!(flags & F_NXDOMAIN))
|
||||
{
|
||||
cname_loop:
|
||||
if (!(p1 = skip_questions(header, qlen)))
|
||||
return 2;
|
||||
|
||||
for (j = 0; j < ntohs(header->ancount); j++)
|
||||
{
|
||||
int secflag = 0;
|
||||
if (!(res = extract_name(header, qlen, &p1, name, 0, 10)))
|
||||
return 2; /* bad packet */
|
||||
|
||||
GETSHORT(aqtype, p1);
|
||||
GETSHORT(aqclass, p1);
|
||||
GETLONG(attl, p1);
|
||||
|
||||
if ((daemon->max_ttl != 0) && (attl > daemon->max_ttl) && !is_sign)
|
||||
{
|
||||
(p1) -= 4;
|
||||
PUTLONG(daemon->max_ttl, p1);
|
||||
}
|
||||
GETSHORT(ardlen, p1);
|
||||
endrr = p1+ardlen;
|
||||
|
||||
/* TTL of record is minimum of CNAMES and PTR */
|
||||
if (attl < cttl)
|
||||
cttl = attl;
|
||||
|
||||
if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == T_PTR))
|
||||
{
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID) && j < daemon->rr_status_sz && daemon->rr_status[j] != 0)
|
||||
{
|
||||
/* validated RR anywhere in CNAME chain, don't cache. */
|
||||
if (cname_short || aqtype == T_CNAME)
|
||||
insert = 0;
|
||||
|
||||
secflag = F_DNSSECOK;
|
||||
/* limit TTL based on signature. */
|
||||
if (daemon->rr_status[j] < cttl)
|
||||
cttl = daemon->rr_status[j];
|
||||
}
|
||||
#endif
|
||||
|
||||
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))
|
||||
return 2;
|
||||
|
||||
if (aqtype == T_CNAME)
|
||||
{
|
||||
if (!cname_count--)
|
||||
return 0; /* looped CNAMES, we can't cache. */
|
||||
#ifdef HAVE_DNSSEC
|
||||
cname_short = 1;
|
||||
#endif
|
||||
goto cname_loop;
|
||||
}
|
||||
|
||||
found = 1;
|
||||
|
||||
if (!name_encoding)
|
||||
log_query(secflag | F_FORWARD | F_UPSTREAM, name, NULL, NULL, aqtype);
|
||||
else
|
||||
{
|
||||
log_query(name_encoding | secflag | F_REVERSE | F_UPSTREAM, name, &addr, NULL, 0);
|
||||
if (insert)
|
||||
cache_insert(name, &addr, C_IN, now, cttl, name_encoding | secflag | F_REVERSE);
|
||||
}
|
||||
}
|
||||
|
||||
p1 = endrr;
|
||||
if (!CHECK_LEN(header, p1, qlen, 0))
|
||||
return 2; /* bad packet */
|
||||
}
|
||||
}
|
||||
if (!(p1 = skip_questions(header, qlen)))
|
||||
return 2;
|
||||
|
||||
if (!found && !option_bool(OPT_NO_NEG))
|
||||
for (j = 0; j < ntohs(header->ancount); j++)
|
||||
{
|
||||
/* For reverse records, we use the name field to store the SOA name. */
|
||||
int substring, have_soa = find_soa(header, qlen, name, &substring, &ttl, no_cache_dnssec, now);
|
||||
int secflag = 0;
|
||||
if (!(res = extract_name(header, qlen, &p1, name, EXTR_NAME_COMPARE, 10)))
|
||||
return 2; /* bad packet */
|
||||
|
||||
flags |= F_NEG | (secure ? F_DNSSECOK : 0);
|
||||
if (name_encoding && ttl)
|
||||
GETSHORT(aqtype, p1);
|
||||
GETSHORT(aqclass, p1);
|
||||
p = p1;
|
||||
GETLONG(ttl, p1);
|
||||
GETSHORT(ardlen, p1);
|
||||
endrr = p1+ardlen;
|
||||
|
||||
if (aqclass == C_IN && res == 1 && aqtype == T_PTR)
|
||||
{
|
||||
flags |= F_REVERSE | name_encoding;
|
||||
if (!have_soa)
|
||||
flags |= F_NO_RR; /* Marks no SOA found. */
|
||||
cache_insert(name + substring, &addr, C_IN, now, ttl, flags);
|
||||
found = 1;
|
||||
|
||||
if ((daemon->max_ttl != 0) && (ttl > daemon->max_ttl))
|
||||
ttl = daemon->max_ttl;
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID) && j < daemon->rr_status_sz && daemon->rr_status[j] != 0)
|
||||
{
|
||||
secflag = F_DNSSECOK;
|
||||
/* limit TTL based on signature. */
|
||||
if (daemon->rr_status[j] < ttl)
|
||||
ttl = daemon->rr_status[j];
|
||||
}
|
||||
#endif
|
||||
|
||||
PUTLONG(ttl, p);
|
||||
|
||||
if (!extract_name(header, qlen, &p1, name, EXTR_NAME_EXTRACT, 0))
|
||||
return 2;
|
||||
log_query(name_encoding | secflag | F_REVERSE | F_UPSTREAM, name, &addr, NULL, 0);
|
||||
cache_insert(name, &addr, C_IN, now, ttl, name_encoding | secflag | F_REVERSE);
|
||||
|
||||
/* restore query into name */
|
||||
p1 = namep;
|
||||
if (!extract_name(header, qlen, &p1, name, EXTR_NAME_EXTRACT, 0))
|
||||
return 2;
|
||||
}
|
||||
|
||||
log_query(flags | F_UPSTREAM, name, &addr, NULL, 0);
|
||||
p1 = endrr;
|
||||
if (!CHECK_LEN(header, p1, qlen, 0))
|
||||
return 2; /* bad packet */
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
if (!ptr || !found)
|
||||
{
|
||||
/* everything other than PTR */
|
||||
struct crec *newc;
|
||||
int addrlen = 0, insert = 1;
|
||||
|
||||
struct crec *newc, *cpp = NULL;
|
||||
int cname_count = CNAME_CHAIN, addrlen = 0, insert = 1;
|
||||
|
||||
if (qtype == T_A)
|
||||
{
|
||||
addrlen = INADDRSZ;
|
||||
@@ -787,12 +797,12 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
flags |= F_IPV6;
|
||||
}
|
||||
else if (qtype != T_CNAME &&
|
||||
(qtype == T_SRV || rr_on_list(daemon->cache_rr, qtype) || rr_on_list(daemon->cache_rr, T_ANY)))
|
||||
(qtype == T_SRV || qtype == T_PTR || rr_on_list(daemon->cache_rr, qtype) || rr_on_list(daemon->cache_rr, T_ANY)))
|
||||
flags |= F_RR;
|
||||
else
|
||||
insert = 0; /* NOTE: do not cache data from CNAME queries. */
|
||||
|
||||
cname_loop1:
|
||||
cname_loop:
|
||||
if (!(p1 = skip_questions(header, qlen)))
|
||||
return 2;
|
||||
|
||||
@@ -800,17 +810,13 @@ 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);
|
||||
GETSHORT(aqclass, p1);
|
||||
GETLONG(attl, p1);
|
||||
if ((daemon->max_ttl != 0) && (attl > daemon->max_ttl) && !is_sign)
|
||||
{
|
||||
(p1) -= 4;
|
||||
PUTLONG(daemon->max_ttl, p1);
|
||||
}
|
||||
p = p1;
|
||||
GETLONG(ttl, p1);
|
||||
GETSHORT(ardlen, p1);
|
||||
endrr = p1+ardlen;
|
||||
|
||||
@@ -823,6 +829,9 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
p1 = endrr;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((daemon->max_ttl != 0) && (ttl > daemon->max_ttl))
|
||||
ttl = daemon->max_ttl;
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID) && j < daemon->rr_status_sz && daemon->rr_status[j] != 0)
|
||||
@@ -830,10 +839,12 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
secflag = F_DNSSECOK;
|
||||
|
||||
/* limit TTl based on sig. */
|
||||
if (daemon->rr_status[j] < attl)
|
||||
attl = daemon->rr_status[j];
|
||||
if (daemon->rr_status[j] < ttl)
|
||||
ttl = daemon->rr_status[j];
|
||||
}
|
||||
#endif
|
||||
|
||||
PUTLONG(ttl, p);
|
||||
|
||||
if (aqtype == T_CNAME)
|
||||
{
|
||||
@@ -844,7 +855,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
|
||||
if (insert)
|
||||
{
|
||||
if ((newc = cache_insert(name, NULL, C_IN, now, attl, F_CNAME | F_FORWARD | secflag)))
|
||||
if ((newc = cache_insert(name, NULL, C_IN, now, ttl, F_CNAME | F_FORWARD | secflag)))
|
||||
{
|
||||
newc->addr.cname.target.cache = NULL;
|
||||
newc->addr.cname.is_name_ptr = 0;
|
||||
@@ -857,16 +868,15 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
}
|
||||
|
||||
cpp = newc;
|
||||
if (attl < cttl)
|
||||
cttl = attl;
|
||||
}
|
||||
|
||||
/* Set the query to the CNAME target and go again unless the query was just for a CNAME. */
|
||||
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)
|
||||
goto cname_loop1;
|
||||
goto cname_loop;
|
||||
|
||||
found = 1;
|
||||
}
|
||||
@@ -932,7 +942,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 +975,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;
|
||||
@@ -990,24 +1000,39 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
private_net6(&addr.addr6, !option_bool(OPT_LOCAL_REBIND)))
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
if (flags & (F_IPV4 | F_IPV6))
|
||||
{
|
||||
/* If we're a child process, send this to the parent,
|
||||
since the ipset and nfset access is not re-entrant. */
|
||||
#ifdef HAVE_IPSET
|
||||
if (ipsets && (flags & (F_IPV4 | F_IPV6)))
|
||||
for (ipsets_cur = ipsets->sets; *ipsets_cur; ipsets_cur++)
|
||||
if (add_to_ipset(*ipsets_cur, &addr, flags, 0) == 0)
|
||||
log_query((flags & (F_IPV4 | F_IPV6)) | F_IPSET, ipsets->domain, &addr, *ipsets_cur, 1);
|
||||
if (ipsets)
|
||||
{
|
||||
if (daemon->pipe_to_parent != -1)
|
||||
cache_send_ipset(PIPE_OP_IPSET, ipsets, flags, &addr);
|
||||
else
|
||||
for (ipsets_cur = ipsets->sets; *ipsets_cur; ipsets_cur++)
|
||||
if (add_to_ipset(*ipsets_cur, &addr, flags, 0) == 0)
|
||||
log_query((flags & (F_IPV4 | F_IPV6)) | F_IPSET, ipsets->domain, &addr, *ipsets_cur, 1);
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_NFTSET
|
||||
if (nftsets && (flags & (F_IPV4 | F_IPV6)))
|
||||
for (nftsets_cur = nftsets->sets; *nftsets_cur; nftsets_cur++)
|
||||
if (add_to_nftset(*nftsets_cur, &addr, flags, 0) == 0)
|
||||
log_query((flags & (F_IPV4 | F_IPV6)) | F_IPSET, nftsets->domain, &addr, *nftsets_cur, 0);
|
||||
if (nftsets)
|
||||
{
|
||||
if (daemon->pipe_to_parent != -1)
|
||||
cache_send_ipset(PIPE_OP_NFTSET, nftsets, flags, &addr);
|
||||
else
|
||||
for (nftsets_cur = nftsets->sets; *nftsets_cur; nftsets_cur++)
|
||||
if (add_to_nftset(*nftsets_cur, &addr, flags, 0) == 0)
|
||||
log_query((flags & (F_IPV4 | F_IPV6)) | F_IPSET, nftsets->domain, &addr, *nftsets_cur, 0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (insert)
|
||||
{
|
||||
newc = cache_insert(name, &addr, C_IN, now, attl, flags | F_FORWARD | secflag);
|
||||
newc = cache_insert(name, &addr, C_IN, now, ttl, flags | F_FORWARD | secflag);
|
||||
if (newc && cpp)
|
||||
{
|
||||
next_uid(newc);
|
||||
@@ -1055,26 +1080,27 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
{
|
||||
int substring, have_soa = find_soa(header, qlen, name, &substring, &ttl, no_cache_dnssec, now);
|
||||
|
||||
/* If there's no SOA to get the TTL from, but there is a CNAME
|
||||
pointing at this, inherit its TTL */
|
||||
if (ttl || cpp)
|
||||
{
|
||||
if (!ttl)
|
||||
ttl = cttl;
|
||||
|
||||
addr.rrdata.datalen = substring;
|
||||
addr.rrdata.rrtype = qtype;
|
||||
|
||||
if (!have_soa)
|
||||
flags |= F_NO_RR; /* Marks no SOA found. */
|
||||
}
|
||||
|
||||
newc = cache_insert(name, &addr, C_IN, now, ttl, F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0));
|
||||
if (newc && cpp)
|
||||
{
|
||||
next_uid(newc);
|
||||
cpp->addr.cname.target.cache = newc;
|
||||
cpp->addr.cname.uid = newc->uid;
|
||||
if (have_soa || daemon->neg_ttl)
|
||||
{
|
||||
if (have_soa)
|
||||
{
|
||||
addr.rrdata.datalen = substring;
|
||||
addr.rrdata.rrtype = qtype;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If daemon->neg_ttl is set, we can cache even without an SOA. */
|
||||
ttl = daemon->neg_ttl;
|
||||
flags |= F_NO_RR; /* Marks no SOA found. */
|
||||
}
|
||||
|
||||
newc = cache_insert(name, &addr, C_IN, now, ttl, F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0));
|
||||
if (newc && cpp)
|
||||
{
|
||||
next_uid(newc);
|
||||
cpp->addr.cname.target.cache = newc;
|
||||
cpp->addr.cname.uid = newc->uid;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1127,7 +1153,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 +1171,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);
|
||||
@@ -1179,7 +1205,8 @@ void report_addresses(struct dns_header *header, size_t len, u32 mark)
|
||||
|
||||
/* If the packet holds exactly one query
|
||||
return F_IPV4 or F_IPV6 and leave the name from the query in name */
|
||||
unsigned int extract_request(struct dns_header *header, size_t qlen, char *name, unsigned short *typep)
|
||||
unsigned int extract_request(struct dns_header *header, size_t qlen, char *name,
|
||||
unsigned short *typep, unsigned short *classp)
|
||||
{
|
||||
unsigned char *p = (unsigned char *)(header+1);
|
||||
int qtype, qclass;
|
||||
@@ -1191,11 +1218,11 @@ unsigned int extract_request(struct dns_header *header, size_t qlen, char *name,
|
||||
|
||||
if (ntohs(header->qdcount) != 1 || OPCODE(header) != QUERY)
|
||||
return 0; /* must be exactly one query. */
|
||||
|
||||
|
||||
if (!(header->hb3 & HB3_QR) && (ntohs(header->ancount) != 0 || ntohs(header->nscount) != 0))
|
||||
return 0; /* non-standard query. */
|
||||
|
||||
if (!extract_name(header, qlen, &p, name, 1, 4))
|
||||
if (!extract_name(header, qlen, &p, name, EXTR_NAME_EXTRACT, 4))
|
||||
return 0; /* bad packet */
|
||||
|
||||
GETSHORT(qtype, p);
|
||||
@@ -1204,6 +1231,9 @@ unsigned int extract_request(struct dns_header *header, size_t qlen, char *name,
|
||||
if (typep)
|
||||
*typep = qtype;
|
||||
|
||||
if (classp)
|
||||
*classp = qclass;
|
||||
|
||||
if (qclass == C_IN)
|
||||
{
|
||||
if (qtype == T_A)
|
||||
@@ -1215,9 +1245,7 @@ unsigned int extract_request(struct dns_header *header, size_t qlen, char *name,
|
||||
}
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
/* F_DNSSECOK as agument to search_servers() inhibits forwarding
|
||||
to servers for domains without a trust anchor. This make the
|
||||
behaviour for DS and DNSKEY queries we forward the same
|
||||
/* Make the behaviour for DS and DNSKEY queries we forward the same
|
||||
as for DS and DNSKEY queries we originate. */
|
||||
if (option_bool(OPT_DNSSEC_VALID) && (qtype == T_DS || qtype == T_DNSKEY))
|
||||
return F_DNSSECOK;
|
||||
@@ -1309,7 +1337,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 +1587,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 +1595,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 +1610,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 if no_cache set. */
|
||||
if (no_cache)
|
||||
rd_bit = 0;
|
||||
|
||||
if (stale)
|
||||
*stale = 0;
|
||||
@@ -1589,14 +1621,12 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
if (filtered)
|
||||
*filtered = 0;
|
||||
|
||||
/* never answer queries with RD unset, to avoid cache snooping. */
|
||||
if ( ntohs(header->qdcount) != 1 ||
|
||||
ntohs(header->ancount) != 0 ||
|
||||
if (ntohs(header->qdcount) != 1 ||
|
||||
ntohs(header->ancount) != 0 ||
|
||||
ntohs(header->nscount) != 0 ||
|
||||
ntohs(header->qdcount) == 0 ||
|
||||
OPCODE(header) != QUERY )
|
||||
return 0;
|
||||
|
||||
|
||||
/* Don't return AD set if checking disabled. */
|
||||
if (header->hb4 & HB4_CD)
|
||||
sec_data = 0;
|
||||
@@ -1615,7 +1645,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 +1686,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 +1845,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 +2001,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 +2183,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 +2258,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))
|
||||
{
|
||||
@@ -2258,14 +2289,11 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
|
||||
For FORWARD NEG records, the addr.rrdata.datalen field of the othewise
|
||||
empty addr is used to held an offset in to the name which yields the SOA
|
||||
name. For REVERSE NEG records, the otherwise empty name field holds the
|
||||
SOA name. If soa_name has zero length, then no SOA is known. soa_lookup
|
||||
MUST be a neg record here.
|
||||
|
||||
name.
|
||||
If the F_NO_RR flag is set, there was no SOA record supplied with the RR. */
|
||||
if (soa_lookup && !(soa_lookup->flags & F_NO_RR))
|
||||
{
|
||||
char *soa_name = soa_lookup->flags & F_REVERSE ? cache_get_name(soa_lookup) : name + soa_lookup->addr.rrdata.datalen;
|
||||
char *soa_name = name + soa_lookup->addr.rrdata.datalen;
|
||||
|
||||
crecp = NULL;
|
||||
while ((crecp = cache_find_by_name(crecp, soa_name, now, F_RR)))
|
||||
@@ -2334,7 +2362,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)
|
||||
|
||||
+323
-122
@@ -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
|
||||
@@ -56,7 +56,8 @@ static void do_options(struct dhcp_context *context,
|
||||
time_t now,
|
||||
unsigned int lease_time,
|
||||
unsigned short fuzz,
|
||||
const char *pxevendor);
|
||||
const char *pxevendor,
|
||||
int leasequery);
|
||||
|
||||
|
||||
static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt);
|
||||
@@ -73,7 +74,7 @@ static void handle_encap(struct dhcp_packet *mess, unsigned char *end, unsigned
|
||||
|
||||
size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
size_t sz, time_t now, int unicast_dest, int loopback,
|
||||
int *is_inform, int pxe, struct in_addr fallback, time_t recvtime)
|
||||
int *is_inform, int pxe, struct in_addr fallback, time_t recvtime, struct in_addr leasequery_source)
|
||||
{
|
||||
unsigned char *opt, *clid = NULL;
|
||||
struct dhcp_lease *ltmp, *lease = NULL;
|
||||
@@ -108,7 +109,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
#endif
|
||||
|
||||
subnet_addr.s_addr = override.s_addr = 0;
|
||||
|
||||
|
||||
/* set tag with name == interface */
|
||||
iface_id.net = iface_name;
|
||||
iface_id.next = NULL;
|
||||
@@ -185,7 +186,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
}
|
||||
}
|
||||
|
||||
if ((opt = option_find(mess, sz, OPTION_AGENT_ID, 1)))
|
||||
if (mess_type != DHCPLEASEQUERY && (opt = option_find(mess, sz, OPTION_AGENT_ID, 1)))
|
||||
{
|
||||
/* Any agent-id needs to be copied back out, verbatim, as the last option
|
||||
in the packet. Here, we shift it to the very end of the buffer, if it doesn't
|
||||
@@ -211,7 +212,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_SERVER_OR, INADDRSZ)))
|
||||
override = option_addr(sopt);
|
||||
|
||||
/* if a circuit-id or remote-is option is provided, exact-match to options. */
|
||||
/* if a circuit-id or remote-is option is provided, exact-match to options. */
|
||||
for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
|
||||
{
|
||||
int search;
|
||||
@@ -224,7 +225,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
search = SUBOPT_SUBSCR_ID;
|
||||
else
|
||||
continue;
|
||||
|
||||
|
||||
if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), search, 1)) &&
|
||||
vendor->len == option_len(sopt) &&
|
||||
memcmp(option_ptr(sopt, 0), vendor->data, vendor->len) == 0)
|
||||
@@ -234,7 +235,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Check for RFC3011 subnet selector - only if RFC3527 one not present */
|
||||
if (subnet_addr.s_addr == 0 && (opt = option_find(mess, sz, OPTION_SUBNET_SELECT, INADDRSZ)))
|
||||
subnet_addr = option_addr(opt);
|
||||
@@ -373,44 +374,47 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
context = context_new;
|
||||
}
|
||||
|
||||
if (!context)
|
||||
if (mess_type != DHCPLEASEQUERY)
|
||||
{
|
||||
const char *via;
|
||||
if (subnet_addr.s_addr)
|
||||
if (!context)
|
||||
{
|
||||
via = _("with subnet selector");
|
||||
inet_ntop(AF_INET, &subnet_addr, daemon->addrbuff, ADDRSTRLEN);
|
||||
}
|
||||
else
|
||||
{
|
||||
via = _("via");
|
||||
if (mess->giaddr.s_addr)
|
||||
inet_ntop(AF_INET, &mess->giaddr, daemon->addrbuff, ADDRSTRLEN);
|
||||
else
|
||||
safe_strncpy(daemon->addrbuff, iface_name, ADDRSTRLEN);
|
||||
}
|
||||
my_syslog(MS_DHCP | LOG_WARNING, _("no address range available for DHCP request %s %s"),
|
||||
via, daemon->addrbuff);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (option_bool(OPT_LOG_OPTS))
|
||||
{
|
||||
struct dhcp_context *context_tmp;
|
||||
for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)
|
||||
{
|
||||
inet_ntop(AF_INET, &context_tmp->start, daemon->namebuff, MAXDNAME);
|
||||
if (context_tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
|
||||
const char *via;
|
||||
if (subnet_addr.s_addr)
|
||||
{
|
||||
inet_ntop(AF_INET, &context_tmp->netmask, daemon->addrbuff, ADDRSTRLEN);
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP subnet: %s/%s"),
|
||||
ntohl(mess->xid), daemon->namebuff, daemon->addrbuff);
|
||||
via = _("with subnet selector");
|
||||
inet_ntop(AF_INET, &subnet_addr, daemon->addrbuff, ADDRSTRLEN);
|
||||
}
|
||||
else
|
||||
{
|
||||
inet_ntop(AF_INET, &context_tmp->end, daemon->addrbuff, ADDRSTRLEN);
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP range: %s -- %s"),
|
||||
ntohl(mess->xid), daemon->namebuff, daemon->addrbuff);
|
||||
via = _("via");
|
||||
if (mess->giaddr.s_addr)
|
||||
inet_ntop(AF_INET, &mess->giaddr, daemon->addrbuff, ADDRSTRLEN);
|
||||
else
|
||||
safe_strncpy(daemon->addrbuff, iface_name, ADDRSTRLEN);
|
||||
}
|
||||
my_syslog(MS_DHCP | LOG_WARNING, _("no address range available for DHCP request %s %s"),
|
||||
via, daemon->addrbuff);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (option_bool(OPT_LOG_OPTS))
|
||||
{
|
||||
struct dhcp_context *context_tmp;
|
||||
for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)
|
||||
{
|
||||
inet_ntop(AF_INET, &context_tmp->start, daemon->namebuff, MAXDNAME);
|
||||
if (context_tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
|
||||
{
|
||||
inet_ntop(AF_INET, &context_tmp->netmask, daemon->addrbuff, ADDRSTRLEN);
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP subnet: %s/%s"),
|
||||
ntohl(mess->xid), daemon->namebuff, daemon->addrbuff);
|
||||
}
|
||||
else
|
||||
{
|
||||
inet_ntop(AF_INET, &context_tmp->end, daemon->addrbuff, ADDRSTRLEN);
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP range: %s -- %s"),
|
||||
ntohl(mess->xid), daemon->namebuff, daemon->addrbuff);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -672,7 +676,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
|
||||
clear_packet(mess, end);
|
||||
do_options(context, mess, end, NULL, hostname, get_domain(mess->yiaddr),
|
||||
netid, subnet_addr, 0, 0, -1, NULL, vendor_class_len, now, 0xffffffff, 0, NULL);
|
||||
netid, subnet_addr, 0, 0, -1, NULL, vendor_class_len, now, 0xffffffff, 0, NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -818,8 +822,8 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (config)
|
||||
|
||||
if (mess_type != DHCPLEASEQUERY && config)
|
||||
{
|
||||
struct dhcp_netid_list *list;
|
||||
|
||||
@@ -860,7 +864,8 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
clid = NULL;
|
||||
|
||||
/* Check if client is PXE client. */
|
||||
if (daemon->enable_pxe &&
|
||||
if (mess_type != DHCPLEASEQUERY &&
|
||||
daemon->enable_pxe &&
|
||||
is_pxe_client(mess, sz, &pxevendor))
|
||||
{
|
||||
if ((opt = option_find(mess, sz, OPTION_PXE_UUID, 17)))
|
||||
@@ -1035,7 +1040,8 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
}
|
||||
|
||||
/* if we're just a proxy server, go no further */
|
||||
if ((context->flags & CONTEXT_PROXY) || pxe)
|
||||
if (mess_type != DHCPLEASEQUERY &&
|
||||
((context->flags & CONTEXT_PROXY) || pxe))
|
||||
return 0;
|
||||
|
||||
if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 0)))
|
||||
@@ -1047,6 +1053,176 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
|
||||
switch (mess_type)
|
||||
{
|
||||
case DHCPLEASEQUERY:
|
||||
mess_type = DHCPLEASEUNKNOWN;
|
||||
|
||||
if (!option_bool(OPT_LEASEQUERY))
|
||||
return 0;
|
||||
|
||||
if (leasequery_source.s_addr == 0)
|
||||
return 0;
|
||||
|
||||
inet_ntop(AF_INET, &leasequery_source, daemon->workspacename, ADDRSTRLEN);
|
||||
|
||||
if (daemon->leasequery_addr)
|
||||
{
|
||||
struct bogus_addr *baddrp;
|
||||
|
||||
for (baddrp = daemon->leasequery_addr; baddrp; baddrp = baddrp->next)
|
||||
if (!baddrp->is6 && is_same_net_prefix(leasequery_source, baddrp->addr.addr4, baddrp->prefix))
|
||||
break;
|
||||
|
||||
if (!baddrp)
|
||||
{
|
||||
my_syslog(MS_DHCP | LOG_WARNING, _("leasequery from %s not permitted"), daemon->workspacename);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
daemon->metrics[METRIC_DHCPLEASEQUERY]++;
|
||||
log_packet("DHCPLEASEQUERY", mess->ciaddr.s_addr ? &mess->ciaddr : NULL, emac_len != 0 ? emac : NULL, emac_len,
|
||||
iface_name, "from ", daemon->workspacename, mess->xid);
|
||||
|
||||
/* Put all the contexts on the ->current list for the next stages. */
|
||||
for (context = daemon->dhcp; context; context = context->next)
|
||||
context->current = context->next;
|
||||
|
||||
/* Have maybe already found the lease by MAC or clid. */
|
||||
if (mess->ciaddr.s_addr != 0 &&
|
||||
!(lease = lease_find_by_addr(mess->ciaddr)) &&
|
||||
address_available(daemon->dhcp, mess->ciaddr, tagif_netid))
|
||||
{
|
||||
mess_type = DHCPLEASEUNASSIGNED;
|
||||
daemon->metrics[METRIC_DHCPLEASEUNASSIGNED]++;
|
||||
}
|
||||
|
||||
if (lease)
|
||||
{
|
||||
/* RFC4388 para 6.4.2 */
|
||||
if (lease->agent_id)
|
||||
{
|
||||
unsigned char *sopt ;
|
||||
|
||||
for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
|
||||
{
|
||||
int search;
|
||||
|
||||
if (vendor->match_type == MATCH_CIRCUIT)
|
||||
search = SUBOPT_CIRCUIT_ID;
|
||||
else if (vendor->match_type == MATCH_REMOTE)
|
||||
search = SUBOPT_REMOTE_ID;
|
||||
else if (vendor->match_type == MATCH_SUBSCRIBER)
|
||||
search = SUBOPT_SUBSCR_ID;
|
||||
else
|
||||
continue;
|
||||
|
||||
if ((sopt = option_find1(lease->agent_id, lease->agent_id + lease->agent_id_len, search, 1)) &&
|
||||
vendor->len == option_len(sopt) &&
|
||||
memcmp(option_ptr(sopt, 0), vendor->data, vendor->len) == 0)
|
||||
{
|
||||
vendor->netid.next = netid;
|
||||
netid = &vendor->netid;
|
||||
}
|
||||
}
|
||||
|
||||
tagif_netid = run_tag_if(netid);
|
||||
}
|
||||
|
||||
/* Now find the context for this lease and config for this host. */
|
||||
if ((context = narrow_context(daemon->dhcp, lease->addr, tagif_netid)))
|
||||
{
|
||||
if ((config = find_config(daemon->dhcp_conf, context, lease->clid, lease->clid_len,
|
||||
lease->hwaddr, lease->hwaddr_len, lease->hwaddr_type, lease->hostname, tagif_netid)))
|
||||
{
|
||||
struct dhcp_netid_list *list;
|
||||
|
||||
for (list = config->netid; list; list = list->next)
|
||||
{
|
||||
list->list->next = netid;
|
||||
netid = list->list;
|
||||
}
|
||||
|
||||
tagif_netid = run_tag_if(netid);
|
||||
}
|
||||
|
||||
if (context->netid.net)
|
||||
{
|
||||
context->netid.next = netid;
|
||||
tagif_netid = run_tag_if(&context->netid);
|
||||
}
|
||||
|
||||
log_tags(tagif_netid, ntohl(mess->xid));
|
||||
emac = extended_hwaddr(lease->hwaddr_type, lease->hwaddr_len, lease->hwaddr, lease->clid_len, lease->clid, &emac_len);
|
||||
mess_type = DHCPLEASEACTIVE;
|
||||
daemon->metrics[METRIC_DHCPLEASEACTIVE]++;
|
||||
}
|
||||
}
|
||||
|
||||
log_packet(mess_type == DHCPLEASEACTIVE ? "DHCPLEASEACTIVE" : (mess_type == DHCPLEASEUNASSIGNED ? "DHCPLEASEUNASSIGNED" : "DHCPLEASEUNKNOWN"),
|
||||
mess_type == DHCPLEASEACTIVE ? &lease->addr : (mess->ciaddr.s_addr != 0 ? &mess->ciaddr : NULL),
|
||||
emac_len != 0 ? emac : NULL, emac_len,
|
||||
iface_name, mess_type == DHCPLEASEACTIVE ? lease->hostname : NULL, NULL, mess->xid);
|
||||
|
||||
clear_packet(mess, end);
|
||||
option_put(mess, end, OPTION_MESSAGE_TYPE, 1, mess_type);
|
||||
|
||||
if (mess_type == DHCPLEASEUNKNOWN)
|
||||
{
|
||||
daemon->metrics[METRIC_DHCPLEASEUNKNOWN]++;
|
||||
mess->ciaddr.s_addr = 0;
|
||||
}
|
||||
|
||||
if (mess_type == DHCPLEASEACTIVE)
|
||||
{
|
||||
unsigned char *p;
|
||||
|
||||
mess->ciaddr = lease->addr;
|
||||
mess->hlen = lease->hwaddr_len;
|
||||
mess->htype = lease->hwaddr_type;
|
||||
memcpy(mess->chaddr, lease->hwaddr, lease->hwaddr_len);
|
||||
|
||||
if (lease->clid && in_list(req_options, OPTION_CLIENT_ID) &&
|
||||
(p = free_space(mess, end, OPTION_CLIENT_ID, lease->clid_len)))
|
||||
memcpy(p, lease->clid, lease->clid_len);
|
||||
|
||||
if (in_list(req_options, OPTION_LEASE_TIME))
|
||||
{
|
||||
if (lease->expires == 0) /* infinite lease */
|
||||
option_put(mess, end, OPTION_LEASE_TIME, 4, 0xffffffff);
|
||||
else
|
||||
option_put(mess, end, OPTION_LEASE_TIME, 4, (unsigned int)(lease->expires - now));
|
||||
}
|
||||
|
||||
if (lease->expires != 0)
|
||||
{
|
||||
time = calc_time(context, config, NULL);
|
||||
|
||||
if (in_list(req_options, OPTION_T1) && (lease->expires - now) > time/2)
|
||||
option_put(mess, end, OPTION_T1, 4, ((unsigned int)(lease->expires - now)) - time/2);
|
||||
if (in_list(req_options, OPTION_T2) && (lease->expires - now) > time/8)
|
||||
option_put(mess, end, OPTION_T2, 4, ((unsigned int)(lease->expires - now)) - time/8);
|
||||
if (in_list(req_options, OPTION_LAST_TRANSACTION) && (lease->expires - now) < time)
|
||||
option_put(mess, end, OPTION_LAST_TRANSACTION, 4, time - ((unsigned int)(lease->expires - now)));
|
||||
}
|
||||
|
||||
if (lease->vendorclass)
|
||||
{
|
||||
memcpy(daemon->dhcp_buff3, lease->vendorclass, lease->vendorclass_len);
|
||||
vendor_class_len = lease->vendorclass_len;
|
||||
}
|
||||
|
||||
subnet_addr.s_addr = 0;
|
||||
do_options(context, mess, end, req_options, lease->hostname, get_domain(lease->addr), netid, subnet_addr,
|
||||
0, 0, -1, NULL, vendor_class_len, now, 0xffffffff, 0, NULL, 1);
|
||||
|
||||
/* Does this have to be last for leasequery replies also? RFC 4388 is silent on the subject. */
|
||||
if (lease->agent_id && in_list(req_options, OPTION_AGENT_ID) &&
|
||||
(p = free_space(mess, end, OPTION_AGENT_ID, lease->agent_id_len)))
|
||||
memcpy(p, lease->agent_id, lease->agent_id_len);
|
||||
}
|
||||
|
||||
return dhcp_packet_size(mess, NULL, real_end);
|
||||
|
||||
case DHCPDECLINE:
|
||||
if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
|
||||
option_addr(opt).s_addr != server_id(context, override, fallback).s_addr)
|
||||
@@ -1197,7 +1373,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
option_put(mess, end, OPTION_LEASE_TIME, 4, time);
|
||||
/* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */
|
||||
do_options(context, mess, end, req_options, offer_hostname, get_domain(mess->yiaddr),
|
||||
netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz, pxevendor);
|
||||
netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz, pxevendor, 0);
|
||||
|
||||
return dhcp_packet_size(mess, agent_id, real_end);
|
||||
|
||||
@@ -1345,7 +1521,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
else if (!lease && (ltmp = lease_find_by_addr(mess->yiaddr)))
|
||||
{
|
||||
/* If a host is configured with more than one MAC address, it's OK to 'nix
|
||||
a lease from one of it's MACs to give the address to another. */
|
||||
a lease from one of its MACs to give the address to another. */
|
||||
if (config && config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type))
|
||||
{
|
||||
inet_ntop(AF_INET, <mp->addr, daemon->addrbuff, ADDRSTRLEN);
|
||||
@@ -1528,7 +1704,21 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
|
||||
lease_set_expires(lease, time, now);
|
||||
lease_set_interface(lease, int_index, now);
|
||||
|
||||
|
||||
if (option_bool(OPT_LEASEQUERY))
|
||||
{
|
||||
if (agent_id)
|
||||
lease_set_agent_id(lease, option_ptr(agent_id, 0), option_len(agent_id));
|
||||
if (vendor_class_len != 0)
|
||||
lease_set_vendorclass(lease, (unsigned char *)daemon->dhcp_buff3, vendor_class_len);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* if leasequery no longer enabled, remove stuff that may have been stored when it was. */
|
||||
lease_set_agent_id(lease, NULL, 0);
|
||||
lease_set_vendorclass(lease, NULL, 0);
|
||||
}
|
||||
|
||||
if (override.s_addr != 0)
|
||||
lease->override = override;
|
||||
else
|
||||
@@ -1544,7 +1734,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
if (rapid_commit)
|
||||
option_put(mess, end, OPTION_RAPID_COMMIT, 0, 0);
|
||||
do_options(context, mess, end, req_options, hostname, get_domain(mess->yiaddr),
|
||||
netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz, pxevendor);
|
||||
netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz, pxevendor, 0);
|
||||
}
|
||||
|
||||
return dhcp_packet_size(mess, agent_id, real_end);
|
||||
@@ -1611,7 +1801,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
}
|
||||
|
||||
do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr),
|
||||
netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, 0xffffffff, 0, pxevendor);
|
||||
netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, 0xffffffff, 0, pxevendor, 0);
|
||||
|
||||
*is_inform = 1; /* handle reply differently */
|
||||
return dhcp_packet_size(mess, agent_id, real_end);
|
||||
@@ -1720,29 +1910,33 @@ static void log_packet(char *type, void *addr, unsigned char *ext_mac,
|
||||
if (!err && !option_bool(OPT_LOG_OPTS) && option_bool(OPT_QUIET_DHCP))
|
||||
return;
|
||||
|
||||
daemon->addrbuff[0] = 0;
|
||||
daemon->addrbuff[0] = daemon->namebuff[0] = 0;
|
||||
|
||||
if (addr)
|
||||
inet_ntop(AF_INET, addr, daemon->addrbuff, ADDRSTRLEN);
|
||||
|
||||
print_mac(daemon->namebuff, ext_mac, mac_len);
|
||||
if (ext_mac)
|
||||
print_mac(daemon->namebuff, ext_mac, mac_len);
|
||||
|
||||
if (option_bool(OPT_LOG_OPTS))
|
||||
my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s%s %s%s",
|
||||
my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s%s%s%s%s",
|
||||
ntohl(xid),
|
||||
type,
|
||||
interface,
|
||||
daemon->addrbuff,
|
||||
addr ? " " : "",
|
||||
daemon->namebuff,
|
||||
ext_mac ? " " : "",
|
||||
string ? string : "",
|
||||
err ? err : "");
|
||||
else
|
||||
my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s%s %s%s",
|
||||
my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s%s%s%s%s",
|
||||
type,
|
||||
interface,
|
||||
daemon->addrbuff,
|
||||
addr ? " " : "",
|
||||
daemon->namebuff,
|
||||
ext_mac ? " " : "",
|
||||
string ? string : "",
|
||||
err ? err : "");
|
||||
|
||||
@@ -2412,7 +2606,8 @@ static void do_options(struct dhcp_context *context,
|
||||
time_t now,
|
||||
unsigned int lease_time,
|
||||
unsigned short fuzz,
|
||||
const char *pxevendor)
|
||||
const char *pxevendor,
|
||||
int leasequery)
|
||||
{
|
||||
struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
|
||||
struct dhcp_boot *boot;
|
||||
@@ -2450,70 +2645,73 @@ static void do_options(struct dhcp_context *context,
|
||||
}
|
||||
}
|
||||
|
||||
for (id_list = daemon->force_broadcast; id_list; id_list = id_list->next)
|
||||
if ((!id_list->list) || match_netid(id_list->list, netid, 0))
|
||||
break;
|
||||
if (id_list)
|
||||
mess->flags |= htons(0x8000); /* force broadcast */
|
||||
|
||||
if (context)
|
||||
mess->siaddr = context->local;
|
||||
|
||||
/* See if we can send the boot stuff as options.
|
||||
To do this we need a requested option list, BOOTP
|
||||
and very old DHCP clients won't have this, we also
|
||||
provide a manual option to disable it.
|
||||
Some PXE ROMs have bugs (surprise!) and need zero-terminated
|
||||
names, so we always send those. */
|
||||
if ((boot = find_boot(tagif)))
|
||||
if (!leasequery)
|
||||
{
|
||||
if (boot->sname)
|
||||
{
|
||||
if (!option_bool(OPT_NO_OVERRIDE) &&
|
||||
req_options &&
|
||||
in_list(req_options, OPTION_SNAME))
|
||||
option_put_string(mess, end, OPTION_SNAME, boot->sname, 1);
|
||||
else
|
||||
safe_strncpy((char *)mess->sname, boot->sname, sizeof(mess->sname));
|
||||
}
|
||||
for (id_list = daemon->force_broadcast; id_list; id_list = id_list->next)
|
||||
if ((!id_list->list) || match_netid(id_list->list, netid, 0))
|
||||
break;
|
||||
if (id_list)
|
||||
mess->flags |= htons(0x8000); /* force broadcast */
|
||||
|
||||
if (boot->file)
|
||||
if (context)
|
||||
mess->siaddr = context->local;
|
||||
|
||||
/* See if we can send the boot stuff as options.
|
||||
To do this we need a requested option list, BOOTP
|
||||
and very old DHCP clients won't have this, we also
|
||||
provide a manual option to disable it.
|
||||
Some PXE ROMs have bugs (surprise!) and need zero-terminated
|
||||
names, so we always send those. */
|
||||
if ((boot = find_boot(tagif)))
|
||||
{
|
||||
if (!option_bool(OPT_NO_OVERRIDE) &&
|
||||
req_options &&
|
||||
in_list(req_options, OPTION_FILENAME))
|
||||
option_put_string(mess, end, OPTION_FILENAME, boot->file, 1);
|
||||
else
|
||||
safe_strncpy((char *)mess->file, boot->file, sizeof(mess->file));
|
||||
if (boot->sname)
|
||||
{
|
||||
if (!option_bool(OPT_NO_OVERRIDE) &&
|
||||
req_options &&
|
||||
in_list(req_options, OPTION_SNAME))
|
||||
option_put_string(mess, end, OPTION_SNAME, boot->sname, 1);
|
||||
else
|
||||
safe_strncpy((char *)mess->sname, boot->sname, sizeof(mess->sname));
|
||||
}
|
||||
|
||||
if (boot->file)
|
||||
{
|
||||
if (!option_bool(OPT_NO_OVERRIDE) &&
|
||||
req_options &&
|
||||
in_list(req_options, OPTION_FILENAME))
|
||||
option_put_string(mess, end, OPTION_FILENAME, boot->file, 1);
|
||||
else
|
||||
safe_strncpy((char *)mess->file, boot->file, sizeof(mess->file));
|
||||
}
|
||||
|
||||
if (boot->next_server.s_addr)
|
||||
mess->siaddr = boot->next_server;
|
||||
else if (boot->tftp_sname)
|
||||
mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
|
||||
}
|
||||
|
||||
if (boot->next_server.s_addr)
|
||||
mess->siaddr = boot->next_server;
|
||||
else if (boot->tftp_sname)
|
||||
mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
|
||||
}
|
||||
else
|
||||
/* Use the values of the relevant options if no dhcp-boot given and
|
||||
they're not explicitly asked for as options. OPTION_END is used
|
||||
as an internal way to specify siaddr without using dhcp-boot, for use in
|
||||
dhcp-optsfile. */
|
||||
{
|
||||
if ((!req_options || !in_list(req_options, OPTION_FILENAME)) &&
|
||||
(opt = option_find2(OPTION_FILENAME)) && !(opt->flags & DHOPT_FORCE))
|
||||
else
|
||||
/* Use the values of the relevant options if no dhcp-boot given and
|
||||
they're not explicitly asked for as options. OPTION_END is used
|
||||
as an internal way to specify siaddr without using dhcp-boot, for use in
|
||||
dhcp-optsfile. */
|
||||
{
|
||||
safe_strncpy((char *)mess->file, (char *)opt->val, sizeof(mess->file));
|
||||
done_file = 1;
|
||||
if ((!req_options || !in_list(req_options, OPTION_FILENAME)) &&
|
||||
(opt = option_find2(OPTION_FILENAME)) && !(opt->flags & DHOPT_FORCE))
|
||||
{
|
||||
safe_strncpy((char *)mess->file, (char *)opt->val, sizeof(mess->file));
|
||||
done_file = 1;
|
||||
}
|
||||
|
||||
if ((!req_options || !in_list(req_options, OPTION_SNAME)) &&
|
||||
(opt = option_find2(OPTION_SNAME)) && !(opt->flags & DHOPT_FORCE))
|
||||
{
|
||||
safe_strncpy((char *)mess->sname, (char *)opt->val, sizeof(mess->sname));
|
||||
done_server = 1;
|
||||
}
|
||||
|
||||
if ((opt = option_find2(OPTION_END)))
|
||||
mess->siaddr.s_addr = ((struct in_addr *)opt->val)->s_addr;
|
||||
}
|
||||
|
||||
if ((!req_options || !in_list(req_options, OPTION_SNAME)) &&
|
||||
(opt = option_find2(OPTION_SNAME)) && !(opt->flags & DHOPT_FORCE))
|
||||
{
|
||||
safe_strncpy((char *)mess->sname, (char *)opt->val, sizeof(mess->sname));
|
||||
done_server = 1;
|
||||
}
|
||||
|
||||
if ((opt = option_find2(OPTION_END)))
|
||||
mess->siaddr.s_addr = ((struct in_addr *)opt->val)->s_addr;
|
||||
}
|
||||
|
||||
/* We don't want to do option-overload for BOOTP, so make the file and sname
|
||||
@@ -2534,7 +2732,7 @@ static void do_options(struct dhcp_context *context,
|
||||
end -= 3;
|
||||
|
||||
/* rfc3011 says this doesn't need to be in the requested options list. */
|
||||
if (subnet_addr.s_addr)
|
||||
if (!leasequery && subnet_addr.s_addr)
|
||||
option_put(mess, end, OPTION_SUBNET_SELECT, INADDRSZ, ntohl(subnet_addr.s_addr));
|
||||
|
||||
if (lease_time != 0xffffffff)
|
||||
@@ -2575,14 +2773,17 @@ static void do_options(struct dhcp_context *context,
|
||||
/* replies to DHCPINFORM may not have a valid context */
|
||||
if (context)
|
||||
{
|
||||
if (!option_find2(OPTION_NETMASK))
|
||||
/* Netmask and broadcast always sent, except leasequery. */
|
||||
if (!option_find2(OPTION_NETMASK) &&
|
||||
(!leasequery || in_list(req_options, OPTION_NETMASK)))
|
||||
option_put(mess, end, OPTION_NETMASK, INADDRSZ, ntohl(context->netmask.s_addr));
|
||||
|
||||
|
||||
/* May not have a "guessed" broadcast address if we got no packets via a relay
|
||||
from this net yet (ie just unicast renewals after a restart */
|
||||
if (context->broadcast.s_addr &&
|
||||
!option_find2(OPTION_BROADCAST))
|
||||
option_put(mess, end, OPTION_BROADCAST, INADDRSZ, ntohl(context->broadcast.s_addr));
|
||||
!option_find2(OPTION_BROADCAST) &&
|
||||
(!leasequery || in_list(req_options, OPTION_BROADCAST)))
|
||||
option_put(mess, end, OPTION_BROADCAST, INADDRSZ, ntohl(context->broadcast.s_addr));
|
||||
|
||||
/* Same comments as broadcast apply, and also may not be able to get a sensible
|
||||
default when using subnet select. User must configure by steam in that case. */
|
||||
@@ -2663,7 +2864,7 @@ static void do_options(struct dhcp_context *context,
|
||||
continue;
|
||||
|
||||
/* was it asked for, or are we sending it anyway? */
|
||||
if (!(opt->flags & DHOPT_FORCE) && !in_list(req_options, optno))
|
||||
if ((!(opt->flags & DHOPT_FORCE) || leasequery) && !in_list(req_options, optno))
|
||||
continue;
|
||||
|
||||
/* prohibit some used-internally options. T1 and T2 already handled. */
|
||||
@@ -2721,7 +2922,7 @@ static void do_options(struct dhcp_context *context,
|
||||
handle_encap(mess, end, req_options, null_term, tagif, pxe_arch != 1);
|
||||
|
||||
force_encap = prune_vendor_opts(tagif);
|
||||
|
||||
|
||||
if (context && pxe_arch != -1)
|
||||
{
|
||||
pxe_misc(mess, end, uuid, pxevendor);
|
||||
@@ -2729,8 +2930,8 @@ static void do_options(struct dhcp_context *context,
|
||||
config_opts = pxe_opts(pxe_arch, tagif, context->local, now);
|
||||
}
|
||||
|
||||
if ((force_encap || in_list(req_options, OPTION_VENDOR_CLASS_OPT)) &&
|
||||
do_encap_opts(config_opts, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, null_term) &&
|
||||
if ((force_encap || in_list(req_options, OPTION_VENDOR_CLASS_OPT) || in_list(req_options, OPTION_VENDOR_ID)) &&
|
||||
(leasequery || do_encap_opts(config_opts, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, null_term)) &&
|
||||
pxe_arch == -1 && !done_vendor_class && vendor_class_len != 0 &&
|
||||
(p = free_space(mess, end, OPTION_VENDOR_ID, vendor_class_len)))
|
||||
/* If we send vendor encapsulated options, and haven't already sent option 60,
|
||||
|
||||
+13
-13
@@ -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
|
||||
@@ -39,8 +39,8 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu
|
||||
static void log6_opts(int nest, unsigned int xid, void *start_opts, void *end_opts);
|
||||
static void log6_packet(struct state *state, char *type, struct in6_addr *addr, char *string);
|
||||
static void log6_quiet(struct state *state, char *type, struct in6_addr *addr, char *string);
|
||||
static void *opt6_find (void *opts, void *end, unsigned int search, unsigned int minsize);
|
||||
static void *opt6_next(void *opts, void *end);
|
||||
static void *opt6_find (uint8_t *opts, uint8_t *end, unsigned int search, unsigned int minsize);
|
||||
static void *opt6_next(uint8_t *opts, uint8_t *end);
|
||||
static unsigned int opt6_uint(unsigned char *opt, int offset, int size);
|
||||
static void get_context_tag(struct state *state, struct dhcp_context *context);
|
||||
static int check_ia(struct state *state, void *opt, void **endp, void **ia_option);
|
||||
@@ -61,11 +61,11 @@ static void calculate_times(struct dhcp_context *context, unsigned int *min_time
|
||||
|
||||
#define opt6_len(opt) ((int)(opt6_uint(opt, -2, 2)))
|
||||
#define opt6_type(opt) (opt6_uint(opt, -4, 2))
|
||||
#define opt6_ptr(opt, i) ((void *)&(((unsigned char *)(opt))[4+(i)]))
|
||||
#define opt6_ptr(opt, i) ((void *)&(((uint8_t *)(opt))[4+(i)]))
|
||||
|
||||
#define opt6_user_vendor_ptr(opt, i) ((void *)&(((unsigned char *)(opt))[2+(i)]))
|
||||
#define opt6_user_vendor_ptr(opt, i) ((void *)&(((uint8_t *)(opt))[2+(i)]))
|
||||
#define opt6_user_vendor_len(opt) ((int)(opt6_uint(opt, -4, 2)))
|
||||
#define opt6_user_vendor_next(opt, end) (opt6_next(((void *) opt) - 2, end))
|
||||
#define opt6_user_vendor_next(opt, end) (opt6_next(((uint8_t *) opt) - 2, end))
|
||||
|
||||
|
||||
unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name,
|
||||
@@ -107,11 +107,11 @@ unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *if
|
||||
static int dhcp6_maybe_relay(struct state *state, unsigned char *inbuff, size_t sz,
|
||||
struct in6_addr *client_addr, int is_unicast, time_t now)
|
||||
{
|
||||
void *end = inbuff + sz;
|
||||
void *opts = inbuff + 34;
|
||||
uint8_t *end = inbuff + sz;
|
||||
uint8_t *opts = inbuff + 34;
|
||||
int msg_type = *inbuff;
|
||||
unsigned char *outmsgtypep;
|
||||
void *opt;
|
||||
uint8_t *opt;
|
||||
struct dhcp_vendor *vendor;
|
||||
|
||||
/* if not an encapsulated relayed message, just do the stuff */
|
||||
@@ -232,7 +232,7 @@ static int dhcp6_maybe_relay(struct state *state, unsigned char *inbuff, size_t
|
||||
|
||||
for (opt = opts; opt; opt = opt6_next(opt, end))
|
||||
{
|
||||
if (opt6_ptr(opt, 0) + opt6_len(opt) > end)
|
||||
if ((uint8_t *)opt6_ptr(opt, 0) + opt6_len(opt) > end)
|
||||
return 0;
|
||||
|
||||
/* Don't copy MAC address into reply. */
|
||||
@@ -1307,7 +1307,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu
|
||||
reallocated. */
|
||||
((unsigned char *)(daemon->outpacket.iov_base))[start_msg] = outmsgtype;
|
||||
|
||||
log6_opts(0, state->xid, daemon->outpacket.iov_base + start_opts, daemon->outpacket.iov_base + save_counter(-1));
|
||||
log6_opts(0, state->xid, (uint8_t *)daemon->outpacket.iov_base + start_opts, (uint8_t *)daemon->outpacket.iov_base + save_counter(-1));
|
||||
|
||||
return 1;
|
||||
|
||||
@@ -2093,7 +2093,7 @@ static void log6_packet(struct state *state, char *type, struct in6_addr *addr,
|
||||
string ? string : "");
|
||||
}
|
||||
|
||||
static void *opt6_find (void *opts, void *end, unsigned int search, unsigned int minsize)
|
||||
static void *opt6_find (uint8_t *opts, uint8_t *end, unsigned int search, unsigned int minsize)
|
||||
{
|
||||
u16 opt, opt_len;
|
||||
void *start;
|
||||
@@ -2120,7 +2120,7 @@ static void *opt6_find (void *opts, void *end, unsigned int search, unsigned int
|
||||
}
|
||||
}
|
||||
|
||||
static void *opt6_next(void *opts, void *end)
|
||||
static void *opt6_next(uint8_t *opts, uint8_t *end)
|
||||
{
|
||||
u16 opt_len;
|
||||
|
||||
|
||||
+3
-6
@@ -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 *))))
|
||||
@@ -380,7 +377,7 @@ int expand_workspace(unsigned char ***wkspc, int *szp, int new)
|
||||
int to_wire(char *name)
|
||||
{
|
||||
unsigned char *l, *p, *q, term;
|
||||
int len;
|
||||
unsigned int len;
|
||||
|
||||
for (l = (unsigned char*)name; *l != 0; l = p)
|
||||
{
|
||||
@@ -412,7 +409,7 @@ int to_wire(char *name)
|
||||
void from_wire(char *name)
|
||||
{
|
||||
unsigned char *l, *p, *last;
|
||||
int len;
|
||||
unsigned int len;
|
||||
|
||||
for (last = (unsigned char *)name; *last != 0; last += *last+1);
|
||||
|
||||
|
||||
+2
-2
@@ -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
|
||||
@@ -61,7 +61,7 @@ void slaac_add_addrs(struct dhcp_lease *lease, time_t now, int force)
|
||||
else if (lease->clid_len == 9 &&
|
||||
lease->clid[0] == ARPHRD_EUI64 &&
|
||||
lease->hwaddr_type == ARPHRD_IEEE1394)
|
||||
/* firewire has EUI-64 identifier as clid */
|
||||
/* FireWire has EUI-64 identifier as clid */
|
||||
memcpy(&addr.s6_addr[8], &lease->clid[1], 8);
|
||||
#endif
|
||||
else
|
||||
|
||||
+299
-195
@@ -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
|
||||
@@ -41,11 +41,11 @@ static void sanitise(char *buf);
|
||||
#define ERR_ILL 4
|
||||
#define ERR_TID 5
|
||||
|
||||
void tftp_request(struct listener *listen, time_t now)
|
||||
static void tftp_request(struct listener *listen, time_t now)
|
||||
{
|
||||
ssize_t len;
|
||||
char *packet = daemon->packet;
|
||||
char *filename, *mode, *p, *end, *opt;
|
||||
char *filename, *mode, *p, *end;
|
||||
union mysockaddr addr, peer;
|
||||
struct msghdr msg;
|
||||
struct iovec iov;
|
||||
@@ -198,11 +198,11 @@ void tftp_request(struct listener *listen, time_t now)
|
||||
|
||||
name = namebuff;
|
||||
|
||||
addra.addr4 = addr.in.sin_addr;
|
||||
|
||||
if (family == AF_INET6)
|
||||
addra.addr6 = addr.in6.sin6_addr;
|
||||
|
||||
else
|
||||
addra.addr4 = addr.in.sin_addr;
|
||||
|
||||
if (daemon->tftp_interfaces)
|
||||
{
|
||||
/* dedicated tftp interface list */
|
||||
@@ -274,7 +274,7 @@ void tftp_request(struct listener *listen, time_t now)
|
||||
}
|
||||
|
||||
/* Enforce simultaneous transfer limit. In non-single-port mode
|
||||
this is doene by not listening on the server socket when
|
||||
this is done by not listening on the server socket when
|
||||
too many transfers are in progress. */
|
||||
if (!transfer && tftp_cnt >= daemon->tftp_max)
|
||||
return;
|
||||
@@ -308,7 +308,9 @@ void tftp_request(struct listener *listen, time_t now)
|
||||
/* May reuse struct transfer from abandoned transfer in single port mode. */
|
||||
if (!transfer && !(transfer = whine_malloc(sizeof(struct tftp_transfer))))
|
||||
return;
|
||||
|
||||
|
||||
memset(transfer, 0, sizeof(struct tftp_transfer));
|
||||
|
||||
if (option_bool(OPT_SINGLE_PORT))
|
||||
transfer->sockfd = listen->tftpfd;
|
||||
else if ((transfer->sockfd = socket(family, SOCK_DGRAM, 0)) == -1)
|
||||
@@ -320,15 +322,13 @@ void tftp_request(struct listener *listen, time_t now)
|
||||
transfer->peer = peer;
|
||||
transfer->source = addra;
|
||||
transfer->if_index = if_index;
|
||||
transfer->timeout = now + 2;
|
||||
transfer->timeout = 2;
|
||||
transfer->start = now;
|
||||
transfer->backoff = 1;
|
||||
transfer->block = 1;
|
||||
transfer->blocksize = 512;
|
||||
transfer->offset = 0;
|
||||
transfer->file = NULL;
|
||||
transfer->opt_blocksize = transfer->opt_transize = 0;
|
||||
transfer->netascii = transfer->carrylf = 0;
|
||||
|
||||
transfer->windowsize = 1;
|
||||
|
||||
(void)prettyprint_addr(&peer, daemon->addrbuff);
|
||||
|
||||
/* if we have a nailed-down range, iterate until we find a free one. */
|
||||
@@ -361,136 +361,167 @@ void tftp_request(struct listener *listen, time_t now)
|
||||
|
||||
p = packet + 2;
|
||||
end = packet + len;
|
||||
|
||||
len = 0;
|
||||
|
||||
if (ntohs(*((unsigned short *)packet)) != OP_RRQ ||
|
||||
!(filename = next(&p, end)) ||
|
||||
!(mode = next(&p, end)) ||
|
||||
(strcasecmp(mode, "octet") != 0 && strcasecmp(mode, "netascii") != 0))
|
||||
if (ntohs(*((unsigned short *)packet)) == OP_WRQ)
|
||||
len = tftp_err(ERR_ILL, packet, _("unsupported write request from %s"),daemon->addrbuff, NULL);
|
||||
else if (ntohs(*((unsigned short *)packet)) == OP_RRQ)
|
||||
{
|
||||
len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), daemon->addrbuff, NULL);
|
||||
is_err = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (strcasecmp(mode, "netascii") == 0)
|
||||
transfer->netascii = 1;
|
||||
|
||||
while ((opt = next(&p, end)))
|
||||
if (!(filename = next(&p, end)))
|
||||
len = tftp_err(ERR_ILL, packet, _("empty filename in request from %s"), daemon->addrbuff, NULL);
|
||||
else if (!(mode = next(&p, end)) || (strcasecmp(mode, "octet") != 0 && strcasecmp(mode, "netascii") != 0))
|
||||
len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"),daemon->addrbuff, NULL);
|
||||
else
|
||||
{
|
||||
if (strcasecmp(opt, "blksize") == 0)
|
||||
char *opt, *arg;
|
||||
|
||||
if (strcasecmp(mode, "netascii") == 0)
|
||||
transfer->netascii = 1;
|
||||
|
||||
while ((opt = next(&p, end)) && (arg = next(&p, end)))
|
||||
{
|
||||
if ((opt = next(&p, end)) && !option_bool(OPT_TFTP_NOBLOCK))
|
||||
unsigned int val = atoi(arg);
|
||||
|
||||
if (strcasecmp(opt, "blksize") == 0 && !option_bool(OPT_TFTP_NOBLOCK))
|
||||
{
|
||||
/* 32 bytes for IP, UDP and TFTP headers, 52 bytes for IPv6 */
|
||||
int overhead = (family == AF_INET) ? 32 : 52;
|
||||
transfer->blocksize = atoi(opt);
|
||||
if (transfer->blocksize < 1)
|
||||
transfer->blocksize = 1;
|
||||
if (transfer->blocksize > (unsigned)daemon->packet_buff_sz - 4)
|
||||
transfer->blocksize = (unsigned)daemon->packet_buff_sz - 4;
|
||||
if (mtu != 0 && transfer->blocksize > (unsigned)mtu - overhead)
|
||||
transfer->blocksize = (unsigned)mtu - overhead;
|
||||
if (val < 1)
|
||||
val = 1;
|
||||
if (val > (unsigned)daemon->packet_buff_sz - 4)
|
||||
val = (unsigned)daemon->packet_buff_sz - 4;
|
||||
if (mtu != 0 && val > (unsigned)mtu - overhead)
|
||||
val = (unsigned)mtu - overhead;
|
||||
transfer->blocksize = val;
|
||||
transfer->opt_blocksize = 1;
|
||||
transfer->block = 0;
|
||||
}
|
||||
}
|
||||
else if (strcasecmp(opt, "tsize") == 0 && next(&p, end) && !transfer->netascii)
|
||||
{
|
||||
transfer->opt_transize = 1;
|
||||
transfer->block = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* cope with backslashes from windows boxen. */
|
||||
for (p = filename; *p; p++)
|
||||
if (*p == '\\')
|
||||
*p = '/';
|
||||
else if (option_bool(OPT_TFTP_LC))
|
||||
*p = tolower((unsigned char)*p);
|
||||
|
||||
strcpy(daemon->namebuff, "/");
|
||||
if (prefix)
|
||||
{
|
||||
if (prefix[0] == '/')
|
||||
daemon->namebuff[0] = 0;
|
||||
strncat(daemon->namebuff, prefix, (MAXDNAME-1) - strlen(daemon->namebuff));
|
||||
if (prefix[strlen(prefix)-1] != '/')
|
||||
strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));
|
||||
|
||||
if (option_bool(OPT_TFTP_APREF_IP))
|
||||
{
|
||||
size_t oldlen = strlen(daemon->namebuff);
|
||||
struct stat statbuf;
|
||||
|
||||
strncat(daemon->namebuff, daemon->addrbuff, (MAXDNAME-1) - strlen(daemon->namebuff));
|
||||
strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));
|
||||
|
||||
/* remove unique-directory if it doesn't exist */
|
||||
if (stat(daemon->namebuff, &statbuf) == -1 || !S_ISDIR(statbuf.st_mode))
|
||||
daemon->namebuff[oldlen] = 0;
|
||||
else if (strcasecmp(opt, "tsize") == 0 && !transfer->netascii)
|
||||
{
|
||||
transfer->opt_transize = 1;
|
||||
transfer->block = 0;
|
||||
}
|
||||
else if (strcasecmp(opt, "timeout") == 0)
|
||||
{
|
||||
if (val > 255)
|
||||
val = 255;
|
||||
transfer->timeout = val;
|
||||
transfer->opt_timeout = 1;
|
||||
transfer->block = 0;
|
||||
}
|
||||
else if (strcasecmp(opt, "windowsize") == 0 && !transfer->netascii)
|
||||
{
|
||||
/* windowsize option only supported for binary transfers. */
|
||||
if (val < 1)
|
||||
val = 1;
|
||||
if (val > TFTP_MAX_WINDOW)
|
||||
val = TFTP_MAX_WINDOW;
|
||||
transfer->windowsize = val;
|
||||
transfer->opt_windowsize = 1;
|
||||
transfer->block = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (option_bool(OPT_TFTP_APREF_MAC))
|
||||
/* cope with backslashes from windows boxen. */
|
||||
for (p = filename; *p; p++)
|
||||
if (*p == '\\')
|
||||
*p = '/';
|
||||
else if (option_bool(OPT_TFTP_LC))
|
||||
*p = tolower((unsigned char)*p);
|
||||
|
||||
strcpy(daemon->namebuff, "/");
|
||||
if (prefix)
|
||||
{
|
||||
unsigned char *macaddr = NULL;
|
||||
unsigned char macbuf[DHCP_CHADDR_MAX];
|
||||
if (prefix[0] == '/')
|
||||
daemon->namebuff[0] = 0;
|
||||
strncat(daemon->namebuff, prefix, (MAXDNAME-1) - strlen(daemon->namebuff));
|
||||
if (prefix[strlen(prefix)-1] != '/')
|
||||
strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));
|
||||
|
||||
#ifdef HAVE_DHCP
|
||||
if (daemon->dhcp && peer.sa.sa_family == AF_INET)
|
||||
{
|
||||
/* Check if the client IP is in our lease database */
|
||||
struct dhcp_lease *lease = lease_find_by_addr(peer.in.sin_addr);
|
||||
if (lease && lease->hwaddr_type == ARPHRD_ETHER && lease->hwaddr_len == ETHER_ADDR_LEN)
|
||||
macaddr = lease->hwaddr;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* If no luck, try to find in ARP table. This only works if client is in same (V)LAN */
|
||||
if (!macaddr && find_mac(&peer, macbuf, 1, now) > 0)
|
||||
macaddr = macbuf;
|
||||
|
||||
if (macaddr)
|
||||
{
|
||||
if (option_bool(OPT_TFTP_APREF_IP))
|
||||
{
|
||||
size_t oldlen = strlen(daemon->namebuff);
|
||||
struct stat statbuf;
|
||||
|
||||
snprintf(daemon->namebuff + oldlen, (MAXDNAME-1) - oldlen, "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x/",
|
||||
macaddr[0], macaddr[1], macaddr[2], macaddr[3], macaddr[4], macaddr[5]);
|
||||
|
||||
strncat(daemon->namebuff, daemon->addrbuff, (MAXDNAME-1) - strlen(daemon->namebuff));
|
||||
strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));
|
||||
|
||||
/* remove unique-directory if it doesn't exist */
|
||||
if (stat(daemon->namebuff, &statbuf) == -1 || !S_ISDIR(statbuf.st_mode))
|
||||
daemon->namebuff[oldlen] = 0;
|
||||
}
|
||||
|
||||
if (option_bool(OPT_TFTP_APREF_MAC))
|
||||
{
|
||||
unsigned char *macaddr = NULL;
|
||||
unsigned char macbuf[DHCP_CHADDR_MAX];
|
||||
|
||||
#ifdef HAVE_DHCP
|
||||
if (daemon->dhcp && peer.sa.sa_family == AF_INET)
|
||||
{
|
||||
/* Check if the client IP is in our lease database */
|
||||
struct dhcp_lease *lease = lease_find_by_addr(peer.in.sin_addr);
|
||||
if (lease && lease->hwaddr_type == ARPHRD_ETHER && lease->hwaddr_len == ETHER_ADDR_LEN)
|
||||
macaddr = lease->hwaddr;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* If no luck, try to find in ARP table. This only works if client is in same (V)LAN */
|
||||
if (!macaddr && find_mac(&peer, macbuf, 1, now) > 0)
|
||||
macaddr = macbuf;
|
||||
|
||||
if (macaddr)
|
||||
{
|
||||
size_t oldlen = strlen(daemon->namebuff);
|
||||
struct stat statbuf;
|
||||
|
||||
snprintf(daemon->namebuff + oldlen, (MAXDNAME-1) - oldlen, "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x/",
|
||||
macaddr[0], macaddr[1], macaddr[2], macaddr[3], macaddr[4], macaddr[5]);
|
||||
|
||||
/* remove unique-directory if it doesn't exist */
|
||||
if (stat(daemon->namebuff, &statbuf) == -1 || !S_ISDIR(statbuf.st_mode))
|
||||
daemon->namebuff[oldlen] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Absolute pathnames OK if they match prefix */
|
||||
if (filename[0] == '/')
|
||||
{
|
||||
if (strstr(filename, daemon->namebuff) == filename)
|
||||
daemon->namebuff[0] = 0;
|
||||
else
|
||||
filename++;
|
||||
}
|
||||
}
|
||||
else if (filename[0] == '/')
|
||||
daemon->namebuff[0] = 0;
|
||||
strncat(daemon->namebuff, filename, (MAXDNAME-1) - strlen(daemon->namebuff));
|
||||
|
||||
/* Absolute pathnames OK if they match prefix */
|
||||
if (filename[0] == '/')
|
||||
/* check permissions and open file */
|
||||
if ((transfer->file = check_tftp_fileperm(&len, prefix, daemon->addrbuff)))
|
||||
{
|
||||
if (strstr(filename, daemon->namebuff) == filename)
|
||||
daemon->namebuff[0] = 0;
|
||||
transfer->lastack = transfer->block;
|
||||
transfer->retransmit = now + transfer->timeout;
|
||||
/* This packet is may be the first data packet, but only if windowsize == 1
|
||||
To get windowsize greater then one requires an option negotiation,
|
||||
in which case this packet is the OACK. */
|
||||
if ((len = get_block(packet, transfer)) == -1)
|
||||
len = tftp_err_oops(packet, daemon->namebuff);
|
||||
else
|
||||
filename++;
|
||||
is_err = 0;
|
||||
}
|
||||
}
|
||||
else if (filename[0] == '/')
|
||||
daemon->namebuff[0] = 0;
|
||||
strncat(daemon->namebuff, filename, (MAXDNAME-1) - strlen(daemon->namebuff));
|
||||
|
||||
/* check permissions and open file */
|
||||
if ((transfer->file = check_tftp_fileperm(&len, prefix, daemon->addrbuff)))
|
||||
{
|
||||
if ((len = get_block(packet, transfer)) == -1)
|
||||
len = tftp_err_oops(packet, daemon->namebuff);
|
||||
else
|
||||
is_err = 0;
|
||||
}
|
||||
}
|
||||
|
||||
send_from(transfer->sockfd, !option_bool(OPT_SINGLE_PORT), packet, len, &peer, &addra, if_index);
|
||||
|
||||
|
||||
if (len)
|
||||
{
|
||||
send_from(transfer->sockfd, !option_bool(OPT_SINGLE_PORT), packet, len, &peer, &addra, if_index);
|
||||
|
||||
#ifdef HAVE_DUMPFILE
|
||||
dump_packet_udp(DUMP_TFTP, (void *)packet, len, NULL, (union mysockaddr *)&peer, transfer->sockfd);
|
||||
dump_packet_udp(DUMP_TFTP, (void *)packet, len, NULL, (union mysockaddr *)&peer, transfer->sockfd);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (is_err)
|
||||
free_transfer(transfer);
|
||||
@@ -606,6 +637,10 @@ void check_tftp_listeners(time_t now)
|
||||
|
||||
if ((len = recvfrom(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0, &peer.sa, &addr_len)) > 0)
|
||||
{
|
||||
#ifdef HAVE_DUMPFILE
|
||||
dump_packet_udp(DUMP_TFTP, (void *)daemon->packet, len, (union mysockaddr *)&peer, NULL, transfer->sockfd);
|
||||
#endif
|
||||
|
||||
if (sockaddr_isequal(&peer, &transfer->peer))
|
||||
handle_tftp(now, transfer, len);
|
||||
else
|
||||
@@ -624,66 +659,90 @@ void check_tftp_listeners(time_t now)
|
||||
|
||||
for (transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; transfer = tmp)
|
||||
{
|
||||
tmp = transfer->next;
|
||||
int endcon = 0, error = 0, timeout = 0;
|
||||
|
||||
if (difftime(now, transfer->timeout) >= 0.0)
|
||||
tmp = transfer->next;
|
||||
|
||||
/* ->start set to zero in handle_tftp() when we recv an error packet. */
|
||||
if (transfer->start == 0)
|
||||
endcon = error = 1;
|
||||
else if (difftime(now, transfer->start) > TFTP_TRANSFER_TIME)
|
||||
{
|
||||
int endcon = 0;
|
||||
endcon = 1;
|
||||
/* don't complain about timeout when we're awaiting the last
|
||||
ACK, some clients never send it */
|
||||
if (get_block(daemon->packet, transfer) > 0)
|
||||
error = timeout = 1;
|
||||
}
|
||||
else if (difftime(now, transfer->retransmit) >= 0.0)
|
||||
{
|
||||
/* Do transmission or re-transmission. When we get an ACK, the call to handle_tftp()
|
||||
bumps transfer->lastack and trips the retransmit timer so that we send the next block(s)
|
||||
here. */
|
||||
ssize_t len;
|
||||
|
||||
/* timeout, retransmit */
|
||||
transfer->timeout += 1 + (1<<(transfer->backoff/2));
|
||||
|
||||
/* we overwrote the buffer... */
|
||||
daemon->srv_save = NULL;
|
||||
|
||||
if ((len = get_block(daemon->packet, transfer)) == -1)
|
||||
{
|
||||
len = tftp_err_oops(daemon->packet, transfer->file->filename);
|
||||
endcon = 1;
|
||||
}
|
||||
else if (++transfer->backoff > 7)
|
||||
{
|
||||
/* don't complain about timeout when we're awaiting the last
|
||||
ACK, some clients never send it */
|
||||
if ((unsigned)len == transfer->blocksize + 4)
|
||||
endcon = 1;
|
||||
len = 0;
|
||||
}
|
||||
|
||||
if (len != 0)
|
||||
{
|
||||
send_from(transfer->sockfd, !option_bool(OPT_SINGLE_PORT), daemon->packet, len,
|
||||
&transfer->peer, &transfer->source, transfer->if_index);
|
||||
#ifdef HAVE_DUMPFILE
|
||||
dump_packet_udp(DUMP_TFTP, (void *)daemon->packet, len, NULL, (union mysockaddr *)&transfer->peer, transfer->sockfd);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (endcon || len == 0)
|
||||
transfer->retransmit += transfer->timeout + (1<<(transfer->backoff/2));
|
||||
transfer->backoff++;
|
||||
transfer->block = transfer->lastack;
|
||||
|
||||
if ((len = get_block(daemon->packet, transfer)) == 0)
|
||||
endcon = 1; /* got last ACK */
|
||||
else
|
||||
{
|
||||
strcpy(daemon->namebuff, transfer->file->filename);
|
||||
sanitise(daemon->namebuff);
|
||||
(void)prettyprint_addr(&transfer->peer, daemon->addrbuff);
|
||||
my_syslog(MS_TFTP | LOG_INFO, endcon ? _("failed sending %s to %s") : _("sent %s to %s"), daemon->namebuff, daemon->addrbuff);
|
||||
/* unlink */
|
||||
*up = tmp;
|
||||
if (endcon)
|
||||
free_transfer(transfer);
|
||||
else
|
||||
/* send a window'a worth of blocks unless we're retransmitting OACK */
|
||||
unsigned int i, winsize = transfer->block ? transfer->windowsize : 1;
|
||||
|
||||
for (i = 0; i < winsize && !endcon; i++, transfer->block++)
|
||||
{
|
||||
/* put on queue to be sent to script and deleted */
|
||||
transfer->next = daemon->tftp_done_trans;
|
||||
daemon->tftp_done_trans = transfer;
|
||||
if (i != 0)
|
||||
len = get_block(daemon->packet, transfer);
|
||||
|
||||
if (len == 0)
|
||||
break;
|
||||
|
||||
if (len == -1)
|
||||
{
|
||||
len = tftp_err_oops(daemon->packet, transfer->file->filename);
|
||||
endcon = error = 1;
|
||||
}
|
||||
|
||||
send_from(transfer->sockfd, !option_bool(OPT_SINGLE_PORT), daemon->packet, len,
|
||||
&transfer->peer, &transfer->source, transfer->if_index);
|
||||
#ifdef HAVE_DUMPFILE
|
||||
dump_packet_udp(DUMP_TFTP, (void *)daemon->packet, len, NULL, (union mysockaddr *)&transfer->peer, transfer->sockfd);
|
||||
#endif
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
up = &transfer->next;
|
||||
}
|
||||
}
|
||||
|
||||
if (endcon)
|
||||
{
|
||||
strcpy(daemon->namebuff, transfer->file->filename);
|
||||
sanitise(daemon->namebuff);
|
||||
(void)prettyprint_addr(&transfer->peer, daemon->addrbuff);
|
||||
if (timeout)
|
||||
my_syslog(MS_TFTP | LOG_ERR, _("timeout sending %s to %s"), daemon->namebuff, daemon->addrbuff);
|
||||
else if (error)
|
||||
my_syslog(MS_TFTP | LOG_ERR, _("failed sending %s to %s"), daemon->namebuff, daemon->addrbuff);
|
||||
else
|
||||
my_syslog(MS_TFTP | LOG_INFO, _("sent %s to %s"), daemon->namebuff, daemon->addrbuff);
|
||||
|
||||
/* unlink */
|
||||
*up = tmp;
|
||||
if (error)
|
||||
free_transfer(transfer);
|
||||
else
|
||||
{
|
||||
/* put on queue to be sent to script and deleted */
|
||||
transfer->next = daemon->tftp_done_trans;
|
||||
daemon->tftp_done_trans = transfer;
|
||||
}
|
||||
}
|
||||
else
|
||||
up = &transfer->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* packet in daemon->packet as this is called. */
|
||||
static void handle_tftp(time_t now, struct tftp_transfer *transfer, ssize_t len)
|
||||
{
|
||||
@@ -693,13 +752,30 @@ static void handle_tftp(time_t now, struct tftp_transfer *transfer, ssize_t len)
|
||||
|
||||
if (len >= (ssize_t)sizeof(struct ack))
|
||||
{
|
||||
if (ntohs(mess->op) == OP_ACK && ntohs(mess->block) == (unsigned short)transfer->block)
|
||||
if (ntohs(mess->op) == OP_ACK)
|
||||
{
|
||||
/* Got ack, ensure we take the (re)transmit path */
|
||||
transfer->timeout = now;
|
||||
transfer->backoff = 0;
|
||||
if (transfer->block++ != 0)
|
||||
transfer->offset += transfer->blocksize - transfer->expansion;
|
||||
/* try and handle 16-bit blockno wrap-around */
|
||||
unsigned int block = (unsigned short)ntohs(mess->block);
|
||||
if (block < transfer->lastack)
|
||||
block |= transfer->block & 0xffff0000;
|
||||
|
||||
/* ignore duplicate ACKs and ACKs for blocks we've not yet sent. */
|
||||
if (block >= transfer->lastack &&
|
||||
block <= transfer->block)
|
||||
{
|
||||
/* Got ack, move forward and ensure we take the (re)transmit path */
|
||||
transfer->retransmit = transfer->start = now;
|
||||
transfer->backoff = 0;
|
||||
transfer->lastack = block + 1;
|
||||
|
||||
/* We have no easy function from block no. to file offset when
|
||||
expanding line breaks in netascii mode, so we update the offset here
|
||||
as each block is acknowledged. This explains why the window size must be
|
||||
one for a netascii transfer; to avoid the block no. doing anything
|
||||
other than incrementing by one. */
|
||||
if (transfer->netascii && block != 0)
|
||||
transfer->offset += transfer->blocksize - transfer->expansion;
|
||||
}
|
||||
}
|
||||
else if (ntohs(mess->op) == OP_ERR)
|
||||
{
|
||||
@@ -720,8 +796,7 @@ static void handle_tftp(time_t now, struct tftp_transfer *transfer, ssize_t len)
|
||||
daemon->addrbuff);
|
||||
|
||||
/* Got err, ensure we take abort */
|
||||
transfer->timeout = now;
|
||||
transfer->backoff = 100;
|
||||
transfer->start = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -742,26 +817,35 @@ 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;
|
||||
}
|
||||
|
||||
/* If we don't do anything, don't write the the input/ouptut
|
||||
buffer. This allows us to pass in safe read-only strings constants. */
|
||||
static void sanitise(char *buf)
|
||||
{
|
||||
unsigned char *q, *r;
|
||||
|
||||
for (q = r = (unsigned char *)buf; *r; r++)
|
||||
if (isprint((int)*r))
|
||||
*(q++) = *r;
|
||||
*q = 0;
|
||||
|
||||
{
|
||||
if (q != r)
|
||||
*q = *r;
|
||||
q++;
|
||||
}
|
||||
|
||||
if (q != r)
|
||||
*q = 0;
|
||||
}
|
||||
|
||||
#define MAXMESSAGE 500 /* limit to make packet < 512 bytes and definitely smaller than buffer */
|
||||
@@ -772,7 +856,10 @@ static ssize_t tftp_err(int err, char *packet, char *message, char *file, char *
|
||||
char message[];
|
||||
} *mess = (struct errmess *)packet;
|
||||
ssize_t len, ret = 4;
|
||||
|
||||
|
||||
/* we overwrote the buffer... */
|
||||
daemon->srv_save = NULL;
|
||||
|
||||
memset(packet, 0, daemon->packet_buff_sz);
|
||||
if (file)
|
||||
sanitise(file);
|
||||
@@ -800,7 +887,10 @@ static ssize_t tftp_err_oops(char *packet, const char *file)
|
||||
static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
|
||||
{
|
||||
memset(packet, 0, daemon->packet_buff_sz);
|
||||
|
||||
|
||||
/* we overwrote the buffer... */
|
||||
daemon->srv_save = NULL;
|
||||
|
||||
if (transfer->block == 0)
|
||||
{
|
||||
/* send OACK */
|
||||
@@ -822,7 +912,17 @@ static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
|
||||
p += (sprintf(p,"tsize") + 1);
|
||||
p += (sprintf(p, "%u", (unsigned int)transfer->file->size) + 1);
|
||||
}
|
||||
|
||||
if (transfer->opt_timeout)
|
||||
{
|
||||
p += (sprintf(p,"timeout") + 1);
|
||||
p += (sprintf(p, "%u", transfer->timeout) + 1);
|
||||
}
|
||||
if (transfer->opt_windowsize)
|
||||
{
|
||||
p += (sprintf(p,"windowsize") + 1);
|
||||
p += (sprintf(p, "%u", (unsigned int)transfer->windowsize) + 1);
|
||||
}
|
||||
|
||||
return p - packet;
|
||||
}
|
||||
else
|
||||
@@ -833,31 +933,35 @@ static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
|
||||
unsigned char data[];
|
||||
} *mess = (struct datamess *)packet;
|
||||
|
||||
size_t size = transfer->file->size - transfer->offset;
|
||||
size_t size;
|
||||
|
||||
if (!transfer->netascii)
|
||||
transfer->offset = (transfer->block - 1) * transfer->blocksize;
|
||||
|
||||
if (transfer->offset > transfer->file->size)
|
||||
return 0; /* finished */
|
||||
|
||||
if (size > transfer->blocksize)
|
||||
if ((size = transfer->file->size - transfer->offset) > transfer->blocksize)
|
||||
size = transfer->blocksize;
|
||||
|
||||
mess->op = htons(OP_DATA);
|
||||
mess->block = htons((unsigned short)(transfer->block));
|
||||
|
||||
if (lseek(transfer->file->fd, transfer->offset, SEEK_SET) == (off_t)-1 ||
|
||||
!read_write(transfer->file->fd, mess->data, size, RW_READ))
|
||||
|
||||
if (size != 0 &&
|
||||
(lseek(transfer->file->fd, transfer->offset, SEEK_SET) == (off_t)-1 ||
|
||||
!read_write(transfer->file->fd, mess->data, size, RW_READ)))
|
||||
return -1;
|
||||
|
||||
transfer->expansion = 0;
|
||||
|
||||
/* Map '\n' to CR-LF in netascii mode */
|
||||
if (transfer->netascii)
|
||||
{
|
||||
size_t i;
|
||||
int newcarrylf;
|
||||
|
||||
|
||||
transfer->expansion = 0;
|
||||
|
||||
for (i = 0, newcarrylf = 0; i < size; i++)
|
||||
if (mess->data[i] == '\n' && ( i != 0 || !transfer->carrylf))
|
||||
if (mess->data[i] == '\n' && (i != 0 || !transfer->carrylf))
|
||||
{
|
||||
transfer->expansion++;
|
||||
|
||||
@@ -872,8 +976,8 @@ static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
transfer->carrylf = newcarrylf;
|
||||
|
||||
}
|
||||
|
||||
return size + 4;
|
||||
|
||||
+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
|
||||
@@ -213,6 +213,11 @@ static int ubus_handle_set_connmark_allowlist(struct ubus_context *ctx, struct u
|
||||
size_t num_patterns = 0;
|
||||
struct blob_attr *tb[policy_len];
|
||||
struct blob_attr *attr;
|
||||
|
||||
(void)ctx;
|
||||
(void)obj;
|
||||
(void)req;
|
||||
(void)method;
|
||||
|
||||
if (blobmsg_parse(policy, policy_len, tb, blob_data(msg), blob_len(msg)))
|
||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
+48
-29
@@ -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
|
||||
@@ -41,7 +41,7 @@ static u32 in[12];
|
||||
static u32 out[8];
|
||||
static int outleft = 0;
|
||||
|
||||
void rand_init()
|
||||
void rand_init(void)
|
||||
{
|
||||
int fd = open(RANDFILE, O_RDONLY);
|
||||
|
||||
@@ -426,7 +426,7 @@ int hostname_order(const char *a, const char *b)
|
||||
|
||||
int hostname_isequal(const char *a, const char *b)
|
||||
{
|
||||
return hostname_order(a, b) == 0;
|
||||
return strlen(a) == strlen(b) && hostname_order(a, b) == 0;
|
||||
}
|
||||
|
||||
/* is b equal to or a subdomain of a return 2 for equal, 1 for subdomain */
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -810,11 +815,25 @@ void close_fds(long max_fd, int spare1, int spare2, int spare3)
|
||||
{
|
||||
/* On Linux, use the /proc/ filesystem to find which files
|
||||
are actually open, rather than iterate over the whole space,
|
||||
for efficiency reasons. If this fails we drop back to the dumb code. */
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
for efficiency reasons.
|
||||
|
||||
On *BSD, the same facility is found at /dev/fd.
|
||||
|
||||
If this fails we drop back to the dumb code.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
#define FDESCFS "/proc/self/fd"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_BSD_NETWORK
|
||||
#define FDESCFS "/dev/fd"
|
||||
#endif
|
||||
|
||||
#ifdef FDESCFS
|
||||
DIR *d;
|
||||
|
||||
if ((d = opendir("/proc/self/fd")))
|
||||
if ((d = opendir(FDESCFS)))
|
||||
{
|
||||
struct dirent *de;
|
||||
|
||||
|
||||
Submodule
+1
Submodule submodules/dnsmasq-debian added at 9fe6b08a52.
Reference in new issue
Block a user