Compare commits
86 Commits
v2.85
...
v2.86test7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a1729deed3 | ||
|
|
fc64b97cd5 | ||
|
|
daddc8cb80 | ||
|
|
527c3c7d0d | ||
|
|
fcb4dcaf7c | ||
|
|
3ca4995d34 | ||
|
|
d387f8f06c | ||
|
|
ea43234c86 | ||
|
|
867e56a45e | ||
|
|
a163c63787 | ||
|
|
8389b943d3 | ||
|
|
f2266d9678 | ||
|
|
56bd806978 | ||
|
|
ac7eeea44d | ||
|
|
b741059549 | ||
|
|
cbd984287f | ||
|
|
32e15c3f45 | ||
|
|
f0dc324e35 | ||
|
|
f83c6cf51a | ||
|
|
c068b3ae2f | ||
|
|
adf9dec1e6 | ||
|
|
767d9cbd96 | ||
|
|
e7ccd95c04 | ||
|
|
719f79a8fd | ||
|
|
96f6444958 | ||
|
|
df25f204ba | ||
|
|
8acdc3ede7 | ||
|
|
857b445522 | ||
|
|
5bcca1219a | ||
|
|
4558c26fcd | ||
|
|
a92c6d77dc | ||
|
|
0c95a5ff53 | ||
|
|
cb6d06bb54 | ||
|
|
3ef955c85a | ||
|
|
5e95c16c32 | ||
|
|
4205e2ebcf | ||
|
|
9d806c51c2 | ||
|
|
a38bb31727 | ||
|
|
8a1ef367e2 | ||
|
|
1291865c92 | ||
|
|
a9ebbee7b6 | ||
|
|
06df5ad7d0 | ||
|
|
66b863c989 | ||
|
|
c9efe8e5e1 | ||
|
|
d515223bb5 | ||
|
|
b908f4334b | ||
|
|
6261aba026 | ||
|
|
85bc7534da | ||
|
|
1b30fd1732 | ||
|
|
8c9196bff8 | ||
|
|
b1daf44954 | ||
|
|
11c52d032b | ||
|
|
be291d979d | ||
|
|
6d1edd8d32 | ||
|
|
25ff956c7d | ||
|
|
38179500f8 | ||
|
|
5f7be5f0d6 | ||
|
|
627056febb | ||
|
|
cbd76447fd | ||
|
|
a60a233329 | ||
|
|
a0a3b8ad3e | ||
|
|
d0ae3f5a4d | ||
|
|
6860cf932b | ||
|
|
0276e0805b | ||
|
|
06ff3d8a26 | ||
|
|
1a3b69aa56 | ||
|
|
8237d06ab7 | ||
|
|
1c9f136b57 | ||
|
|
5ab7e4a475 | ||
|
|
3236f358f8 | ||
|
|
4a6550d69a | ||
|
|
ff523d0c67 | ||
|
|
3c93e8eb41 | ||
|
|
88a482fdb9 | ||
|
|
12a9aa7c62 | ||
|
|
50ccf9c585 | ||
|
|
d100eb05a3 | ||
|
|
10d8b5f001 | ||
|
|
ffd3ceb856 | ||
|
|
d942aa9321 | ||
|
|
6469fefe89 | ||
|
|
b082842ee7 | ||
|
|
3573ca0eec | ||
|
|
ad90eb075d | ||
|
|
d55e2d086d | ||
|
|
fe9c966a49 |
89
CHANGELOG
89
CHANGELOG
@@ -1,3 +1,92 @@
|
||||
version 2.86
|
||||
Handle DHCPREBIND requests in the DHCPv6 server code.
|
||||
Thanks to Aichun Li for spotting this omission, and the initial
|
||||
patch.
|
||||
|
||||
Fix bug which caused dnsmasq to lose track of processes forked
|
||||
to handle TCP DNS connections under heavy load. The code
|
||||
checked that at least one free process table slot was
|
||||
available before listening on TCP sockets, but didn't take
|
||||
into account that more than one TCP connection could
|
||||
arrive, so that check was not sufficient to ensure that
|
||||
there would be slots for all new processes. It compounded
|
||||
this error by silently failing to store the process when
|
||||
it did run out of slots. Even when this bug is triggered,
|
||||
all the right things happen, and answers are still returned.
|
||||
Only under very exceptional circumstances, does the bug
|
||||
manifest itself: see
|
||||
https://lists.thekelleys.org.uk/pipermail/dnsmasq-discuss/2021q2/014976.html
|
||||
Thanks to Tijs Van Buggenhout for finding the conditions under
|
||||
which the bug manifests itself, and then working out
|
||||
exactly what was going on.
|
||||
|
||||
Major rewrite of the DNS server and domain handling code.
|
||||
This should be largely transparent, but it drastically
|
||||
improves performance and reduces memory foot-print when
|
||||
configuring large numbers domains of the form
|
||||
local=/adserver.com/
|
||||
or
|
||||
local=/adserver.com/#
|
||||
Lookup times now grow as log-to-base-2 of the number of domains,
|
||||
rather than greater than linearly, as before.
|
||||
The change makes multiple addresses associated with a domain work
|
||||
address=/example.com/1.2.3.4
|
||||
address=/example.com/5.6.7.8
|
||||
It also handles multiple upstream servers for a domain better; using
|
||||
the same try/retry algorithms as non domain-specific servers. This
|
||||
also applies to DNSSEC-generated queries.
|
||||
Finally, some of the oldest and gnarliest code in dnsmasq has had
|
||||
a significant clean-up. It's far from perfect, but it _is_ better.
|
||||
|
||||
Revise resource handling for number of concurrent DNS queries. This
|
||||
used to have a global limit, but that has a problem when using
|
||||
different servers for different upstream domains. Queries which are
|
||||
routed by domain to an upstream server which is not responding will
|
||||
build up and trigger the limit, which breaks DNS service for
|
||||
all other domains which could be handled by other servers. The
|
||||
change is to make the limit per server-group, where a server group
|
||||
is the set of servers configured for a particular domain. In the
|
||||
common case, where only default servers are declared, there is
|
||||
no effective change.
|
||||
|
||||
Improve efficiency of DNSSEC. The sharing point for DNSSEC RR data
|
||||
used to be when it entered the cache, having been validated. After
|
||||
that queries requiring the KEY or DS records would share the cached
|
||||
values. There is a common case in dual-stack hosts that queries for
|
||||
A and AAAA records for the same domain are made simultaneously.
|
||||
If required keys were not in the cache, this would result in two
|
||||
requests being sent upstream for the same key data (and all the
|
||||
subsequent chain-of-trust queries.) Now we combine these requests
|
||||
and elide the duplicates, resulting in fewer queries upstream
|
||||
and better performance. To keep a better handle on what's
|
||||
going on, the "extra" logging mode has been modified to associate
|
||||
queries and answers for DNSSEC queries in the same way as ordinary
|
||||
queries. The requesting address and port have been removed from
|
||||
DNSSEC logging lines, since this is no longer strictly defined.
|
||||
|
||||
Connection track mark based DNS query filtering. Thanks to
|
||||
Etan Kissling for implementing this It extends query filtering
|
||||
support beyond what is currently possible
|
||||
with the `--ipset` configuration option, by adding support for:
|
||||
1) Specifying allowlists on a per-client basis, based on their
|
||||
associated Linux connection track mark.
|
||||
2) Dynamic configuration of allowlists via Ubus.
|
||||
3) Reporting when a DNS query resolves or is rejected via Ubus.
|
||||
4) DNS name patterns containing wildcards.
|
||||
Disallowed queries are not forwarded; they are rejected
|
||||
with a REFUSED error code.
|
||||
|
||||
Allow smaller than 64 prefix lengths in synth-domain, with caveats.
|
||||
--synth-domain=1234:4567::/56,example.com is now valid.
|
||||
|
||||
Make domains generated by --synth-domain appear in replies
|
||||
when in authoritative mode.
|
||||
|
||||
Ensure CAP_NET_ADMIN capability is available when
|
||||
conntrack is configured. Thanks to Yick Xie for spotting
|
||||
the lack of this.
|
||||
|
||||
|
||||
version 2.85
|
||||
Fix problem with DNS retries in 2.83/2.84.
|
||||
The new logic in 2.83/2.84 which merges distinct requests
|
||||
|
||||
2
FAQ
2
FAQ
@@ -236,7 +236,7 @@ Q: What network types are supported by the DHCP server?
|
||||
A: Ethernet (and 802.11 wireless) are supported on all platforms. On
|
||||
Linux all network types (including FireWire) are supported.
|
||||
|
||||
Q: What are these strange "bind-interface" and "bind-dynamic" options?
|
||||
Q: What are these strange "bind-interfaces" and "bind-dynamic" options?
|
||||
|
||||
A: Dnsmasq from v2.63 can operate in one of three different "networking
|
||||
modes". This is unfortunate as it requires users configuring dnsmasq
|
||||
|
||||
4
Makefile
4
Makefile
@@ -79,10 +79,10 @@ copts_conf = .copts_$(sum)
|
||||
objs = cache.o rfc1035.o util.o option.o forward.o network.o \
|
||||
dnsmasq.o dhcp.o lease.o rfc2131.o netlink.o dbus.o bpf.o \
|
||||
helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o \
|
||||
dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o \
|
||||
dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o pattern.o \
|
||||
domain.o dnssec.o blockdata.o tables.o loop.o inotify.o \
|
||||
poll.o rrfilter.o edns0.o arp.o crypto.o dump.o ubus.o \
|
||||
metrics.o hash_questions.o
|
||||
metrics.o hash-questions.o domain-match.o
|
||||
|
||||
hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \
|
||||
dns-protocol.h radv-protocol.h ip6addr.h metrics.h
|
||||
|
||||
@@ -11,7 +11,8 @@ LOCAL_SRC_FILES := bpf.c cache.c dbus.c dhcp.c dnsmasq.c \
|
||||
radv.c slaac.c auth.c ipset.c domain.c \
|
||||
dnssec.c dnssec-openssl.c blockdata.c tables.c \
|
||||
loop.c inotify.c poll.c rrfilter.c edns0.c arp.c \
|
||||
crypto.c dump.c ubus.c metrics.c hash_questions.c
|
||||
crypto.c dump.c ubus.c metrics.c hash-questions.c \
|
||||
domain-match.c
|
||||
|
||||
LOCAL_MODULE := dnsmasq
|
||||
|
||||
|
||||
@@ -153,7 +153,11 @@ int main(int argc, char **argv)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
lease.s_addr = inet_addr(argv[1]);
|
||||
if (inet_pton(AF_INET, argv[1], &lease) < 1)
|
||||
{
|
||||
fprintf(stderr, "invalid address: %s\n", argv[1]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
memset(&packet, 0, sizeof(packet));
|
||||
|
||||
@@ -176,8 +180,8 @@ int main(int argc, char **argv)
|
||||
|
||||
*(p++) = OPTION_END;
|
||||
|
||||
dest.sin_family = AF_INET;
|
||||
dest.sin_addr.s_addr = inet_addr("127.0.0.1");
|
||||
dest.sin_family = AF_INET;
|
||||
(void)inet_pton(AF_INET, "127.0.0.1", &dest.sin_addr);
|
||||
dest.sin_port = ntohs(DHCP_SERVER_PORT);
|
||||
|
||||
if (sendto(fd, &packet, sizeof(packet), 0,
|
||||
|
||||
@@ -288,13 +288,12 @@ int main(int argc, char **argv)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (inet_addr(argv[2]) == INADDR_NONE)
|
||||
if (inet_pton(AF_INET, argv[2], &lease.s_addr) < 1)
|
||||
{
|
||||
perror("invalid ip address");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
lease.s_addr = inet_addr(argv[2]);
|
||||
server = find_interface(lease, nl, if_nametoindex(argv[1]), fd, &ifr);
|
||||
|
||||
memset(&packet, 0, sizeof(packet));
|
||||
|
||||
8
debian/changelog
vendored
8
debian/changelog
vendored
@@ -1,3 +1,9 @@
|
||||
dnsmasq (2.86-1) unstable; urgency=low
|
||||
|
||||
* Fix debian/changelog format error. (closes: #986626)
|
||||
|
||||
-- Simon Kelley <simon@thekelleys.org.uk> Thu, 08 Apr 2021 22:39:00 +0100
|
||||
|
||||
dnsmasq (2.85-1) unstable; urgency=low
|
||||
|
||||
* New upstream.
|
||||
@@ -1075,7 +1081,7 @@ dnsmasq (2.6-2) unstable; urgency=low
|
||||
* Added note about the --bind-interfaces option to
|
||||
readme.Debian (closes: #241700)
|
||||
|
||||
-- Simon Kelley <simon@thekelleys.org.uk> Tues, 13 Apr 2004 18:37:55 +0000
|
||||
-- Simon Kelley <simon@thekelleys.org.uk> Tue, 13 Apr 2004 18:37:55 +0000
|
||||
|
||||
dnsmasq (2.6-1) unstable; urgency=low
|
||||
|
||||
|
||||
@@ -327,8 +327,8 @@ are re-written. So
|
||||
maps 192.168.0.10->192.168.0.40 to 10.0.0.10->10.0.0.40
|
||||
.TP
|
||||
.B \-B, --bogus-nxdomain=<ipaddr>[/prefix]
|
||||
Transform replies which contain the IP specified address or subnet into "No such
|
||||
domain" replies. This is intended to counteract a devious move made by
|
||||
Transform replies which contain the specified address or subnet into "No such
|
||||
domain" replies. IPv4 and IPv6 are supported. This is intended to counteract a devious move made by
|
||||
Verisign in September 2003 when they started returning the address of
|
||||
an advertising web page in response to queries for unregistered names,
|
||||
instead of the correct NXDOMAIN response. This option tells dnsmasq to
|
||||
@@ -336,7 +336,7 @@ fake the correct response when it sees this behaviour. As at Sept 2003
|
||||
the IP address being returned by Verisign is 64.94.110.11
|
||||
.TP
|
||||
.B --ignore-address=<ipaddr>[/prefix]
|
||||
Ignore replies to A-record queries which include the specified address or subnet.
|
||||
Ignore replies to A or AAAA queries which include the specified address or subnet.
|
||||
No error is generated, dnsmasq simply continues to listen for another reply.
|
||||
This is useful to defeat blocking strategies which rely on quickly supplying a
|
||||
forged answer to a DNS request for certain domain, before the correct answer can arrive.
|
||||
@@ -371,7 +371,10 @@ provides service at that name, rather than the default which is
|
||||
.TP
|
||||
.B --enable-ubus[=<service-name>]
|
||||
Enable dnsmasq UBus interface. It sends notifications via UBus on
|
||||
DHCPACK and DHCPRELEASE events. Furthermore it offers metrics.
|
||||
DHCPACK and DHCPRELEASE events. Furthermore it offers metrics
|
||||
and allows configuration of Linux connection track mark based filtering.
|
||||
When DNS query filtering based on Linux connection track marks is enabled
|
||||
UBus notifications are generated for each resolved or filtered DNS query.
|
||||
Requires that dnsmasq has been built with UBus support. If the service
|
||||
name is given, dnsmasq provides service at that namespace, rather than
|
||||
the default which is
|
||||
@@ -457,13 +460,22 @@ repeated domain or ipaddr parts as required.
|
||||
More specific domains take precedence over less specific domains, so:
|
||||
.B --server=/google.com/1.2.3.4
|
||||
.B --server=/www.google.com/2.3.4.5
|
||||
will send queries for *.google.com to 1.2.3.4, except *www.google.com,
|
||||
which will go to 2.3.4.5
|
||||
will send queries for google.com and gmail.google.com to 1.2.3.4, but www.google.com
|
||||
will go to 2.3.4.5
|
||||
|
||||
Matching of domains is normally done on complete labels, so /google.com/ matches google.com and www.google.com
|
||||
but NOT supergoogle.com. This can be overridden with a * at the start of a pattern only: /*google.com/
|
||||
will match google.com and www.google.com AND supergoogle.com. The non-wildcard form has priority, so
|
||||
if /google.com/ and /*google.com/ are both specified then google.com and www.google.com will match /google.com/
|
||||
and /*google.com/ will only match supergoogle.com.
|
||||
|
||||
For historical reasons, the pattern /.google.com/ is equivalent to /google.com/ if you wish to match any subdomain
|
||||
of google.com but NOT google.com itself, use /*.google.com/
|
||||
|
||||
The special server address '#' means, "use the standard servers", so
|
||||
.B --server=/google.com/1.2.3.4
|
||||
.B --server=/www.google.com/#
|
||||
will send queries for *.google.com to 1.2.3.4, except *www.google.com which will
|
||||
will send queries for google.com and its subdomains to 1.2.3.4, except www.google.com (and its subdomains) which will
|
||||
be forwarded as usual.
|
||||
|
||||
Also permitted is a -S
|
||||
@@ -510,7 +522,7 @@ To include multiple IP addresses for a single query, use
|
||||
Note that /etc/hosts and DHCP leases override this for individual
|
||||
names. A common use of this is to redirect the entire doubleclick.net
|
||||
domain to some friendly local web server to avoid banner ads. The
|
||||
domain specification works in the same was as for \fB--server\fP, with
|
||||
domain specification works in the same way as for \fB--server\fP, with
|
||||
the additional facility that \fB/#/\fP matches any domain. Thus
|
||||
\fB--address=/#/1.2.3.4\fP will always return \fB1.2.3.4\fP for any
|
||||
query not answered from \fB/etc/hosts\fP or DHCP and not sent to an
|
||||
@@ -536,6 +548,32 @@ These IP sets must already exist. See
|
||||
.BR ipset (8)
|
||||
for more details.
|
||||
.TP
|
||||
.B --connmark-allowlist-enable[=<mask>]
|
||||
Enables filtering of incoming DNS queries with associated Linux connection track marks
|
||||
according to individual allowlists configured via a series of \fB--connmark-allowlist\fP
|
||||
options. Disallowed queries are not forwarded; they are rejected with a REFUSED error code.
|
||||
DNS queries are only allowed if they do not have an associated Linux connection
|
||||
track mark, or if the queried domains match the configured DNS patterns for the
|
||||
associated Linux connection track mark. If no allowlist is configured for a
|
||||
Linux connection track mark, all DNS queries associated with that mark are rejected.
|
||||
If a mask is specified, Linux connection track marks are first bitwise ANDed
|
||||
with the given mask before being processed.
|
||||
.TP
|
||||
.B --connmark-allowlist=<connmark>[/<mask>][,<pattern>[/<pattern>...]]
|
||||
Configures the DNS patterns that are allowed in DNS queries associated with
|
||||
the given Linux connection track mark.
|
||||
If a mask is specified, Linux connection track marks are first bitwise ANDed
|
||||
with the given mask before they are compared to the given connection track mark.
|
||||
Patterns follow the syntax of DNS names, but additionally allow the wildcard
|
||||
character "*" to be used up to twice per label to match 0 or more characters
|
||||
within that label. Note that the wildcard never matches a dot (e.g., "*.example.com"
|
||||
matches "api.example.com" but not "api.us.example.com"). Patterns must be
|
||||
fully qualified, i.e., consist of at least two labels. The final label must not be
|
||||
fully numeric, and must not be the "local" pseudo-TLD. A pattern must end with at least
|
||||
two literal (non-wildcard) labels.
|
||||
Instead of a pattern, "*" can be specified to disable allowlist filtering
|
||||
for a given Linux connection track mark entirely.
|
||||
.TP
|
||||
.B \-m, --mx-host=<mx name>[[,<hostname>],<preference>]
|
||||
Return an MX record named <mx name> pointing to the given hostname (if
|
||||
given), or
|
||||
@@ -667,7 +705,8 @@ configured a zero is added in front of the label. ::1 becomes 0--1.
|
||||
V4 mapped IPv6 addresses, which have a representation like ::ffff:1.2.3.4 are handled specially, and become like 0--ffff-1-2-3-4
|
||||
|
||||
The address range can be of the form
|
||||
<ip address>,<ip address> or <ip address>/<netmask> in both forms of the option.
|
||||
<start address>,<end address> or <ip address>/<prefix-length> in both forms of the option. For IPv6 the start and end addresses
|
||||
must fall in the same /64 network, or prefix-length must be greater than or equal to 64 except that shorter prefix lengths than 64 are allowed only if non-sequential names are in use.
|
||||
.TP
|
||||
.B --dumpfile=<path/to/file>
|
||||
Specify the location of a pcap-format file which dnsmasq uses to dump copies of network packets for debugging purposes. If the file exists when dnsmasq starts, it is not deleted; new packets are added to the end.
|
||||
@@ -711,7 +750,13 @@ will add the /24 and /96 subnets of the requestor for IPv4 and IPv6 requestors,
|
||||
will add 1.2.3.0/24 for IPv4 requestors and ::/0 for IPv6 requestors.
|
||||
.B --add-subnet=1.2.3.4/24,1.2.3.4/24
|
||||
will add 1.2.3.0/24 for both IPv4 and IPv6 requestors.
|
||||
|
||||
.TP
|
||||
.B --umbrella[=deviceid:<deviceid>[,orgid:<orgid>]]
|
||||
Embeds the requestor's IP address in DNS queries forwarded upstream.
|
||||
If device id or organization id are specified, the information is
|
||||
included in the forwarded queries and may be able to be used in
|
||||
filtering policies and reporting. The order of the deviceid and orgid
|
||||
attributes is irrelevant, but must be separated by a comma.
|
||||
.TP
|
||||
.B \-c, --cache-size=<cachesize>
|
||||
Set the size of dnsmasq's cache. The default is 150 names. Setting the cache size to zero disables caching. Note: huge cache size impacts performance.
|
||||
@@ -725,7 +770,8 @@ identical queries without forwarding them again.
|
||||
Set the maximum number of concurrent DNS queries. The default value is
|
||||
150, which should be fine for most setups. The only known situation
|
||||
where this needs to be increased is when using web-server log file
|
||||
resolvers, which can generate large numbers of concurrent queries.
|
||||
resolvers, which can generate large numbers of concurrent queries. This
|
||||
parameter actually controls the number of concurrent queries per server group, where a server group is the set of server(s) associated with a single domain. So if a domain has it's own server via --server=/example.com/1.2.3.4 and 1.2.3.4 is not responding, but queries for *.example.com cannot go elsewhere, then other queries will not be affected. On configurations with many such server groups and tight resources, this value may need to be reduced.
|
||||
.TP
|
||||
.B --dnssec
|
||||
Validate DNS replies and cache DNSSEC data. When forwarding DNS queries, dnsmasq requests the
|
||||
@@ -869,7 +915,7 @@ in
|
||||
.B --dhcp-host
|
||||
options. If the lease time is given, then leases
|
||||
will be given for that length of time. The lease time is in seconds,
|
||||
or minutes (eg 45m) or hours (eg 1h) or "infinite". If not given,
|
||||
or minutes (eg 45m) or hours (eg 1h) or days (2d) or weeks (1w) or "infinite". If not given,
|
||||
the default lease time is one hour for IPv4 and one day for IPv6. The
|
||||
minimum lease time is two minutes. For IPv6 ranges, the lease time
|
||||
maybe "deprecated"; this sets the preferred lifetime sent in a DHCP
|
||||
@@ -1517,7 +1563,6 @@ instance
|
||||
will enable dnsmasq to also provide proxy PXE service to those PXE clients with
|
||||
.I HW-Client
|
||||
in as their identifier.
|
||||
>>>>>>> 907def3... pxe: support pxe clients with custom vendor-class
|
||||
.TP
|
||||
.B \-X, --dhcp-lease-max=<number>
|
||||
Limits dnsmasq to the specified maximum number of DHCP leases. The
|
||||
@@ -1568,10 +1613,11 @@ tried. This flag disables this check. Use with caution.
|
||||
Extra logging for DHCP: log all the options sent to DHCP clients and
|
||||
the tags used to determine them.
|
||||
.TP
|
||||
.B --quiet-dhcp, --quiet-dhcp6, --quiet-ra
|
||||
.B --quiet-dhcp, --quiet-dhcp6, --quiet-ra, --quiet-tftp
|
||||
Suppress logging of the routine operation of these protocols. Errors and
|
||||
problems will still be logged. \fB--quiet-dhcp\fP and quiet-dhcp6 are
|
||||
over-ridden by \fB--log-dhcp\fP.
|
||||
problems will still be logged. \fB--quiet-tftp\fP does not consider file not
|
||||
found to be an error. \fB--quiet-dhcp\fP and quiet-dhcp6 are over-ridden by
|
||||
\fB--log-dhcp\fP.
|
||||
.TP
|
||||
.B \-l, --dhcp-leasefile=<path>
|
||||
Use the specified file to store DHCP lease information.
|
||||
@@ -2373,6 +2419,10 @@ following data is used to populate the authoritative zone.
|
||||
.B --mx-host, --srv-host, --dns-rr, --txt-record, --naptr-record, --caa-record,
|
||||
as long as the record names are in the authoritative domain.
|
||||
.PP
|
||||
.B --synth-domain
|
||||
as long as the domain is in the authoritative zone and, for
|
||||
reverse (PTR) queries, the address is in the relevant subnet.
|
||||
.PP
|
||||
.B --cname
|
||||
as long as the record name is in the authoritative domain. If the
|
||||
target of the CNAME is unqualified, then it is qualified with the
|
||||
|
||||
42
src/auth.c
42
src/auth.c
@@ -105,7 +105,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
int nameoffset, axfroffset = 0;
|
||||
int q, anscount = 0, authcount = 0;
|
||||
struct crec *crecp;
|
||||
int auth = !local_query, trunc = 0, nxdomain = 1, soa = 0, ns = 0, axfr = 0;
|
||||
int auth = !local_query, trunc = 0, nxdomain = 1, soa = 0, ns = 0, axfr = 0, out_of_zone = 0;
|
||||
struct auth_zone *zone = NULL;
|
||||
struct addrlist *subnet = NULL;
|
||||
char *cut;
|
||||
@@ -146,6 +146,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
if (qclass != C_IN)
|
||||
{
|
||||
auth = 0;
|
||||
out_of_zone = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -159,6 +160,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
|
||||
if (!zone)
|
||||
{
|
||||
out_of_zone = 1;
|
||||
auth = 0;
|
||||
continue;
|
||||
}
|
||||
@@ -253,6 +255,17 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
|
||||
} while ((crecp = cache_find_by_addr(crecp, &addr, now, flag)));
|
||||
|
||||
if (!found && is_rev_synth(flag, &addr, name) && (local_query || in_zone(zone, name, NULL)))
|
||||
{
|
||||
log_query(F_CONFIG | F_REVERSE | flag, name, &addr, NULL);
|
||||
found = 1;
|
||||
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
daemon->auth_ttl, NULL,
|
||||
T_PTR, C_IN, "d", name))
|
||||
anscount++;
|
||||
}
|
||||
|
||||
if (found)
|
||||
nxdomain = 0;
|
||||
else
|
||||
@@ -273,6 +286,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
|
||||
if (!zone)
|
||||
{
|
||||
out_of_zone = 1;
|
||||
auth = 0;
|
||||
continue;
|
||||
}
|
||||
@@ -400,6 +414,17 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
anscount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found && is_name_synthetic(flag, name, &addr) )
|
||||
{
|
||||
found = 1;
|
||||
nxdomain = 0;
|
||||
|
||||
log_query(F_FORWARD | F_CONFIG | flag, name, &addr, NULL);
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
daemon->auth_ttl, NULL, qtype, C_IN, qtype == T_A ? "4" : "6", &addr))
|
||||
anscount++;
|
||||
}
|
||||
|
||||
if (!cut)
|
||||
{
|
||||
@@ -855,10 +880,22 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
SET_RCODE(header, NXDOMAIN);
|
||||
else
|
||||
SET_RCODE(header, NOERROR); /* no error */
|
||||
|
||||
header->ancount = htons(anscount);
|
||||
header->nscount = htons(authcount);
|
||||
header->arcount = htons(0);
|
||||
|
||||
if (!local_query && out_of_zone)
|
||||
{
|
||||
SET_RCODE(header, REFUSED);
|
||||
header->ancount = htons(0);
|
||||
header->nscount = htons(0);
|
||||
addr.log.rcode = REFUSED;
|
||||
addr.log.ede = EDE_NOT_AUTH;
|
||||
log_query(F_UPSTREAM | F_RCODE, "error", &addr, NULL);
|
||||
return resize_packet(header, ansp - (unsigned char *)header, NULL, 0);
|
||||
}
|
||||
|
||||
/* Advertise our packet size limit in our reply */
|
||||
if (have_pseudoheader)
|
||||
return add_pseudoheader(header, ansp - (unsigned char *)header, (unsigned char *)limit, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0);
|
||||
@@ -867,6 +904,3 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -174,4 +174,3 @@ struct blockdata *blockdata_read(int fd, size_t len)
|
||||
{
|
||||
return blockdata_alloc_real(fd, NULL, len);
|
||||
}
|
||||
|
||||
|
||||
103
src/cache.c
103
src/cache.c
@@ -1602,21 +1602,18 @@ int cache_make_stat(struct txt_record *t)
|
||||
case TXT_STAT_SERVERS:
|
||||
/* sum counts from different records for same server */
|
||||
for (serv = daemon->servers; serv; serv = serv->next)
|
||||
serv->flags &= ~SERV_COUNTED;
|
||||
serv->flags &= ~SERV_MARK;
|
||||
|
||||
for (serv = daemon->servers; serv; serv = serv->next)
|
||||
if (!(serv->flags &
|
||||
(SERV_NO_ADDR | SERV_LITERAL_ADDRESS | SERV_COUNTED | SERV_USE_RESOLV | SERV_NO_REBIND)))
|
||||
if (!(serv->flags & SERV_MARK))
|
||||
{
|
||||
char *new, *lenp;
|
||||
int port, newlen, bytes_avail, bytes_needed;
|
||||
unsigned int queries = 0, failed_queries = 0;
|
||||
for (serv1 = serv; serv1; serv1 = serv1->next)
|
||||
if (!(serv1->flags &
|
||||
(SERV_NO_ADDR | SERV_LITERAL_ADDRESS | SERV_COUNTED | SERV_USE_RESOLV | SERV_NO_REBIND)) &&
|
||||
sockaddr_isequal(&serv->addr, &serv1->addr))
|
||||
if (!(serv1->flags & SERV_MARK) && sockaddr_isequal(&serv->addr, &serv1->addr))
|
||||
{
|
||||
serv1->flags |= SERV_COUNTED;
|
||||
serv1->flags |= SERV_MARK;
|
||||
queries += serv1->queries;
|
||||
failed_queries += serv1->failed_queries;
|
||||
}
|
||||
@@ -1644,6 +1641,7 @@ int cache_make_stat(struct txt_record *t)
|
||||
}
|
||||
t->txt = (unsigned char *)buff;
|
||||
t->len = p - buff;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1686,27 +1684,24 @@ void dump_cache(time_t now)
|
||||
|
||||
/* sum counts from different records for same server */
|
||||
for (serv = daemon->servers; serv; serv = serv->next)
|
||||
serv->flags &= ~SERV_COUNTED;
|
||||
serv->flags &= ~SERV_MARK;
|
||||
|
||||
for (serv = daemon->servers; serv; serv = serv->next)
|
||||
if (!(serv->flags &
|
||||
(SERV_NO_ADDR | SERV_LITERAL_ADDRESS | SERV_COUNTED | SERV_USE_RESOLV | SERV_NO_REBIND)))
|
||||
if (!(serv->flags & SERV_MARK))
|
||||
{
|
||||
int port;
|
||||
unsigned int queries = 0, failed_queries = 0;
|
||||
for (serv1 = serv; serv1; serv1 = serv1->next)
|
||||
if (!(serv1->flags &
|
||||
(SERV_NO_ADDR | SERV_LITERAL_ADDRESS | SERV_COUNTED | SERV_USE_RESOLV | SERV_NO_REBIND)) &&
|
||||
sockaddr_isequal(&serv->addr, &serv1->addr))
|
||||
if (!(serv1->flags & SERV_MARK) && sockaddr_isequal(&serv->addr, &serv1->addr))
|
||||
{
|
||||
serv1->flags |= SERV_COUNTED;
|
||||
serv1->flags |= SERV_MARK;
|
||||
queries += serv1->queries;
|
||||
failed_queries += serv1->failed_queries;
|
||||
}
|
||||
port = prettyprint_addr(&serv->addr, daemon->addrbuff);
|
||||
my_syslog(LOG_INFO, _("server %s#%d: queries sent %u, retried or failed %u"), daemon->addrbuff, port, queries, failed_queries);
|
||||
}
|
||||
|
||||
|
||||
if (option_bool(OPT_DEBUG) || option_bool(OPT_LOG))
|
||||
{
|
||||
struct crec *cache ;
|
||||
@@ -1867,10 +1862,44 @@ char *querystr(char *desc, unsigned short type)
|
||||
return buff ? buff : "";
|
||||
}
|
||||
|
||||
static char *edestr(int ede)
|
||||
{
|
||||
switch (ede)
|
||||
{
|
||||
case EDE_OTHER: return "other";
|
||||
case EDE_USUPDNSKEY: return "unsupported DNSKEY algorithm";
|
||||
case EDE_USUPDS: return "unsupported DS digest";
|
||||
case EDE_STALE: return "stale answer";
|
||||
case EDE_FORGED: return "forged";
|
||||
case EDE_DNSSEC_IND: return "DNSSEC indeterminate";
|
||||
case EDE_DNSSEC_BOGUS: return "DNSSEC bogus";
|
||||
case EDE_SIG_EXP: return "DNSSEC signature expired";
|
||||
case EDE_SIG_NYV: return "DNSSEC sig not yet valid";
|
||||
case EDE_NO_DNSKEY: return "DNSKEY missing";
|
||||
case EDE_NO_RRSIG: return "RRSIG missing";
|
||||
case EDE_NO_ZONEKEY: return "no zone key bit set";
|
||||
case EDE_NO_NSEC: return "NSEC(3) missing";
|
||||
case EDE_CACHED_ERR: return "cached error";
|
||||
case EDE_NOT_READY: return "not ready";
|
||||
case EDE_BLOCKED: return "blocked";
|
||||
case EDE_CENSORED: return "censored";
|
||||
case EDE_FILTERED: return "filtered";
|
||||
case EDE_PROHIBITED: return "prohibited";
|
||||
case EDE_STALE_NXD: return "stale NXDOMAIN";
|
||||
case EDE_NOT_AUTH: return "not authoritative";
|
||||
case EDE_NOT_SUP: return "not supported";
|
||||
case EDE_NO_AUTH: return "no reachable authority";
|
||||
case EDE_NETERR: return "network error";
|
||||
case EDE_INVALID_DATA: return "invalid data";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg)
|
||||
{
|
||||
char *source, *dest = daemon->addrbuff;
|
||||
char *verb = "is";
|
||||
char *extra = "";
|
||||
|
||||
if (!option_bool(OPT_LOG))
|
||||
return;
|
||||
@@ -1885,14 +1914,20 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg)
|
||||
{
|
||||
unsigned int rcode = addr->log.rcode;
|
||||
|
||||
if (rcode == SERVFAIL)
|
||||
dest = "SERVFAIL";
|
||||
else if (rcode == REFUSED)
|
||||
dest = "REFUSED";
|
||||
else if (rcode == NOTIMP)
|
||||
dest = "not implemented";
|
||||
else
|
||||
sprintf(daemon->addrbuff, "%u", rcode);
|
||||
if (rcode == SERVFAIL)
|
||||
dest = "SERVFAIL";
|
||||
else if (rcode == REFUSED)
|
||||
dest = "REFUSED";
|
||||
else if (rcode == NOTIMP)
|
||||
dest = "not implemented";
|
||||
else
|
||||
sprintf(daemon->addrbuff, "%u", rcode);
|
||||
|
||||
if (addr->log.ede != EDE_UNSET)
|
||||
{
|
||||
extra = daemon->addrbuff;
|
||||
sprintf(extra, " (EDE: %s)", edestr(addr->log.ede));
|
||||
}
|
||||
}
|
||||
else
|
||||
inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
|
||||
@@ -1938,7 +1973,15 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg)
|
||||
else if (flags & F_UPSTREAM)
|
||||
source = "reply";
|
||||
else if (flags & F_SECSTAT)
|
||||
source = "validation";
|
||||
{
|
||||
if (addr && addr->log.ede != EDE_UNSET && option_bool(OPT_EXTRALOG))
|
||||
{
|
||||
extra = daemon->addrbuff;
|
||||
sprintf(extra, " (EDE: %s)", edestr(addr->log.ede));
|
||||
}
|
||||
source = "validation";
|
||||
dest = arg;
|
||||
}
|
||||
else if (flags & F_AUTH)
|
||||
source = "auth";
|
||||
else if (flags & F_SERVER)
|
||||
@@ -1971,14 +2014,14 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg)
|
||||
|
||||
if (option_bool(OPT_EXTRALOG))
|
||||
{
|
||||
int port = prettyprint_addr(daemon->log_source_addr, daemon->addrbuff2);
|
||||
if (flags & F_NOEXTRA)
|
||||
my_syslog(LOG_INFO, "* %s/%u %s %s %s %s", daemon->addrbuff2, port, source, name, verb, dest);
|
||||
my_syslog(LOG_INFO, "%u %s %s %s %s%s", daemon->log_display_id, source, name, verb, dest, extra);
|
||||
else
|
||||
my_syslog(LOG_INFO, "%u %s/%u %s %s %s %s", daemon->log_display_id, daemon->addrbuff2, port, source, name, verb, dest);
|
||||
{
|
||||
int port = prettyprint_addr(daemon->log_source_addr, daemon->addrbuff2);
|
||||
my_syslog(LOG_INFO, "%u %s/%u %s %s %s %s%s", daemon->log_display_id, daemon->addrbuff2, port, source, name, verb, dest, extra);
|
||||
}
|
||||
}
|
||||
else
|
||||
my_syslog(LOG_INFO, "%s %s %s %s", source, name, verb, dest);
|
||||
my_syslog(LOG_INFO, "%s %s %s %s%s", source, name, verb, dest, extra);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#define SAFE_PKTSZ 1280 /* "go anywhere" UDP packet size */
|
||||
#define KEYBLOCK_LEN 40 /* choose to minimise fragmentation when storing DNSSEC keys */
|
||||
#define DNSSEC_WORK 50 /* Max number of queries to validate one question */
|
||||
#define TIMEOUT 10 /* drop UDP queries after TIMEOUT seconds */
|
||||
#define TIMEOUT 10 /* drop UDP queries after TIMEOUT seconds */
|
||||
#define FORWARD_TEST 50 /* try all servers every 50 queries */
|
||||
#define FORWARD_TIME 20 /* or 20 seconds */
|
||||
#define UDP_TEST_TIME 60 /* How often to reset our idea of max packet size. */
|
||||
@@ -143,6 +143,7 @@ NO_SCRIPT
|
||||
NO_LARGEFILE
|
||||
NO_AUTH
|
||||
NO_DUMPFILE
|
||||
NO_LOOP
|
||||
NO_INOTIFY
|
||||
these are available to explicitly disable compile time options which would
|
||||
otherwise be enabled automatically or which are enabled by default
|
||||
@@ -447,7 +448,4 @@ static char *compile_opts =
|
||||
#endif
|
||||
"dumpfile";
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#endif /* defined(HAVE_DHCP) */
|
||||
|
||||
@@ -82,7 +82,4 @@ static int callback(enum nf_conntrack_msg_type type, struct nf_conntrack *ct, vo
|
||||
return NFCT_CB_CONTINUE;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#endif /* HAVE_CONNTRACK */
|
||||
|
||||
93
src/crypto.c
93
src/crypto.c
@@ -16,30 +16,34 @@
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
#if defined(HAVE_DNSSEC) || defined(HAVE_CRYPTOHASH)
|
||||
|
||||
/* Minimal version of nettle */
|
||||
#define MIN_VERSION(major, minor) (NETTLE_VERSION_MAJOR == (major) && NETTLE_VERSION_MINOR >= (minor)) || \
|
||||
(NETTLE_VERSION_MAJOR > (major))
|
||||
|
||||
/* bignum.h includes version.h and works on
|
||||
earlier releases of nettle which don't have version.h */
|
||||
#include <nettle/bignum.h>
|
||||
#if !defined(NETTLE_VERSION_MAJOR)
|
||||
# define NETTLE_VERSION_MAJOR 2
|
||||
# define NETTLE_VERSION_MINOR 0
|
||||
#endif
|
||||
#define MIN_VERSION(major, minor) ((NETTLE_VERSION_MAJOR == (major) && NETTLE_VERSION_MINOR >= (minor)) || \
|
||||
(NETTLE_VERSION_MAJOR > (major)))
|
||||
|
||||
#endif /* defined(HAVE_DNSSEC) || defined(HAVE_CRYPTOHASH) */
|
||||
|
||||
#if defined(HAVE_DNSSEC)
|
||||
#include <nettle/rsa.h>
|
||||
#include <nettle/ecdsa.h>
|
||||
#include <nettle/ecc-curve.h>
|
||||
#if !defined(NETTLE_VERSION_MAJOR)
|
||||
#define NETTLE_VERSION_MAJOR 2
|
||||
#endif
|
||||
#if MIN_VERSION(3, 1)
|
||||
#include <nettle/eddsa.h>
|
||||
#endif
|
||||
#if MIN_VERSION(3, 6)
|
||||
# include <nettle/gostdsa.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_DNSSEC) || defined(HAVE_CRYPTOHASH)
|
||||
#include <nettle/nettle-meta.h>
|
||||
#include <nettle/bignum.h>
|
||||
|
||||
#if MIN_VERSION(3, 1)
|
||||
/* Implement a "hash-function" to the nettle API, which simply returns
|
||||
the input data, concatenated into a single, statically maintained, buffer.
|
||||
|
||||
@@ -93,7 +97,6 @@ static void null_hash_update(void *ctxv, size_t length, const uint8_t *src)
|
||||
ctx->len += length;
|
||||
}
|
||||
|
||||
|
||||
static void null_hash_digest(void *ctx, size_t length, uint8_t *dst)
|
||||
{
|
||||
(void)length;
|
||||
@@ -112,33 +115,7 @@ static struct nettle_hash null_hash = {
|
||||
(nettle_hash_digest_func *) null_hash_digest
|
||||
};
|
||||
|
||||
/* Find pointer to correct hash function in nettle library */
|
||||
const struct nettle_hash *hash_find(char *name)
|
||||
{
|
||||
if (!name)
|
||||
return NULL;
|
||||
|
||||
/* We provide a "null" hash which returns the input data as digest. */
|
||||
if (strcmp(null_hash.name, name) == 0)
|
||||
return &null_hash;
|
||||
|
||||
/* libnettle >= 3.4 provides nettle_lookup_hash() which avoids nasty ABI
|
||||
incompatibilities if sizeof(nettle_hashes) changes between library
|
||||
versions. */
|
||||
#if MIN_VERSION(3, 4)
|
||||
return nettle_lookup_hash(name);
|
||||
#else
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; nettle_hashes[i]; i++)
|
||||
if (strcmp(nettle_hashes[i]->name, name) == 0)
|
||||
return nettle_hashes[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
#endif /* MIN_VERSION(3, 1) */
|
||||
|
||||
/* expand ctx and digest memory allocations if necessary and init hash function */
|
||||
int hash_init(const struct nettle_hash *hash, void **ctxp, unsigned char **digestp)
|
||||
@@ -178,10 +155,6 @@ int hash_init(const struct nettle_hash *hash, void **ctxp, unsigned char **diges
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif /* defined(HAVE_DNSSEC) || defined(HAVE_CRYPTOHASH) */
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
|
||||
static int dnsmasq_rsa_verify(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len,
|
||||
unsigned char *digest, size_t digest_len, int algo)
|
||||
{
|
||||
@@ -415,6 +388,7 @@ static int (*verify_func(int algo))(struct blockdata *key_data, unsigned int key
|
||||
|
||||
case 13: case 14:
|
||||
return dnsmasq_ecdsa_verify;
|
||||
|
||||
#if MIN_VERSION(3, 1)
|
||||
case 15: case 16:
|
||||
return dnsmasq_eddsa_verify;
|
||||
@@ -489,4 +463,37 @@ char *nsec3_digest_name(int digest)
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* defined(HAVE_DNSSEC) */
|
||||
|
||||
#if defined(HAVE_DNSSEC) || defined(HAVE_CRYPTOHASH)
|
||||
/* Find pointer to correct hash function in nettle library */
|
||||
const struct nettle_hash *hash_find(char *name)
|
||||
{
|
||||
if (!name)
|
||||
return NULL;
|
||||
|
||||
#if MIN_VERSION(3,1) && defined(HAVE_DNSSEC)
|
||||
/* We provide a "null" hash which returns the input data as digest. */
|
||||
if (strcmp(null_hash.name, name) == 0)
|
||||
return &null_hash;
|
||||
#endif
|
||||
|
||||
/* libnettle >= 3.4 provides nettle_lookup_hash() which avoids nasty ABI
|
||||
incompatibilities if sizeof(nettle_hashes) changes between library
|
||||
versions. */
|
||||
#if MIN_VERSION(3, 4)
|
||||
return nettle_lookup_hash(name);
|
||||
#else
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; nettle_hashes[i]; i++)
|
||||
if (strcmp(nettle_hashes[i]->name, name) == 0)
|
||||
return nettle_hashes[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* defined(HAVE_DNSSEC) || defined(HAVE_CRYPTOHASH) */
|
||||
|
||||
18
src/dbus.c
18
src/dbus.c
@@ -215,7 +215,7 @@ static void dbus_read_servers(DBusMessage *message)
|
||||
domain = NULL;
|
||||
|
||||
if (!skip)
|
||||
add_update_server(SERV_FROM_DBUS, &addr, &source_addr, NULL, domain);
|
||||
add_update_server(SERV_FROM_DBUS, &addr, &source_addr, NULL, domain, NULL);
|
||||
|
||||
} while (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING);
|
||||
}
|
||||
@@ -276,7 +276,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
|
||||
{
|
||||
const char *str = NULL;
|
||||
union mysockaddr addr, source_addr;
|
||||
int flags = 0;
|
||||
u16 flags = 0;
|
||||
char interface[IF_NAMESIZE];
|
||||
char *str_addr, *str_domain = NULL;
|
||||
|
||||
@@ -361,10 +361,6 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
|
||||
strcpy(str_addr, str);
|
||||
}
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
memset(&source_addr, 0, sizeof(source_addr));
|
||||
memset(&interface, 0, sizeof(interface));
|
||||
|
||||
/* parse the IP address */
|
||||
if ((addr_err = parse_server(str_addr, &addr, &source_addr, (char *) &interface, &flags)))
|
||||
{
|
||||
@@ -377,7 +373,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
|
||||
/* 0.0.0.0 for server address == NULL, for Dbus */
|
||||
if (addr.in.sin_family == AF_INET &&
|
||||
addr.in.sin_addr.s_addr == 0)
|
||||
flags |= SERV_NO_ADDR;
|
||||
flags |= SERV_LITERAL_ADDRESS;
|
||||
|
||||
if (strings)
|
||||
{
|
||||
@@ -392,7 +388,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
|
||||
else
|
||||
p = NULL;
|
||||
|
||||
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str_domain);
|
||||
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str_domain, NULL);
|
||||
} while ((str_domain = p));
|
||||
}
|
||||
else
|
||||
@@ -407,7 +403,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
|
||||
dbus_message_iter_get_basic(&string_iter, &str);
|
||||
dbus_message_iter_next (&string_iter);
|
||||
|
||||
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str);
|
||||
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str, NULL);
|
||||
} while (dbus_message_iter_get_arg_type(&string_iter) == DBUS_TYPE_STRING);
|
||||
}
|
||||
|
||||
@@ -416,7 +412,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
|
||||
}
|
||||
|
||||
cleanup_servers();
|
||||
|
||||
|
||||
if (dup)
|
||||
free(dup);
|
||||
|
||||
@@ -715,7 +711,7 @@ DBusHandlerResult message_handler(DBusConnection *connection,
|
||||
if (new_servers)
|
||||
{
|
||||
my_syslog(LOG_INFO, _("setting upstream servers from DBus"));
|
||||
check_servers();
|
||||
check_servers(0);
|
||||
if (option_bool(OPT_RELOAD))
|
||||
clear_cache = 1;
|
||||
}
|
||||
|
||||
19
src/dhcp.c
19
src/dhcp.c
@@ -468,8 +468,11 @@ void dhcp_packet(time_t now, int pxe_fd)
|
||||
|
||||
/* This can fail when, eg, iptables DROPS destination 255.255.255.255 */
|
||||
if (errno != 0)
|
||||
my_syslog(MS_DHCP | LOG_WARNING, _("Error sending DHCP packet to %s: %s"),
|
||||
inet_ntoa(dest.sin_addr), strerror(errno));
|
||||
{
|
||||
inet_ntop(AF_INET, &dest.sin_addr, daemon->addrbuff, ADDRSTRLEN);
|
||||
my_syslog(MS_DHCP | LOG_WARNING, _("Error sending DHCP packet to %s: %s"),
|
||||
daemon->addrbuff, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
/* check against secondary interface addresses */
|
||||
@@ -521,10 +524,11 @@ static void guess_range_netmask(struct in_addr addr, struct in_addr netmask)
|
||||
!(is_same_net(addr, context->start, netmask) &&
|
||||
is_same_net(addr, context->end, netmask)))
|
||||
{
|
||||
strcpy(daemon->dhcp_buff, inet_ntoa(context->start));
|
||||
strcpy(daemon->dhcp_buff2, inet_ntoa(context->end));
|
||||
inet_ntop(AF_INET, &context->start, daemon->dhcp_buff, DHCP_BUFF_SZ);
|
||||
inet_ntop(AF_INET, &context->end, daemon->dhcp_buff2, DHCP_BUFF_SZ);
|
||||
inet_ntop(AF_INET, &netmask, daemon->addrbuff, ADDRSTRLEN);
|
||||
my_syslog(MS_DHCP | LOG_WARNING, _("DHCP range %s -- %s is not consistent with netmask %s"),
|
||||
daemon->dhcp_buff, daemon->dhcp_buff2, inet_ntoa(netmask));
|
||||
daemon->dhcp_buff, daemon->dhcp_buff2, daemon->addrbuff);
|
||||
}
|
||||
context->netmask = netmask;
|
||||
}
|
||||
@@ -922,7 +926,7 @@ void dhcp_read_ethers(void)
|
||||
|
||||
if (!*cp)
|
||||
{
|
||||
if ((addr.s_addr = inet_addr(ip)) == (in_addr_t)-1)
|
||||
if (inet_pton(AF_INET, ip, &addr.s_addr) < 1)
|
||||
{
|
||||
my_syslog(MS_DHCP | LOG_ERR, _("bad address at %s line %d"), ETHERSFILE, lineno);
|
||||
continue;
|
||||
@@ -1097,7 +1101,8 @@ static int relay_upstream4(struct dhcp_relay *relay, struct dhcp_packet *mess,
|
||||
if (option_bool(OPT_LOG_OPTS))
|
||||
{
|
||||
inet_ntop(AF_INET, &relay->local, daemon->addrbuff, ADDRSTRLEN);
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay %s -> %s"), daemon->addrbuff, inet_ntoa(relay->server.addr4));
|
||||
inet_ntop(AF_INET, &relay->server.addr4, daemon->dhcp_buff2, DHCP_BUFF_SZ);
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay %s -> %s"), daemon->addrbuff, daemon->dhcp_buff2);
|
||||
}
|
||||
|
||||
/* Save this for replies */
|
||||
|
||||
@@ -72,4 +72,3 @@
|
||||
#define DHCP6NOBINDING 3
|
||||
#define DHCP6NOTONLINK 4
|
||||
#define DHCP6USEMULTI 5
|
||||
|
||||
|
||||
@@ -825,6 +825,4 @@ void dhcp_construct_contexts(time_t now)
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* HAVE_DHCP6 */
|
||||
|
||||
@@ -80,8 +80,41 @@
|
||||
|
||||
#define EDNS0_OPTION_MAC 65001 /* dyndns.org temporary assignment */
|
||||
#define EDNS0_OPTION_CLIENT_SUBNET 8 /* IANA */
|
||||
#define EDNS0_OPTION_EDE 15 /* IANA - RFC 8914 */
|
||||
#define EDNS0_OPTION_NOMDEVICEID 65073 /* Nominum temporary assignment */
|
||||
#define EDNS0_OPTION_NOMCPEID 65074 /* Nominum temporary assignment */
|
||||
#define EDNS0_OPTION_UMBRELLA 20292 /* Cisco Umbrella temporary assignment */
|
||||
|
||||
/* RFC-8914 extended errors, negative values are our definitions */
|
||||
#define EDE_UNSET -1 /* No extended DNS error available */
|
||||
#define EDE_OTHER 0 /* Other */
|
||||
#define EDE_USUPDNSKEY 1 /* Unsupported DNSKEY algo */
|
||||
#define EDE_USUPDS 2 /* Unsupported DS Digest */
|
||||
#define EDE_STALE 3 /* Stale answer */
|
||||
#define EDE_FORGED 4 /* Forged answer */
|
||||
#define EDE_DNSSEC_IND 5 /* DNSSEC Indeterminate */
|
||||
#define EDE_DNSSEC_BOGUS 6 /* DNSSEC Bogus */
|
||||
#define EDE_SIG_EXP 7 /* Signature Expired */
|
||||
#define EDE_SIG_NYV 8 /* Signature Not Yet Valid */
|
||||
#define EDE_NO_DNSKEY 9 /* DNSKEY missing */
|
||||
#define EDE_NO_RRSIG 10 /* RRSIGs missing */
|
||||
#define EDE_NO_ZONEKEY 11 /* No Zone Key Bit Set */
|
||||
#define EDE_NO_NSEC 12 /* NSEC Missing */
|
||||
#define EDE_CACHED_ERR 13 /* Cached Error */
|
||||
#define EDE_NOT_READY 14 /* Not Ready */
|
||||
#define EDE_BLOCKED 15 /* Blocked */
|
||||
#define EDE_CENSORED 16 /* Censored */
|
||||
#define EDE_FILTERED 17 /* Filtered */
|
||||
#define EDE_PROHIBITED 18 /* Prohibited */
|
||||
#define EDE_STALE_NXD 19 /* Stale NXDOMAIN */
|
||||
#define EDE_NOT_AUTH 20 /* Not Authoritative */
|
||||
#define EDE_NOT_SUP 21 /* Not Supported */
|
||||
#define EDE_NO_AUTH 22 /* No Reachable Authority */
|
||||
#define EDE_NETERR 23 /* Network error */
|
||||
#define EDE_INVALID_DATA 24 /* Invalid Data */
|
||||
|
||||
|
||||
|
||||
|
||||
struct dns_header {
|
||||
u16 id;
|
||||
|
||||
159
src/dnsmasq.c
159
src/dnsmasq.c
@@ -24,7 +24,7 @@ struct daemon *daemon;
|
||||
static volatile pid_t pid = 0;
|
||||
static volatile int pipewrite;
|
||||
|
||||
static int set_dns_listeners(time_t now);
|
||||
static void set_dns_listeners(void);
|
||||
static void check_dns_listeners(time_t now);
|
||||
static void sig_handler(int sig);
|
||||
static void async_event(int pipe, time_t now);
|
||||
@@ -109,7 +109,6 @@ int main (int argc, char **argv)
|
||||
daemon->packet_buff_sz = daemon->edns_pktsz + MAXDNAME + RRFIXEDSZ;
|
||||
daemon->packet = safe_malloc(daemon->packet_buff_sz);
|
||||
|
||||
daemon->addrbuff = safe_malloc(ADDRSTRLEN);
|
||||
if (option_bool(OPT_EXTRALOG))
|
||||
daemon->addrbuff2 = safe_malloc(ADDRSTRLEN);
|
||||
|
||||
@@ -135,6 +134,13 @@ int main (int argc, char **argv)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS)
|
||||
/* CONNTRACK UBUS code uses this buffer, so if not allocated above,
|
||||
we need to allocate it here. */
|
||||
if (option_bool(OPT_CMARK_ALST_EN) && !daemon->workspacename)
|
||||
daemon->workspacename = safe_malloc(MAXDNAME);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DHCP
|
||||
if (!daemon->lease_file)
|
||||
{
|
||||
@@ -205,8 +211,13 @@ int main (int argc, char **argv)
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CONNTRACK
|
||||
if (option_bool(OPT_CONNTRACK) && (daemon->query_port != 0 || daemon->osport))
|
||||
die (_("cannot use --conntrack AND --query-port"), NULL, EC_BADCONF);
|
||||
if (option_bool(OPT_CONNTRACK))
|
||||
{
|
||||
if (daemon->query_port != 0 || daemon->osport)
|
||||
die (_("cannot use --conntrack AND --query-port"), NULL, EC_BADCONF);
|
||||
|
||||
need_cap_net_admin = 1;
|
||||
}
|
||||
#else
|
||||
if (option_bool(OPT_CONNTRACK))
|
||||
die(_("conntrack support not available: set HAVE_CONNTRACK in src/config.h"), NULL, EC_BADCONF);
|
||||
@@ -430,8 +441,6 @@ int main (int argc, char **argv)
|
||||
#ifdef HAVE_DBUS
|
||||
{
|
||||
char *err;
|
||||
daemon->dbus = NULL;
|
||||
daemon->watches = NULL;
|
||||
if ((err = dbus_init()))
|
||||
die(_("DBus error: %s"), err, EC_MISC);
|
||||
}
|
||||
@@ -442,8 +451,9 @@ int main (int argc, char **argv)
|
||||
if (option_bool(OPT_UBUS))
|
||||
#ifdef HAVE_UBUS
|
||||
{
|
||||
daemon->ubus = NULL;
|
||||
ubus_init();
|
||||
char *err;
|
||||
if ((err = ubus_init()))
|
||||
die(_("UBus error: %s"), err, EC_MISC);
|
||||
}
|
||||
#else
|
||||
die(_("UBus not available: set HAVE_UBUS in src/config.h"), NULL, EC_BADCONF);
|
||||
@@ -1025,7 +1035,7 @@ int main (int argc, char **argv)
|
||||
close(err_pipe[1]);
|
||||
|
||||
if (daemon->port != 0)
|
||||
check_servers();
|
||||
check_servers(0);
|
||||
|
||||
pid = getpid();
|
||||
|
||||
@@ -1040,16 +1050,10 @@ int main (int argc, char **argv)
|
||||
|
||||
while (1)
|
||||
{
|
||||
int t, timeout = -1;
|
||||
int timeout = -1;
|
||||
|
||||
poll_reset();
|
||||
|
||||
/* if we are out of resources, find how long we have to wait
|
||||
for some to come free, we'll loop around then and restart
|
||||
listening for queries */
|
||||
if ((t = set_dns_listeners(now)) != 0)
|
||||
timeout = t * 1000;
|
||||
|
||||
/* Whilst polling for the dbus, or doing a tftp transfer, wake every quarter second */
|
||||
if (daemon->tftp_trans ||
|
||||
(option_bool(OPT_DBUS) && !daemon->dbus))
|
||||
@@ -1059,15 +1063,18 @@ int main (int argc, char **argv)
|
||||
else if (is_dad_listeners())
|
||||
timeout = 1000;
|
||||
|
||||
#ifdef HAVE_DBUS
|
||||
set_dbus_listeners();
|
||||
#endif
|
||||
set_dns_listeners();
|
||||
|
||||
#ifdef HAVE_DBUS
|
||||
if (option_bool(OPT_DBUS))
|
||||
set_dbus_listeners();
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_UBUS
|
||||
if (option_bool(OPT_UBUS))
|
||||
set_ubus_listeners();
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef HAVE_DHCP
|
||||
if (daemon->dhcp || daemon->relay4)
|
||||
{
|
||||
@@ -1187,28 +1194,44 @@ int main (int argc, char **argv)
|
||||
|
||||
#ifdef HAVE_DBUS
|
||||
/* if we didn't create a DBus connection, retry now. */
|
||||
if (option_bool(OPT_DBUS) && !daemon->dbus)
|
||||
if (option_bool(OPT_DBUS))
|
||||
{
|
||||
char *err;
|
||||
if ((err = dbus_init()))
|
||||
my_syslog(LOG_WARNING, _("DBus error: %s"), err);
|
||||
if (daemon->dbus)
|
||||
my_syslog(LOG_INFO, _("connected to system DBus"));
|
||||
if (!daemon->dbus)
|
||||
{
|
||||
char *err = dbus_init();
|
||||
|
||||
if (daemon->dbus)
|
||||
my_syslog(LOG_INFO, _("connected to system DBus"));
|
||||
else if (err)
|
||||
{
|
||||
my_syslog(LOG_ERR, _("DBus error: %s"), err);
|
||||
reset_option_bool(OPT_DBUS); /* fatal error, stop trying. */
|
||||
}
|
||||
}
|
||||
|
||||
check_dbus_listeners();
|
||||
}
|
||||
check_dbus_listeners();
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_UBUS
|
||||
/* if we didn't create a UBus connection, retry now. */
|
||||
if (option_bool(OPT_UBUS))
|
||||
{
|
||||
/* if we didn't create a UBus connection, retry now. */
|
||||
if (!daemon->ubus)
|
||||
{
|
||||
ubus_init();
|
||||
}
|
||||
{
|
||||
if (!daemon->ubus)
|
||||
{
|
||||
char *err = ubus_init();
|
||||
|
||||
check_ubus_listeners();
|
||||
}
|
||||
if (daemon->ubus)
|
||||
my_syslog(LOG_INFO, _("connected to system UBus"));
|
||||
else if (err)
|
||||
{
|
||||
my_syslog(LOG_ERR, _("UBus error: %s"), err);
|
||||
reset_option_bool(OPT_UBUS); /* fatal error, stop trying. */
|
||||
}
|
||||
}
|
||||
|
||||
check_ubus_listeners();
|
||||
}
|
||||
#endif
|
||||
|
||||
check_dns_listeners(now);
|
||||
@@ -1441,7 +1464,7 @@ static void async_event(int pipe, time_t now)
|
||||
}
|
||||
|
||||
if (check)
|
||||
check_servers();
|
||||
check_servers(0);
|
||||
}
|
||||
|
||||
#ifdef HAVE_DHCP
|
||||
@@ -1640,7 +1663,7 @@ static void poll_resolv(int force, int do_reload, time_t now)
|
||||
{
|
||||
my_syslog(LOG_INFO, _("reading %s"), latest->name);
|
||||
warned = 0;
|
||||
check_servers();
|
||||
check_servers(0);
|
||||
if (option_bool(OPT_RELOAD) && do_reload)
|
||||
clear_cache_and_reload(now);
|
||||
}
|
||||
@@ -1683,12 +1706,12 @@ void clear_cache_and_reload(time_t now)
|
||||
#endif
|
||||
}
|
||||
|
||||
static int set_dns_listeners(time_t now)
|
||||
static void set_dns_listeners(void)
|
||||
{
|
||||
struct serverfd *serverfdp;
|
||||
struct listener *listener;
|
||||
struct randfd_list *rfl;
|
||||
int wait = 0, i;
|
||||
int i;
|
||||
|
||||
#ifdef HAVE_TFTP
|
||||
int tftp = 0;
|
||||
@@ -1701,10 +1724,6 @@ static int set_dns_listeners(time_t now)
|
||||
}
|
||||
#endif
|
||||
|
||||
/* will we be able to get memory? */
|
||||
if (daemon->port != 0)
|
||||
get_new_frec(now, &wait, NULL);
|
||||
|
||||
for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
|
||||
poll_listen(serverfdp->fd, POLLIN);
|
||||
|
||||
@@ -1716,36 +1735,34 @@ static int set_dns_listeners(time_t now)
|
||||
for (rfl = daemon->rfl_poll; rfl; rfl = rfl->next)
|
||||
poll_listen(rfl->rfd->fd, POLLIN);
|
||||
|
||||
/* check to see if we have free tcp process slots. */
|
||||
for (i = MAX_PROCS - 1; i >= 0; i--)
|
||||
if (daemon->tcp_pids[i] == 0 && daemon->tcp_pipes[i] == -1)
|
||||
break;
|
||||
|
||||
for (listener = daemon->listeners; listener; listener = listener->next)
|
||||
{
|
||||
/* only listen for queries if we have resources */
|
||||
if (listener->fd != -1 && wait == 0)
|
||||
if (listener->fd != -1)
|
||||
poll_listen(listener->fd, POLLIN);
|
||||
|
||||
/* death of a child goes through the select loop, so
|
||||
we don't need to explicitly arrange to wake up here */
|
||||
if (listener->tcpfd != -1)
|
||||
for (i = 0; i < MAX_PROCS; i++)
|
||||
if (daemon->tcp_pids[i] == 0 && daemon->tcp_pipes[i] == -1)
|
||||
{
|
||||
poll_listen(listener->tcpfd, POLLIN);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
/* Only listen for TCP connections when a process slot
|
||||
is available. Death of a child goes through the select loop, so
|
||||
we don't need to explicitly arrange to wake up here,
|
||||
we'll be called again when a slot becomes available. */
|
||||
if (listener->tcpfd != -1 && i >= 0)
|
||||
poll_listen(listener->tcpfd, POLLIN);
|
||||
|
||||
#ifdef HAVE_TFTP
|
||||
/* tftp == 0 in single-port mode. */
|
||||
if (tftp <= daemon->tftp_max && listener->tftpfd != -1)
|
||||
poll_listen(listener->tftpfd, POLLIN);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
if (!option_bool(OPT_DEBUG))
|
||||
for (i = 0; i < MAX_PROCS; i++)
|
||||
if (daemon->tcp_pipes[i] != -1)
|
||||
poll_listen(daemon->tcp_pipes[i], POLLIN);
|
||||
|
||||
return wait;
|
||||
}
|
||||
|
||||
static void check_dns_listeners(time_t now)
|
||||
@@ -1797,7 +1814,16 @@ static void check_dns_listeners(time_t now)
|
||||
tftp_request(listener, now);
|
||||
#endif
|
||||
|
||||
if (listener->tcpfd != -1 && poll_check(listener->tcpfd, POLLIN))
|
||||
/* check to see if we have a free tcp process slot.
|
||||
Note that we can't assume that because we had
|
||||
at least one a poll() time, that we still do.
|
||||
There may be more waiting connections after
|
||||
poll() returns then free process slots. */
|
||||
for (i = MAX_PROCS - 1; i >= 0; i--)
|
||||
if (daemon->tcp_pids[i] == 0 && daemon->tcp_pipes[i] == -1)
|
||||
break;
|
||||
|
||||
if (listener->tcpfd != -1 && i >= 0 && poll_check(listener->tcpfd, POLLIN))
|
||||
{
|
||||
int confd, client_ok = 1;
|
||||
struct irec *iface = NULL;
|
||||
@@ -1887,7 +1913,6 @@ static void check_dns_listeners(time_t now)
|
||||
close(pipefd[0]);
|
||||
else
|
||||
{
|
||||
int i;
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
/* The child process inherits the netlink socket,
|
||||
which it never uses, but when the parent (us)
|
||||
@@ -1907,13 +1932,9 @@ static void check_dns_listeners(time_t now)
|
||||
read_write(pipefd[0], &a, 1, 1);
|
||||
#endif
|
||||
|
||||
for (i = 0; i < MAX_PROCS; i++)
|
||||
if (daemon->tcp_pids[i] == 0 && daemon->tcp_pipes[i] == -1)
|
||||
{
|
||||
daemon->tcp_pids[i] = p;
|
||||
daemon->tcp_pipes[i] = pipefd[0];
|
||||
break;
|
||||
}
|
||||
/* i holds index of free slot */
|
||||
daemon->tcp_pids[i] = p;
|
||||
daemon->tcp_pipes[i] = pipefd[0];
|
||||
}
|
||||
close(confd);
|
||||
|
||||
@@ -2092,7 +2113,7 @@ int delay_dhcp(time_t start, int sec, int fd, uint32_t addr, unsigned short id)
|
||||
poll_reset();
|
||||
if (fd != -1)
|
||||
poll_listen(fd, POLLIN);
|
||||
set_dns_listeners(now);
|
||||
set_dns_listeners();
|
||||
set_log_writer();
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
|
||||
173
src/dnsmasq.h
173
src/dnsmasq.h
@@ -107,6 +107,7 @@ typedef unsigned long long u64;
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
@@ -270,7 +271,11 @@ struct event_desc {
|
||||
#define OPT_SINGLE_PORT 60
|
||||
#define OPT_LEASE_RENEW 61
|
||||
#define OPT_LOG_DEBUG 62
|
||||
#define OPT_LAST 63
|
||||
#define OPT_UMBRELLA 63
|
||||
#define OPT_UMBRELLA_DEVID 64
|
||||
#define OPT_CMARK_ALST_EN 65
|
||||
#define OPT_QUIET_TFTP 66
|
||||
#define OPT_LAST 67
|
||||
|
||||
#define OPTION_BITS (sizeof(unsigned int)*8)
|
||||
#define OPTION_SIZE ( (OPT_LAST/OPTION_BITS)+((OPT_LAST%OPTION_BITS)!=0) )
|
||||
@@ -320,12 +325,14 @@ union all_addr {
|
||||
/* for log_query */
|
||||
struct {
|
||||
unsigned short keytag, algo, digest, rcode;
|
||||
int ede;
|
||||
} log;
|
||||
};
|
||||
|
||||
|
||||
struct bogus_addr {
|
||||
struct in_addr addr, mask;
|
||||
int is6, prefix;
|
||||
union all_addr addr;
|
||||
struct bogus_addr *next;
|
||||
};
|
||||
|
||||
@@ -496,7 +503,7 @@ struct crec {
|
||||
#define F_NO_RR (1u<<25)
|
||||
#define F_IPSET (1u<<26)
|
||||
#define F_NOEXTRA (1u<<27)
|
||||
#define F_SERVFAIL (1u<<28) /* currently unused. */
|
||||
#define F_DOMAINSRV (1u<<28)
|
||||
#define F_RCODE (1u<<29)
|
||||
#define F_SRV (1u<<30)
|
||||
|
||||
@@ -522,19 +529,20 @@ union mysockaddr {
|
||||
#define IFACE_PERMANENT 4
|
||||
|
||||
|
||||
#define SERV_FROM_RESOLV 1 /* 1 for servers from resolv, 0 for command line. */
|
||||
#define SERV_NO_ADDR 2 /* no server, this domain is local only */
|
||||
#define SERV_LITERAL_ADDRESS 4 /* addr is the answer, not the server */
|
||||
#define SERV_HAS_DOMAIN 8 /* server for one domain only */
|
||||
/* The actual values here matter, since we sort on them to get records in the order
|
||||
IPv6 addr, IPv4 addr, all zero return, no-data return, send upstream. */
|
||||
#define SERV_LITERAL_ADDRESS 1 /* addr is the answer, or NoDATA is the answer, depending on the next three flags */
|
||||
#define SERV_ALL_ZEROS 2 /* return all zeros for A and AAAA */
|
||||
#define SERV_4ADDR 4 /* addr is IPv4 */
|
||||
#define SERV_6ADDR 8 /* addr is IPv6 */
|
||||
#define SERV_HAS_SOURCE 16 /* source address defined */
|
||||
#define SERV_FOR_NODOTS 32 /* server for names with no domain part only */
|
||||
#define SERV_WARNED_RECURSIVE 64 /* avoid warning spam */
|
||||
#define SERV_FROM_DBUS 128 /* 1 if source is DBus */
|
||||
#define SERV_MARK 256 /* for mark-and-delete */
|
||||
#define SERV_TYPE (SERV_HAS_DOMAIN | SERV_FOR_NODOTS)
|
||||
#define SERV_COUNTED 512 /* workspace for log code */
|
||||
#define SERV_MARK 256 /* for mark-and-delete and log code */
|
||||
#define SERV_WILDCARD 512 /* domain has leading '*' */
|
||||
#define SERV_USE_RESOLV 1024 /* forward this domain in the normal way */
|
||||
#define SERV_NO_REBIND 2048 /* inhibit dns-rebind protection */
|
||||
#define SERV_FROM_RESOLV 2048 /* 1 for servers from resolv, 0 for command line. */
|
||||
#define SERV_FROM_FILE 4096 /* read from --servers-file */
|
||||
#define SERV_LOOP 8192 /* server causes forwarding loop */
|
||||
#define SERV_DO_DNSSEC 16384 /* Validate DNSSEC when using this server */
|
||||
@@ -559,19 +567,46 @@ struct randfd_list {
|
||||
struct randfd_list *next;
|
||||
};
|
||||
|
||||
|
||||
struct server {
|
||||
u16 flags, domain_len;
|
||||
char *domain;
|
||||
struct server *next;
|
||||
int serial, arrayposn;
|
||||
int last_server;
|
||||
union mysockaddr addr, source_addr;
|
||||
char interface[IF_NAMESIZE+1];
|
||||
unsigned int ifindex; /* corresponding to interface, above */
|
||||
struct serverfd *sfd;
|
||||
char *domain; /* set if this server only handles a domain. */
|
||||
int flags, tcpfd, edns_pktsz;
|
||||
int tcpfd, edns_pktsz;
|
||||
time_t pktsz_reduced;
|
||||
unsigned int queries, failed_queries;
|
||||
time_t forwardtime;
|
||||
int forwardcount;
|
||||
#ifdef HAVE_LOOP
|
||||
u32 uid;
|
||||
#endif
|
||||
struct server *next;
|
||||
};
|
||||
|
||||
/* First four fields must match struct server in next three definitions.. */
|
||||
struct serv_addr4 {
|
||||
u16 flags, domain_len;
|
||||
char *domain;
|
||||
struct server *next;
|
||||
struct in_addr addr;
|
||||
};
|
||||
|
||||
struct serv_addr6 {
|
||||
u16 flags, domain_len;
|
||||
char *domain;
|
||||
struct server *next;
|
||||
struct in6_addr addr;
|
||||
};
|
||||
|
||||
struct serv_local {
|
||||
u16 flags, domain_len;
|
||||
char *domain;
|
||||
struct server *next;
|
||||
};
|
||||
|
||||
struct ipsets {
|
||||
@@ -580,6 +615,12 @@ struct ipsets {
|
||||
struct ipsets *next;
|
||||
};
|
||||
|
||||
struct allowlist {
|
||||
u32 mark, mask;
|
||||
char **patterns;
|
||||
struct allowlist *next;
|
||||
};
|
||||
|
||||
struct irec {
|
||||
union mysockaddr addr;
|
||||
struct in_addr netmask; /* only valid for IPv4 */
|
||||
@@ -649,17 +690,28 @@ struct hostsfile {
|
||||
#define DUMP_BOGUS 0x0040
|
||||
#define DUMP_SEC_BOGUS 0x0080
|
||||
|
||||
|
||||
/* DNSSEC status values. */
|
||||
#define STAT_SECURE 1
|
||||
#define STAT_INSECURE 2
|
||||
#define STAT_BOGUS 3
|
||||
#define STAT_NEED_DS 4
|
||||
#define STAT_NEED_KEY 5
|
||||
#define STAT_TRUNCATED 6
|
||||
#define STAT_SECURE_WILDCARD 7
|
||||
#define STAT_OK 8
|
||||
#define STAT_ABANDONED 9
|
||||
#define STAT_SECURE 0x10000
|
||||
#define STAT_INSECURE 0x20000
|
||||
#define STAT_BOGUS 0x30000
|
||||
#define STAT_NEED_DS 0x40000
|
||||
#define STAT_NEED_KEY 0x50000
|
||||
#define STAT_TRUNCATED 0x60000
|
||||
#define STAT_SECURE_WILDCARD 0x70000
|
||||
#define STAT_OK 0x80000
|
||||
#define STAT_ABANDONED 0x90000
|
||||
|
||||
#define DNSSEC_FAIL_NYV 0x0001 /* key not yet valid */
|
||||
#define DNSSEC_FAIL_EXP 0x0002 /* key expired */
|
||||
#define DNSSEC_FAIL_INDET 0x0004 /* indetermined */
|
||||
#define DNSSEC_FAIL_NOKEYSUP 0x0008 /* no supported key algo. */
|
||||
#define DNSSEC_FAIL_NOSIG 0x0010 /* No RRsigs */
|
||||
#define DNSSEC_FAIL_NOZONE 0x0020 /* No Zone bit set */
|
||||
#define DNSSEC_FAIL_NONSEC 0x0040 /* No NSEC */
|
||||
#define DNSSEC_FAIL_NODSSUP 0x0080 /* no supported DS algo. */
|
||||
#define DNSSEC_FAIL_NOKEY 0x0100 /* no DNSKEY */
|
||||
|
||||
#define STAT_ISEQUAL(a, b) (((a) & 0xffff0000) == (b))
|
||||
|
||||
#define FREC_NOREBIND 1
|
||||
#define FREC_CHECKING_DISABLED 2
|
||||
@@ -696,6 +748,7 @@ struct frec {
|
||||
struct blockdata *stash; /* Saved reply, whilst we validate */
|
||||
size_t stash_len;
|
||||
struct frec *dependent; /* Query awaiting internally-generated DNSKEY or DS query */
|
||||
struct frec *next_dependent; /* list of above. */
|
||||
struct frec *blocking_query; /* Query which is blocking us. */
|
||||
#endif
|
||||
struct frec *next;
|
||||
@@ -908,10 +961,10 @@ struct dhcp_bridge {
|
||||
};
|
||||
|
||||
struct cond_domain {
|
||||
char *domain, *prefix;
|
||||
char *domain, *prefix; /* prefix is text-prefix on domain name */
|
||||
struct in_addr start, end;
|
||||
struct in6_addr start6, end6;
|
||||
int is6, indexed;
|
||||
int is6, indexed, prefixlen;
|
||||
struct cond_domain *next;
|
||||
};
|
||||
|
||||
@@ -1052,8 +1105,12 @@ extern struct daemon {
|
||||
char *lease_change_command;
|
||||
struct iname *if_names, *if_addrs, *if_except, *dhcp_except, *auth_peers, *tftp_interfaces;
|
||||
struct bogus_addr *bogus_addr, *ignore_addr;
|
||||
struct server *servers;
|
||||
struct server *servers, *local_domains, **serverarray, *no_rebind;
|
||||
int server_has_wildcard;
|
||||
int serverarraysz, serverarrayhwm;
|
||||
struct ipsets *ipsets;
|
||||
u32 allowlist_mask;
|
||||
struct allowlist *allowlists;
|
||||
int log_fac; /* log facility */
|
||||
char *log_file; /* optional log file */
|
||||
int max_logs; /* queue limit */
|
||||
@@ -1061,6 +1118,9 @@ extern struct daemon {
|
||||
int port, query_port, min_port, max_port;
|
||||
unsigned long local_ttl, neg_ttl, max_ttl, min_cache_ttl, max_cache_ttl, auth_ttl, dhcp_ttl, use_dhcp_ttl;
|
||||
char *dns_client_id;
|
||||
u32 umbrella_org;
|
||||
u32 umbrella_asset;
|
||||
u8 umbrella_device[8];
|
||||
struct hostsfile *addn_hosts;
|
||||
struct dhcp_context *dhcp, *dhcp6;
|
||||
struct ra_interface *ra_interfaces;
|
||||
@@ -1121,9 +1181,6 @@ extern struct daemon {
|
||||
struct serverfd *sfds;
|
||||
struct irec *interfaces;
|
||||
struct listener *listeners;
|
||||
struct server *last_server;
|
||||
time_t forwardtime;
|
||||
int forwardcount;
|
||||
struct server *srv_save; /* Used for resend on DoD */
|
||||
size_t packet_len; /* " " */
|
||||
int fd_save; /* " " */
|
||||
@@ -1239,12 +1296,13 @@ unsigned char *skip_questions(struct dns_header *header, size_t plen);
|
||||
unsigned char *skip_section(unsigned char *ansp, int count, struct dns_header *header, size_t plen);
|
||||
unsigned int extract_request(struct dns_header *header, size_t qlen,
|
||||
char *name, unsigned short *typep);
|
||||
size_t setup_reply(struct dns_header *header, size_t qlen,
|
||||
union all_addr *addrp, unsigned int flags,
|
||||
unsigned long ttl);
|
||||
void setup_reply(struct dns_header *header, unsigned int flags, int ede);
|
||||
int extract_addresses(struct dns_header *header, size_t qlen, char *name,
|
||||
time_t now, char **ipsets, int is_sign, int check_rebind,
|
||||
int no_cache_dnssec, int secure, int *doctored);
|
||||
#if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS)
|
||||
void report_addresses(struct dns_header *header, size_t len, u32 mark);
|
||||
#endif
|
||||
size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
struct in_addr local_addr, struct in_addr local_netmask,
|
||||
time_t now, int ad_reqd, int do_bit, int have_pseudoheader);
|
||||
@@ -1269,6 +1327,7 @@ int in_zone(struct auth_zone *zone, char *name, char **cut);
|
||||
#endif
|
||||
|
||||
/* dnssec.c */
|
||||
#ifdef HAVE_DNSSEC
|
||||
size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char *name, int class, int type, int edns_pktsz);
|
||||
int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class);
|
||||
int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class);
|
||||
@@ -1277,6 +1336,8 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
||||
int dnskey_keytag(int alg, int flags, unsigned char *key, int keylen);
|
||||
size_t filter_rrsigs(struct dns_header *header, size_t plen);
|
||||
int setup_timestamp(void);
|
||||
int errflags_to_ede(int status);
|
||||
#endif
|
||||
|
||||
/* hash_questions.c */
|
||||
void hash_questions_init(void);
|
||||
@@ -1310,6 +1371,7 @@ int hostname_issubdomain(char *a, char *b);
|
||||
time_t dnsmasq_time(void);
|
||||
int netmask_length(struct in_addr mask);
|
||||
int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask);
|
||||
int is_same_net_prefix(struct in_addr a, struct in_addr b, int prefix);
|
||||
int is_same_net6(struct in6_addr *a, struct in6_addr *b, int prefixlen);
|
||||
u64 addr6part(struct in6_addr *addr);
|
||||
void setaddr6part(struct in6_addr *addr, u64 host);
|
||||
@@ -1351,7 +1413,7 @@ void set_option_bool(unsigned int opt);
|
||||
void reset_option_bool(unsigned int opt);
|
||||
struct hostsfile *expand_filelist(struct hostsfile *list);
|
||||
char *parse_server(char *arg, union mysockaddr *addr,
|
||||
union mysockaddr *source_addr, char *interface, int *flags);
|
||||
union mysockaddr *source_addr, char *interface, u16 *flags);
|
||||
int option_read_dynfile(char *file, int flags);
|
||||
|
||||
/* forward.c */
|
||||
@@ -1360,7 +1422,6 @@ void receive_query(struct listener *listen, time_t now);
|
||||
unsigned char *tcp_request(int confd, time_t now,
|
||||
union mysockaddr *local_addr, struct in_addr netmask, int auth_dns);
|
||||
void server_gone(struct server *server);
|
||||
struct frec *get_new_frec(time_t now, int *wait, struct frec *force);
|
||||
int send_from(int fd, int nowild, char *packet, size_t len,
|
||||
union mysockaddr *to, union all_addr *source,
|
||||
unsigned int iface);
|
||||
@@ -1373,14 +1434,7 @@ int indextoname(int fd, int index, char *name);
|
||||
int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifindex, int is_tcp);
|
||||
void pre_allocate_sfds(void);
|
||||
int reload_servers(char *fname);
|
||||
void mark_servers(int flag);
|
||||
void cleanup_servers(void);
|
||||
void add_update_server(int flags,
|
||||
union mysockaddr *addr,
|
||||
union mysockaddr *source_addr,
|
||||
const char *interface,
|
||||
const char *domain);
|
||||
void check_servers(void);
|
||||
void check_servers(int no_loop_call);
|
||||
int enumerate_interfaces(int reset);
|
||||
void create_wildcard_listeners(void);
|
||||
void create_bound_listeners(int dienow);
|
||||
@@ -1513,10 +1567,14 @@ void emit_dbus_signal(int action, struct dhcp_lease *lease, char *hostname);
|
||||
|
||||
/* ubus.c */
|
||||
#ifdef HAVE_UBUS
|
||||
void ubus_init(void);
|
||||
char *ubus_init(void);
|
||||
void set_ubus_listeners(void);
|
||||
void check_ubus_listeners(void);
|
||||
void ubus_event_bcast(const char *type, const char *mac, const char *ip, const char *name, const char *interface);
|
||||
# ifdef HAVE_CONNTRACK
|
||||
void ubus_event_bcast_connmark_allowlist_refused(u32 mark, const char *name);
|
||||
void ubus_event_bcast_connmark_allowlist_resolved(u32 mark, const char *pattern, const char *ip, u32 ttl);
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* ipset.c */
|
||||
@@ -1525,6 +1583,13 @@ void ipset_init(void);
|
||||
int add_to_ipset(const char *setname, const union all_addr *ipaddr, int flags, int remove);
|
||||
#endif
|
||||
|
||||
/* pattern.c */
|
||||
#ifdef HAVE_CONNTRACK
|
||||
int is_valid_dns_name(const char *value);
|
||||
int is_valid_dns_name_pattern(const char *value);
|
||||
int is_dns_name_matching_pattern(const char *name, const char *pattern);
|
||||
#endif
|
||||
|
||||
/* helper.c */
|
||||
#if defined(HAVE_SCRIPT)
|
||||
int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd);
|
||||
@@ -1690,3 +1755,23 @@ int do_arp_script_run(void);
|
||||
void dump_init(void);
|
||||
void dump_packet(int mask, void *packet, size_t len, union mysockaddr *src, union mysockaddr *dst);
|
||||
#endif
|
||||
|
||||
/* domain-match.c */
|
||||
void build_server_array(void);
|
||||
int lookup_domain(char *qdomain, int flags, int *lowout, int *highout);
|
||||
int filter_servers(int seed, int flags, int *lowout, int *highout);
|
||||
int is_local_answer(time_t now, int first, char *name);
|
||||
size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header *header,
|
||||
char *name, char *limit, int first, int last, int ede);
|
||||
int server_samegroup(struct server *a, struct server *b);
|
||||
#ifdef HAVE_DNSSEC
|
||||
int dnssec_server(struct server *server, char *keyname, int *firstp, int *lastp);
|
||||
#endif
|
||||
void mark_servers(int flag);
|
||||
void cleanup_servers(void);
|
||||
int add_update_server(int flags,
|
||||
union mysockaddr *addr,
|
||||
union mysockaddr *source_addr,
|
||||
const char *interface,
|
||||
const char *domain,
|
||||
union all_addr *local_addr);
|
||||
|
||||
162
src/dnssec.c
162
src/dnssec.c
@@ -215,14 +215,6 @@ static int is_check_date(unsigned long curtime)
|
||||
return !daemon->dnssec_no_time_check;
|
||||
}
|
||||
|
||||
/* Check whether today/now is between date_start and date_end */
|
||||
static int check_date_range(unsigned long curtime, u32 date_start, u32 date_end)
|
||||
{
|
||||
/* We must explicitly check against wanted values, because of SERIAL_UNDEF */
|
||||
return serial_compare_32(curtime, date_start) == SERIAL_GT
|
||||
&& serial_compare_32(curtime, date_end) == SERIAL_LT;
|
||||
}
|
||||
|
||||
/* Return bytes of canonicalised rrdata one by one.
|
||||
Init state->ip with the RR, and state->end with the end of same.
|
||||
Init state->op to NULL.
|
||||
@@ -534,7 +526,8 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
|
||||
struct crec *crecp = NULL;
|
||||
u16 *rr_desc = rrfilter_desc(type);
|
||||
u32 sig_expiration, sig_inception;
|
||||
|
||||
int failflags = DNSSEC_FAIL_NOSIG | DNSSEC_FAIL_NYV | DNSSEC_FAIL_EXP | DNSSEC_FAIL_NOKEYSUP;
|
||||
|
||||
unsigned long curtime = time(0);
|
||||
int time_check = is_check_date(curtime);
|
||||
|
||||
@@ -557,6 +550,8 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
|
||||
void *ctx;
|
||||
char *name_start;
|
||||
u32 nsigttl, ttl, orig_ttl;
|
||||
|
||||
failflags &= ~DNSSEC_FAIL_NOSIG;
|
||||
|
||||
p = sigs[j];
|
||||
GETLONG(ttl, p);
|
||||
@@ -574,12 +569,31 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
|
||||
if (!extract_name(header, plen, &p, keyname, 1, 0))
|
||||
return STAT_BOGUS;
|
||||
|
||||
if ((time_check && !check_date_range(curtime, sig_inception, sig_expiration)) ||
|
||||
labels > name_labels ||
|
||||
!(hash = hash_find(algo_digest_name(algo))) ||
|
||||
if (!time_check)
|
||||
failflags &= ~(DNSSEC_FAIL_NYV | DNSSEC_FAIL_EXP);
|
||||
else
|
||||
{
|
||||
/* We must explicitly check against wanted values, because of SERIAL_UNDEF */
|
||||
if (serial_compare_32(curtime, sig_inception) == SERIAL_LT)
|
||||
continue;
|
||||
else
|
||||
failflags &= ~DNSSEC_FAIL_NYV;
|
||||
|
||||
if (serial_compare_32(curtime, sig_expiration) == SERIAL_GT)
|
||||
continue;
|
||||
else
|
||||
failflags &= ~DNSSEC_FAIL_EXP;
|
||||
}
|
||||
|
||||
if (!(hash = hash_find(algo_digest_name(algo))))
|
||||
continue;
|
||||
else
|
||||
failflags &= ~DNSSEC_FAIL_NOKEYSUP;
|
||||
|
||||
if (labels > name_labels ||
|
||||
!hash_init(hash, &ctx, &digest))
|
||||
continue;
|
||||
|
||||
|
||||
/* OK, we have the signature record, see if the relevant DNSKEY is in the cache. */
|
||||
if (!key && !(crecp = cache_find_by_name(NULL, keyname, now, F_DNSKEY)))
|
||||
return STAT_NEED_KEY;
|
||||
@@ -730,7 +744,8 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
|
||||
}
|
||||
}
|
||||
|
||||
return STAT_BOGUS;
|
||||
/* If we reach this point, no verifying key was found */
|
||||
return STAT_BOGUS | failflags | DNSSEC_FAIL_NOKEY;
|
||||
}
|
||||
|
||||
|
||||
@@ -751,17 +766,18 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
|
||||
unsigned long ttl, sig_ttl;
|
||||
struct blockdata *key;
|
||||
union all_addr a;
|
||||
int failflags = DNSSEC_FAIL_NOSIG | DNSSEC_FAIL_NODSSUP | DNSSEC_FAIL_NOZONE | DNSSEC_FAIL_NOKEY;
|
||||
|
||||
if (ntohs(header->qdcount) != 1 ||
|
||||
RCODE(header) == SERVFAIL || RCODE(header) == REFUSED ||
|
||||
!extract_name(header, plen, &p, name, 1, 4))
|
||||
return STAT_BOGUS;
|
||||
return STAT_BOGUS | DNSSEC_FAIL_NOKEY;
|
||||
|
||||
GETSHORT(qtype, p);
|
||||
GETSHORT(qclass, p);
|
||||
|
||||
if (qtype != T_DNSKEY || qclass != class || ntohs(header->ancount) == 0)
|
||||
return STAT_BOGUS;
|
||||
return STAT_BOGUS | DNSSEC_FAIL_NOKEY;
|
||||
|
||||
/* See if we have cached a DS record which validates this key */
|
||||
if (!(crecp = cache_find_by_name(NULL, name, now, F_DS)))
|
||||
@@ -795,14 +811,17 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
|
||||
|
||||
GETSHORT(flags, p);
|
||||
if (*p++ != 3)
|
||||
return STAT_BOGUS;
|
||||
return STAT_BOGUS | DNSSEC_FAIL_NOKEY;
|
||||
algo = *p++;
|
||||
keytag = dnskey_keytag(algo, flags, p, rdlen - 4);
|
||||
key = NULL;
|
||||
|
||||
/* key must have zone key flag set */
|
||||
if (flags & 0x100)
|
||||
key = blockdata_alloc((char*)p, rdlen - 4);
|
||||
{
|
||||
key = blockdata_alloc((char*)p, rdlen - 4);
|
||||
failflags &= ~DNSSEC_FAIL_NOZONE;
|
||||
}
|
||||
|
||||
p = psave;
|
||||
|
||||
@@ -823,15 +842,23 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
|
||||
unsigned char *digest, *ds_digest;
|
||||
const struct nettle_hash *hash;
|
||||
int sigcnt, rrcnt;
|
||||
|
||||
int wire_len;
|
||||
|
||||
if (recp1->addr.ds.algo == algo &&
|
||||
recp1->addr.ds.keytag == keytag &&
|
||||
recp1->uid == (unsigned int)class &&
|
||||
(hash = hash_find(ds_digest_name(recp1->addr.ds.digest))) &&
|
||||
hash_init(hash, &ctx, &digest))
|
||||
|
||||
recp1->uid == (unsigned int)class)
|
||||
{
|
||||
int wire_len = to_wire(name);
|
||||
failflags &= ~DNSSEC_FAIL_NOKEY;
|
||||
|
||||
if (!(hash = hash_find(ds_digest_name(recp1->addr.ds.digest))))
|
||||
continue;
|
||||
else
|
||||
failflags &= ~DNSSEC_FAIL_NODSSUP;
|
||||
|
||||
if (!hash_init(hash, &ctx, &digest))
|
||||
continue;
|
||||
|
||||
wire_len = to_wire(name);
|
||||
|
||||
/* Note that digest may be different between DSs, so
|
||||
we can't move this outside the loop. */
|
||||
@@ -846,12 +873,23 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
|
||||
(ds_digest = blockdata_retrieve(recp1->addr.ds.keydata, recp1->addr.ds.keylen, NULL)) &&
|
||||
memcmp(ds_digest, digest, recp1->addr.ds.keylen) == 0 &&
|
||||
explore_rrset(header, plen, class, T_DNSKEY, name, keyname, &sigcnt, &rrcnt) &&
|
||||
sigcnt != 0 && rrcnt != 0 &&
|
||||
validate_rrset(now, header, plen, class, T_DNSKEY, sigcnt, rrcnt, name, keyname,
|
||||
NULL, key, rdlen - 4, algo, keytag, &sig_ttl) == STAT_SECURE)
|
||||
rrcnt != 0)
|
||||
{
|
||||
valid = 1;
|
||||
break;
|
||||
if (sigcnt == 0)
|
||||
continue;
|
||||
else
|
||||
failflags &= ~DNSSEC_FAIL_NOSIG;
|
||||
|
||||
rc = validate_rrset(now, header, plen, class, T_DNSKEY, sigcnt, rrcnt, name, keyname,
|
||||
NULL, key, rdlen - 4, algo, keytag, &sig_ttl);
|
||||
|
||||
failflags &= rc;
|
||||
|
||||
if (STAT_ISEQUAL(rc, STAT_SECURE))
|
||||
{
|
||||
valid = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -936,7 +974,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
|
||||
}
|
||||
|
||||
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DNSKEY");
|
||||
return STAT_BOGUS;
|
||||
return STAT_BOGUS | failflags;
|
||||
}
|
||||
|
||||
/* The DNS packet is expected to contain the answer to a DS query
|
||||
@@ -971,10 +1009,11 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
|
||||
else
|
||||
rc = dnssec_validate_reply(now, header, plen, name, keyname, NULL, 0, &neganswer, &nons, &neg_ttl);
|
||||
|
||||
if (rc == STAT_INSECURE)
|
||||
if (STAT_ISEQUAL(rc, STAT_INSECURE))
|
||||
{
|
||||
my_syslog(LOG_WARNING, _("Insecure DS reply received for %s, check domain configuration and upstream DNS server DNSSEC support"), name);
|
||||
rc = STAT_BOGUS;
|
||||
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS - not secure");
|
||||
return STAT_BOGUS | DNSSEC_FAIL_INDET;
|
||||
}
|
||||
|
||||
p = (unsigned char *)(header+1);
|
||||
@@ -984,13 +1023,13 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
|
||||
/* If the key needed to validate the DS is on the same domain as the DS, we'll
|
||||
loop getting nowhere. Stop that now. This can happen of the DS answer comes
|
||||
from the DS's zone, and not the parent zone. */
|
||||
if (rc == STAT_BOGUS || (rc == STAT_NEED_KEY && hostname_isequal(name, keyname)))
|
||||
if (STAT_ISEQUAL(rc, STAT_NEED_KEY) && hostname_isequal(name, keyname))
|
||||
{
|
||||
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS");
|
||||
return STAT_BOGUS;
|
||||
}
|
||||
|
||||
if (rc != STAT_SECURE)
|
||||
if (!STAT_ISEQUAL(rc, STAT_SECURE))
|
||||
return rc;
|
||||
|
||||
if (!neganswer)
|
||||
@@ -1456,7 +1495,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
|
||||
if (!(p = skip_name(nsecs[i], header, plen, 15)))
|
||||
return 0; /* bad packet */
|
||||
|
||||
p += 10; /* type, class, TTL, rdlen */
|
||||
p += 10; /* type, class, TTL, rdlen */
|
||||
algo = *p++;
|
||||
|
||||
if ((hash = hash_find(nsec3_digest_name(algo))))
|
||||
@@ -1959,15 +1998,15 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
||||
if (check_unsigned && i < ntohs(header->ancount))
|
||||
{
|
||||
rc = zone_status(name, class1, keyname, now);
|
||||
if (rc == STAT_SECURE)
|
||||
rc = STAT_BOGUS;
|
||||
if (STAT_ISEQUAL(rc, STAT_SECURE))
|
||||
rc = STAT_BOGUS | DNSSEC_FAIL_NOSIG;
|
||||
if (class)
|
||||
*class = class1; /* Class for NEED_DS or NEED_KEY */
|
||||
}
|
||||
else
|
||||
rc = STAT_INSECURE;
|
||||
|
||||
if (rc != STAT_INSECURE)
|
||||
if (!STAT_ISEQUAL(rc, STAT_INSECURE))
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
@@ -1978,7 +2017,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
||||
strcpy(daemon->workspacename, keyname);
|
||||
rc = zone_status(daemon->workspacename, class1, keyname, now);
|
||||
|
||||
if (rc == STAT_BOGUS || rc == STAT_NEED_KEY || rc == STAT_NEED_DS)
|
||||
if (STAT_ISEQUAL(rc, STAT_BOGUS) || STAT_ISEQUAL(rc, STAT_NEED_KEY) || STAT_ISEQUAL(rc, STAT_NEED_DS))
|
||||
{
|
||||
if (class)
|
||||
*class = class1; /* Class for NEED_DS or NEED_KEY */
|
||||
@@ -1986,13 +2025,13 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
||||
}
|
||||
|
||||
/* Zone is insecure, don't need to validate RRset */
|
||||
if (rc == STAT_SECURE)
|
||||
if (STAT_ISEQUAL(rc, STAT_SECURE))
|
||||
{
|
||||
unsigned long sig_ttl;
|
||||
rc = validate_rrset(now, header, plen, class1, type1, sigcnt,
|
||||
rrcnt, name, keyname, &wildname, NULL, 0, 0, 0, &sig_ttl);
|
||||
|
||||
if (rc == STAT_BOGUS || rc == STAT_NEED_KEY || rc == STAT_NEED_DS)
|
||||
if (STAT_ISEQUAL(rc, STAT_BOGUS) || STAT_ISEQUAL(rc, STAT_NEED_KEY) || STAT_ISEQUAL(rc, STAT_NEED_DS))
|
||||
{
|
||||
if (class)
|
||||
*class = class1; /* Class for DS or DNSKEY */
|
||||
@@ -2025,21 +2064,21 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
||||
Note that we may not yet have validated the NSEC/NSEC3 RRsets.
|
||||
That's not a problem since if the RRsets later fail
|
||||
we'll return BOGUS then. */
|
||||
if (rc == STAT_SECURE_WILDCARD &&
|
||||
if (STAT_ISEQUAL(rc, STAT_SECURE_WILDCARD) &&
|
||||
!prove_non_existence(header, plen, keyname, name, type1, class1, wildname, NULL, NULL))
|
||||
return STAT_BOGUS;
|
||||
return STAT_BOGUS | DNSSEC_FAIL_NONSEC;
|
||||
|
||||
rc = STAT_SECURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rc == STAT_INSECURE)
|
||||
if (STAT_ISEQUAL(rc, STAT_INSECURE))
|
||||
secure = STAT_INSECURE;
|
||||
}
|
||||
|
||||
/* OK, all the RRsets validate, now see if we have a missing answer or CNAME target. */
|
||||
if (secure == STAT_SECURE)
|
||||
if (STAT_ISEQUAL(secure, STAT_SECURE))
|
||||
for (j = 0; j <targetidx; j++)
|
||||
if ((p2 = targets[j]))
|
||||
{
|
||||
@@ -2057,16 +2096,16 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
||||
{
|
||||
/* Empty DS without NSECS */
|
||||
if (qtype == T_DS)
|
||||
return STAT_BOGUS;
|
||||
return STAT_BOGUS | DNSSEC_FAIL_NONSEC;
|
||||
|
||||
if ((rc = zone_status(name, qclass, keyname, now)) != STAT_SECURE)
|
||||
if (STAT_ISEQUAL((rc = zone_status(name, qclass, keyname, now)), STAT_SECURE))
|
||||
{
|
||||
if (class)
|
||||
*class = qclass; /* Class for NEED_DS or NEED_KEY */
|
||||
return rc;
|
||||
}
|
||||
|
||||
return STAT_BOGUS; /* signed zone, no NSECs */
|
||||
return STAT_BOGUS | DNSSEC_FAIL_NONSEC; /* signed zone, no NSECs */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2130,4 +2169,31 @@ size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char
|
||||
return ret;
|
||||
}
|
||||
|
||||
int errflags_to_ede(int status)
|
||||
{
|
||||
/* We can end up with more than one flag set for some errors,
|
||||
so this encodes a rough priority so the (eg) No sig is reported
|
||||
before no-unexpired-sig. */
|
||||
|
||||
if (status & DNSSEC_FAIL_NYV)
|
||||
return EDE_SIG_NYV;
|
||||
else if (status & DNSSEC_FAIL_EXP)
|
||||
return EDE_SIG_EXP;
|
||||
else if (status & DNSSEC_FAIL_NOKEYSUP)
|
||||
return EDE_USUPDNSKEY;
|
||||
else if (status & DNSSEC_FAIL_NOZONE)
|
||||
return EDE_NO_ZONEKEY;
|
||||
else if (status & DNSSEC_FAIL_NOKEY)
|
||||
return EDE_NO_DNSKEY;
|
||||
else if (status & DNSSEC_FAIL_NODSSUP)
|
||||
return EDE_USUPDS;
|
||||
else if (status & DNSSEC_FAIL_NONSEC)
|
||||
return EDE_NO_NSEC;
|
||||
else if (status & DNSSEC_FAIL_INDET)
|
||||
return EDE_DNSSEC_IND;
|
||||
else if (status & DNSSEC_FAIL_NOSIG)
|
||||
return EDE_NO_RRSIG;
|
||||
else
|
||||
return EDE_UNSET;
|
||||
}
|
||||
#endif /* HAVE_DNSSEC */
|
||||
|
||||
698
src/domain-match.c
Normal file
698
src/domain-match.c
Normal file
@@ -0,0 +1,698 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 dated June, 1991, or
|
||||
(at your option) version 3 dated 29 June, 2007.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
static int order(char *qdomain, size_t qlen, struct server *serv);
|
||||
static int order_qsort(const void *a, const void *b);
|
||||
static int order_servers(struct server *s, struct server *s2);
|
||||
|
||||
/* If the server is USE_RESOLV or LITERAL_ADDRES, it lives on the local_domains chain. */
|
||||
#define SERV_IS_LOCAL (SERV_USE_RESOLV | SERV_LITERAL_ADDRESS)
|
||||
|
||||
void build_server_array(void)
|
||||
{
|
||||
struct server *serv;
|
||||
int count = 0;
|
||||
|
||||
for (serv = daemon->servers; serv; serv = serv->next)
|
||||
#ifdef HAVE_LOOP
|
||||
if (!(serv->flags & SERV_LOOP))
|
||||
#endif
|
||||
{
|
||||
count++;
|
||||
if (serv->flags & SERV_WILDCARD)
|
||||
daemon->server_has_wildcard = 1;
|
||||
}
|
||||
|
||||
for (serv = daemon->local_domains; serv; serv = serv->next)
|
||||
{
|
||||
count++;
|
||||
if (serv->flags & SERV_WILDCARD)
|
||||
daemon->server_has_wildcard = 1;
|
||||
}
|
||||
|
||||
daemon->serverarraysz = count;
|
||||
|
||||
if (count > daemon->serverarrayhwm)
|
||||
{
|
||||
struct server **new;
|
||||
|
||||
count += 10; /* A few extra without re-allocating. */
|
||||
|
||||
if ((new = whine_malloc(count * sizeof(struct server *))))
|
||||
{
|
||||
if (daemon->serverarray)
|
||||
free(daemon->serverarray);
|
||||
|
||||
daemon->serverarray = new;
|
||||
daemon->serverarrayhwm = count;
|
||||
}
|
||||
}
|
||||
|
||||
count = 0;
|
||||
|
||||
for (serv = daemon->servers; serv; serv = serv->next)
|
||||
#ifdef HAVE_LOOP
|
||||
if (!(serv->flags & SERV_LOOP))
|
||||
#endif
|
||||
{
|
||||
daemon->serverarray[count] = serv;
|
||||
serv->serial = count;
|
||||
serv->last_server = -1;
|
||||
count++;
|
||||
}
|
||||
|
||||
for (serv = daemon->local_domains; serv; serv = serv->next, count++)
|
||||
daemon->serverarray[count] = serv;
|
||||
|
||||
qsort(daemon->serverarray, daemon->serverarraysz, sizeof(struct server *), order_qsort);
|
||||
|
||||
/* servers need the location in the array to find all the whole
|
||||
set of equivalent servers from a pointer to a single one. */
|
||||
for (count = 0; count < daemon->serverarraysz; count++)
|
||||
if (!(daemon->serverarray[count]->flags & SERV_IS_LOCAL))
|
||||
daemon->serverarray[count]->arrayposn = count;
|
||||
}
|
||||
|
||||
/* we're looking for the server whose domain is the longest exact match
|
||||
to the RH end of qdomain, or a local address if the flags match.
|
||||
Add '.' to the LHS of the query string so
|
||||
server=/.example.com/ works.
|
||||
|
||||
A flag of F_SERVER returns an upstream server only.
|
||||
A flag of F_DNSSECOK returns a DNSSEC capable server only and
|
||||
also disables NODOTS servers from consideration.
|
||||
A flag of F_DOMAINSRV returns a domain-specific server only.
|
||||
A flag of F_CONFIG returns anything that generates a local
|
||||
reply of IPv4 or IPV6.
|
||||
return 0 if nothing found, 1 otherwise.
|
||||
*/
|
||||
int lookup_domain(char *domain, int flags, int *lowout, int *highout)
|
||||
{
|
||||
int rc, crop_query, nodots;
|
||||
ssize_t qlen;
|
||||
int try, high, low = 0;
|
||||
int nlow = 0, nhigh = 0;
|
||||
char *cp, *qdomain = domain;
|
||||
|
||||
/* may be no configured servers. */
|
||||
if (daemon->serverarraysz == 0)
|
||||
return 0;
|
||||
|
||||
/* find query length and presence of '.' */
|
||||
for (cp = qdomain, nodots = 1, qlen = 0; *cp; qlen++, cp++)
|
||||
if (*cp == '.')
|
||||
nodots = 0;
|
||||
|
||||
/* Handle empty name, and searches for DNSSEC queries without
|
||||
diverting to NODOTS servers. */
|
||||
if (qlen == 0 || flags & F_DNSSECOK)
|
||||
nodots = 0;
|
||||
|
||||
/* Search shorter and shorter RHS substrings for a match */
|
||||
while (qlen >= 0)
|
||||
{
|
||||
/* Note that when we chop off a label, all the possible matches
|
||||
MUST be at a larger index than the nearest failing match with one more
|
||||
character, since the array is sorted longest to smallest. Hence
|
||||
we don't reset low to zero here, we can go further below and crop the
|
||||
search string to the size of the largest remaining server
|
||||
when this match fails. */
|
||||
high = daemon->serverarraysz;
|
||||
crop_query = 1;
|
||||
|
||||
/* binary search */
|
||||
while (1)
|
||||
{
|
||||
try = (low + high)/2;
|
||||
|
||||
if ((rc = order(qdomain, qlen, daemon->serverarray[try])) == 0)
|
||||
break;
|
||||
|
||||
if (rc < 0)
|
||||
{
|
||||
if (high == try)
|
||||
{
|
||||
/* qdomain is longer or same length as longest domain, and try == 0
|
||||
crop the query to the longest domain. */
|
||||
crop_query = qlen - daemon->serverarray[try]->domain_len;
|
||||
break;
|
||||
}
|
||||
high = try;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (low == try)
|
||||
{
|
||||
/* try now points to the last domain that sorts before the query, so
|
||||
we know that a substring of the query shorter than it is required to match, so
|
||||
find the largest domain that's shorter than try. Note that just going to
|
||||
try+1 is not optimal, consider searching bbb in (aaa,ccc,bb). try will point
|
||||
to aaa, since ccc sorts after bbb, but the first domain that has a chance to
|
||||
match is bb. So find the length of the first domain later than try which is
|
||||
is shorter than it.
|
||||
There's a nasty edge case when qdomain sorts before _any_ of the
|
||||
server domains, where try _doesn't point_ to the last domain that sorts
|
||||
before the query, since no such domain exists. In that case, the loop
|
||||
exits via the rc < 0 && high == try path above and this code is
|
||||
not executed. */
|
||||
ssize_t len, old = daemon->serverarray[try]->domain_len;
|
||||
while (++try != daemon->serverarraysz)
|
||||
{
|
||||
if (old != (len = daemon->serverarray[try]->domain_len))
|
||||
{
|
||||
crop_query = qlen - len;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
low = try;
|
||||
}
|
||||
};
|
||||
|
||||
if (rc == 0)
|
||||
{
|
||||
int found = 1;
|
||||
|
||||
if (daemon->server_has_wildcard)
|
||||
{
|
||||
/* if we have example.com and *example.com we need to check against *example.com,
|
||||
but the binary search may have found either. Use the fact that example.com is sorted before *example.com
|
||||
We favour example.com in the case that both match (ie www.example.com) */
|
||||
while (try != 0 && order(qdomain, qlen, daemon->serverarray[try-1]) == 0)
|
||||
try--;
|
||||
|
||||
if (!(qdomain == domain || *qdomain == 0 || *(qdomain-1) == '.'))
|
||||
{
|
||||
while (try < daemon->serverarraysz-1 && order(qdomain, qlen, daemon->serverarray[try+1]) == 0)
|
||||
try++;
|
||||
|
||||
if (!(daemon->serverarray[try]->flags & SERV_WILDCARD))
|
||||
found = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (found)
|
||||
{
|
||||
/* We've matched a setting which says to use servers without a domain.
|
||||
Continue the search with empty query */
|
||||
if (daemon->serverarray[try]->flags & SERV_USE_RESOLV)
|
||||
crop_query = qlen;
|
||||
else if (filter_servers(try, flags, &nlow, &nhigh))
|
||||
/* We have a match, but it may only be (say) an IPv6 address, and
|
||||
if the query wasn't for an AAAA record, it's no good, and we need
|
||||
to continue generalising */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* crop_query must be at least one always. */
|
||||
if (crop_query == 0)
|
||||
crop_query = 1;
|
||||
|
||||
/* strip chars off the query based on the largest possible remaining match,
|
||||
then continue to the start of the next label unless we have a wildcard
|
||||
domain somewhere, in which case we have to go one at a time. */
|
||||
qlen -= crop_query;
|
||||
qdomain += crop_query;
|
||||
if (!daemon->server_has_wildcard)
|
||||
while (qlen > 0 && (*(qdomain-1) != '.'))
|
||||
qlen--, qdomain++;
|
||||
}
|
||||
|
||||
/* domain has no dots, and we have at least one server configured to handle such,
|
||||
These servers always sort to the very end of the array.
|
||||
A configured server eg server=/lan/ will take precdence. */
|
||||
if (nodots &&
|
||||
(daemon->serverarray[daemon->serverarraysz-1]->flags & SERV_FOR_NODOTS) &&
|
||||
(nlow == nhigh || daemon->serverarray[nlow]->domain_len == 0))
|
||||
filter_servers(daemon->serverarraysz-1, flags, &nlow, &nhigh);
|
||||
|
||||
if (lowout)
|
||||
*lowout = nlow;
|
||||
|
||||
if (highout)
|
||||
*highout = nhigh;
|
||||
|
||||
if (nlow == nhigh)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Return first server in group of equivalent servers; this is the "master" record. */
|
||||
int server_samegroup(struct server *a, struct server *b)
|
||||
{
|
||||
return order_servers(a, b) == 0;
|
||||
}
|
||||
|
||||
int filter_servers(int seed, int flags, int *lowout, int *highout)
|
||||
{
|
||||
int nlow = seed, nhigh = seed;
|
||||
int i;
|
||||
|
||||
/* expand nlow and nhigh to cover all the records with the same domain
|
||||
nlow is the first, nhigh - 1 is the last. nlow=nhigh means no servers,
|
||||
which can happen below. */
|
||||
while (nlow > 0 && order_servers(daemon->serverarray[nlow-1], daemon->serverarray[nlow]) == 0)
|
||||
nlow--;
|
||||
|
||||
while (nhigh < daemon->serverarraysz-1 && order_servers(daemon->serverarray[nhigh], daemon->serverarray[nhigh+1]) == 0)
|
||||
nhigh++;
|
||||
|
||||
nhigh++;
|
||||
|
||||
#define SERV_LOCAL_ADDRESS (SERV_6ADDR | SERV_4ADDR | SERV_ALL_ZEROS)
|
||||
|
||||
if (flags & F_CONFIG)
|
||||
{
|
||||
/* We're just lookin for any matches that return an RR. */
|
||||
for (i = nlow; i < nhigh; i++)
|
||||
if (daemon->serverarray[i]->flags & SERV_LOCAL_ADDRESS)
|
||||
break;
|
||||
|
||||
/* failed, return failure. */
|
||||
if (i == nhigh)
|
||||
nhigh = nlow;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Now the servers are on order between low and high, in the order
|
||||
IPv6 addr, IPv4 addr, return zero for both, send upstream, no-data return.
|
||||
|
||||
See which of those match our query in that priority order and narrow (low, high) */
|
||||
|
||||
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_6ADDR); i++);
|
||||
|
||||
if (i != nlow && (flags & F_IPV6))
|
||||
nhigh = i;
|
||||
else
|
||||
{
|
||||
nlow = i;
|
||||
|
||||
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_4ADDR); i++);
|
||||
|
||||
if (i != nlow && (flags & F_IPV4))
|
||||
nhigh = i;
|
||||
else
|
||||
{
|
||||
nlow = i;
|
||||
|
||||
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_ALL_ZEROS); i++);
|
||||
|
||||
if (i != nlow && (flags & (F_IPV4 | F_IPV6)))
|
||||
nhigh = i;
|
||||
else
|
||||
{
|
||||
nlow = i;
|
||||
|
||||
/* now look for a server */
|
||||
for (i = nlow; i < nhigh && !(daemon->serverarray[i]->flags & SERV_LITERAL_ADDRESS); i++);
|
||||
|
||||
if (i != nlow)
|
||||
{
|
||||
/* If we want a server that can do DNSSEC, and this one can't,
|
||||
return nothing, similarly if were looking only for a server
|
||||
for a particular domain. */
|
||||
if ((flags & F_DNSSECOK) && !(daemon->serverarray[nlow]->flags & SERV_DO_DNSSEC))
|
||||
nlow = nhigh;
|
||||
else if ((flags & F_DOMAINSRV) && daemon->serverarray[nlow]->domain_len == 0)
|
||||
nlow = nhigh;
|
||||
else
|
||||
nhigh = i;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* --local=/domain/, only return if we don't need a server. */
|
||||
if (flags & (F_DNSSECOK | F_DOMAINSRV | F_SERVER))
|
||||
nhigh = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*lowout = nlow;
|
||||
*highout = nhigh;
|
||||
|
||||
return (nlow != nhigh);
|
||||
}
|
||||
|
||||
int is_local_answer(time_t now, int first, char *name)
|
||||
{
|
||||
int flags = 0;
|
||||
int rc = 0;
|
||||
|
||||
if ((flags = daemon->serverarray[first]->flags) & SERV_LITERAL_ADDRESS)
|
||||
{
|
||||
if (flags & SERV_4ADDR)
|
||||
rc = F_IPV4;
|
||||
else if (flags & SERV_6ADDR)
|
||||
rc = F_IPV6;
|
||||
else if (flags & SERV_ALL_ZEROS)
|
||||
rc = F_IPV4 | F_IPV6;
|
||||
else
|
||||
{
|
||||
/* argument first is the first struct server which matches the query type;
|
||||
now roll back to the server which is just the same domain, to check if that
|
||||
provides an answer of a different type. */
|
||||
|
||||
for (;first > 0 && order_servers(daemon->serverarray[first-1], daemon->serverarray[first]) == 0; first--);
|
||||
|
||||
if ((daemon->serverarray[first]->flags & SERV_LOCAL_ADDRESS) ||
|
||||
check_for_local_domain(name, now))
|
||||
rc = F_NOERR;
|
||||
else
|
||||
rc = F_NXDOMAIN;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header *header, char *name, char *limit, int first, int last, int ede)
|
||||
{
|
||||
int trunc = 0;
|
||||
unsigned char *p;
|
||||
int start;
|
||||
union all_addr addr;
|
||||
|
||||
if (flags & (F_NXDOMAIN | F_NOERR))
|
||||
log_query(flags | gotname | F_NEG | F_CONFIG | F_FORWARD, name, NULL, NULL);
|
||||
|
||||
setup_reply(header, flags, ede);
|
||||
|
||||
if (!(p = skip_questions(header, size)))
|
||||
return 0;
|
||||
|
||||
if (flags & gotname & F_IPV4)
|
||||
for (start = first; start != last; start++)
|
||||
{
|
||||
struct serv_addr4 *srv = (struct serv_addr4 *)daemon->serverarray[start];
|
||||
|
||||
if (srv->flags & SERV_ALL_ZEROS)
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
else
|
||||
addr.addr4 = srv->addr;
|
||||
|
||||
header->ancount = htons(ntohs(header->ancount) + 1);
|
||||
add_resource_record(header, limit, &trunc, sizeof(struct dns_header), &p, daemon->local_ttl, NULL, T_A, C_IN, "4", &addr);
|
||||
log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV6, name, (union all_addr *)&addr, NULL);
|
||||
}
|
||||
|
||||
if (flags & gotname & F_IPV6)
|
||||
for (start = first; start != last; start++)
|
||||
{
|
||||
struct serv_addr6 *srv = (struct serv_addr6 *)daemon->serverarray[start];
|
||||
|
||||
if (srv->flags & SERV_ALL_ZEROS)
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
else
|
||||
addr.addr6 = srv->addr;
|
||||
|
||||
header->ancount = htons(ntohs(header->ancount) + 1);
|
||||
add_resource_record(header, limit, &trunc, sizeof(struct dns_header), &p, daemon->local_ttl, NULL, T_AAAA, C_IN, "6", &addr);
|
||||
log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV4, name, (union all_addr *)&addr, NULL);
|
||||
}
|
||||
|
||||
if (trunc)
|
||||
header->hb3 |= HB3_TC;
|
||||
|
||||
return p - (unsigned char *)header;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
int dnssec_server(struct server *server, char *keyname, int *firstp, int *lastp)
|
||||
{
|
||||
int first, last, index;
|
||||
|
||||
/* Find server to send DNSSEC query to. This will normally be the
|
||||
same as for the original query, but may be another if
|
||||
servers for domains are involved. */
|
||||
if (!lookup_domain(keyname, F_DNSSECOK, &first, &last))
|
||||
return -1;
|
||||
|
||||
for (index = first; index != last; index++)
|
||||
if (daemon->serverarray[index] == server)
|
||||
break;
|
||||
|
||||
/* No match to server used for original query.
|
||||
Use newly looked up set. */
|
||||
if (index == last)
|
||||
index = daemon->serverarray[first]->last_server == -1 ?
|
||||
first : daemon->serverarray[first]->last_server;
|
||||
|
||||
if (firstp)
|
||||
*firstp = first;
|
||||
|
||||
if (lastp)
|
||||
*lastp = last;
|
||||
|
||||
return index;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* order by size, then by dictionary order */
|
||||
static int order(char *qdomain, size_t qlen, struct server *serv)
|
||||
{
|
||||
size_t dlen = 0;
|
||||
|
||||
/* servers for dotless names always sort last
|
||||
searched for name is never dotless. */
|
||||
if (serv->flags & SERV_FOR_NODOTS)
|
||||
return -1;
|
||||
|
||||
dlen = serv->domain_len;
|
||||
|
||||
if (qlen < dlen)
|
||||
return 1;
|
||||
|
||||
if (qlen > dlen)
|
||||
return -1;
|
||||
|
||||
return strcmp(qdomain, serv->domain);
|
||||
}
|
||||
|
||||
static int order_servers(struct server *s1, struct server *s2)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* need full comparison of dotless servers in
|
||||
order_qsort() and filter_servers() */
|
||||
|
||||
if (s1->flags & SERV_FOR_NODOTS)
|
||||
return (s2->flags & SERV_FOR_NODOTS) ? 0 : 1;
|
||||
|
||||
if ((rc = order(s1->domain, s1->domain_len, s2)) != 0)
|
||||
return rc;
|
||||
|
||||
/* For identical domains, sort wildcard ones first */
|
||||
if (s1->flags & SERV_WILDCARD)
|
||||
return (s2->flags & SERV_WILDCARD) ? 0 : 1;
|
||||
|
||||
return (s2->flags & SERV_WILDCARD) ? -1 : 0;
|
||||
}
|
||||
|
||||
static int order_qsort(const void *a, const void *b)
|
||||
{
|
||||
int rc;
|
||||
|
||||
struct server *s1 = *((struct server **)a);
|
||||
struct server *s2 = *((struct server **)b);
|
||||
|
||||
rc = order_servers(s1, s2);
|
||||
|
||||
/* Sort all literal NODATA and local IPV4 or IPV6 responses together,
|
||||
in a very specific order. We flip the SERV_LITERAL_ADDRESS bit
|
||||
so the order is IPv6 literal, IPv4 literal, all-zero literal,
|
||||
upstream server, NXDOMAIN literal. */
|
||||
if (rc == 0)
|
||||
rc = ((s2->flags & (SERV_LITERAL_ADDRESS | SERV_4ADDR | SERV_6ADDR | SERV_ALL_ZEROS)) ^ SERV_LITERAL_ADDRESS) -
|
||||
((s1->flags & (SERV_LITERAL_ADDRESS | SERV_4ADDR | SERV_6ADDR | SERV_ALL_ZEROS)) ^ SERV_LITERAL_ADDRESS);
|
||||
|
||||
/* Finally, order by appearance in /etc/resolv.conf etc, for --strict-order */
|
||||
if (rc == 0)
|
||||
if (!(s1->flags & SERV_LITERAL_ADDRESS))
|
||||
rc = s1->serial - s2->serial;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void mark_servers(int flag)
|
||||
{
|
||||
struct server *serv;
|
||||
|
||||
/* mark everything with argument flag */
|
||||
for (serv = daemon->servers; serv; serv = serv->next)
|
||||
if (serv->flags & flag)
|
||||
serv->flags |= SERV_MARK;
|
||||
else
|
||||
serv->flags &= ~SERV_MARK;
|
||||
|
||||
for (serv = daemon->local_domains; serv; serv = serv->next)
|
||||
if (serv->flags & flag)
|
||||
serv->flags |= SERV_MARK;
|
||||
else
|
||||
serv->flags &= ~SERV_MARK;
|
||||
}
|
||||
|
||||
void cleanup_servers(void)
|
||||
{
|
||||
struct server *serv, *tmp, **up;
|
||||
|
||||
/* unlink and free anything still marked. */
|
||||
for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp)
|
||||
{
|
||||
tmp = serv->next;
|
||||
if (serv->flags & SERV_MARK)
|
||||
{
|
||||
server_gone(serv);
|
||||
*up = serv->next;
|
||||
free(serv->domain);
|
||||
free(serv);
|
||||
}
|
||||
else
|
||||
up = &serv->next;
|
||||
}
|
||||
|
||||
for (serv = daemon->local_domains, up = &daemon->local_domains; serv; serv = tmp)
|
||||
{
|
||||
tmp = serv->next;
|
||||
if (serv->flags & SERV_MARK)
|
||||
{
|
||||
*up = serv->next;
|
||||
free(serv->domain);
|
||||
free(serv);
|
||||
}
|
||||
else
|
||||
up = &serv->next;
|
||||
}
|
||||
}
|
||||
|
||||
int add_update_server(int flags,
|
||||
union mysockaddr *addr,
|
||||
union mysockaddr *source_addr,
|
||||
const char *interface,
|
||||
const char *domain,
|
||||
union all_addr *local_addr)
|
||||
{
|
||||
struct server *serv = NULL;
|
||||
char *alloc_domain;
|
||||
|
||||
if (!domain)
|
||||
domain = "";
|
||||
|
||||
/* .domain == domain, for historical reasons. */
|
||||
if (*domain == '.')
|
||||
while (*domain == '.') domain++;
|
||||
else if (*domain == '*')
|
||||
{
|
||||
domain++;
|
||||
if (*domain != 0)
|
||||
flags |= SERV_WILDCARD;
|
||||
}
|
||||
|
||||
if (*domain == 0)
|
||||
alloc_domain = whine_malloc(1);
|
||||
else if (!(alloc_domain = canonicalise((char *)domain, NULL)))
|
||||
return 0;
|
||||
|
||||
/* See if there is a suitable candidate, and unmark
|
||||
only do this for forwarding servers, not
|
||||
address or local, to avoid delays on large numbers. */
|
||||
if (flags & SERV_IS_LOCAL)
|
||||
for (serv = daemon->servers; serv; serv = serv->next)
|
||||
if ((serv->flags & SERV_MARK) &&
|
||||
hostname_isequal(alloc_domain, serv->domain))
|
||||
break;
|
||||
|
||||
if (serv)
|
||||
{
|
||||
free(alloc_domain);
|
||||
alloc_domain = serv->domain;
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t size;
|
||||
|
||||
if (flags & SERV_LITERAL_ADDRESS)
|
||||
{
|
||||
if (flags & SERV_6ADDR)
|
||||
size = sizeof(struct serv_addr6);
|
||||
else if (flags & SERV_4ADDR)
|
||||
size = sizeof(struct serv_addr4);
|
||||
else
|
||||
size = sizeof(struct serv_local);
|
||||
}
|
||||
else
|
||||
size = sizeof(struct server);
|
||||
|
||||
if (!(serv = whine_malloc(size)))
|
||||
return 0;
|
||||
|
||||
if (flags & SERV_IS_LOCAL)
|
||||
{
|
||||
serv->next = daemon->local_domains;
|
||||
daemon->local_domains = serv;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct server *s;
|
||||
/* Add to the end of the chain, for order */
|
||||
if (!daemon->servers)
|
||||
daemon->servers = serv;
|
||||
else
|
||||
{
|
||||
for (s = daemon->servers; s->next; s = s->next);
|
||||
s->next = serv;
|
||||
}
|
||||
|
||||
serv->next = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(flags & SERV_IS_LOCAL))
|
||||
memset(serv, 0, sizeof(struct server));
|
||||
|
||||
serv->flags = flags;
|
||||
serv->domain = alloc_domain;
|
||||
serv->domain_len = strlen(alloc_domain);
|
||||
|
||||
if (flags & SERV_4ADDR)
|
||||
((struct serv_addr4*)serv)->addr = local_addr->addr4;
|
||||
|
||||
if (flags & SERV_6ADDR)
|
||||
((struct serv_addr6*)serv)->addr = local_addr->addr6;
|
||||
|
||||
if (!(flags & SERV_IS_LOCAL))
|
||||
{
|
||||
#ifdef HAVE_LOOP
|
||||
serv->uid = rand32();
|
||||
#endif
|
||||
|
||||
if (interface)
|
||||
safe_strncpy(serv->interface, interface, sizeof(serv->interface));
|
||||
if (addr)
|
||||
serv->addr = *addr;
|
||||
if (source_addr)
|
||||
serv->source_addr = *source_addr;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
68
src/domain.c
68
src/domain.c
@@ -18,8 +18,9 @@
|
||||
|
||||
|
||||
static struct cond_domain *search_domain(struct in_addr addr, struct cond_domain *c);
|
||||
static int match_domain(struct in_addr addr, struct cond_domain *c);
|
||||
static struct cond_domain *search_domain6(struct in6_addr *addr, struct cond_domain *c);
|
||||
|
||||
static int match_domain6(struct in6_addr *addr, struct cond_domain *c);
|
||||
|
||||
int is_name_synthetic(int flags, char *name, union all_addr *addr)
|
||||
{
|
||||
@@ -135,28 +136,9 @@ int is_name_synthetic(int flags, char *name, union all_addr *addr)
|
||||
}
|
||||
|
||||
if (hostname_isequal(c->domain, p+1) && inet_pton(prot, tail, addr))
|
||||
{
|
||||
if (prot == AF_INET)
|
||||
{
|
||||
if (!c->is6 &&
|
||||
ntohl(addr->addr4.s_addr) >= ntohl(c->start.s_addr) &&
|
||||
ntohl(addr->addr4.s_addr) <= ntohl(c->end.s_addr))
|
||||
found = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
u64 addrpart = addr6part(&addr->addr6);
|
||||
|
||||
if (c->is6 &&
|
||||
is_same_net6(&addr->addr6, &c->start6, 64) &&
|
||||
addrpart >= addr6part(&c->start6) &&
|
||||
addrpart <= addr6part(&c->end6))
|
||||
found = 1;
|
||||
}
|
||||
}
|
||||
|
||||
found = (prot == AF_INET) ? match_domain(addr->addr4, c) : match_domain6(&addr->addr6, c);
|
||||
}
|
||||
|
||||
|
||||
/* restore name */
|
||||
for (p = tail; *p; p++)
|
||||
if (*p == '.' || *p == ':')
|
||||
@@ -246,14 +228,22 @@ int is_rev_synth(int flag, union all_addr *addr, char *name)
|
||||
}
|
||||
|
||||
|
||||
static int match_domain(struct in_addr addr, struct cond_domain *c)
|
||||
{
|
||||
if (!c->is6 &&
|
||||
ntohl(addr.s_addr) >= ntohl(c->start.s_addr) &&
|
||||
ntohl(addr.s_addr) <= ntohl(c->end.s_addr))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct cond_domain *search_domain(struct in_addr addr, struct cond_domain *c)
|
||||
{
|
||||
for (; c; c = c->next)
|
||||
if (!c->is6 &&
|
||||
ntohl(addr.s_addr) >= ntohl(c->start.s_addr) &&
|
||||
ntohl(addr.s_addr) <= ntohl(c->end.s_addr))
|
||||
if (match_domain(addr, c))
|
||||
return c;
|
||||
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -267,16 +257,30 @@ char *get_domain(struct in_addr addr)
|
||||
return daemon->domain_suffix;
|
||||
}
|
||||
|
||||
|
||||
static struct cond_domain *search_domain6(struct in6_addr *addr, struct cond_domain *c)
|
||||
static int match_domain6(struct in6_addr *addr, struct cond_domain *c)
|
||||
{
|
||||
u64 addrpart = addr6part(addr);
|
||||
|
||||
if (c->is6)
|
||||
{
|
||||
if (c->prefixlen >= 64)
|
||||
{
|
||||
if (is_same_net6(addr, &c->start6, 64) &&
|
||||
addrpart >= addr6part(&c->start6) &&
|
||||
addrpart <= addr6part(&c->end6))
|
||||
return 1;
|
||||
}
|
||||
else if (is_same_net6(addr, &c->start6, c->prefixlen))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct cond_domain *search_domain6(struct in6_addr *addr, struct cond_domain *c)
|
||||
{
|
||||
for (; c; c = c->next)
|
||||
if (c->is6 &&
|
||||
is_same_net6(addr, &c->start6, 64) &&
|
||||
addrpart >= addr6part(&c->start6) &&
|
||||
addrpart <= addr6part(&c->end6))
|
||||
if (match_domain6(addr, c))
|
||||
return c;
|
||||
|
||||
return NULL;
|
||||
|
||||
63
src/edns0.c
63
src/edns0.c
@@ -427,6 +427,66 @@ int check_source(struct dns_header *header, size_t plen, unsigned char *pseudohe
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* See https://docs.umbrella.com/umbrella-api/docs/identifying-dns-traffic for
|
||||
* detailed information on packet formating.
|
||||
*/
|
||||
#define UMBRELLA_VERSION 1
|
||||
#define UMBRELLA_TYPESZ 2
|
||||
|
||||
#define UMBRELLA_ASSET 0x0004
|
||||
#define UMBRELLA_ASSETSZ sizeof(daemon->umbrella_asset)
|
||||
#define UMBRELLA_ORG 0x0008
|
||||
#define UMBRELLA_ORGSZ sizeof(daemon->umbrella_org)
|
||||
#define UMBRELLA_IPV4 0x0010
|
||||
#define UMBRELLA_IPV6 0x0020
|
||||
#define UMBRELLA_DEVICE 0x0040
|
||||
#define UMBRELLA_DEVICESZ sizeof(daemon->umbrella_device)
|
||||
|
||||
struct umbrella_opt {
|
||||
u8 magic[4];
|
||||
u8 version;
|
||||
u8 flags;
|
||||
/* We have 4 possible fields since we'll never send both IPv4 and
|
||||
* IPv6, so using the larger of the two to calculate max buffer size.
|
||||
* Each field also has a type header. So the following accounts for
|
||||
* the type headers and each field size to get a max buffer size.
|
||||
*/
|
||||
u8 fields[4 * UMBRELLA_TYPESZ + UMBRELLA_ORGSZ + IN6ADDRSZ + UMBRELLA_DEVICESZ + UMBRELLA_ASSETSZ];
|
||||
};
|
||||
|
||||
static size_t add_umbrella_opt(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *source, int *cacheable)
|
||||
{
|
||||
*cacheable = 0;
|
||||
|
||||
struct umbrella_opt opt = {{"ODNS"}, UMBRELLA_VERSION, 0, {}};
|
||||
u8 *u = &opt.fields[0];
|
||||
|
||||
if (daemon->umbrella_org) {
|
||||
PUTSHORT(UMBRELLA_ORG, u);
|
||||
PUTLONG(daemon->umbrella_org, u);
|
||||
}
|
||||
|
||||
int family = source->sa.sa_family;
|
||||
PUTSHORT(family == AF_INET ? UMBRELLA_IPV4 : UMBRELLA_IPV6, u);
|
||||
int size = family == AF_INET ? INADDRSZ : IN6ADDRSZ;
|
||||
memcpy(u, get_addrp(source, family), size);
|
||||
u += size;
|
||||
|
||||
if (option_bool(OPT_UMBRELLA_DEVID)) {
|
||||
PUTSHORT(UMBRELLA_DEVICE, u);
|
||||
memcpy(u, (char *)&daemon->umbrella_device, UMBRELLA_DEVICESZ);
|
||||
u += UMBRELLA_DEVICESZ;
|
||||
}
|
||||
|
||||
if (daemon->umbrella_asset) {
|
||||
PUTSHORT(UMBRELLA_ASSET, u);
|
||||
PUTLONG(daemon->umbrella_asset, u);
|
||||
}
|
||||
|
||||
int len = u - &opt.magic[0];
|
||||
return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, EDNS0_OPTION_UMBRELLA, (unsigned char *)&opt, len, 0, 1);
|
||||
}
|
||||
|
||||
/* Set *check_subnet if we add a client subnet option, which needs to checked
|
||||
in the reply. Set *cacheable to zero if we add an option which the answer
|
||||
may depend on. */
|
||||
@@ -445,6 +505,9 @@ size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *l
|
||||
if (daemon->dns_client_id)
|
||||
plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMCPEID,
|
||||
(unsigned char *)daemon->dns_client_id, strlen(daemon->dns_client_id), 0, 1);
|
||||
|
||||
if (option_bool(OPT_UMBRELLA))
|
||||
plen = add_umbrella_opt(header, plen, limit, source, cacheable);
|
||||
|
||||
if (option_bool(OPT_CLIENT_SUBNET))
|
||||
{
|
||||
|
||||
2310
src/forward.c
2310
src/forward.c
File diff suppressed because it is too large
Load Diff
18
src/helper.c
18
src/helper.c
@@ -235,7 +235,6 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
|
||||
}
|
||||
else
|
||||
continue;
|
||||
|
||||
|
||||
/* stringify MAC into dhcp_buff */
|
||||
p = daemon->dhcp_buff;
|
||||
@@ -433,7 +432,8 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
|
||||
buf = grab_extradata_lua(buf, end, "relay_address");
|
||||
else if (data.giaddr.s_addr != 0)
|
||||
{
|
||||
lua_pushstring(lua, inet_ntoa(data.giaddr));
|
||||
inet_ntop(AF_INET, &data.giaddr, daemon->addrbuff, ADDRSTRLEN);
|
||||
lua_pushstring(lua, daemon->addrbuff);
|
||||
lua_setfield(lua, -2, "relay_address");
|
||||
}
|
||||
|
||||
@@ -611,8 +611,13 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
|
||||
|
||||
if (is6)
|
||||
buf = grab_extradata(buf, end, "DNSMASQ_RELAY_ADDRESS", &err);
|
||||
else
|
||||
my_setenv("DNSMASQ_RELAY_ADDRESS", data.giaddr.s_addr != 0 ? inet_ntoa(data.giaddr) : NULL, &err);
|
||||
else
|
||||
{
|
||||
const char *giaddr = NULL;
|
||||
if (data.giaddr.s_addr != 0)
|
||||
giaddr = inet_ntop(AF_INET, &data.giaddr, daemon->addrbuff, ADDRSTRLEN);
|
||||
my_setenv("DNSMASQ_RELAY_ADDRESS", giaddr, &err);
|
||||
}
|
||||
|
||||
for (i = 0; buf; i++)
|
||||
{
|
||||
@@ -882,7 +887,4 @@ void helper_write(void)
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#endif /* HAVE_SCRIPT */
|
||||
|
||||
@@ -295,4 +295,3 @@ int inotify_check(time_t now)
|
||||
}
|
||||
|
||||
#endif /* INOTIFY */
|
||||
|
||||
|
||||
@@ -31,4 +31,3 @@
|
||||
&& ((__const uint32_t *) (a))[1] == 0 \
|
||||
&& ((__const uint32_t *) (a))[2] == 0 \
|
||||
&& ((__const uint32_t *) (a))[3] == 0)
|
||||
|
||||
|
||||
@@ -1021,6 +1021,7 @@ void lease_set_hostname(struct dhcp_lease *lease, const char *name, int auth, ch
|
||||
}
|
||||
|
||||
kill_name(lease_tmp);
|
||||
lease_tmp->flags |= LEASE_CHANGED; /* run script on change */
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1201,8 +1202,4 @@ void lease_add_extradata(struct dhcp_lease *lease, unsigned char *data, unsigned
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
#endif /* HAVE_DHCP */
|
||||
|
||||
24
src/loop.c
24
src/loop.c
@@ -30,12 +30,14 @@ void loop_send_probes()
|
||||
/* Loop through all upstream servers not for particular domains, and send a query to that server which is
|
||||
identifiable, via the uid. If we see that query back again, then the server is looping, and we should not use it. */
|
||||
for (serv = daemon->servers; serv; serv = serv->next)
|
||||
if (!(serv->flags &
|
||||
(SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND | SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_LOOP)))
|
||||
if (strlen(serv->domain) == 0 &&
|
||||
!(serv->flags & (SERV_FOR_NODOTS)))
|
||||
{
|
||||
ssize_t len = loop_make_probe(serv->uid);
|
||||
int fd;
|
||||
|
||||
serv->flags &= ~SERV_LOOP;
|
||||
|
||||
if ((fd = allocate_rfd(&rfds, serv)) == -1)
|
||||
continue;
|
||||
|
||||
@@ -96,15 +98,15 @@ int detect_loop(char *query, int type)
|
||||
uid = strtol(query, NULL, 16);
|
||||
|
||||
for (serv = daemon->servers; serv; serv = serv->next)
|
||||
if (!(serv->flags &
|
||||
(SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND | SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_LOOP)) &&
|
||||
uid == serv->uid)
|
||||
{
|
||||
serv->flags |= SERV_LOOP;
|
||||
check_servers(); /* log new state */
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (strlen(serv->domain) == 0 &&
|
||||
!(serv->flags & SERV_LOOP) &&
|
||||
uid == serv->uid)
|
||||
{
|
||||
serv->flags |= SERV_LOOP;
|
||||
check_servers(1); /* log new state - don't send more probes. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -402,6 +402,4 @@ static unsigned nl_async(struct nlmsghdr *h, unsigned state)
|
||||
}
|
||||
return state;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* HAVE_LINUX_NETWORK */
|
||||
|
||||
353
src/network.c
353
src/network.c
@@ -31,7 +31,7 @@ int indextoname(int fd, int index, char *name)
|
||||
|
||||
safe_strncpy(name, ifr.ifr_name, IF_NAMESIZE);
|
||||
|
||||
return 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -1479,8 +1479,7 @@ void pre_allocate_sfds(void)
|
||||
}
|
||||
|
||||
for (srv = daemon->servers; srv; srv = srv->next)
|
||||
if (!(srv->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND)) &&
|
||||
!allocate_sfd(&srv->source_addr, srv->interface, srv->ifindex) &&
|
||||
if (!allocate_sfd(&srv->source_addr, srv->interface, srv->ifindex) &&
|
||||
errno != 0 &&
|
||||
option_bool(OPT_NOWILD))
|
||||
{
|
||||
@@ -1495,136 +1494,23 @@ void pre_allocate_sfds(void)
|
||||
}
|
||||
}
|
||||
|
||||
void mark_servers(int flag)
|
||||
{
|
||||
struct server *serv;
|
||||
|
||||
/* mark everything with argument flag */
|
||||
for (serv = daemon->servers; serv; serv = serv->next)
|
||||
{
|
||||
if (serv->flags & flag)
|
||||
serv->flags |= SERV_MARK;
|
||||
#ifdef HAVE_LOOP
|
||||
/* Give looped servers another chance */
|
||||
serv->flags &= ~SERV_LOOP;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void cleanup_servers(void)
|
||||
{
|
||||
struct server *serv, *tmp, **up;
|
||||
|
||||
/* unlink and free anything still marked. */
|
||||
for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp)
|
||||
{
|
||||
tmp = serv->next;
|
||||
if (serv->flags & SERV_MARK)
|
||||
{
|
||||
server_gone(serv);
|
||||
*up = serv->next;
|
||||
if (serv->domain)
|
||||
free(serv->domain);
|
||||
free(serv);
|
||||
}
|
||||
else
|
||||
up = &serv->next;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LOOP
|
||||
/* Now we have a new set of servers, test for loops. */
|
||||
loop_send_probes();
|
||||
#endif
|
||||
}
|
||||
|
||||
void add_update_server(int flags,
|
||||
union mysockaddr *addr,
|
||||
union mysockaddr *source_addr,
|
||||
const char *interface,
|
||||
const char *domain)
|
||||
{
|
||||
struct server *serv, *next = NULL;
|
||||
char *domain_str = NULL;
|
||||
|
||||
/* See if there is a suitable candidate, and unmark */
|
||||
for (serv = daemon->servers; serv; serv = serv->next)
|
||||
if (serv->flags & SERV_MARK)
|
||||
{
|
||||
if (domain)
|
||||
{
|
||||
if (!(serv->flags & SERV_HAS_DOMAIN) || !hostname_isequal(domain, serv->domain))
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (serv->flags & SERV_HAS_DOMAIN)
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (serv)
|
||||
{
|
||||
domain_str = serv->domain;
|
||||
next = serv->next;
|
||||
}
|
||||
else if ((serv = whine_malloc(sizeof (struct server))))
|
||||
{
|
||||
/* Not found, create a new one. */
|
||||
if (domain && !(domain_str = whine_malloc(strlen(domain)+1)))
|
||||
{
|
||||
free(serv);
|
||||
serv = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct server *s;
|
||||
/* Add to the end of the chain, for order */
|
||||
if (!daemon->servers)
|
||||
daemon->servers = serv;
|
||||
else
|
||||
{
|
||||
for (s = daemon->servers; s->next; s = s->next);
|
||||
s->next = serv;
|
||||
}
|
||||
if (domain)
|
||||
strcpy(domain_str, domain);
|
||||
}
|
||||
}
|
||||
|
||||
if (serv)
|
||||
{
|
||||
memset(serv, 0, sizeof(struct server));
|
||||
serv->flags = flags;
|
||||
serv->domain = domain_str;
|
||||
serv->next = next;
|
||||
serv->queries = serv->failed_queries = 0;
|
||||
#ifdef HAVE_LOOP
|
||||
serv->uid = rand32();
|
||||
#endif
|
||||
|
||||
if (domain)
|
||||
serv->flags |= SERV_HAS_DOMAIN;
|
||||
|
||||
if (interface)
|
||||
safe_strncpy(serv->interface, interface, sizeof(serv->interface));
|
||||
if (addr)
|
||||
serv->addr = *addr;
|
||||
if (source_addr)
|
||||
serv->source_addr = *source_addr;
|
||||
}
|
||||
}
|
||||
|
||||
void check_servers(void)
|
||||
void check_servers(int no_loop_check)
|
||||
{
|
||||
struct irec *iface;
|
||||
struct server *serv;
|
||||
struct serverfd *sfd, *tmp, **up;
|
||||
int port = 0, count;
|
||||
int locals = 0;
|
||||
|
||||
#ifdef HAVE_LOOP
|
||||
if (!no_loop_check)
|
||||
loop_send_probes();
|
||||
#endif
|
||||
|
||||
/* interface may be new since startup */
|
||||
/* clear all marks. */
|
||||
mark_servers(0);
|
||||
|
||||
/* interface may be new since startup */
|
||||
if (!option_bool(OPT_NOWILD))
|
||||
enumerate_interfaces(0);
|
||||
|
||||
@@ -1634,114 +1520,117 @@ void check_servers(void)
|
||||
|
||||
for (count = 0, serv = daemon->servers; serv; serv = serv->next)
|
||||
{
|
||||
if (!(serv->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND)))
|
||||
{
|
||||
/* Init edns_pktsz for newly created server records. */
|
||||
if (serv->edns_pktsz == 0)
|
||||
serv->edns_pktsz = daemon->edns_pktsz;
|
||||
|
||||
/* Init edns_pktsz for newly created server records. */
|
||||
if (serv->edns_pktsz == 0)
|
||||
serv->edns_pktsz = daemon->edns_pktsz;
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID))
|
||||
{
|
||||
if (!(serv->flags & SERV_FOR_NODOTS))
|
||||
serv->flags |= SERV_DO_DNSSEC;
|
||||
if (option_bool(OPT_DNSSEC_VALID))
|
||||
{
|
||||
if (!(serv->flags & SERV_FOR_NODOTS))
|
||||
serv->flags |= SERV_DO_DNSSEC;
|
||||
|
||||
/* Disable DNSSEC validation when using server=/domain/.... servers
|
||||
unless there's a configured trust anchor. */
|
||||
if (strlen(serv->domain) != 0)
|
||||
{
|
||||
struct ds_config *ds;
|
||||
char *domain = serv->domain;
|
||||
|
||||
/* Disable DNSSEC validation when using server=/domain/.... servers
|
||||
unless there's a configured trust anchor. */
|
||||
if (serv->flags & SERV_HAS_DOMAIN)
|
||||
{
|
||||
struct ds_config *ds;
|
||||
char *domain = serv->domain;
|
||||
|
||||
/* .example.com is valid */
|
||||
while (*domain == '.')
|
||||
domain++;
|
||||
|
||||
for (ds = daemon->ds; ds; ds = ds->next)
|
||||
if (ds->name[0] != 0 && hostname_isequal(domain, ds->name))
|
||||
break;
|
||||
|
||||
if (!ds)
|
||||
serv->flags &= ~SERV_DO_DNSSEC;
|
||||
}
|
||||
/* .example.com is valid */
|
||||
while (*domain == '.')
|
||||
domain++;
|
||||
|
||||
for (ds = daemon->ds; ds; ds = ds->next)
|
||||
if (ds->name[0] != 0 && hostname_isequal(domain, ds->name))
|
||||
break;
|
||||
|
||||
if (!ds)
|
||||
serv->flags &= ~SERV_DO_DNSSEC;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
port = prettyprint_addr(&serv->addr, daemon->namebuff);
|
||||
|
||||
/* 0.0.0.0 is nothing, the stack treats it like 127.0.0.1 */
|
||||
if (serv->addr.sa.sa_family == AF_INET &&
|
||||
serv->addr.in.sin_addr.s_addr == 0)
|
||||
{
|
||||
serv->flags |= SERV_MARK;
|
||||
continue;
|
||||
}
|
||||
|
||||
for (iface = daemon->interfaces; iface; iface = iface->next)
|
||||
if (sockaddr_isequal(&serv->addr, &iface->addr))
|
||||
break;
|
||||
if (iface)
|
||||
{
|
||||
my_syslog(LOG_WARNING, _("ignoring nameserver %s - local interface"), daemon->namebuff);
|
||||
serv->flags |= SERV_MARK;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Do we need a socket set? */
|
||||
if (!serv->sfd &&
|
||||
!(serv->sfd = allocate_sfd(&serv->source_addr, serv->interface, serv->ifindex)) &&
|
||||
errno != 0)
|
||||
{
|
||||
my_syslog(LOG_WARNING,
|
||||
_("ignoring nameserver %s - cannot make/bind socket: %s"),
|
||||
daemon->namebuff, strerror(errno));
|
||||
serv->flags |= SERV_MARK;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (serv->sfd)
|
||||
serv->sfd->used = 1;
|
||||
|
||||
port = prettyprint_addr(&serv->addr, daemon->namebuff);
|
||||
|
||||
/* 0.0.0.0 is nothing, the stack treats it like 127.0.0.1 */
|
||||
if (serv->addr.sa.sa_family == AF_INET &&
|
||||
serv->addr.in.sin_addr.s_addr == 0)
|
||||
{
|
||||
serv->flags |= SERV_MARK;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(serv->flags & SERV_NO_REBIND) && !(serv->flags & SERV_LITERAL_ADDRESS))
|
||||
for (iface = daemon->interfaces; iface; iface = iface->next)
|
||||
if (sockaddr_isequal(&serv->addr, &iface->addr))
|
||||
break;
|
||||
if (iface)
|
||||
{
|
||||
if (++count > SERVERS_LOGGED)
|
||||
continue;
|
||||
|
||||
if (serv->flags & (SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_USE_RESOLV))
|
||||
{
|
||||
char *s1, *s2, *s3 = "";
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID) && !(serv->flags & SERV_DO_DNSSEC))
|
||||
s3 = _("(no DNSSEC)");
|
||||
#endif
|
||||
if (!(serv->flags & SERV_HAS_DOMAIN))
|
||||
s1 = _("unqualified"), s2 = _("names");
|
||||
else if (strlen(serv->domain) == 0)
|
||||
s1 = _("default"), s2 = "";
|
||||
else
|
||||
s1 = _("domain"), s2 = serv->domain;
|
||||
|
||||
if (serv->flags & SERV_NO_ADDR)
|
||||
{
|
||||
count--;
|
||||
if (++locals <= LOCALS_LOGGED)
|
||||
my_syslog(LOG_INFO, _("using only locally-known addresses for %s %s"), s1, s2);
|
||||
}
|
||||
else if (serv->flags & SERV_USE_RESOLV)
|
||||
my_syslog(LOG_INFO, _("using standard nameservers for %s %s"), s1, s2);
|
||||
else
|
||||
my_syslog(LOG_INFO, _("using nameserver %s#%d for %s %s %s"), daemon->namebuff, port, s1, s2, s3);
|
||||
}
|
||||
#ifdef HAVE_LOOP
|
||||
else if (serv->flags & SERV_LOOP)
|
||||
my_syslog(LOG_INFO, _("NOT using nameserver %s#%d - query loop detected"), daemon->namebuff, port);
|
||||
#endif
|
||||
else if (serv->interface[0] != 0)
|
||||
my_syslog(LOG_INFO, _("using nameserver %s#%d(via %s)"), daemon->namebuff, port, serv->interface);
|
||||
else
|
||||
my_syslog(LOG_INFO, _("using nameserver %s#%d"), daemon->namebuff, port);
|
||||
my_syslog(LOG_WARNING, _("ignoring nameserver %s - local interface"), daemon->namebuff);
|
||||
serv->flags |= SERV_MARK;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Do we need a socket set? */
|
||||
if (!serv->sfd &&
|
||||
!(serv->sfd = allocate_sfd(&serv->source_addr, serv->interface, serv->ifindex)) &&
|
||||
errno != 0)
|
||||
{
|
||||
my_syslog(LOG_WARNING,
|
||||
_("ignoring nameserver %s - cannot make/bind socket: %s"),
|
||||
daemon->namebuff, strerror(errno));
|
||||
serv->flags |= SERV_MARK;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (serv->sfd)
|
||||
serv->sfd->used = 1;
|
||||
|
||||
if (++count > SERVERS_LOGGED)
|
||||
continue;
|
||||
|
||||
if (strlen(serv->domain) != 0 || (serv->flags & SERV_FOR_NODOTS))
|
||||
{
|
||||
char *s1, *s2, *s3 = "", *s4 = "";
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID) && !(serv->flags & SERV_DO_DNSSEC))
|
||||
s3 = _("(no DNSSEC)");
|
||||
#endif
|
||||
if (serv->flags & SERV_FOR_NODOTS)
|
||||
s1 = _("unqualified"), s2 = _("names");
|
||||
else if (strlen(serv->domain) == 0)
|
||||
s1 = _("default"), s2 = "";
|
||||
else
|
||||
s1 = _("domain"), s2 = serv->domain, s4 = (serv->flags & SERV_WILDCARD) ? "*" : "";
|
||||
|
||||
my_syslog(LOG_INFO, _("using nameserver %s#%d for %s %s%s %s"), daemon->namebuff, port, s1, s4, s2, s3);
|
||||
}
|
||||
#ifdef HAVE_LOOP
|
||||
else if (serv->flags & SERV_LOOP)
|
||||
my_syslog(LOG_INFO, _("NOT using nameserver %s#%d - query loop detected"), daemon->namebuff, port);
|
||||
#endif
|
||||
else if (serv->interface[0] != 0)
|
||||
my_syslog(LOG_INFO, _("using nameserver %s#%d(via %s)"), daemon->namebuff, port, serv->interface);
|
||||
else
|
||||
my_syslog(LOG_INFO, _("using nameserver %s#%d"), daemon->namebuff, port);
|
||||
|
||||
}
|
||||
|
||||
for (count = 0, serv = daemon->local_domains; serv; serv = serv->next)
|
||||
{
|
||||
if (++count > SERVERS_LOGGED)
|
||||
continue;
|
||||
|
||||
if ((serv->flags & SERV_LITERAL_ADDRESS) &&
|
||||
!(serv->flags & (SERV_6ADDR | SERV_4ADDR | SERV_ALL_ZEROS)))
|
||||
{
|
||||
count--;
|
||||
if (++locals <= LOCALS_LOGGED)
|
||||
my_syslog(LOG_INFO, _("using only locally-known addresses for %s"), serv->domain);
|
||||
}
|
||||
else if (serv->flags & SERV_USE_RESOLV)
|
||||
my_syslog(LOG_INFO, _("using standard nameservers for %s"), serv->domain);
|
||||
}
|
||||
|
||||
if (locals > LOCALS_LOGGED)
|
||||
@@ -1763,7 +1652,8 @@ void check_servers(void)
|
||||
up = &sfd->next;
|
||||
}
|
||||
|
||||
cleanup_servers();
|
||||
cleanup_servers(); /* remove servers we just deleted. */
|
||||
build_server_array();
|
||||
}
|
||||
|
||||
/* Return zero if no servers found, in that case we keep polling.
|
||||
@@ -1798,7 +1688,7 @@ int reload_servers(char *fname)
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
memset(&source_addr, 0, sizeof(source_addr));
|
||||
|
||||
if ((addr.in.sin_addr.s_addr = inet_addr(token)) != (in_addr_t) -1)
|
||||
if (inet_pton(AF_INET, token, &addr.in.sin_addr) > 0)
|
||||
{
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
source_addr.in.sin_len = addr.in.sin_len = sizeof(source_addr.in);
|
||||
@@ -1836,7 +1726,7 @@ int reload_servers(char *fname)
|
||||
continue;
|
||||
}
|
||||
|
||||
add_update_server(SERV_FROM_RESOLV, &addr, &source_addr, NULL, NULL);
|
||||
add_update_server(SERV_FROM_RESOLV, &addr, &source_addr, NULL, NULL, NULL);
|
||||
gotone = 1;
|
||||
}
|
||||
|
||||
@@ -1869,8 +1759,3 @@ void newaddress(time_t now)
|
||||
lease_find_interfaces(now);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
653
src/option.c
653
src/option.c
@@ -170,6 +170,10 @@ struct myoption {
|
||||
#define LOPT_PXE_VENDOR 361
|
||||
#define LOPT_DYNHOST 362
|
||||
#define LOPT_LOG_DEBUG 363
|
||||
#define LOPT_UMBRELLA 364
|
||||
#define LOPT_CMARK_ALST_EN 365
|
||||
#define LOPT_CMARK_ALST 366
|
||||
#define LOPT_QUIET_TFTP 367
|
||||
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
static const struct option opts[] =
|
||||
@@ -323,6 +327,8 @@ static const struct myoption opts[] =
|
||||
{ "auth-sec-servers", 1, 0, LOPT_AUTHSFS },
|
||||
{ "auth-peer", 1, 0, LOPT_AUTHPEER },
|
||||
{ "ipset", 1, 0, LOPT_IPSET },
|
||||
{ "connmark-allowlist-enable", 2, 0, LOPT_CMARK_ALST_EN },
|
||||
{ "connmark-allowlist", 1, 0, LOPT_CMARK_ALST },
|
||||
{ "synth-domain", 1, 0, LOPT_SYNTH },
|
||||
{ "dnssec", 0, 0, LOPT_SEC_VALID },
|
||||
{ "trust-anchor", 1, 0, LOPT_TRUST_ANCHOR },
|
||||
@@ -345,6 +351,8 @@ static const struct myoption opts[] =
|
||||
{ "dhcp-ignore-clid", 0, 0, LOPT_IGNORE_CLID },
|
||||
{ "dynamic-host", 1, 0, LOPT_DYNHOST },
|
||||
{ "log-debug", 0, 0, LOPT_LOG_DEBUG },
|
||||
{ "umbrella", 2, 0, LOPT_UMBRELLA },
|
||||
{ "quiet-tftp", 0, 0, LOPT_QUIET_TFTP },
|
||||
{ NULL, 0, 0, 0 }
|
||||
};
|
||||
|
||||
@@ -506,6 +514,8 @@ static struct {
|
||||
{ LOPT_AUTHSFS, ARG_DUP, "<NS>[,<NS>...]", gettext_noop("Secondary authoritative nameservers for forward domains"), NULL },
|
||||
{ LOPT_AUTHPEER, ARG_DUP, "<ipaddr>[,<ipaddr>...]", gettext_noop("Peers which are allowed to do zone transfer"), NULL },
|
||||
{ LOPT_IPSET, ARG_DUP, "/<domain>[/<domain>...]/<ipset>...", gettext_noop("Specify ipsets to which matching domains should be added"), NULL },
|
||||
{ LOPT_CMARK_ALST_EN, ARG_ONE, "[=<mask>]", gettext_noop("Enable filtering of DNS queries with connection-track marks."), NULL },
|
||||
{ LOPT_CMARK_ALST, ARG_DUP, "<connmark>[/<mask>][,<pattern>[/<pattern>...]]", gettext_noop("Set allowed DNS patterns for a connection-track mark."), NULL },
|
||||
{ LOPT_SYNTH, ARG_DUP, "<domain>,<range>,[<prefix>]", gettext_noop("Specify a domain and address range for synthesised names"), NULL },
|
||||
{ LOPT_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 },
|
||||
@@ -527,6 +537,8 @@ static struct {
|
||||
{ LOPT_DUMPFILE, ARG_ONE, "<path>", gettext_noop("Path to debug packet dump file"), NULL },
|
||||
{ LOPT_DUMPMASK, ARG_ONE, "<hex>", gettext_noop("Mask which packets to dump"), NULL },
|
||||
{ LOPT_SCRIPT_TIME, OPT_LEASE_RENEW, NULL, gettext_noop("Call dhcp-script when lease expiry changes."), NULL },
|
||||
{ LOPT_UMBRELLA, ARG_ONE, "[=<optspec>]", gettext_noop("Send Cisco Umbrella identifiers including remote IP."), NULL },
|
||||
{ LOPT_QUIET_TFTP, OPT_QUIET_TFTP, NULL, gettext_noop("Do not log routine TFTP."), NULL },
|
||||
{ 0, 0, NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
@@ -641,6 +653,9 @@ static char *canonicalise_opt(char *s)
|
||||
if (!s)
|
||||
return 0;
|
||||
|
||||
if (strlen(s) == 0)
|
||||
return opt_string_alloc("");
|
||||
|
||||
unhide_metas(s);
|
||||
if (!(ret = canonicalise(s, &nomem)) && nomem)
|
||||
{
|
||||
@@ -653,7 +668,7 @@ static char *canonicalise_opt(char *s)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int atoi_check(char *a, int *res)
|
||||
static int numeric_check(char *a)
|
||||
{
|
||||
char *p;
|
||||
|
||||
@@ -666,10 +681,32 @@ static int atoi_check(char *a, int *res)
|
||||
if (*p < '0' || *p > '9')
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int atoi_check(char *a, int *res)
|
||||
{
|
||||
if (!numeric_check(a))
|
||||
return 0;
|
||||
*res = atoi(a);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int strtoul_check(char *a, u32 *res)
|
||||
{
|
||||
unsigned long x;
|
||||
|
||||
if (!numeric_check(a))
|
||||
return 0;
|
||||
x = strtoul(a, NULL, 10);
|
||||
if (errno || x > UINT32_MAX) {
|
||||
errno = 0;
|
||||
return 0;
|
||||
}
|
||||
*res = (u32)x;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int atoi_check16(char *a, int *res)
|
||||
{
|
||||
if (!(atoi_check(a, res)) ||
|
||||
@@ -787,21 +824,23 @@ static char *parse_mysockaddr(char *arg, union mysockaddr *addr)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_addr, char *interface, int *flags)
|
||||
char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_addr, char *interface, u16 *flags)
|
||||
{
|
||||
int source_port = 0, serv_port = NAMESERVER_PORT;
|
||||
char *portno, *source;
|
||||
char *interface_opt = NULL;
|
||||
int scope_index = 0;
|
||||
char *scope_id;
|
||||
|
||||
if (!arg || strlen(arg) == 0)
|
||||
|
||||
*interface = 0;
|
||||
|
||||
if (strcmp(arg, "#") == 0)
|
||||
{
|
||||
*flags |= SERV_NO_ADDR;
|
||||
*interface = 0;
|
||||
if (flags)
|
||||
*flags |= SERV_USE_RESOLV;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
if ((source = split_chr(arg, '@')) && /* is there a source. */
|
||||
(portno = split_chr(source, '#')) &&
|
||||
!atoi_check16(portno, &source_port))
|
||||
@@ -896,66 +935,52 @@ char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_a
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct server *add_rev4(struct in_addr addr, int msize)
|
||||
static int domain_rev4(char *domain, struct in_addr addr, int msize)
|
||||
{
|
||||
struct server *serv = opt_malloc(sizeof(struct server));
|
||||
in_addr_t a = ntohl(addr.s_addr);
|
||||
char *p;
|
||||
|
||||
memset(serv, 0, sizeof(struct server));
|
||||
p = serv->domain = opt_malloc(29); /* strlen("xxx.yyy.zzz.ttt.in-addr.arpa")+1 */
|
||||
|
||||
in_addr_t a = ntohl(addr.s_addr);
|
||||
|
||||
*domain = 0;
|
||||
|
||||
switch (msize)
|
||||
{
|
||||
case 32:
|
||||
p += sprintf(p, "%u.", a & 0xff);
|
||||
domain += sprintf(domain, "%u.", a & 0xff);
|
||||
/* fall through */
|
||||
case 24:
|
||||
p += sprintf(p, "%d.", (a >> 8) & 0xff);
|
||||
domain += sprintf(domain, "%d.", (a >> 8) & 0xff);
|
||||
/* fall through */
|
||||
case 16:
|
||||
p += sprintf(p, "%d.", (a >> 16) & 0xff);
|
||||
domain += sprintf(domain, "%d.", (a >> 16) & 0xff);
|
||||
/* fall through */
|
||||
case 8:
|
||||
p += sprintf(p, "%d.", (a >> 24) & 0xff);
|
||||
domain += sprintf(domain, "%d.", (a >> 24) & 0xff);
|
||||
break;
|
||||
default:
|
||||
free(serv->domain);
|
||||
free(serv);
|
||||
return NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
p += sprintf(p, "in-addr.arpa");
|
||||
|
||||
serv->flags = SERV_HAS_DOMAIN;
|
||||
serv->next = daemon->servers;
|
||||
daemon->servers = serv;
|
||||
|
||||
return serv;
|
||||
|
||||
domain += sprintf(domain, "in-addr.arpa");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct server *add_rev6(struct in6_addr *addr, int msize)
|
||||
static int domain_rev6(char *domain, struct in6_addr *addr, int msize)
|
||||
{
|
||||
struct server *serv = opt_malloc(sizeof(struct server));
|
||||
char *p;
|
||||
int i;
|
||||
|
||||
memset(serv, 0, sizeof(struct server));
|
||||
p = serv->domain = opt_malloc(73); /* strlen("32*<n.>ip6.arpa")+1 */
|
||||
|
||||
if (msize > 128 || msize%4)
|
||||
return 0;
|
||||
|
||||
*domain = 0;
|
||||
|
||||
for (i = msize-1; i >= 0; i -= 4)
|
||||
{
|
||||
int dig = ((unsigned char *)addr)[i>>3];
|
||||
p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
|
||||
domain += sprintf(domain, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
|
||||
}
|
||||
p += sprintf(p, "ip6.arpa");
|
||||
domain += sprintf(domain, "ip6.arpa");
|
||||
|
||||
serv->flags = SERV_HAS_DOMAIN;
|
||||
serv->next = daemon->servers;
|
||||
daemon->servers = serv;
|
||||
|
||||
return serv;
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DHCP
|
||||
@@ -1045,6 +1070,8 @@ static void dhcp_config_free(struct dhcp_config *config)
|
||||
|
||||
if (config->flags & CONFIG_CLID)
|
||||
free(config->clid);
|
||||
if (config->flags & CONFIG_NAME)
|
||||
free(config->hostname);
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
if (config->flags & CONFIG_ADDR6)
|
||||
@@ -1161,11 +1188,15 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags)
|
||||
{
|
||||
new->u.vendor_class = (unsigned char *)opt_string_alloc(arg+7);
|
||||
new->flags |= DHOPT_VENDOR;
|
||||
if ((new->flags & DHOPT_ENCAPSULATE) || flags == DHOPT_MATCH)
|
||||
goto_err(_("inappropriate vendor:"));
|
||||
}
|
||||
else if (strstr(arg, "encap:") == arg)
|
||||
{
|
||||
new->u.encap = atoi(arg+6);
|
||||
new->flags |= DHOPT_ENCAPSULATE;
|
||||
if ((new->flags & DHOPT_VENDOR) || flags == DHOPT_MATCH)
|
||||
goto_err(_("inappropriate encap:"));
|
||||
}
|
||||
else if (strstr(arg, "vi-encap:") == arg)
|
||||
{
|
||||
@@ -1640,16 +1671,6 @@ void reset_option_bool(unsigned int opt)
|
||||
option_var(opt) &= ~(option_val(opt));
|
||||
}
|
||||
|
||||
static void server_list_free(struct server *list)
|
||||
{
|
||||
while (list)
|
||||
{
|
||||
struct server *tmp = list;
|
||||
list = list->next;
|
||||
free(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
static int one_opt(int option, char *arg, char *errstr, char *gen_err, int command_line, int servers_only)
|
||||
{
|
||||
int i;
|
||||
@@ -2210,7 +2231,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
arg = comma;
|
||||
comma = split(arg);
|
||||
daemon->hostmaster = opt_string_alloc(arg);
|
||||
for (cp = daemon->hostmaster; *cp; cp++)
|
||||
for (cp = daemon->hostmaster; cp && *cp; cp++)
|
||||
if (*cp == '@')
|
||||
*cp = '.';
|
||||
|
||||
@@ -2238,12 +2259,13 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
set_option_bool(OPT_RESOLV_DOMAIN);
|
||||
else
|
||||
{
|
||||
char *d;
|
||||
char *d, *d_raw = arg;
|
||||
comma = split(arg);
|
||||
if (!(d = canonicalise_opt(arg)))
|
||||
if (!(d = canonicalise_opt(d_raw)))
|
||||
ret_err(gen_err);
|
||||
else
|
||||
{
|
||||
free(d); /* allocate this again below. */
|
||||
if (comma)
|
||||
{
|
||||
struct cond_domain *new = opt_malloc(sizeof(struct cond_domain));
|
||||
@@ -2251,6 +2273,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
|
||||
new->prefix = NULL;
|
||||
new->indexed = 0;
|
||||
new->prefixlen = 0;
|
||||
|
||||
unhide_metas(comma);
|
||||
if ((netpart = split_chr(comma, '/')))
|
||||
@@ -2262,7 +2285,12 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
ret_err_free(gen_err, new);
|
||||
else if (inet_pton(AF_INET, comma, &new->start))
|
||||
{
|
||||
int mask = (1 << (32 - msize)) - 1;
|
||||
int mask;
|
||||
|
||||
if (msize > 32)
|
||||
ret_err_free(_("bad prefix length"), new);
|
||||
|
||||
mask = (1 << (32 - msize)) - 1;
|
||||
new->is6 = 0;
|
||||
new->start.s_addr = ntohl(htonl(new->start.s_addr) & ~mask);
|
||||
new->end.s_addr = new->start.s_addr | htonl(mask);
|
||||
@@ -2279,41 +2307,39 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
ret_err_free(gen_err, new);
|
||||
else
|
||||
{
|
||||
/* generate the equivalent of
|
||||
local=/xxx.yyy.zzz.in-addr.arpa/ */
|
||||
struct server *serv = add_rev4(new->start, msize);
|
||||
if (!serv)
|
||||
ret_err_free(_("bad prefix"), new);
|
||||
|
||||
serv->flags |= SERV_NO_ADDR;
|
||||
|
||||
char domain[29]; /* strlen("xxx.yyy.zzz.ttt.in-addr.arpa")+1 */
|
||||
/* local=/xxx.yyy.zzz.in-addr.arpa/ */
|
||||
/* domain_rev4 can't fail here, msize checked above. */
|
||||
domain_rev4(domain, new->start, msize);
|
||||
add_update_server(SERV_LITERAL_ADDRESS, NULL, NULL, NULL, domain, NULL);
|
||||
|
||||
/* local=/<domain>/ */
|
||||
serv = opt_malloc(sizeof(struct server));
|
||||
memset(serv, 0, sizeof(struct server));
|
||||
serv->domain = d;
|
||||
serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
|
||||
serv->next = daemon->servers;
|
||||
daemon->servers = serv;
|
||||
/* d_raw can't failed to canonicalise here, checked above. */
|
||||
add_update_server(SERV_LITERAL_ADDRESS, NULL, NULL, NULL, d_raw, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (inet_pton(AF_INET6, comma, &new->start6))
|
||||
{
|
||||
u64 mask = (1LLU << (128 - msize)) - 1LLU;
|
||||
u64 addrpart = addr6part(&new->start6);
|
||||
u64 mask, addrpart = addr6part(&new->start6);
|
||||
|
||||
if (msize > 128)
|
||||
ret_err_free(_("bad prefix length"), new);
|
||||
|
||||
mask = (1LLU << (128 - msize)) - 1LLU;
|
||||
|
||||
new->is6 = 1;
|
||||
new->prefixlen = msize;
|
||||
|
||||
/* prefix==64 overflows the mask calculation above */
|
||||
if (msize == 64)
|
||||
if (msize <= 64)
|
||||
mask = (u64)-1LL;
|
||||
|
||||
new->end6 = new->start6;
|
||||
setaddr6part(&new->start6, addrpart & ~mask);
|
||||
setaddr6part(&new->end6, addrpart | mask);
|
||||
|
||||
if (msize < 64)
|
||||
ret_err_free(gen_err, new);
|
||||
else if (arg)
|
||||
if (arg)
|
||||
{
|
||||
if (option != 's')
|
||||
{
|
||||
@@ -2325,18 +2351,15 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
ret_err_free(gen_err, new);
|
||||
else
|
||||
{
|
||||
char domain[73]; /* strlen("32*<n.>ip6.arpa")+1 */
|
||||
/* generate the equivalent of
|
||||
local=/xxx.yyy.zzz.ip6.arpa/ */
|
||||
struct server *serv = add_rev6(&new->start6, msize);
|
||||
serv->flags |= SERV_NO_ADDR;
|
||||
|
||||
domain_rev6(domain, &new->start6, msize);
|
||||
add_update_server(SERV_LITERAL_ADDRESS, NULL, NULL, NULL, domain, NULL);
|
||||
|
||||
/* local=/<domain>/ */
|
||||
serv = opt_malloc(sizeof(struct server));
|
||||
memset(serv, 0, sizeof(struct server));
|
||||
serv->domain = d;
|
||||
serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
|
||||
serv->next = daemon->servers;
|
||||
daemon->servers = serv;
|
||||
/* d_raw can't failed to canonicalise here, checked above. */
|
||||
add_update_server(SERV_LITERAL_ADDRESS, NULL, NULL, NULL, d_raw, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2376,7 +2399,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
}
|
||||
}
|
||||
|
||||
new->domain = d;
|
||||
new->domain = canonicalise_opt(d_raw);
|
||||
if (option == 's')
|
||||
{
|
||||
new->next = daemon->cond_domain;
|
||||
@@ -2385,19 +2408,21 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
else
|
||||
{
|
||||
char *star;
|
||||
new->next = daemon->synth_domains;
|
||||
daemon->synth_domains = new;
|
||||
if (new->prefix &&
|
||||
(star = strrchr(new->prefix, '*'))
|
||||
&& *(star+1) == 0)
|
||||
{
|
||||
*star = 0;
|
||||
new->indexed = 1;
|
||||
if (new->is6 && new->prefixlen < 64)
|
||||
ret_err_free(_("prefix length too small"), new);
|
||||
}
|
||||
new->next = daemon->synth_domains;
|
||||
daemon->synth_domains = new;
|
||||
}
|
||||
}
|
||||
else if (option == 's')
|
||||
daemon->domain_suffix = d;
|
||||
daemon->domain_suffix = canonicalise_opt(d_raw);
|
||||
else
|
||||
ret_err(gen_err);
|
||||
}
|
||||
@@ -2409,6 +2434,41 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
daemon->dns_client_id = opt_string_alloc(arg);
|
||||
break;
|
||||
|
||||
case LOPT_UMBRELLA: /* --umbrella */
|
||||
set_option_bool(OPT_UMBRELLA);
|
||||
while (arg) {
|
||||
comma = split(arg);
|
||||
if (strstr(arg, "deviceid:")) {
|
||||
arg += 9;
|
||||
if (strlen(arg) != 16)
|
||||
ret_err(gen_err);
|
||||
for (char *p = arg; *p; p++) {
|
||||
if (!isxdigit((int)*p))
|
||||
ret_err(gen_err);
|
||||
}
|
||||
set_option_bool(OPT_UMBRELLA_DEVID);
|
||||
|
||||
u8 *u = daemon->umbrella_device;
|
||||
char word[3];
|
||||
for (u8 i = 0; i < sizeof(daemon->umbrella_device); i++, arg+=2) {
|
||||
memcpy(word, &(arg[0]), 2);
|
||||
*u++ = strtoul(word, NULL, 16);
|
||||
}
|
||||
}
|
||||
else if (strstr(arg, "orgid:")) {
|
||||
if (!strtoul_check(arg+6, &daemon->umbrella_org)) {
|
||||
ret_err(gen_err);
|
||||
}
|
||||
}
|
||||
else if (strstr(arg, "assetid:")) {
|
||||
if (!strtoul_check(arg+8, &daemon->umbrella_asset)) {
|
||||
ret_err(gen_err);
|
||||
}
|
||||
}
|
||||
arg = comma;
|
||||
}
|
||||
break;
|
||||
|
||||
case LOPT_ADD_MAC: /* --add-mac */
|
||||
if (!arg)
|
||||
set_option_bool(OPT_ADD_MAC);
|
||||
@@ -2487,30 +2547,47 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
case 'B': /* --bogus-nxdomain */
|
||||
case LOPT_IGNORE_ADDR: /* --ignore-address */
|
||||
{
|
||||
struct in_addr addr;
|
||||
int prefix = 32;
|
||||
union all_addr addr;
|
||||
int prefix, is6 = 0;
|
||||
struct bogus_addr *baddr;
|
||||
|
||||
unhide_metas(arg);
|
||||
|
||||
if (!arg ||
|
||||
((comma = split_chr(arg, '/')) && !atoi_check(comma, &prefix)) ||
|
||||
(inet_pton(AF_INET, arg, &addr) != 1))
|
||||
ret_err(gen_err); /* error */
|
||||
((comma = split_chr(arg, '/')) && !atoi_check(comma, &prefix)))
|
||||
ret_err(gen_err);
|
||||
|
||||
if (inet_pton(AF_INET6, arg, &addr.addr6) == 1)
|
||||
is6 = 1;
|
||||
else if (inet_pton(AF_INET, arg, &addr.addr4) != 1)
|
||||
ret_err(gen_err);
|
||||
|
||||
if (!comma)
|
||||
{
|
||||
if (is6)
|
||||
prefix = 128;
|
||||
else
|
||||
prefix = 32;
|
||||
}
|
||||
|
||||
if (prefix > 128 || (!is6 && prefix > 32))
|
||||
ret_err(gen_err);
|
||||
|
||||
baddr = opt_malloc(sizeof(struct bogus_addr));
|
||||
if (option == 'B')
|
||||
{
|
||||
baddr->next = daemon->bogus_addr;
|
||||
daemon->bogus_addr = baddr;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct bogus_addr *baddr = opt_malloc(sizeof(struct bogus_addr));
|
||||
if (option == 'B')
|
||||
{
|
||||
baddr->next = daemon->bogus_addr;
|
||||
daemon->bogus_addr = baddr;
|
||||
}
|
||||
else
|
||||
{
|
||||
baddr->next = daemon->ignore_addr;
|
||||
daemon->ignore_addr = baddr;
|
||||
}
|
||||
baddr->mask.s_addr = htonl(~((1 << (32 - prefix)) - 1));
|
||||
baddr->addr.s_addr = addr.s_addr & baddr->mask.s_addr;
|
||||
baddr->next = daemon->ignore_addr;
|
||||
daemon->ignore_addr = baddr;
|
||||
}
|
||||
|
||||
baddr->prefix = prefix;
|
||||
baddr->is6 = is6;
|
||||
baddr->addr = addr;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2556,108 +2633,108 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
} while (arg);
|
||||
break;
|
||||
|
||||
case LOPT_NO_REBIND: /* --rebind-domain-ok */
|
||||
{
|
||||
struct server *new;
|
||||
|
||||
unhide_metas(arg);
|
||||
|
||||
if (*arg == '/')
|
||||
arg++;
|
||||
|
||||
do {
|
||||
comma = split_chr(arg, '/');
|
||||
new = opt_malloc(sizeof(struct serv_local));
|
||||
new->domain = opt_string_alloc(arg);
|
||||
new->domain_len = strlen(arg);
|
||||
new->next = daemon->no_rebind;
|
||||
daemon->no_rebind = new;
|
||||
arg = comma;
|
||||
} while (arg && *arg);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'S': /* --server */
|
||||
case LOPT_LOCAL: /* --local */
|
||||
case 'A': /* --address */
|
||||
case LOPT_NO_REBIND: /* --rebind-domain-ok */
|
||||
{
|
||||
struct server *serv, *newlist = NULL;
|
||||
|
||||
char *lastdomain = NULL, *domain = "";
|
||||
u16 flags = 0;
|
||||
char *err;
|
||||
union all_addr addr;
|
||||
union mysockaddr serv_addr, source_addr;
|
||||
char interface[IF_NAMESIZE+1];
|
||||
|
||||
unhide_metas(arg);
|
||||
|
||||
if (arg && (*arg == '/' || option == LOPT_NO_REBIND))
|
||||
/* split the domain args, if any and skip to the end of them. */
|
||||
if (arg && *arg == '/')
|
||||
{
|
||||
int rebind = !(*arg == '/');
|
||||
char *end = NULL;
|
||||
if (!rebind)
|
||||
arg++;
|
||||
while (rebind || (end = split_chr(arg, '/')))
|
||||
char *last;
|
||||
|
||||
domain = lastdomain = ++arg;
|
||||
|
||||
while ((last = split_chr(arg, '/')))
|
||||
{
|
||||
char *domain = NULL;
|
||||
/* elide leading dots - they are implied in the search algorithm */
|
||||
while (*arg == '.') arg++;
|
||||
/* # matches everything and becomes a zero length domain string */
|
||||
if (strcmp(arg, "#") == 0)
|
||||
domain = "";
|
||||
else if (strlen (arg) != 0 && !(domain = canonicalise_opt(arg)))
|
||||
ret_err(gen_err);
|
||||
serv = opt_malloc(sizeof(struct server));
|
||||
memset(serv, 0, sizeof(struct server));
|
||||
serv->next = newlist;
|
||||
newlist = serv;
|
||||
serv->domain = domain;
|
||||
serv->flags = domain ? SERV_HAS_DOMAIN : SERV_FOR_NODOTS;
|
||||
arg = end;
|
||||
if (rebind)
|
||||
break;
|
||||
}
|
||||
if (!newlist)
|
||||
ret_err(gen_err);
|
||||
}
|
||||
else
|
||||
{
|
||||
newlist = opt_malloc(sizeof(struct server));
|
||||
memset(newlist, 0, sizeof(struct server));
|
||||
#ifdef HAVE_LOOP
|
||||
newlist->uid = rand32();
|
||||
#endif
|
||||
}
|
||||
|
||||
if (servers_only && option == 'S')
|
||||
newlist->flags |= SERV_FROM_FILE;
|
||||
|
||||
if (option == 'A')
|
||||
{
|
||||
newlist->flags |= SERV_LITERAL_ADDRESS;
|
||||
if (!(newlist->flags & SERV_TYPE))
|
||||
{
|
||||
server_list_free(newlist);
|
||||
ret_err(gen_err);
|
||||
lastdomain = arg;
|
||||
arg = last;
|
||||
}
|
||||
}
|
||||
else if (option == LOPT_NO_REBIND)
|
||||
newlist->flags |= SERV_NO_REBIND;
|
||||
|
||||
if (!arg || !*arg)
|
||||
flags = SERV_LITERAL_ADDRESS;
|
||||
else if (option == 'A')
|
||||
{
|
||||
if (!(newlist->flags & SERV_NO_REBIND))
|
||||
newlist->flags |= SERV_NO_ADDR; /* no server */
|
||||
/* # as literal address means return zero address for 4 and 6 */
|
||||
if (strcmp(arg, "#") == 0)
|
||||
flags = SERV_ALL_ZEROS | SERV_LITERAL_ADDRESS;
|
||||
else if (inet_pton(AF_INET, arg, &addr.addr4) > 0)
|
||||
flags = SERV_4ADDR | SERV_LITERAL_ADDRESS;
|
||||
else if (inet_pton(AF_INET6, arg, &addr.addr6) > 0)
|
||||
flags = SERV_6ADDR | SERV_LITERAL_ADDRESS;
|
||||
else
|
||||
ret_err(_("Bad address in --address"));
|
||||
}
|
||||
|
||||
else if (strcmp(arg, "#") == 0)
|
||||
newlist->flags |= SERV_USE_RESOLV; /* treat in ordinary way */
|
||||
else
|
||||
{
|
||||
char *err = parse_server(arg, &newlist->addr, &newlist->source_addr, newlist->interface, &newlist->flags);
|
||||
if (err)
|
||||
{
|
||||
server_list_free(newlist);
|
||||
ret_err(err);
|
||||
}
|
||||
if ((err = parse_server(arg, &serv_addr, &source_addr, interface, &flags)))
|
||||
ret_err(err);
|
||||
|
||||
/* server=//1.2.3.4 is special. */
|
||||
if (strlen(domain) == 0 && lastdomain)
|
||||
flags |= SERV_FOR_NODOTS;
|
||||
|
||||
}
|
||||
|
||||
if (servers_only && option == 'S')
|
||||
flags |= SERV_FROM_FILE;
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, &addr))
|
||||
ret_err(gen_err);
|
||||
|
||||
if (!lastdomain || domain == lastdomain)
|
||||
break;
|
||||
|
||||
domain += strlen(domain) + 1;
|
||||
}
|
||||
|
||||
serv = newlist;
|
||||
while (serv->next)
|
||||
{
|
||||
serv->next->flags |= serv->flags & ~(SERV_HAS_DOMAIN | SERV_FOR_NODOTS);
|
||||
serv->next->addr = serv->addr;
|
||||
serv->next->source_addr = serv->source_addr;
|
||||
strcpy(serv->next->interface, serv->interface);
|
||||
serv = serv->next;
|
||||
}
|
||||
serv->next = daemon->servers;
|
||||
daemon->servers = newlist;
|
||||
break;
|
||||
break;
|
||||
}
|
||||
|
||||
case LOPT_REV_SERV: /* --rev-server */
|
||||
{
|
||||
char *string;
|
||||
int size;
|
||||
struct server *serv;
|
||||
u16 flags = 0;
|
||||
char domain[73]; /* strlen("32*<n.>ip6.arpa")+1 */
|
||||
struct in_addr addr4;
|
||||
struct in6_addr addr6;
|
||||
|
||||
union mysockaddr serv_addr, source_addr;
|
||||
char interface[IF_NAMESIZE+1];
|
||||
|
||||
unhide_metas(arg);
|
||||
if (!arg)
|
||||
ret_err(gen_err);
|
||||
@@ -2669,22 +2746,27 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
|
||||
if (inet_pton(AF_INET, arg, &addr4))
|
||||
{
|
||||
serv = add_rev4(addr4, size);
|
||||
if (!serv)
|
||||
ret_err(_("bad prefix"));
|
||||
if (!domain_rev4(domain, addr4, size))
|
||||
ret_err(_("bad IPv4 prefix"));
|
||||
}
|
||||
else if (inet_pton(AF_INET6, arg, &addr6))
|
||||
serv = add_rev6(&addr6, size);
|
||||
{
|
||||
if (!domain_rev6(domain, &addr6, size))
|
||||
ret_err(_("bad IPv6 prefix"));
|
||||
}
|
||||
else
|
||||
ret_err(gen_err);
|
||||
|
||||
string = parse_server(comma, &serv->addr, &serv->source_addr, serv->interface, &serv->flags);
|
||||
|
||||
if (string)
|
||||
if (!comma)
|
||||
flags |= SERV_LITERAL_ADDRESS;
|
||||
else if ((string = parse_server(comma, &serv_addr, &source_addr, interface, &flags)))
|
||||
ret_err(string);
|
||||
|
||||
if (servers_only)
|
||||
serv->flags |= SERV_FROM_FILE;
|
||||
flags |= SERV_FROM_FILE;
|
||||
|
||||
if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
|
||||
ret_err(gen_err);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -2755,6 +2837,135 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
}
|
||||
#endif
|
||||
|
||||
case LOPT_CMARK_ALST_EN: /* --connmark-allowlist-enable */
|
||||
#ifndef HAVE_CONNTRACK
|
||||
ret_err(_("recompile with HAVE_CONNTRACK defined to enable connmark-allowlist directives"));
|
||||
break;
|
||||
#else
|
||||
{
|
||||
u32 mask = UINT32_MAX;
|
||||
|
||||
if (arg)
|
||||
if (!strtoul_check(arg, &mask) || mask < 1)
|
||||
ret_err(gen_err);
|
||||
|
||||
set_option_bool(OPT_CMARK_ALST_EN);
|
||||
daemon->allowlist_mask = mask;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
case LOPT_CMARK_ALST: /* --connmark-allowlist */
|
||||
#ifndef HAVE_CONNTRACK
|
||||
ret_err(_("recompile with HAVE_CONNTRACK defined to enable connmark-allowlist directives"));
|
||||
break;
|
||||
#else
|
||||
{
|
||||
struct allowlist *allowlists;
|
||||
char **patterns, **patterns_pos;
|
||||
u32 mark, mask = UINT32_MAX;
|
||||
size_t num_patterns = 0;
|
||||
|
||||
char *c, *m = NULL;
|
||||
char *separator;
|
||||
unhide_metas(arg);
|
||||
if (!arg)
|
||||
ret_err(gen_err);
|
||||
c = arg;
|
||||
if (*c < '0' || *c > '9')
|
||||
ret_err(gen_err);
|
||||
while (*c && *c != ',')
|
||||
{
|
||||
if (*c == '/')
|
||||
{
|
||||
if (m)
|
||||
ret_err(gen_err);
|
||||
*c = '\0';
|
||||
m = ++c;
|
||||
}
|
||||
if (*c < '0' || *c > '9')
|
||||
ret_err(gen_err);
|
||||
c++;
|
||||
}
|
||||
separator = c;
|
||||
if (!*separator)
|
||||
break;
|
||||
while (c && *c)
|
||||
{
|
||||
char *end = strchr(++c, '/');
|
||||
if (end)
|
||||
*end = '\0';
|
||||
if (strcmp(c, "*") && !is_valid_dns_name_pattern(c))
|
||||
ret_err(gen_err);
|
||||
if (end)
|
||||
*end = '/';
|
||||
if (num_patterns >= UINT16_MAX - 1)
|
||||
ret_err(gen_err);
|
||||
num_patterns++;
|
||||
c = end;
|
||||
}
|
||||
|
||||
*separator = '\0';
|
||||
if (!strtoul_check(arg, &mark) || mark < 1 || mark > UINT32_MAX)
|
||||
ret_err(gen_err);
|
||||
if (m)
|
||||
if (!strtoul_check(m, &mask) || mask < 1 || mask > UINT32_MAX || (mark & ~mask))
|
||||
ret_err(gen_err);
|
||||
if (num_patterns)
|
||||
*separator = ',';
|
||||
for (allowlists = daemon->allowlists; allowlists; allowlists = allowlists->next)
|
||||
if (allowlists->mark == mark && allowlists->mask == mask)
|
||||
ret_err(gen_err);
|
||||
|
||||
patterns = opt_malloc((num_patterns + 1) * sizeof(char *));
|
||||
if (!patterns)
|
||||
goto fail_cmark_allowlist;
|
||||
patterns_pos = patterns;
|
||||
c = separator;
|
||||
while (c && *c)
|
||||
{
|
||||
char *end = strchr(++c, '/');
|
||||
if (end)
|
||||
*end = '\0';
|
||||
if (!(*patterns_pos++ = opt_string_alloc(c)))
|
||||
goto fail_cmark_allowlist;
|
||||
if (end)
|
||||
*end = '/';
|
||||
c = end;
|
||||
}
|
||||
*patterns_pos++ = NULL;
|
||||
|
||||
allowlists = opt_malloc(sizeof(struct allowlist));
|
||||
if (!allowlists)
|
||||
goto fail_cmark_allowlist;
|
||||
memset(allowlists, 0, sizeof(struct allowlist));
|
||||
allowlists->mark = mark;
|
||||
allowlists->mask = mask;
|
||||
allowlists->patterns = patterns;
|
||||
allowlists->next = daemon->allowlists;
|
||||
daemon->allowlists = allowlists;
|
||||
break;
|
||||
|
||||
fail_cmark_allowlist:
|
||||
if (patterns)
|
||||
{
|
||||
for (patterns_pos = patterns; *patterns_pos; patterns_pos++)
|
||||
{
|
||||
free(*patterns_pos);
|
||||
*patterns_pos = NULL;
|
||||
}
|
||||
free(patterns);
|
||||
patterns = NULL;
|
||||
}
|
||||
if (allowlists)
|
||||
{
|
||||
free(allowlists);
|
||||
allowlists = NULL;
|
||||
}
|
||||
ret_err(gen_err);
|
||||
}
|
||||
#endif
|
||||
|
||||
case 'c': /* --cache-size */
|
||||
{
|
||||
int size;
|
||||
@@ -3401,7 +3612,9 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
for (configs = daemon->dhcp_conf; configs; configs = configs->next)
|
||||
if ((configs->flags & CONFIG_ADDR) && configs->addr.s_addr == in.s_addr)
|
||||
{
|
||||
sprintf(errstr, _("duplicate dhcp-host IP address %s"), inet_ntoa(in));
|
||||
inet_ntop(AF_INET, &in, daemon->addrbuff, ADDRSTRLEN);
|
||||
sprintf(errstr, _("duplicate dhcp-host IP address %s"),
|
||||
daemon->addrbuff);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -3972,13 +4185,13 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
comma = split(arg);
|
||||
new->interface = opt_string_alloc(split(comma));
|
||||
new->iface_index = 0;
|
||||
if (inet_pton(AF_INET, arg, &new->local) && inet_pton(AF_INET, comma, &new->server))
|
||||
if (comma && inet_pton(AF_INET, arg, &new->local) && inet_pton(AF_INET, comma, &new->server))
|
||||
{
|
||||
new->next = daemon->relay4;
|
||||
daemon->relay4 = new;
|
||||
}
|
||||
#ifdef HAVE_DHCP6
|
||||
else if (inet_pton(AF_INET6, arg, &new->local) && inet_pton(AF_INET6, comma, &new->server))
|
||||
else if (comma && inet_pton(AF_INET6, arg, &new->local) && inet_pton(AF_INET6, comma, &new->server))
|
||||
{
|
||||
new->next = daemon->relay6;
|
||||
daemon->relay6 = new;
|
||||
@@ -4473,8 +4686,7 @@ err:
|
||||
}
|
||||
else
|
||||
{
|
||||
int nomem;
|
||||
char *canon = canonicalise(arg, &nomem);
|
||||
char *canon = canonicalise_opt(arg);
|
||||
struct name_list *nl;
|
||||
if (!canon)
|
||||
{
|
||||
@@ -4927,9 +5139,9 @@ void read_servers_file(void)
|
||||
}
|
||||
|
||||
mark_servers(SERV_FROM_FILE);
|
||||
cleanup_servers();
|
||||
|
||||
read_file(daemon->servers_file, f, LOPT_REV_SERV);
|
||||
cleanup_servers();
|
||||
check_servers(0);
|
||||
}
|
||||
|
||||
|
||||
@@ -4945,30 +5157,8 @@ static void clear_dynamic_conf(void)
|
||||
|
||||
if (configs->flags & CONFIG_BANK)
|
||||
{
|
||||
struct hwaddr_config *mac, *tmp;
|
||||
struct dhcp_netid_list *list, *tmplist;
|
||||
|
||||
for (mac = configs->hwaddr; mac; mac = tmp)
|
||||
{
|
||||
tmp = mac->next;
|
||||
free(mac);
|
||||
}
|
||||
|
||||
if (configs->flags & CONFIG_CLID)
|
||||
free(configs->clid);
|
||||
|
||||
for (list = configs->netid; list; list = tmplist)
|
||||
{
|
||||
free(list->list);
|
||||
tmplist = list->next;
|
||||
free(list);
|
||||
}
|
||||
|
||||
if (configs->flags & CONFIG_NAME)
|
||||
free(configs->hostname);
|
||||
|
||||
*up = configs->next;
|
||||
free(configs);
|
||||
*up = cp;
|
||||
dhcp_config_free(configs);
|
||||
}
|
||||
else
|
||||
up = &configs->next;
|
||||
@@ -4978,7 +5168,6 @@ static void clear_dynamic_conf(void)
|
||||
static void clear_dynamic_opt(void)
|
||||
{
|
||||
struct dhcp_opt *opts, *cp, **up;
|
||||
struct dhcp_netid *id, *next;
|
||||
|
||||
for (up = &daemon->dhcp_opts, opts = daemon->dhcp_opts; opts; opts = cp)
|
||||
{
|
||||
@@ -4986,17 +5175,8 @@ static void clear_dynamic_opt(void)
|
||||
|
||||
if (opts->flags & DHOPT_BANK)
|
||||
{
|
||||
if ((opts->flags & DHOPT_VENDOR))
|
||||
free(opts->u.vendor_class);
|
||||
free(opts->val);
|
||||
for (id = opts->netid; id; id = next)
|
||||
{
|
||||
next = id->next;
|
||||
free(id->net);
|
||||
free(id);
|
||||
}
|
||||
*up = opts->next;
|
||||
free(opts);
|
||||
*up = cp;
|
||||
dhcp_opt_free(opts);
|
||||
}
|
||||
else
|
||||
up = &opts->next;
|
||||
@@ -5056,7 +5236,8 @@ void read_opts(int argc, char **argv, char *compile_opts)
|
||||
daemon = opt_malloc(sizeof(struct daemon));
|
||||
memset(daemon, 0, sizeof(struct daemon));
|
||||
daemon->namebuff = buff;
|
||||
|
||||
daemon->addrbuff = safe_malloc(ADDRSTRLEN);
|
||||
|
||||
/* Set defaults - everything else is zero or NULL */
|
||||
daemon->cachesize = CACHESIZ;
|
||||
daemon->ftabsize = FTABSIZ;
|
||||
|
||||
386
src/pattern.c
Normal file
386
src/pattern.c
Normal file
@@ -0,0 +1,386 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 dated June, 1991, or
|
||||
(at your option) version 3 dated 29 June, 2007.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
#ifdef HAVE_CONNTRACK
|
||||
|
||||
#define LOG(...) \
|
||||
do { \
|
||||
my_syslog(LOG_WARNING, __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define ASSERT(condition) \
|
||||
do { \
|
||||
if (!(condition)) \
|
||||
LOG("[pattern.c:%d] Assertion failure: %s", __LINE__, #condition); \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* Determines whether a given string value matches against a glob pattern
|
||||
* which may contain zero-or-more-character wildcards denoted by '*'.
|
||||
*
|
||||
* Based on "Glob Matching Can Be Simple And Fast Too" by Russ Cox,
|
||||
* See https://research.swtch.com/glob
|
||||
*
|
||||
* @param value A string value.
|
||||
* @param num_value_bytes The number of bytes of the string value.
|
||||
* @param pattern A glob pattern.
|
||||
* @param num_pattern_bytes The number of bytes of the glob pattern.
|
||||
*
|
||||
* @return 1 If the provided value matches against the glob pattern.
|
||||
* @return 0 Otherwise.
|
||||
*/
|
||||
static int is_string_matching_glob_pattern(
|
||||
const char *value,
|
||||
size_t num_value_bytes,
|
||||
const char *pattern,
|
||||
size_t num_pattern_bytes)
|
||||
{
|
||||
ASSERT(value);
|
||||
ASSERT(pattern);
|
||||
|
||||
size_t value_index = 0;
|
||||
size_t next_value_index = 0;
|
||||
size_t pattern_index = 0;
|
||||
size_t next_pattern_index = 0;
|
||||
while (value_index < num_value_bytes || pattern_index < num_pattern_bytes)
|
||||
{
|
||||
if (pattern_index < num_pattern_bytes)
|
||||
{
|
||||
char pattern_character = pattern[pattern_index];
|
||||
if ('a' <= pattern_character && pattern_character <= 'z')
|
||||
pattern_character -= 'a' - 'A';
|
||||
if (pattern_character == '*')
|
||||
{
|
||||
// zero-or-more-character wildcard
|
||||
// Try to match at value_index, otherwise restart at value_index + 1 next.
|
||||
next_pattern_index = pattern_index;
|
||||
pattern_index++;
|
||||
if (value_index < num_value_bytes)
|
||||
next_value_index = value_index + 1;
|
||||
else
|
||||
next_value_index = 0;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// ordinary character
|
||||
if (value_index < num_value_bytes)
|
||||
{
|
||||
char value_character = value[value_index];
|
||||
if ('a' <= value_character && value_character <= 'z')
|
||||
value_character -= 'a' - 'A';
|
||||
if (value_character == pattern_character)
|
||||
{
|
||||
pattern_index++;
|
||||
value_index++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (next_value_index)
|
||||
{
|
||||
pattern_index = next_pattern_index;
|
||||
value_index = next_value_index;
|
||||
continue;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a given string value represents a valid DNS name.
|
||||
*
|
||||
* - DNS names must adhere to RFC 1123: 1 to 253 characters in length, consisting of a sequence of labels
|
||||
* delimited by dots ("."). Each label must be 1 to 63 characters in length, contain only
|
||||
* ASCII letters ("a"-"Z"), digits ("0"-"9"), or hyphens ("-") and must not start or end with a hyphen.
|
||||
*
|
||||
* - A valid name must be fully qualified, i.e., consist of at least two labels.
|
||||
* The final label must not be fully numeric, and must not be the "local" pseudo-TLD.
|
||||
*
|
||||
* - Examples:
|
||||
* Valid: "example.com"
|
||||
* Invalid: "ipcamera", "ipcamera.local", "8.8.8.8"
|
||||
*
|
||||
* @param value A string value.
|
||||
*
|
||||
* @return 1 If the provided string value is a valid DNS name.
|
||||
* @return 0 Otherwise.
|
||||
*/
|
||||
int is_valid_dns_name(const char *value)
|
||||
{
|
||||
ASSERT(value);
|
||||
|
||||
size_t num_bytes = 0;
|
||||
size_t num_labels = 0;
|
||||
const char *label = NULL;
|
||||
int is_label_numeric = 1;
|
||||
for (const char *c = value;; c++)
|
||||
{
|
||||
if (*c &&
|
||||
*c != '-' && *c != '.' &&
|
||||
(*c < '0' || *c > '9') &&
|
||||
(*c < 'A' || *c > 'Z') &&
|
||||
(*c < 'a' || *c > 'z'))
|
||||
{
|
||||
LOG("Invalid DNS name: Invalid character %c.", *c);
|
||||
return 0;
|
||||
}
|
||||
if (*c)
|
||||
num_bytes++;
|
||||
if (!label)
|
||||
{
|
||||
if (!*c || *c == '.')
|
||||
{
|
||||
LOG("Invalid DNS name: Empty label.");
|
||||
return 0;
|
||||
}
|
||||
if (*c == '-')
|
||||
{
|
||||
LOG("Invalid DNS name: Label starts with hyphen.");
|
||||
return 0;
|
||||
}
|
||||
label = c;
|
||||
}
|
||||
if (*c && *c != '.')
|
||||
{
|
||||
if (*c < '0' || *c > '9')
|
||||
is_label_numeric = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (c[-1] == '-')
|
||||
{
|
||||
LOG("Invalid DNS name: Label ends with hyphen.");
|
||||
return 0;
|
||||
}
|
||||
size_t num_label_bytes = (size_t) (c - label);
|
||||
if (num_label_bytes > 63)
|
||||
{
|
||||
LOG("Invalid DNS name: Label is too long (%zu).", num_label_bytes);
|
||||
return 0;
|
||||
}
|
||||
num_labels++;
|
||||
if (!*c)
|
||||
{
|
||||
if (num_labels < 2)
|
||||
{
|
||||
LOG("Invalid DNS name: Not enough labels (%zu).", num_labels);
|
||||
return 0;
|
||||
}
|
||||
if (is_label_numeric)
|
||||
{
|
||||
LOG("Invalid DNS name: Final label is fully numeric.");
|
||||
return 0;
|
||||
}
|
||||
if (num_label_bytes == 5 &&
|
||||
(label[0] == 'l' || label[0] == 'L') &&
|
||||
(label[1] == 'o' || label[1] == 'O') &&
|
||||
(label[2] == 'c' || label[2] == 'C') &&
|
||||
(label[3] == 'a' || label[3] == 'A') &&
|
||||
(label[4] == 'l' || label[4] == 'L'))
|
||||
{
|
||||
LOG("Invalid DNS name: \"local\" pseudo-TLD.");
|
||||
return 0;
|
||||
}
|
||||
if (num_bytes < 1 || num_bytes > 253)
|
||||
{
|
||||
LOG("DNS name has invalid length (%zu).", num_bytes);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
label = NULL;
|
||||
is_label_numeric = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a given string value represents a valid DNS name pattern.
|
||||
*
|
||||
* - DNS names must adhere to RFC 1123: 1 to 253 characters in length, consisting of a sequence of labels
|
||||
* delimited by dots ("."). Each label must be 1 to 63 characters in length, contain only
|
||||
* ASCII letters ("a"-"Z"), digits ("0"-"9"), or hyphens ("-") and must not start or end with a hyphen.
|
||||
*
|
||||
* - Patterns follow the syntax of DNS names, but additionally allow the wildcard character "*" to be used up to
|
||||
* twice per label to match 0 or more characters within that label. Note that the wildcard never matches a dot
|
||||
* (e.g., "*.example.com" matches "api.example.com" but not "api.us.example.com").
|
||||
*
|
||||
* - A valid name or pattern must be fully qualified, i.e., consist of at least two labels.
|
||||
* The final label must not be fully numeric, and must not be the "local" pseudo-TLD.
|
||||
* A pattern must end with at least two literal (non-wildcard) labels.
|
||||
*
|
||||
* - Examples:
|
||||
* Valid: "example.com", "*.example.com", "video*.example.com", "api*.*.example.com", "*-prod-*.example.com"
|
||||
* Invalid: "ipcamera", "ipcamera.local", "*", "*.com", "8.8.8.8"
|
||||
*
|
||||
* @param value A string value.
|
||||
*
|
||||
* @return 1 If the provided string value is a valid DNS name pattern.
|
||||
* @return 0 Otherwise.
|
||||
*/
|
||||
int is_valid_dns_name_pattern(const char *value)
|
||||
{
|
||||
ASSERT(value);
|
||||
|
||||
size_t num_bytes = 0;
|
||||
size_t num_labels = 0;
|
||||
const char *label = NULL;
|
||||
int is_label_numeric = 1;
|
||||
size_t num_wildcards = 0;
|
||||
int previous_label_has_wildcard = 1;
|
||||
for (const char *c = value;; c++)
|
||||
{
|
||||
if (*c &&
|
||||
*c != '*' && // Wildcard.
|
||||
*c != '-' && *c != '.' &&
|
||||
(*c < '0' || *c > '9') &&
|
||||
(*c < 'A' || *c > 'Z') &&
|
||||
(*c < 'a' || *c > 'z'))
|
||||
{
|
||||
LOG("Invalid DNS name pattern: Invalid character %c.", *c);
|
||||
return 0;
|
||||
}
|
||||
if (*c && *c != '*')
|
||||
num_bytes++;
|
||||
if (!label)
|
||||
{
|
||||
if (!*c || *c == '.')
|
||||
{
|
||||
LOG("Invalid DNS name pattern: Empty label.");
|
||||
return 0;
|
||||
}
|
||||
if (*c == '-')
|
||||
{
|
||||
LOG("Invalid DNS name pattern: Label starts with hyphen.");
|
||||
return 0;
|
||||
}
|
||||
label = c;
|
||||
}
|
||||
if (*c && *c != '.')
|
||||
{
|
||||
if (*c < '0' || *c > '9')
|
||||
is_label_numeric = 0;
|
||||
if (*c == '*')
|
||||
{
|
||||
if (num_wildcards >= 2)
|
||||
{
|
||||
LOG("Invalid DNS name pattern: Wildcard character used more than twice per label.");
|
||||
return 0;
|
||||
}
|
||||
num_wildcards++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (c[-1] == '-')
|
||||
{
|
||||
LOG("Invalid DNS name pattern: Label ends with hyphen.");
|
||||
return 0;
|
||||
}
|
||||
size_t num_label_bytes = (size_t) (c - label) - num_wildcards;
|
||||
if (num_label_bytes > 63)
|
||||
{
|
||||
LOG("Invalid DNS name pattern: Label is too long (%zu).", num_label_bytes);
|
||||
return 0;
|
||||
}
|
||||
num_labels++;
|
||||
if (!*c)
|
||||
{
|
||||
if (num_labels < 2)
|
||||
{
|
||||
LOG("Invalid DNS name pattern: Not enough labels (%zu).", num_labels);
|
||||
return 0;
|
||||
}
|
||||
if (num_wildcards != 0 || previous_label_has_wildcard)
|
||||
{
|
||||
LOG("Invalid DNS name pattern: Wildcard within final two labels.");
|
||||
return 0;
|
||||
}
|
||||
if (is_label_numeric)
|
||||
{
|
||||
LOG("Invalid DNS name pattern: Final label is fully numeric.");
|
||||
return 0;
|
||||
}
|
||||
if (num_label_bytes == 5 &&
|
||||
(label[0] == 'l' || label[0] == 'L') &&
|
||||
(label[1] == 'o' || label[1] == 'O') &&
|
||||
(label[2] == 'c' || label[2] == 'C') &&
|
||||
(label[3] == 'a' || label[3] == 'A') &&
|
||||
(label[4] == 'l' || label[4] == 'L'))
|
||||
{
|
||||
LOG("Invalid DNS name pattern: \"local\" pseudo-TLD.");
|
||||
return 0;
|
||||
}
|
||||
if (num_bytes < 1 || num_bytes > 253)
|
||||
{
|
||||
LOG("DNS name pattern has invalid length after removing wildcards (%zu).", num_bytes);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
label = NULL;
|
||||
is_label_numeric = 1;
|
||||
previous_label_has_wildcard = num_wildcards != 0;
|
||||
num_wildcards = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a given DNS name matches against a DNS name pattern.
|
||||
*
|
||||
* @param name A valid DNS name.
|
||||
* @param pattern A valid DNS name pattern.
|
||||
*
|
||||
* @return 1 If the provided DNS name matches against the DNS name pattern.
|
||||
* @return 0 Otherwise.
|
||||
*/
|
||||
int is_dns_name_matching_pattern(const char *name, const char *pattern)
|
||||
{
|
||||
ASSERT(name);
|
||||
ASSERT(is_valid_dns_name(name));
|
||||
ASSERT(pattern);
|
||||
ASSERT(is_valid_dns_name_pattern(pattern));
|
||||
|
||||
const char *n = name;
|
||||
const char *p = pattern;
|
||||
|
||||
do {
|
||||
const char *name_label = n;
|
||||
while (*n && *n != '.')
|
||||
n++;
|
||||
const char *pattern_label = p;
|
||||
while (*p && *p != '.')
|
||||
p++;
|
||||
if (!is_string_matching_glob_pattern(
|
||||
name_label, (size_t) (n - name_label),
|
||||
pattern_label, (size_t) (p - pattern_label)))
|
||||
break;
|
||||
if (*n)
|
||||
n++;
|
||||
if (*p)
|
||||
p++;
|
||||
} while (*n && *p);
|
||||
|
||||
return !*n && !*p;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -53,6 +53,3 @@ struct prefix_opt {
|
||||
#define ICMP6_OPT_RT_INFO 24
|
||||
#define ICMP6_OPT_RDNSS 25
|
||||
#define ICMP6_OPT_DNSSL 31
|
||||
|
||||
|
||||
|
||||
|
||||
251
src/rfc1035.c
251
src/rfc1035.c
@@ -364,7 +364,7 @@ int private_net(struct in_addr addr, int ban_localhost)
|
||||
|
||||
return
|
||||
(((ip_addr & 0xFF000000) == 0x7F000000) && ban_localhost) /* 127.0.0.0/8 (loopback) */ ||
|
||||
((ip_addr & 0xFF000000) == 0x00000000) /* RFC 5735 section 3. "here" network */ ||
|
||||
(((ip_addr & 0xFF000000) == 0x00000000) && ban_localhost) /* RFC 5735 section 3. "here" network */ ||
|
||||
((ip_addr & 0xFF000000) == 0x0A000000) /* 10.0.0.0/8 (private) */ ||
|
||||
((ip_addr & 0xFFF00000) == 0xAC100000) /* 172.16.0.0/12 (private) */ ||
|
||||
((ip_addr & 0xFFFF0000) == 0xC0A80000) /* 192.168.0.0/16 (private) */ ||
|
||||
@@ -375,12 +375,21 @@ int private_net(struct in_addr addr, int ban_localhost)
|
||||
((ip_addr & 0xFFFFFFFF) == 0xFFFFFFFF) /* 255.255.255.255/32 (broadcast)*/ ;
|
||||
}
|
||||
|
||||
static int private_net6(struct in6_addr *a)
|
||||
static int private_net6(struct in6_addr *a, int ban_localhost)
|
||||
{
|
||||
return
|
||||
IN6_IS_ADDR_UNSPECIFIED(a) || /* RFC 6303 4.3 */
|
||||
IN6_IS_ADDR_LOOPBACK(a) || /* RFC 6303 4.3 */
|
||||
/* Block IPv4-mapped IPv6 addresses in private IPv4 address space */
|
||||
if (IN6_IS_ADDR_V4MAPPED(a))
|
||||
{
|
||||
struct in_addr v4;
|
||||
v4.s_addr = ((const uint32_t *) (a))[3];
|
||||
return private_net(v4, ban_localhost);
|
||||
}
|
||||
|
||||
return
|
||||
(IN6_IS_ADDR_UNSPECIFIED(a) && ban_localhost) || /* RFC 6303 4.3 */
|
||||
(IN6_IS_ADDR_LOOPBACK(a) && ban_localhost) || /* RFC 6303 4.3 */
|
||||
IN6_IS_ADDR_LINKLOCAL(a) || /* RFC 6303 4.5 */
|
||||
IN6_IS_ADDR_SITELOCAL(a) ||
|
||||
((unsigned char *)a)[0] == 0xfd || /* RFC 6303 4.4 */
|
||||
((u32 *)a)[0] == htonl(0x20010db8); /* RFC 6303 4.6 */
|
||||
}
|
||||
@@ -795,28 +804,9 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
private_net(addr.addr4, !option_bool(OPT_LOCAL_REBIND)))
|
||||
return 1;
|
||||
|
||||
/* Block IPv4-mapped IPv6 addresses in private IPv4 address space */
|
||||
if (flags & F_IPV6)
|
||||
{
|
||||
if (IN6_IS_ADDR_V4MAPPED(&addr.addr6))
|
||||
{
|
||||
struct in_addr v4;
|
||||
v4.s_addr = ((const uint32_t *) (&addr.addr6))[3];
|
||||
if (private_net(v4, !option_bool(OPT_LOCAL_REBIND)))
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Check for link-local (LL) and site-local (ULA) IPv6 addresses */
|
||||
if (IN6_IS_ADDR_LINKLOCAL(&addr.addr6) ||
|
||||
IN6_IS_ADDR_SITELOCAL(&addr.addr6))
|
||||
return 1;
|
||||
|
||||
/* Check for the IPv6 loopback address (::1) when
|
||||
option rebind-localhost-ok is NOT set */
|
||||
if (!option_bool(OPT_LOCAL_REBIND) &&
|
||||
IN6_IS_ADDR_LOOPBACK(&addr.addr6))
|
||||
return 1;
|
||||
}
|
||||
if ((flags & F_IPV6) &&
|
||||
private_net6(&addr.addr6, !option_bool(OPT_LOCAL_REBIND)))
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef HAVE_IPSET
|
||||
@@ -884,6 +874,92 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS)
|
||||
/* Don't pass control chars and weird escapes to UBus. */
|
||||
static int safe_name(char *name)
|
||||
{
|
||||
unsigned char *r;
|
||||
|
||||
for (r = (unsigned char *)name; *r; r++)
|
||||
if (!isprint((int)*r))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void report_addresses(struct dns_header *header, size_t len, u32 mark)
|
||||
{
|
||||
unsigned char *p, *endrr;
|
||||
int i;
|
||||
unsigned long attl;
|
||||
struct allowlist *allowlists;
|
||||
char **pattern_pos;
|
||||
|
||||
if (RCODE(header) != NOERROR)
|
||||
return;
|
||||
|
||||
for (allowlists = daemon->allowlists; allowlists; allowlists = allowlists->next)
|
||||
if (allowlists->mark == (mark & daemon->allowlist_mask & allowlists->mask))
|
||||
for (pattern_pos = allowlists->patterns; *pattern_pos; pattern_pos++)
|
||||
if (!strcmp(*pattern_pos, "*"))
|
||||
return;
|
||||
|
||||
if (!(p = skip_questions(header, len)))
|
||||
return;
|
||||
for (i = ntohs(header->ancount); i != 0; i--)
|
||||
{
|
||||
int aqtype, aqclass, ardlen;
|
||||
|
||||
if (!extract_name(header, len, &p, daemon->namebuff, 1, 10))
|
||||
return;
|
||||
|
||||
if (!CHECK_LEN(header, p, len, 10))
|
||||
return;
|
||||
GETSHORT(aqtype, p);
|
||||
GETSHORT(aqclass, p);
|
||||
GETLONG(attl, p);
|
||||
GETSHORT(ardlen, p);
|
||||
|
||||
if (!CHECK_LEN(header, p, len, ardlen))
|
||||
return;
|
||||
endrr = p+ardlen;
|
||||
|
||||
if (aqclass == C_IN)
|
||||
{
|
||||
if (aqtype == T_CNAME)
|
||||
{
|
||||
if (!extract_name(header, len, &p, daemon->workspacename, 1, 0))
|
||||
return;
|
||||
if (safe_name(daemon->namebuff) && safe_name(daemon->workspacename))
|
||||
ubus_event_bcast_connmark_allowlist_resolved(mark, daemon->namebuff, daemon->workspacename, attl);
|
||||
}
|
||||
if (aqtype == T_A)
|
||||
{
|
||||
struct in_addr addr;
|
||||
char ip[INET_ADDRSTRLEN];
|
||||
if (ardlen != INADDRSZ)
|
||||
return;
|
||||
memcpy(&addr, p, ardlen);
|
||||
if (inet_ntop(AF_INET, &addr, ip, sizeof ip) && safe_name(daemon->namebuff))
|
||||
ubus_event_bcast_connmark_allowlist_resolved(mark, daemon->namebuff, ip, attl);
|
||||
}
|
||||
else if (aqtype == T_AAAA)
|
||||
{
|
||||
struct in6_addr addr;
|
||||
char ip[INET6_ADDRSTRLEN];
|
||||
if (ardlen != IN6ADDRSZ)
|
||||
return;
|
||||
memcpy(&addr, p, ardlen);
|
||||
if (inet_ntop(AF_INET6, &addr, ip, sizeof ip) && safe_name(daemon->namebuff))
|
||||
ubus_event_bcast_connmark_allowlist_resolved(mark, daemon->namebuff, ip, attl);
|
||||
}
|
||||
}
|
||||
|
||||
p = endrr;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* If the packet holds exactly one query
|
||||
return F_IPV4 or F_IPV6 and leave the name from the query in name */
|
||||
unsigned int extract_request(struct dns_header *header, size_t qlen, char *name, unsigned short *typep)
|
||||
@@ -894,9 +970,14 @@ unsigned int extract_request(struct dns_header *header, size_t qlen, char *name,
|
||||
if (typep)
|
||||
*typep = 0;
|
||||
|
||||
*name = 0; /* return empty name if no query found. */
|
||||
|
||||
if (ntohs(header->qdcount) != 1 || OPCODE(header) != QUERY)
|
||||
return 0; /* must be exactly one query. */
|
||||
|
||||
if (!(header->hb3 & HB3_QR) && (ntohs(header->ancount) != 0 || ntohs(header->nscount) != 0))
|
||||
return 0; /* non-standard query. */
|
||||
|
||||
if (!extract_name(header, qlen, &p, name, 1, 4))
|
||||
return 0; /* bad packet */
|
||||
|
||||
@@ -916,24 +997,20 @@ unsigned int extract_request(struct dns_header *header, size_t qlen, char *name,
|
||||
return F_IPV4 | F_IPV6;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
/* F_DNSSECOK as agument to search_servers() inhibits forwarding
|
||||
to servers for domains without a trust anchor. This make the
|
||||
behaviour for DS and DNSKEY queries we forward the same
|
||||
as for DS and DNSKEY queries we originate. */
|
||||
if (qtype == T_DS || qtype == T_DNSKEY)
|
||||
if (option_bool(OPT_DNSSEC_VALID) && (qtype == T_DS || qtype == T_DNSKEY))
|
||||
return F_DNSSECOK;
|
||||
#endif
|
||||
|
||||
return F_QUERY;
|
||||
}
|
||||
|
||||
size_t setup_reply(struct dns_header *header, size_t qlen,
|
||||
union all_addr *addrp, unsigned int flags, unsigned long ttl)
|
||||
void setup_reply(struct dns_header *header, unsigned int flags, int ede)
|
||||
{
|
||||
unsigned char *p;
|
||||
|
||||
if (!(p = skip_questions(header, qlen)))
|
||||
return 0;
|
||||
|
||||
/* clear authoritative and truncated flags, set QR flag */
|
||||
header->hb3 = (header->hb3 & ~(HB3_AA | HB3_TC )) | HB3_QR;
|
||||
/* clear AD flag, set RA flag */
|
||||
@@ -946,40 +1023,19 @@ size_t setup_reply(struct dns_header *header, size_t qlen,
|
||||
SET_RCODE(header, NOERROR); /* empty domain */
|
||||
else if (flags == F_NXDOMAIN)
|
||||
SET_RCODE(header, NXDOMAIN);
|
||||
else if (flags == F_SERVFAIL)
|
||||
{
|
||||
union all_addr a;
|
||||
a.log.rcode = SERVFAIL;
|
||||
log_query(F_CONFIG | F_RCODE, "error", &a, NULL);
|
||||
SET_RCODE(header, SERVFAIL);
|
||||
}
|
||||
else if (flags & ( F_IPV4 | F_IPV6))
|
||||
{
|
||||
if (flags & F_IPV4)
|
||||
{ /* we know the address */
|
||||
SET_RCODE(header, NOERROR);
|
||||
header->ancount = htons(1);
|
||||
header->hb3 |= HB3_AA;
|
||||
add_resource_record(header, NULL, NULL, sizeof(struct dns_header), &p, ttl, NULL, T_A, C_IN, "4", addrp);
|
||||
}
|
||||
|
||||
if (flags & F_IPV6)
|
||||
{
|
||||
SET_RCODE(header, NOERROR);
|
||||
header->ancount = htons(ntohs(header->ancount) + 1);
|
||||
header->hb3 |= HB3_AA;
|
||||
add_resource_record(header, NULL, NULL, sizeof(struct dns_header), &p, ttl, NULL, T_AAAA, C_IN, "6", addrp);
|
||||
}
|
||||
SET_RCODE(header, NOERROR);
|
||||
header->hb3 |= HB3_AA;
|
||||
}
|
||||
else /* nowhere to forward to */
|
||||
{
|
||||
union all_addr a;
|
||||
a.log.rcode = REFUSED;
|
||||
a.log.ede = ede;
|
||||
log_query(F_CONFIG | F_RCODE, "error", &a, NULL);
|
||||
SET_RCODE(header, REFUSED);
|
||||
}
|
||||
|
||||
return p - (unsigned char *)header;
|
||||
}
|
||||
|
||||
/* check if name matches local names ie from /etc/hosts or DHCP or local mx names. */
|
||||
@@ -1023,7 +1079,6 @@ static int check_bad_address(struct dns_header *header, size_t qlen, struct bogu
|
||||
int i, qtype, qclass, rdlen;
|
||||
unsigned long ttl;
|
||||
struct bogus_addr *baddrp;
|
||||
struct in_addr addr;
|
||||
|
||||
/* skip over questions */
|
||||
if (!(p = skip_questions(header, qlen)))
|
||||
@@ -1045,17 +1100,33 @@ static int check_bad_address(struct dns_header *header, size_t qlen, struct bogu
|
||||
if (ttlp)
|
||||
*ttlp = ttl;
|
||||
|
||||
if (qclass == C_IN && qtype == T_A)
|
||||
if (qclass == C_IN)
|
||||
{
|
||||
if (!CHECK_LEN(header, p, qlen, INADDRSZ))
|
||||
return 0;
|
||||
|
||||
for (baddrp = baddr; baddrp; baddrp = baddrp->next)
|
||||
if (qtype == T_A)
|
||||
{
|
||||
memcpy(&addr, p, INADDRSZ);
|
||||
struct in_addr addr;
|
||||
|
||||
if ((addr.s_addr & baddrp->mask.s_addr) == baddrp->addr.s_addr)
|
||||
return 1;
|
||||
if (!CHECK_LEN(header, p, qlen, INADDRSZ))
|
||||
return 0;
|
||||
|
||||
memcpy(&addr, p, INADDRSZ);
|
||||
|
||||
for (baddrp = baddr; baddrp; baddrp = baddrp->next)
|
||||
if (!baddrp->is6 && is_same_net_prefix(addr, baddrp->addr.addr4, baddrp->prefix))
|
||||
return 1;
|
||||
}
|
||||
else if (qtype == T_AAAA)
|
||||
{
|
||||
struct in6_addr addr;
|
||||
|
||||
if (!CHECK_LEN(header, p, qlen, IN6ADDRSZ))
|
||||
return 0;
|
||||
|
||||
memcpy(&addr, p, IN6ADDRSZ);
|
||||
|
||||
for (baddrp = baddr; baddrp; baddrp = baddrp->next)
|
||||
if (baddrp->is6 && is_same_net6(&addr, &baddrp->addr.addr6, baddrp->prefix))
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1553,43 +1624,17 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
anscount++;
|
||||
}
|
||||
}
|
||||
else if (option_bool(OPT_BOGUSPRIV) && (
|
||||
(is_arpa == F_IPV6 && private_net6(&addr.addr6)) ||
|
||||
(is_arpa == F_IPV4 && private_net(addr.addr4, 1))))
|
||||
else if (option_bool(OPT_BOGUSPRIV) &&
|
||||
((is_arpa == F_IPV6 && private_net6(&addr.addr6, 1)) || (is_arpa == F_IPV4 && private_net(addr.addr4, 1))) &&
|
||||
!lookup_domain(name, F_DOMAINSRV, NULL, NULL))
|
||||
{
|
||||
struct server *serv;
|
||||
unsigned int namelen = strlen(name);
|
||||
char *nameend = name + namelen;
|
||||
|
||||
/* see if have rev-server set */
|
||||
for (serv = daemon->servers; serv; serv = serv->next)
|
||||
{
|
||||
unsigned int domainlen;
|
||||
char *matchstart;
|
||||
|
||||
if ((serv->flags & (SERV_HAS_DOMAIN | SERV_NO_ADDR)) != SERV_HAS_DOMAIN)
|
||||
continue;
|
||||
|
||||
domainlen = strlen(serv->domain);
|
||||
if (domainlen == 0 || domainlen > namelen)
|
||||
continue;
|
||||
|
||||
matchstart = nameend - domainlen;
|
||||
if (hostname_isequal(matchstart, serv->domain) &&
|
||||
(namelen == domainlen || *(matchstart-1) == '.' ))
|
||||
break;
|
||||
}
|
||||
|
||||
/* if no configured server, not in cache, enabled and private IPV4 address, return NXDOMAIN */
|
||||
if (!serv)
|
||||
{
|
||||
ans = 1;
|
||||
sec_data = 0;
|
||||
nxdomain = 1;
|
||||
if (!dryrun)
|
||||
log_query(F_CONFIG | F_REVERSE | is_arpa | F_NEG | F_NXDOMAIN,
|
||||
name, &addr, NULL);
|
||||
}
|
||||
ans = 1;
|
||||
sec_data = 0;
|
||||
nxdomain = 1;
|
||||
if (!dryrun)
|
||||
log_query(F_CONFIG | F_REVERSE | is_arpa | F_NEG | F_NXDOMAIN,
|
||||
name, &addr, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
105
src/rfc2131.c
105
src/rfc2131.c
@@ -372,9 +372,22 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
|
||||
if (!context)
|
||||
{
|
||||
my_syslog(MS_DHCP | LOG_WARNING, _("no address range available for DHCP request %s %s"),
|
||||
subnet_addr.s_addr ? _("with subnet selector") : _("via"),
|
||||
subnet_addr.s_addr ? inet_ntoa(subnet_addr) : (mess->giaddr.s_addr ? inet_ntoa(mess->giaddr) : iface_name));
|
||||
const char *via;
|
||||
if (subnet_addr.s_addr)
|
||||
{
|
||||
via = _("with subnet selector");
|
||||
inet_ntop(AF_INET, &subnet_addr, daemon->addrbuff, ADDRSTRLEN);
|
||||
}
|
||||
else
|
||||
{
|
||||
via = _("via");
|
||||
if (mess->giaddr.s_addr)
|
||||
inet_ntop(AF_INET, &mess->giaddr, daemon->addrbuff, ADDRSTRLEN);
|
||||
else
|
||||
safe_strncpy(daemon->addrbuff, iface_name, ADDRSTRLEN);
|
||||
}
|
||||
my_syslog(MS_DHCP | LOG_WARNING, _("no address range available for DHCP request %s %s"),
|
||||
via, daemon->addrbuff);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -383,13 +396,19 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
struct dhcp_context *context_tmp;
|
||||
for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)
|
||||
{
|
||||
strcpy(daemon->namebuff, inet_ntoa(context_tmp->start));
|
||||
inet_ntop(AF_INET, &context_tmp->start, daemon->namebuff, MAXDNAME);
|
||||
if (context_tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP subnet: %s/%s"),
|
||||
ntohl(mess->xid), daemon->namebuff, inet_ntoa(context_tmp->netmask));
|
||||
{
|
||||
inet_ntop(AF_INET, &context_tmp->netmask, daemon->addrbuff, ADDRSTRLEN);
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP subnet: %s/%s"),
|
||||
ntohl(mess->xid), daemon->namebuff, daemon->addrbuff);
|
||||
}
|
||||
else
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP range: %s -- %s"),
|
||||
ntohl(mess->xid), daemon->namebuff, inet_ntoa(context_tmp->end));
|
||||
{
|
||||
inet_ntop(AF_INET, &context_tmp->end, daemon->addrbuff, ADDRSTRLEN);
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP range: %s -- %s"),
|
||||
ntohl(mess->xid), daemon->namebuff, daemon->addrbuff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1031,8 +1050,9 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
config->addr.s_addr == option_addr(opt).s_addr)
|
||||
{
|
||||
prettyprint_time(daemon->dhcp_buff, DECLINE_BACKOFF);
|
||||
inet_ntop(AF_INET, &config->addr, daemon->addrbuff, ADDRSTRLEN);
|
||||
my_syslog(MS_DHCP | LOG_WARNING, _("disabling DHCP static address %s for %s"),
|
||||
inet_ntoa(config->addr), daemon->dhcp_buff);
|
||||
daemon->addrbuff, daemon->dhcp_buff);
|
||||
config->flags |= CONFIG_DECLINED;
|
||||
config->decline_time = now;
|
||||
}
|
||||
@@ -1078,7 +1098,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
|
||||
if (have_config(config, CONFIG_ADDR))
|
||||
{
|
||||
char *addrs = inet_ntoa(config->addr);
|
||||
inet_ntop(AF_INET, &config->addr, daemon->addrbuff, ADDRSTRLEN);
|
||||
|
||||
if ((ltmp = lease_find_by_addr(config->addr)) &&
|
||||
ltmp != lease &&
|
||||
@@ -1088,7 +1108,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
unsigned char *mac = extended_hwaddr(ltmp->hwaddr_type, ltmp->hwaddr_len,
|
||||
ltmp->hwaddr, ltmp->clid_len, ltmp->clid, &len);
|
||||
my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is leased to %s"),
|
||||
addrs, print_mac(daemon->namebuff, mac, len));
|
||||
daemon->addrbuff, print_mac(daemon->namebuff, mac, len));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1097,10 +1117,10 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
if (context->router.s_addr == config->addr.s_addr)
|
||||
break;
|
||||
if (tmp)
|
||||
my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is in use by the server or relay"), addrs);
|
||||
my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is in use by the server or relay"), daemon->addrbuff);
|
||||
else if (have_config(config, CONFIG_DECLINED) &&
|
||||
difftime(now, config->decline_time) < (float)DECLINE_BACKOFF)
|
||||
my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it was previously declined"), addrs);
|
||||
my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it was previously declined"), daemon->addrbuff);
|
||||
else
|
||||
conf = config->addr;
|
||||
}
|
||||
@@ -1303,9 +1323,10 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
a lease from one of it's MACs to give the address to another. */
|
||||
if (config && config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type))
|
||||
{
|
||||
inet_ntop(AF_INET, <mp->addr, daemon->addrbuff, ADDRSTRLEN);
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("abandoning lease to %s of %s"),
|
||||
print_mac(daemon->namebuff, ltmp->hwaddr, ltmp->hwaddr_len),
|
||||
inet_ntoa(ltmp->addr));
|
||||
daemon->addrbuff);
|
||||
lease = ltmp;
|
||||
}
|
||||
else
|
||||
@@ -1674,42 +1695,40 @@ static void add_extradata_opt(struct dhcp_lease *lease, unsigned char *opt)
|
||||
static void log_packet(char *type, void *addr, unsigned char *ext_mac,
|
||||
int mac_len, char *interface, char *string, char *err, u32 xid)
|
||||
{
|
||||
struct in_addr a;
|
||||
|
||||
if (!err && !option_bool(OPT_LOG_OPTS) && option_bool(OPT_QUIET_DHCP))
|
||||
return;
|
||||
|
||||
/* addr may be misaligned */
|
||||
daemon->addrbuff[0] = 0;
|
||||
if (addr)
|
||||
memcpy(&a, addr, sizeof(a));
|
||||
inet_ntop(AF_INET, addr, daemon->addrbuff, ADDRSTRLEN);
|
||||
|
||||
print_mac(daemon->namebuff, ext_mac, mac_len);
|
||||
|
||||
if(option_bool(OPT_LOG_OPTS))
|
||||
my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s%s %s%s",
|
||||
ntohl(xid),
|
||||
type,
|
||||
interface,
|
||||
addr ? inet_ntoa(a) : "",
|
||||
addr ? " " : "",
|
||||
daemon->namebuff,
|
||||
string ? string : "",
|
||||
err ? err : "");
|
||||
else
|
||||
my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s%s %s%s",
|
||||
if (option_bool(OPT_LOG_OPTS))
|
||||
my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s%s %s%s",
|
||||
ntohl(xid),
|
||||
type,
|
||||
interface,
|
||||
addr ? inet_ntoa(a) : "",
|
||||
daemon->addrbuff,
|
||||
addr ? " " : "",
|
||||
daemon->namebuff,
|
||||
string ? string : "",
|
||||
err ? err : "");
|
||||
|
||||
else
|
||||
my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s%s %s%s",
|
||||
type,
|
||||
interface,
|
||||
daemon->addrbuff,
|
||||
addr ? " " : "",
|
||||
daemon->namebuff,
|
||||
string ? string : "",
|
||||
err ? err : "");
|
||||
|
||||
#ifdef HAVE_UBUS
|
||||
if (!strcmp(type, "DHCPACK"))
|
||||
ubus_event_bcast("dhcp.ack", daemon->namebuff, addr ? inet_ntoa(a) : NULL, string ? string : NULL, interface);
|
||||
else if (!strcmp(type, "DHCPRELEASE"))
|
||||
ubus_event_bcast("dhcp.release", daemon->namebuff, addr ? inet_ntoa(a) : NULL, string ? string : NULL, interface);
|
||||
if (!strcmp(type, "DHCPACK"))
|
||||
ubus_event_bcast("dhcp.ack", daemon->namebuff, addr ? daemon->addrbuff : NULL, string, interface);
|
||||
else if (!strcmp(type, "DHCPRELEASE"))
|
||||
ubus_event_bcast("dhcp.release", daemon->namebuff, addr ? daemon->addrbuff : NULL, string, interface);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1861,7 +1880,10 @@ static size_t dhcp_packet_size(struct dhcp_packet *mess, unsigned char *agent_id
|
||||
if (option_bool(OPT_LOG_OPTS))
|
||||
{
|
||||
if (mess->siaddr.s_addr != 0)
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("%u next server: %s"), ntohl(mess->xid), inet_ntoa(mess->siaddr));
|
||||
{
|
||||
inet_ntop(AF_INET, &mess->siaddr, daemon->addrbuff, ADDRSTRLEN);
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("%u next server: %s"), ntohl(mess->xid), daemon->addrbuff);
|
||||
}
|
||||
|
||||
if ((mess->flags & htons(0x8000)) && mess->ciaddr.s_addr == 0)
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("%u broadcast response"), ntohl(mess->xid));
|
||||
@@ -2785,11 +2807,4 @@ static void apply_delay(u32 xid, time_t recvtime, struct dhcp_netid *netid)
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif /* HAVE_DHCP */
|
||||
|
||||
@@ -919,11 +919,14 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
|
||||
|
||||
|
||||
case DHCP6RENEW:
|
||||
case DHCP6REBIND:
|
||||
{
|
||||
int address_assigned = 0;
|
||||
|
||||
/* set reply message type */
|
||||
*outmsgtypep = DHCP6REPLY;
|
||||
|
||||
log6_quiet(state, "DHCPRENEW", NULL, NULL);
|
||||
log6_quiet(state, msg_type == DHCP6RENEW ? "DHCPRENEW" : "DHCPREBIND", NULL, NULL);
|
||||
|
||||
for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end))
|
||||
{
|
||||
@@ -952,24 +955,35 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
|
||||
state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA,
|
||||
state->iaid, &req_addr)))
|
||||
{
|
||||
/* If the server cannot find a client entry for the IA the server
|
||||
returns the IA containing no addresses with a Status Code option set
|
||||
to NoBinding in the Reply message. */
|
||||
save_counter(iacntr);
|
||||
t1cntr = 0;
|
||||
|
||||
log6_packet(state, "DHCPREPLY", &req_addr, _("lease not found"));
|
||||
|
||||
o1 = new_opt6(OPTION6_STATUS_CODE);
|
||||
put_opt6_short(DHCP6NOBINDING);
|
||||
put_opt6_string(_("no binding found"));
|
||||
end_opt6(o1);
|
||||
|
||||
preferred_time = valid_time = 0;
|
||||
break;
|
||||
if (msg_type == DHCP6REBIND)
|
||||
{
|
||||
/* When rebinding, we can create a lease if it doesn't exist. */
|
||||
lease = lease6_allocate(&req_addr, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA);
|
||||
if (lease)
|
||||
lease_set_iaid(lease, state->iaid);
|
||||
else
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If the server cannot find a client entry for the IA the server
|
||||
returns the IA containing no addresses with a Status Code option set
|
||||
to NoBinding in the Reply message. */
|
||||
save_counter(iacntr);
|
||||
t1cntr = 0;
|
||||
|
||||
log6_packet(state, "DHCPREPLY", &req_addr, _("lease not found"));
|
||||
|
||||
o1 = new_opt6(OPTION6_STATUS_CODE);
|
||||
put_opt6_short(DHCP6NOBINDING);
|
||||
put_opt6_string(_("no binding found"));
|
||||
end_opt6(o1);
|
||||
|
||||
preferred_time = valid_time = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ((this_context = address6_available(state->context, &req_addr, tagif, 1)) ||
|
||||
(this_context = address6_valid(state->context, &req_addr, tagif, 1)))
|
||||
{
|
||||
@@ -1000,6 +1014,8 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
|
||||
|
||||
if (preferred_time == 0)
|
||||
message = _("deprecated");
|
||||
|
||||
address_assigned = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1022,10 +1038,18 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
|
||||
end_ia(t1cntr, min_time, 1);
|
||||
end_opt6(o);
|
||||
}
|
||||
|
||||
if (!address_assigned && msg_type == DHCP6REBIND)
|
||||
{
|
||||
/* can't create lease for any address, return error */
|
||||
o1 = new_opt6(OPTION6_STATUS_CODE);
|
||||
put_opt6_short(DHCP6NOADDRS);
|
||||
put_opt6_string(_("no addresses available"));
|
||||
end_opt6(o1);
|
||||
}
|
||||
|
||||
tagif = add_options(state, 0);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case DHCP6CONFIRM:
|
||||
|
||||
@@ -761,7 +761,8 @@ static ssize_t tftp_err(int err, char *packet, char *message, char *file)
|
||||
len = snprintf(mess->message, MAXMESSAGE, message, file, errstr);
|
||||
ret += (len < MAXMESSAGE) ? len + 1 : MAXMESSAGE; /* include terminating zero */
|
||||
|
||||
my_syslog(MS_TFTP | LOG_ERR, "%s", mess->message);
|
||||
if (err != ERR_FNF || !option_bool(OPT_QUIET_TFTP))
|
||||
my_syslog(MS_TFTP | LOG_ERR, "%s", mess->message);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
225
src/ubus.c
225
src/ubus.c
@@ -21,17 +21,44 @@
|
||||
#include <libubus.h>
|
||||
|
||||
static struct blob_buf b;
|
||||
static int notify;
|
||||
static int error_logged = 0;
|
||||
|
||||
static int ubus_handle_metrics(struct ubus_context *ctx, struct ubus_object *obj,
|
||||
struct ubus_request_data *req, const char *method,
|
||||
struct blob_attr *msg);
|
||||
|
||||
#ifdef HAVE_CONNTRACK
|
||||
enum {
|
||||
SET_CONNMARK_ALLOWLIST_MARK,
|
||||
SET_CONNMARK_ALLOWLIST_MASK,
|
||||
SET_CONNMARK_ALLOWLIST_PATTERNS
|
||||
};
|
||||
static const struct blobmsg_policy set_connmark_allowlist_policy[] = {
|
||||
[SET_CONNMARK_ALLOWLIST_MARK] = {
|
||||
.name = "mark",
|
||||
.type = BLOBMSG_TYPE_INT32
|
||||
},
|
||||
[SET_CONNMARK_ALLOWLIST_MASK] = {
|
||||
.name = "mask",
|
||||
.type = BLOBMSG_TYPE_INT32
|
||||
},
|
||||
[SET_CONNMARK_ALLOWLIST_PATTERNS] = {
|
||||
.name = "patterns",
|
||||
.type = BLOBMSG_TYPE_ARRAY
|
||||
}
|
||||
};
|
||||
static int ubus_handle_set_connmark_allowlist(struct ubus_context *ctx, struct ubus_object *obj,
|
||||
struct ubus_request_data *req, const char *method,
|
||||
struct blob_attr *msg);
|
||||
#endif
|
||||
|
||||
static void ubus_subscribe_cb(struct ubus_context *ctx, struct ubus_object *obj);
|
||||
|
||||
static const struct ubus_method ubus_object_methods[] = {
|
||||
UBUS_METHOD_NOARG("metrics", ubus_handle_metrics),
|
||||
#ifdef HAVE_CONNTRACK
|
||||
UBUS_METHOD("set_connmark_allowlist", ubus_handle_set_connmark_allowlist, set_connmark_allowlist_policy),
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct ubus_object_type ubus_object_type =
|
||||
@@ -50,17 +77,16 @@ static void ubus_subscribe_cb(struct ubus_context *ctx, struct ubus_object *obj)
|
||||
(void)ctx;
|
||||
|
||||
my_syslog(LOG_DEBUG, _("UBus subscription callback: %s subscriber(s)"), obj->has_subscribers ? "1" : "0");
|
||||
notify = obj->has_subscribers;
|
||||
}
|
||||
|
||||
static void ubus_destroy(struct ubus_context *ubus)
|
||||
{
|
||||
ubus_free(ubus);
|
||||
daemon->ubus = NULL;
|
||||
|
||||
// Forces re-initialization when we're reusing the same definitions later on.
|
||||
ubus_object.id = 0;
|
||||
ubus_object_type.id = 0;
|
||||
|
||||
ubus_free(ubus);
|
||||
daemon->ubus = NULL;
|
||||
}
|
||||
|
||||
static void ubus_disconnect_cb(struct ubus_context *ubus)
|
||||
@@ -76,42 +102,27 @@ static void ubus_disconnect_cb(struct ubus_context *ubus)
|
||||
}
|
||||
}
|
||||
|
||||
void ubus_init()
|
||||
char *ubus_init()
|
||||
{
|
||||
struct ubus_context *ubus = NULL;
|
||||
int ret = 0;
|
||||
|
||||
ubus = ubus_connect(NULL);
|
||||
if (!ubus)
|
||||
{
|
||||
if (!error_logged)
|
||||
{
|
||||
my_syslog(LOG_ERR, _("Cannot initialize UBus: connection failed"));
|
||||
error_logged = 1;
|
||||
}
|
||||
|
||||
ubus_destroy(ubus);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(ubus = ubus_connect(NULL)))
|
||||
return NULL;
|
||||
|
||||
ubus_object.name = daemon->ubus_name;
|
||||
ret = ubus_add_object(ubus, &ubus_object);
|
||||
if (ret)
|
||||
{
|
||||
if (!error_logged)
|
||||
{
|
||||
my_syslog(LOG_ERR, _("Cannot add object to UBus: %s"), ubus_strerror(ret));
|
||||
error_logged = 1;
|
||||
}
|
||||
ubus_destroy(ubus);
|
||||
return;
|
||||
}
|
||||
|
||||
return (char *)ubus_strerror(ret);
|
||||
}
|
||||
|
||||
ubus->connection_lost = ubus_disconnect_cb;
|
||||
daemon->ubus = ubus;
|
||||
error_logged = 0;
|
||||
|
||||
my_syslog(LOG_INFO, _("Connected to system UBus"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void set_ubus_listeners()
|
||||
@@ -178,12 +189,128 @@ static int ubus_handle_metrics(struct ubus_context *ctx, struct ubus_object *obj
|
||||
return ubus_send_reply(ctx, req, b.head);
|
||||
}
|
||||
|
||||
#ifdef HAVE_CONNTRACK
|
||||
static int ubus_handle_set_connmark_allowlist(struct ubus_context *ctx, struct ubus_object *obj,
|
||||
struct ubus_request_data *req, const char *method,
|
||||
struct blob_attr *msg)
|
||||
{
|
||||
const struct blobmsg_policy *policy = set_connmark_allowlist_policy;
|
||||
size_t policy_len = countof(set_connmark_allowlist_policy);
|
||||
struct allowlist *allowlists = NULL, **allowlists_pos;
|
||||
char **patterns = NULL, **patterns_pos;
|
||||
u32 mark, mask = UINT32_MAX;
|
||||
size_t num_patterns = 0;
|
||||
struct blob_attr *tb[policy_len];
|
||||
struct blob_attr *attr;
|
||||
|
||||
if (blobmsg_parse(policy, policy_len, tb, blob_data(msg), blob_len(msg)))
|
||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
if (!tb[SET_CONNMARK_ALLOWLIST_MARK])
|
||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||
mark = blobmsg_get_u32(tb[SET_CONNMARK_ALLOWLIST_MARK]);
|
||||
if (!mark)
|
||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
if (tb[SET_CONNMARK_ALLOWLIST_MASK])
|
||||
{
|
||||
mask = blobmsg_get_u32(tb[SET_CONNMARK_ALLOWLIST_MASK]);
|
||||
if (!mask || (mark & ~mask))
|
||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (tb[SET_CONNMARK_ALLOWLIST_PATTERNS])
|
||||
{
|
||||
struct blob_attr *head = blobmsg_data(tb[SET_CONNMARK_ALLOWLIST_PATTERNS]);
|
||||
size_t len = blobmsg_data_len(tb[SET_CONNMARK_ALLOWLIST_PATTERNS]);
|
||||
__blob_for_each_attr(attr, head, len)
|
||||
{
|
||||
char *pattern;
|
||||
if (blob_id(attr) != BLOBMSG_TYPE_STRING)
|
||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||
if (!(pattern = blobmsg_get_string(attr)))
|
||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||
if (strcmp(pattern, "*") && !is_valid_dns_name_pattern(pattern))
|
||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||
num_patterns++;
|
||||
}
|
||||
}
|
||||
|
||||
for (allowlists_pos = &daemon->allowlists; *allowlists_pos; allowlists_pos = &(*allowlists_pos)->next)
|
||||
if ((*allowlists_pos)->mark == mark && (*allowlists_pos)->mask == mask)
|
||||
{
|
||||
struct allowlist *allowlists_next = (*allowlists_pos)->next;
|
||||
for (patterns_pos = (*allowlists_pos)->patterns; *patterns_pos; patterns_pos++)
|
||||
{
|
||||
free(*patterns_pos);
|
||||
*patterns_pos = NULL;
|
||||
}
|
||||
free((*allowlists_pos)->patterns);
|
||||
(*allowlists_pos)->patterns = NULL;
|
||||
free(*allowlists_pos);
|
||||
*allowlists_pos = allowlists_next;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!num_patterns)
|
||||
return UBUS_STATUS_OK;
|
||||
|
||||
patterns = whine_malloc((num_patterns + 1) * sizeof(char *));
|
||||
if (!patterns)
|
||||
goto fail;
|
||||
patterns_pos = patterns;
|
||||
if (tb[SET_CONNMARK_ALLOWLIST_PATTERNS])
|
||||
{
|
||||
struct blob_attr *head = blobmsg_data(tb[SET_CONNMARK_ALLOWLIST_PATTERNS]);
|
||||
size_t len = blobmsg_data_len(tb[SET_CONNMARK_ALLOWLIST_PATTERNS]);
|
||||
__blob_for_each_attr(attr, head, len)
|
||||
{
|
||||
char *pattern;
|
||||
if (!(pattern = blobmsg_get_string(attr)))
|
||||
goto fail;
|
||||
if (!(*patterns_pos = whine_malloc(strlen(pattern) + 1)))
|
||||
goto fail;
|
||||
strcpy(*patterns_pos++, pattern);
|
||||
}
|
||||
}
|
||||
|
||||
allowlists = whine_malloc(sizeof(struct allowlist));
|
||||
if (!allowlists)
|
||||
goto fail;
|
||||
memset(allowlists, 0, sizeof(struct allowlist));
|
||||
allowlists->mark = mark;
|
||||
allowlists->mask = mask;
|
||||
allowlists->patterns = patterns;
|
||||
allowlists->next = daemon->allowlists;
|
||||
daemon->allowlists = allowlists;
|
||||
return UBUS_STATUS_OK;
|
||||
|
||||
fail:
|
||||
if (patterns)
|
||||
{
|
||||
for (patterns_pos = patterns; *patterns_pos; patterns_pos++)
|
||||
{
|
||||
free(*patterns_pos);
|
||||
*patterns_pos = NULL;
|
||||
}
|
||||
free(patterns);
|
||||
patterns = NULL;
|
||||
}
|
||||
if (allowlists)
|
||||
{
|
||||
free(allowlists);
|
||||
allowlists = NULL;
|
||||
}
|
||||
return UBUS_STATUS_UNKNOWN_ERROR;
|
||||
}
|
||||
#endif
|
||||
|
||||
void ubus_event_bcast(const char *type, const char *mac, const char *ip, const char *name, const char *interface)
|
||||
{
|
||||
struct ubus_context *ubus = (struct ubus_context *)daemon->ubus;
|
||||
int ret;
|
||||
|
||||
if (!ubus || !notify)
|
||||
if (!ubus || !ubus_object.has_subscribers)
|
||||
return;
|
||||
|
||||
blob_buf_init(&b, BLOBMSG_TYPE_TABLE);
|
||||
@@ -197,9 +324,47 @@ void ubus_event_bcast(const char *type, const char *mac, const char *ip, const c
|
||||
blobmsg_add_string(&b, "interface", interface);
|
||||
|
||||
ret = ubus_notify(ubus, &ubus_object, type, b.head, -1);
|
||||
if (!ret)
|
||||
if (ret)
|
||||
my_syslog(LOG_ERR, _("Failed to send UBus event: %s"), ubus_strerror(ret));
|
||||
}
|
||||
|
||||
#ifdef HAVE_CONNTRACK
|
||||
void ubus_event_bcast_connmark_allowlist_refused(u32 mark, const char *name)
|
||||
{
|
||||
struct ubus_context *ubus = (struct ubus_context *)daemon->ubus;
|
||||
int ret;
|
||||
|
||||
if (!ubus || !ubus_object.has_subscribers)
|
||||
return;
|
||||
|
||||
blob_buf_init(&b, 0);
|
||||
blobmsg_add_u32(&b, "mark", mark);
|
||||
blobmsg_add_string(&b, "name", name);
|
||||
|
||||
ret = ubus_notify(ubus, &ubus_object, "connmark-allowlist.refused", b.head, -1);
|
||||
if (ret)
|
||||
my_syslog(LOG_ERR, _("Failed to send UBus event: %s"), ubus_strerror(ret));
|
||||
}
|
||||
|
||||
void ubus_event_bcast_connmark_allowlist_resolved(u32 mark, const char *name, const char *value, u32 ttl)
|
||||
{
|
||||
struct ubus_context *ubus = (struct ubus_context *)daemon->ubus;
|
||||
int ret;
|
||||
|
||||
if (!ubus || !ubus_object.has_subscribers)
|
||||
return;
|
||||
|
||||
blob_buf_init(&b, 0);
|
||||
blobmsg_add_u32(&b, "mark", mark);
|
||||
blobmsg_add_string(&b, "name", name);
|
||||
blobmsg_add_string(&b, "value", value);
|
||||
blobmsg_add_u32(&b, "ttl", ttl);
|
||||
|
||||
ret = ubus_notify(ubus, &ubus_object, "connmark-allowlist.resolved", b.head, /* timeout: */ 1000);
|
||||
if (ret)
|
||||
my_syslog(LOG_ERR, _("Failed to send UBus event: %s"), ubus_strerror(ret));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* HAVE_UBUS */
|
||||
|
||||
12
src/util.c
12
src/util.c
@@ -436,7 +436,17 @@ int netmask_length(struct in_addr mask)
|
||||
int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask)
|
||||
{
|
||||
return (a.s_addr & mask.s_addr) == (b.s_addr & mask.s_addr);
|
||||
}
|
||||
}
|
||||
|
||||
int is_same_net_prefix(struct in_addr a, struct in_addr b, int prefix)
|
||||
{
|
||||
struct in_addr mask;
|
||||
|
||||
mask.s_addr = htonl(~((1 << (32 - prefix)) - 1));
|
||||
|
||||
return is_same_net(a, b, mask);
|
||||
}
|
||||
|
||||
|
||||
int is_same_net6(struct in6_addr *a, struct in6_addr *b, int prefixlen)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user