mirror of
http://thekelleys.org.uk/git/dnsmasq.git
synced 2026-11-06 15:51:41 +00:00
Compare commits
118
Commits
v2.86test2
...
v2.87test2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
47aefca5e4 | ||
|
|
981fb03710 | ||
|
|
ef2f8d70d2 | ||
|
|
d9995a1add | ||
|
|
ea7a05ad43 | ||
|
|
26bbf5a314 | ||
|
|
c147329823 | ||
|
|
eb88eed1fc | ||
|
|
8312a3ba4f | ||
|
|
35f93081dc | ||
|
|
de372d6914 | ||
|
|
4ac517e4ac | ||
|
|
e3651367b3 | ||
|
|
02ea41ddd1 | ||
|
|
51ffae4eab | ||
|
|
afe84f37f8 | ||
|
|
0afeef0e00 | ||
|
|
94a17fd97f | ||
|
|
5b5ec55445 | ||
|
|
1e6565c1a5 | ||
|
|
fc522515b9 | ||
|
|
9881b0736d | ||
|
|
e52b4b1466 | ||
|
|
2f45670951 | ||
|
|
50d75ae514 | ||
|
|
dea69a12aa | ||
|
|
e0ce3c12f2 | ||
|
|
93cf516bf1 | ||
|
|
6f4de018af | ||
|
|
6e91cf3172 | ||
|
|
9cb7f8a655 | ||
|
|
5d8d1ad14b | ||
|
|
cac9ca38f6 | ||
|
|
c4523639d5 | ||
|
|
1ce1c6beae | ||
|
|
51d56df7a3 | ||
|
|
860a9a57d6 | ||
|
|
c83e33d608 | ||
|
|
7b80c75d9d | ||
|
|
79337f99ae | ||
|
|
a42ee397f3 | ||
|
|
e58f8bb8c1 | ||
|
|
58cf958e41 | ||
|
|
06d01f7ae4 | ||
|
|
1a33eec0ba | ||
|
|
82de7a1e96 | ||
|
|
8e9bde57c5 | ||
|
|
1bb70e08be | ||
|
|
9afcb7ae61 | ||
|
|
545c4955e6 | ||
|
|
2f2d59b35c | ||
|
|
a1729deed3 | ||
|
|
fc64b97cd5 | ||
|
|
daddc8cb80 | ||
|
|
527c3c7d0d | ||
|
|
fcb4dcaf7c | ||
|
|
3ca4995d34 | ||
|
|
d387f8f06c | ||
|
|
ea43234c86 | ||
|
|
867e56a45e | ||
|
|
a163c63787 | ||
|
|
8389b943d3 | ||
|
|
f2266d9678 | ||
|
|
56bd806978 | ||
|
|
ac7eeea44d | ||
|
|
b741059549 | ||
|
|
cbd984287f | ||
|
|
32e15c3f45 | ||
|
|
f0dc324e35 | ||
|
|
f83c6cf51a | ||
|
|
c068b3ae2f | ||
|
|
adf9dec1e6 | ||
|
|
767d9cbd96 | ||
|
|
e7ccd95c04 | ||
|
|
719f79a8fd | ||
|
|
96f6444958 | ||
|
|
df25f204ba | ||
|
|
8acdc3ede7 | ||
|
|
857b445522 | ||
|
|
5bcca1219a | ||
|
|
4558c26fcd | ||
|
|
a92c6d77dc | ||
|
|
0c95a5ff53 | ||
|
|
cb6d06bb54 | ||
|
|
3ef955c85a | ||
|
|
5e95c16c32 | ||
|
|
4205e2ebcf | ||
|
|
9d806c51c2 | ||
|
|
a38bb31727 | ||
|
|
8a1ef367e2 | ||
|
|
1291865c92 | ||
|
|
a9ebbee7b6 | ||
|
|
06df5ad7d0 | ||
|
|
66b863c989 | ||
|
|
c9efe8e5e1 | ||
|
|
d515223bb5 | ||
|
|
b908f4334b | ||
|
|
6261aba026 | ||
|
|
85bc7534da | ||
|
|
1b30fd1732 | ||
|
|
8c9196bff8 | ||
|
|
b1daf44954 | ||
|
|
11c52d032b | ||
|
|
be291d979d | ||
|
|
6d1edd8d32 | ||
|
|
25ff956c7d | ||
|
|
38179500f8 | ||
|
|
5f7be5f0d6 | ||
|
|
627056febb | ||
|
|
cbd76447fd | ||
|
|
a60a233329 | ||
|
|
a0a3b8ad3e | ||
|
|
d0ae3f5a4d | ||
|
|
6860cf932b | ||
|
|
0276e0805b | ||
|
|
06ff3d8a26 | ||
|
|
1a3b69aa56 | ||
|
|
8237d06ab7 |
No files matched your search
@@ -1,6 +1,17 @@
|
|||||||
|
version 2.87
|
||||||
|
Allow arbitrary prefix lengths in --rev-server and
|
||||||
|
--domain=....,local
|
||||||
|
|
||||||
|
Replace --address=/#/..... functionality which got
|
||||||
|
missed in the 2.86 domain search rewrite.
|
||||||
|
|
||||||
|
Add --nftset option, like --ipset but for the newer nftables.
|
||||||
|
Thanks to Chen Zhenge for the patch.
|
||||||
|
|
||||||
|
|
||||||
version 2.86
|
version 2.86
|
||||||
Handle DHCPREBIND requests in the DHCPv6 server code.
|
Handle DHCPREBIND requests in the DHCPv6 server code.
|
||||||
Thanks to Aichun Li for spotting this ommision, and the initial
|
Thanks to Aichun Li for spotting this omission, and the initial
|
||||||
patch.
|
patch.
|
||||||
|
|
||||||
Fix bug which caused dnsmasq to lose track of processes forked
|
Fix bug which caused dnsmasq to lose track of processes forked
|
||||||
@@ -33,7 +44,7 @@ version 2.86
|
|||||||
address=/example.com/1.2.3.4
|
address=/example.com/1.2.3.4
|
||||||
address=/example.com/5.6.7.8
|
address=/example.com/5.6.7.8
|
||||||
It also handles multiple upstream servers for a domain better; using
|
It also handles multiple upstream servers for a domain better; using
|
||||||
the same try/retry alogrithms as non domain-specific servers. This
|
the same try/retry algorithms as non domain-specific servers. This
|
||||||
also applies to DNSSEC-generated queries.
|
also applies to DNSSEC-generated queries.
|
||||||
Finally, some of the oldest and gnarliest code in dnsmasq has had
|
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.
|
a significant clean-up. It's far from perfect, but it _is_ better.
|
||||||
@@ -64,6 +75,37 @@ version 2.86
|
|||||||
queries. The requesting address and port have been removed from
|
queries. The requesting address and port have been removed from
|
||||||
DNSSEC logging lines, since this is no longer strictly defined.
|
DNSSEC logging lines, since this is no longer strictly defined.
|
||||||
|
|
||||||
|
Connection track mark based DNS query filtering. Thanks to
|
||||||
|
Etan Kissling for implementing this It extends query filtering
|
||||||
|
support beyond what is currently possible
|
||||||
|
with the `--ipset` configuration option, by adding support for:
|
||||||
|
1) Specifying allowlists on a per-client basis, based on their
|
||||||
|
associated Linux connection track mark.
|
||||||
|
2) Dynamic configuration of allowlists via Ubus.
|
||||||
|
3) Reporting when a DNS query resolves or is rejected via Ubus.
|
||||||
|
4) DNS name patterns containing wildcards.
|
||||||
|
Disallowed queries are not forwarded; they are rejected
|
||||||
|
with a REFUSED error code.
|
||||||
|
|
||||||
|
Allow smaller than 64 prefix lengths in synth-domain, with caveats.
|
||||||
|
--synth-domain=1234:4567::/56,example.com is now valid.
|
||||||
|
|
||||||
|
Make domains generated by --synth-domain appear in replies
|
||||||
|
when in authoritative mode.
|
||||||
|
|
||||||
|
Ensure CAP_NET_ADMIN capability is available when
|
||||||
|
conntrack is configured. Thanks to Yick Xie for spotting
|
||||||
|
the lack of this.
|
||||||
|
|
||||||
|
When --dhcp-hostsfile --dhcp-optsfile and --addn-hosts are
|
||||||
|
given a directory as argument, define the order in which
|
||||||
|
files within that directory are read (alphabetical order
|
||||||
|
of filename). Thanks to Ed Wildgoose for the initial patch
|
||||||
|
and motivation for this.
|
||||||
|
|
||||||
|
Allow adding IP address to nftables set in addition to
|
||||||
|
ipset.
|
||||||
|
|
||||||
|
|
||||||
version 2.85
|
version 2.85
|
||||||
Fix problem with DNS retries in 2.83/2.84.
|
Fix problem with DNS retries in 2.83/2.84.
|
||||||
@@ -135,6 +177,9 @@ version 2.85
|
|||||||
Tweak TFTP code to check sender of all received packets, as
|
Tweak TFTP code to check sender of all received packets, as
|
||||||
specified in RFC 1350 para 4.
|
specified in RFC 1350 para 4.
|
||||||
|
|
||||||
|
Support some wildcard matching of input tags to --tag-if.
|
||||||
|
Thanks to Geoff Back for the idea and the patch.
|
||||||
|
|
||||||
|
|
||||||
version 2.84
|
version 2.84
|
||||||
Fix a problem, introduced in 2.83, which could see DNS replies
|
Fix a problem, introduced in 2.83, which could see DNS replies
|
||||||
|
|||||||
@@ -236,7 +236,7 @@ Q: What network types are supported by the DHCP server?
|
|||||||
A: Ethernet (and 802.11 wireless) are supported on all platforms. On
|
A: Ethernet (and 802.11 wireless) are supported on all platforms. On
|
||||||
Linux all network types (including FireWire) are supported.
|
Linux all network types (including FireWire) are supported.
|
||||||
|
|
||||||
Q: What are these strange "bind-interface" and "bind-dynamic" options?
|
Q: What are these strange "bind-interfaces" and "bind-dynamic" options?
|
||||||
|
|
||||||
A: Dnsmasq from v2.63 can operate in one of three different "networking
|
A: Dnsmasq from v2.63 can operate in one of three different "networking
|
||||||
modes". This is unfortunate as it requires users configuring dnsmasq
|
modes". This is unfortunate as it requires users configuring dnsmasq
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ nettle_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC $(PKG_CO
|
|||||||
HAVE_NETTLEHASH $(PKG_CONFIG) --libs nettle`
|
HAVE_NETTLEHASH $(PKG_CONFIG) --libs nettle`
|
||||||
gmp_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC NO_GMP --copy -lgmp`
|
gmp_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC NO_GMP --copy -lgmp`
|
||||||
sunos_libs = `if uname | grep SunOS >/dev/null 2>&1; then echo -lsocket -lnsl -lposix4; fi`
|
sunos_libs = `if uname | grep SunOS >/dev/null 2>&1; then echo -lsocket -lnsl -lposix4; fi`
|
||||||
|
nft_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_NFTSET $(PKG_CONFIG) --libs libnftables`
|
||||||
version = -DVERSION='\"`$(top)/bld/get-version $(top)`\"'
|
version = -DVERSION='\"`$(top)/bld/get-version $(top)`\"'
|
||||||
|
|
||||||
sum?=$(shell $(CC) -DDNSMASQ_COMPILE_OPTS $(COPTS) -E $(top)/$(SRC)/dnsmasq.h | ( md5sum 2>/dev/null || md5 ) | cut -f 1 -d ' ')
|
sum?=$(shell $(CC) -DDNSMASQ_COMPILE_OPTS $(COPTS) -E $(top)/$(SRC)/dnsmasq.h | ( md5sum 2>/dev/null || md5 ) | cut -f 1 -d ' ')
|
||||||
@@ -79,10 +80,10 @@ copts_conf = .copts_$(sum)
|
|||||||
objs = cache.o rfc1035.o util.o option.o forward.o network.o \
|
objs = cache.o rfc1035.o util.o option.o forward.o network.o \
|
||||||
dnsmasq.o dhcp.o lease.o rfc2131.o netlink.o dbus.o bpf.o \
|
dnsmasq.o dhcp.o lease.o rfc2131.o netlink.o dbus.o bpf.o \
|
||||||
helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o \
|
helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o \
|
||||||
dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o \
|
dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o pattern.o \
|
||||||
domain.o dnssec.o blockdata.o tables.o loop.o inotify.o \
|
domain.o dnssec.o blockdata.o tables.o loop.o inotify.o \
|
||||||
poll.o rrfilter.o edns0.o arp.o crypto.o dump.o ubus.o \
|
poll.o rrfilter.o edns0.o arp.o crypto.o dump.o ubus.o \
|
||||||
metrics.o hash-questions.o domain-match.o
|
metrics.o hash-questions.o domain-match.o nftset.o
|
||||||
|
|
||||||
hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \
|
hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \
|
||||||
dns-protocol.h radv-protocol.h ip6addr.h metrics.h
|
dns-protocol.h radv-protocol.h ip6addr.h metrics.h
|
||||||
@@ -91,7 +92,7 @@ all : $(BUILDDIR)
|
|||||||
@cd $(BUILDDIR) && $(MAKE) \
|
@cd $(BUILDDIR) && $(MAKE) \
|
||||||
top="$(top)" \
|
top="$(top)" \
|
||||||
build_cflags="$(version) $(dbus_cflags) $(idn2_cflags) $(idn_cflags) $(ct_cflags) $(lua_cflags) $(nettle_cflags)" \
|
build_cflags="$(version) $(dbus_cflags) $(idn2_cflags) $(idn_cflags) $(ct_cflags) $(lua_cflags) $(nettle_cflags)" \
|
||||||
build_libs="$(dbus_libs) $(idn2_libs) $(idn_libs) $(ct_libs) $(lua_libs) $(sunos_libs) $(nettle_libs) $(gmp_libs) $(ubus_libs)" \
|
build_libs="$(dbus_libs) $(idn2_libs) $(idn_libs) $(ct_libs) $(lua_libs) $(sunos_libs) $(nettle_libs) $(gmp_libs) $(ubus_libs) $(nft_libs)" \
|
||||||
-f $(top)/Makefile dnsmasq
|
-f $(top)/Makefile dnsmasq
|
||||||
|
|
||||||
mostly_clean :
|
mostly_clean :
|
||||||
@@ -116,7 +117,7 @@ all-i18n : $(BUILDDIR)
|
|||||||
top="$(top)" \
|
top="$(top)" \
|
||||||
i18n=-DLOCALEDIR=\'\"$(LOCALEDIR)\"\' \
|
i18n=-DLOCALEDIR=\'\"$(LOCALEDIR)\"\' \
|
||||||
build_cflags="$(version) $(dbus_cflags) $(idn2_cflags) $(idn_cflags) $(ct_cflags) $(lua_cflags) $(nettle_cflags)" \
|
build_cflags="$(version) $(dbus_cflags) $(idn2_cflags) $(idn_cflags) $(ct_cflags) $(lua_cflags) $(nettle_cflags)" \
|
||||||
build_libs="$(dbus_libs) $(idn2_libs) $(idn_libs) $(ct_libs) $(lua_libs) $(sunos_libs) $(nettle_libs) $(gmp_libs) $(ubus_libs)" \
|
build_libs="$(dbus_libs) $(idn2_libs) $(idn_libs) $(ct_libs) $(lua_libs) $(sunos_libs) $(nettle_libs) $(gmp_libs) $(ubus_libs) $(nft_libs)" \
|
||||||
-f $(top)/Makefile dnsmasq
|
-f $(top)/Makefile dnsmasq
|
||||||
for f in `cd $(PO); echo *.po`; do \
|
for f in `cd $(PO); echo *.po`; do \
|
||||||
cd $(top) && cd $(BUILDDIR) && $(MAKE) top="$(top)" -f $(top)/Makefile $${f%.po}.mo; \
|
cd $(top) && cd $(BUILDDIR) && $(MAKE) top="$(top)" -f $(top)/Makefile $${f%.po}.mo; \
|
||||||
|
|||||||
@@ -153,7 +153,11 @@ int main(int argc, char **argv)
|
|||||||
exit(1);
|
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));
|
memset(&packet, 0, sizeof(packet));
|
||||||
|
|
||||||
@@ -177,7 +181,7 @@ int main(int argc, char **argv)
|
|||||||
*(p++) = OPTION_END;
|
*(p++) = OPTION_END;
|
||||||
|
|
||||||
dest.sin_family = AF_INET;
|
dest.sin_family = AF_INET;
|
||||||
dest.sin_addr.s_addr = inet_addr("127.0.0.1");
|
(void)inet_pton(AF_INET, "127.0.0.1", &dest.sin_addr);
|
||||||
dest.sin_port = ntohs(DHCP_SERVER_PORT);
|
dest.sin_port = ntohs(DHCP_SERVER_PORT);
|
||||||
|
|
||||||
if (sendto(fd, &packet, sizeof(packet), 0,
|
if (sendto(fd, &packet, sizeof(packet), 0,
|
||||||
|
|||||||
@@ -280,6 +280,7 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
/* This voodoo fakes up a packet coming from the correct interface, which really matters for
|
/* This voodoo fakes up a packet coming from the correct interface, which really matters for
|
||||||
a DHCP server */
|
a DHCP server */
|
||||||
|
memset(&ifr, 0, sizeof(ifr));
|
||||||
strncpy(ifr.ifr_name, argv[1], sizeof(ifr.ifr_name)-1);
|
strncpy(ifr.ifr_name, argv[1], sizeof(ifr.ifr_name)-1);
|
||||||
ifr.ifr_name[sizeof(ifr.ifr_name)-1] = '\0';
|
ifr.ifr_name[sizeof(ifr.ifr_name)-1] = '\0';
|
||||||
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) == -1)
|
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) == -1)
|
||||||
@@ -288,13 +289,12 @@ int main(int argc, char **argv)
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inet_addr(argv[2]) == INADDR_NONE)
|
if (inet_pton(AF_INET, argv[2], &lease.s_addr) < 1)
|
||||||
{
|
{
|
||||||
perror("invalid ip address");
|
perror("invalid ip address");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
lease.s_addr = inet_addr(argv[2]);
|
|
||||||
server = find_interface(lease, nl, if_nametoindex(argv[1]), fd, &ifr);
|
server = find_interface(lease, nl, if_nametoindex(argv[1]), fd, &ifr);
|
||||||
|
|
||||||
memset(&packet, 0, sizeof(packet));
|
memset(&packet, 0, sizeof(packet));
|
||||||
|
|||||||
@@ -318,6 +318,12 @@ void usage(const char* arg, FILE* stream)
|
|||||||
fprintf (stream, "Usage: %s %s\n", arg, usage_string);
|
fprintf (stream, "Usage: %s %s\n", arg, usage_string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void fail_fatal(const char *errstr, int exitcode)
|
||||||
|
{
|
||||||
|
perror(errstr);
|
||||||
|
exit(exitcode);
|
||||||
|
}
|
||||||
|
|
||||||
int send_release_packet(const char* iface, struct dhcp6_packet* packet)
|
int send_release_packet(const char* iface, struct dhcp6_packet* packet)
|
||||||
{
|
{
|
||||||
struct sockaddr_in6 server_addr, client_addr;
|
struct sockaddr_in6 server_addr, client_addr;
|
||||||
@@ -343,18 +349,19 @@ int send_release_packet(const char* iface, struct dhcp6_packet* packet)
|
|||||||
client_addr.sin6_port = htons(DHCP6_CLIENT_PORT);
|
client_addr.sin6_port = htons(DHCP6_CLIENT_PORT);
|
||||||
client_addr.sin6_flowinfo = 0;
|
client_addr.sin6_flowinfo = 0;
|
||||||
client_addr.sin6_scope_id =0;
|
client_addr.sin6_scope_id =0;
|
||||||
inet_pton(AF_INET6, "::", &client_addr.sin6_addr);
|
if (inet_pton(AF_INET6, "::", &client_addr.sin6_addr) <= 0)
|
||||||
bind(sock, (struct sockaddr*)&client_addr, sizeof(struct sockaddr_in6));
|
fail_fatal("inet_pton", 5);
|
||||||
inet_pton(AF_INET6, DHCP6_MULTICAST_ADDRESS, &server_addr.sin6_addr);
|
if (bind(sock, (struct sockaddr*)&client_addr, sizeof(struct sockaddr_in6)) != 0)
|
||||||
|
perror("bind"); /* continue on bind error */
|
||||||
|
if (inet_pton(AF_INET6, DHCP6_MULTICAST_ADDRESS, &server_addr.sin6_addr) <= 0)
|
||||||
|
fail_fatal("inet_pton", 5);
|
||||||
server_addr.sin6_port = htons(DHCP6_SERVER_PORT);
|
server_addr.sin6_port = htons(DHCP6_SERVER_PORT);
|
||||||
int16_t recv_size = 0;
|
ssize_t recv_size = 0;
|
||||||
|
int result;
|
||||||
for (i = 0; i < 5; i++)
|
for (i = 0; i < 5; i++)
|
||||||
{
|
{
|
||||||
if (sendto(sock, packet->buf, packet->len, 0, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
|
if (sendto(sock, packet->buf, packet->len, 0, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
|
||||||
{
|
fail_fatal("sendto failed", 4);
|
||||||
perror("sendto failed");
|
|
||||||
exit(4);
|
|
||||||
}
|
|
||||||
|
|
||||||
recv_size = recvfrom(sock, response, sizeof(response), MSG_DONTWAIT, NULL, 0);
|
recv_size = recvfrom(sock, response, sizeof(response), MSG_DONTWAIT, NULL, 0);
|
||||||
if (recv_size == -1)
|
if (recv_size == -1)
|
||||||
@@ -367,16 +374,18 @@ int send_release_packet(const char* iface, struct dhcp6_packet* packet)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
perror("recvfrom");
|
perror("recvfrom");
|
||||||
|
result = UNSPEC_FAIL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
int16_t result = parse_packet(response, recv_size);
|
{
|
||||||
|
result = parse_packet(response, recv_size);
|
||||||
if (result == NOT_REPLY_CODE)
|
if (result == NOT_REPLY_CODE)
|
||||||
{
|
{
|
||||||
sleep(1);
|
sleep(1);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
close(sock);
|
close(sock);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+6
@@ -1,3 +1,9 @@
|
|||||||
|
dnsmasq (2.87-1) unstable; urgency=low
|
||||||
|
|
||||||
|
* New upstream.
|
||||||
|
|
||||||
|
-- Simon Kelley <simon@thekelleys.org.uk> Wed, 08 Sep 2021 23:11:25 +0000
|
||||||
|
|
||||||
dnsmasq (2.86-1) unstable; urgency=low
|
dnsmasq (2.86-1) unstable; urgency=low
|
||||||
|
|
||||||
* Fix debian/changelog format error. (closes: #986626)
|
* Fix debian/changelog format error. (closes: #986626)
|
||||||
|
|||||||
@@ -85,6 +85,16 @@
|
|||||||
# subdomains to the vpn and search ipsets:
|
# subdomains to the vpn and search ipsets:
|
||||||
#ipset=/yahoo.com/google.com/vpn,search
|
#ipset=/yahoo.com/google.com/vpn,search
|
||||||
|
|
||||||
|
# Add the IPs of all queries to yahoo.com, google.com, and their
|
||||||
|
# subdomains to netfilters sets, which is equivalent to
|
||||||
|
# 'nft add element ip test vpn { ... }; nft add element ip test search { ... }'
|
||||||
|
#nftset=/yahoo.com/google.com/ip#test#vpn,ip#test#search
|
||||||
|
|
||||||
|
# Use netfilters sets for both IPv4 and IPv6:
|
||||||
|
# This adds all addresses in *.yahoo.com to vpn4 and vpn6 for IPv4 and IPv6 addresses.
|
||||||
|
#nftset=/yahoo.com/4#ip#test#vpn4
|
||||||
|
#nftset=/yahoo.com/6#ip#test#vpn6
|
||||||
|
|
||||||
# You can control how dnsmasq talks to a server: this forces
|
# You can control how dnsmasq talks to a server: this forces
|
||||||
# queries to 10.1.2.3 to be routed via eth1
|
# queries to 10.1.2.3 to be routed via eth1
|
||||||
# server=10.1.2.3@eth1
|
# server=10.1.2.3@eth1
|
||||||
|
|||||||
+85
-21
@@ -1,4 +1,4 @@
|
|||||||
.TH DNSMASQ 8 2020-04-05
|
.TH DNSMASQ 8 2021-08-16
|
||||||
.SH NAME
|
.SH NAME
|
||||||
dnsmasq \- A lightweight DHCP and caching DNS server.
|
dnsmasq \- A lightweight DHCP and caching DNS server.
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
@@ -55,7 +55,8 @@ Don't read the hostnames in /etc/hosts.
|
|||||||
.B \-H, --addn-hosts=<file>
|
.B \-H, --addn-hosts=<file>
|
||||||
Additional hosts file. Read the specified file as well as /etc/hosts. If \fB--no-hosts\fP is given, read
|
Additional hosts file. Read the specified file as well as /etc/hosts. If \fB--no-hosts\fP is given, read
|
||||||
only the specified file. This option may be repeated for more than one
|
only the specified file. This option may be repeated for more than one
|
||||||
additional hosts file. If a directory is given, then read all the files contained in that directory.
|
additional hosts file. If a directory is given, then read all the files contained in that directory
|
||||||
|
in alphabetical order.
|
||||||
.TP
|
.TP
|
||||||
.B --hostsdir=<path>
|
.B --hostsdir=<path>
|
||||||
Read all the hosts files contained in the directory. New or changed files
|
Read all the hosts files contained in the directory. New or changed files
|
||||||
@@ -327,8 +328,8 @@ are re-written. So
|
|||||||
maps 192.168.0.10->192.168.0.40 to 10.0.0.10->10.0.0.40
|
maps 192.168.0.10->192.168.0.40 to 10.0.0.10->10.0.0.40
|
||||||
.TP
|
.TP
|
||||||
.B \-B, --bogus-nxdomain=<ipaddr>[/prefix]
|
.B \-B, --bogus-nxdomain=<ipaddr>[/prefix]
|
||||||
Transform replies which contain the IP specified address or subnet into "No such
|
Transform replies which contain the specified address or subnet into "No such
|
||||||
domain" replies. This is intended to counteract a devious move made by
|
domain" replies. IPv4 and IPv6 are supported. This is intended to counteract a devious move made by
|
||||||
Verisign in September 2003 when they started returning the address of
|
Verisign in September 2003 when they started returning the address of
|
||||||
an advertising web page in response to queries for unregistered names,
|
an advertising web page in response to queries for unregistered names,
|
||||||
instead of the correct NXDOMAIN response. This option tells dnsmasq to
|
instead of the correct NXDOMAIN response. This option tells dnsmasq to
|
||||||
@@ -336,7 +337,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
|
the IP address being returned by Verisign is 64.94.110.11
|
||||||
.TP
|
.TP
|
||||||
.B --ignore-address=<ipaddr>[/prefix]
|
.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.
|
No error is generated, dnsmasq simply continues to listen for another reply.
|
||||||
This is useful to defeat blocking strategies which rely on quickly supplying a
|
This is useful to defeat blocking strategies which rely on quickly supplying a
|
||||||
forged answer to a DNS request for certain domain, before the correct answer can arrive.
|
forged answer to a DNS request for certain domain, before the correct answer can arrive.
|
||||||
@@ -371,7 +372,10 @@ provides service at that name, rather than the default which is
|
|||||||
.TP
|
.TP
|
||||||
.B --enable-ubus[=<service-name>]
|
.B --enable-ubus[=<service-name>]
|
||||||
Enable dnsmasq UBus interface. It sends notifications via UBus on
|
Enable dnsmasq UBus interface. It sends notifications via UBus on
|
||||||
DHCPACK and DHCPRELEASE events. Furthermore it offers metrics.
|
DHCPACK and DHCPRELEASE events. Furthermore it offers metrics
|
||||||
|
and allows configuration of Linux connection track mark based filtering.
|
||||||
|
When DNS query filtering based on Linux connection track marks is enabled
|
||||||
|
UBus notifications are generated for each resolved or filtered DNS query.
|
||||||
Requires that dnsmasq has been built with UBus support. If the service
|
Requires that dnsmasq has been built with UBus support. If the service
|
||||||
name is given, dnsmasq provides service at that namespace, rather than
|
name is given, dnsmasq provides service at that namespace, rather than
|
||||||
the default which is
|
the default which is
|
||||||
@@ -457,13 +461,22 @@ repeated domain or ipaddr parts as required.
|
|||||||
More specific domains take precedence over less specific domains, so:
|
More specific domains take precedence over less specific domains, so:
|
||||||
.B --server=/google.com/1.2.3.4
|
.B --server=/google.com/1.2.3.4
|
||||||
.B --server=/www.google.com/2.3.4.5
|
.B --server=/www.google.com/2.3.4.5
|
||||||
will send queries for *.google.com to 1.2.3.4, except *www.google.com,
|
will send queries for google.com and gmail.google.com to 1.2.3.4, but www.google.com
|
||||||
which will go to 2.3.4.5
|
will go to 2.3.4.5
|
||||||
|
|
||||||
|
Matching of domains is normally done on complete labels, so /google.com/ matches google.com and www.google.com
|
||||||
|
but NOT supergoogle.com. This can be overridden with a * at the start of a pattern only: /*google.com/
|
||||||
|
will match google.com and www.google.com AND supergoogle.com. The non-wildcard form has priority, so
|
||||||
|
if /google.com/ and /*google.com/ are both specified then google.com and www.google.com will match /google.com/
|
||||||
|
and /*google.com/ will only match supergoogle.com.
|
||||||
|
|
||||||
|
For historical reasons, the pattern /.google.com/ is equivalent to /google.com/ if you wish to match any subdomain
|
||||||
|
of google.com but NOT google.com itself, use /*.google.com/
|
||||||
|
|
||||||
The special server address '#' means, "use the standard servers", so
|
The special server address '#' means, "use the standard servers", so
|
||||||
.B --server=/google.com/1.2.3.4
|
.B --server=/google.com/1.2.3.4
|
||||||
.B --server=/www.google.com/#
|
.B --server=/www.google.com/#
|
||||||
will send queries for *.google.com to 1.2.3.4, except *www.google.com which will
|
will send queries for google.com and its subdomains to 1.2.3.4, except www.google.com (and its subdomains) which will
|
||||||
be forwarded as usual.
|
be forwarded as usual.
|
||||||
|
|
||||||
Also permitted is a -S
|
Also permitted is a -S
|
||||||
@@ -492,13 +505,14 @@ source address specified but the port may be specified directly as
|
|||||||
part of the source address. Forcing queries to an interface is not
|
part of the source address. Forcing queries to an interface is not
|
||||||
implemented on all platforms supported by dnsmasq.
|
implemented on all platforms supported by dnsmasq.
|
||||||
.TP
|
.TP
|
||||||
.B --rev-server=<ip-address>/<prefix-len>[,<ipaddr>][#<port>][@<interface>][@<source-ip>[#<port>]]
|
.B --rev-server=<ip-address>[/<prefix-len>][,<ipaddr>][#<port>][@<interface>][@<source-ip>[#<port>]]
|
||||||
This is functionally the same as
|
This is functionally the same as
|
||||||
.B --server,
|
.B --server,
|
||||||
but provides some syntactic sugar to make specifying address-to-name queries easier. For example
|
but provides some syntactic sugar to make specifying address-to-name queries easier. For example
|
||||||
.B --rev-server=1.2.3.0/24,192.168.0.1
|
.B --rev-server=1.2.3.0/24,192.168.0.1
|
||||||
is exactly equivalent to
|
is exactly equivalent to
|
||||||
.B --server=/3.2.1.in-addr.arpa/192.168.0.1
|
.B --server=/3.2.1.in-addr.arpa/192.168.0.1
|
||||||
|
Allowed prefix lengths are 1-32 (IPv4) and 1-128 (IPv6). If the prefix length is omitted, dnsmasq substitutes either 32 (IPv4) or 128 (IPv6).
|
||||||
.TP
|
.TP
|
||||||
.B \-A, --address=/<domain>[/<domain>...]/[<ipaddr>]
|
.B \-A, --address=/<domain>[/<domain>...]/[<ipaddr>]
|
||||||
Specify an IP address to return for any host in the given domains.
|
Specify an IP address to return for any host in the given domains.
|
||||||
@@ -536,6 +550,41 @@ These IP sets must already exist. See
|
|||||||
.BR ipset (8)
|
.BR ipset (8)
|
||||||
for more details.
|
for more details.
|
||||||
.TP
|
.TP
|
||||||
|
.B --nftset=/<domain>[/<domain>...]/[(6|4)#[<family>#]<table>#<set>[,[(6|4)#[<family>#]<table>#<set>]...]
|
||||||
|
Similar to the \fB--ipset\fP option, but accepts one or more nftables
|
||||||
|
sets to add IP addresses into.
|
||||||
|
These sets must already exist. See
|
||||||
|
.BR nft (8)
|
||||||
|
for more details. The family, table and set are passed directly to the nft. If the spec starts with 4# or 6# then
|
||||||
|
only A or AAAA records respectively are added to the set. Since an nftset can hold only IPv4 or IPv6 addresses, this
|
||||||
|
avoids errors being logged for addresses of the wrong type.
|
||||||
|
.TP
|
||||||
|
.B --connmark-allowlist-enable[=<mask>]
|
||||||
|
Enables filtering of incoming DNS queries with associated Linux connection track marks
|
||||||
|
according to individual allowlists configured via a series of \fB--connmark-allowlist\fP
|
||||||
|
options. Disallowed queries are not forwarded; they are rejected with a REFUSED error code.
|
||||||
|
DNS queries are only allowed if they do not have an associated Linux connection
|
||||||
|
track mark, or if the queried domains match the configured DNS patterns for the
|
||||||
|
associated Linux connection track mark. If no allowlist is configured for a
|
||||||
|
Linux connection track mark, all DNS queries associated with that mark are rejected.
|
||||||
|
If a mask is specified, Linux connection track marks are first bitwise ANDed
|
||||||
|
with the given mask before being processed.
|
||||||
|
.TP
|
||||||
|
.B --connmark-allowlist=<connmark>[/<mask>][,<pattern>[/<pattern>...]]
|
||||||
|
Configures the DNS patterns that are allowed in DNS queries associated with
|
||||||
|
the given Linux connection track mark.
|
||||||
|
If a mask is specified, Linux connection track marks are first bitwise ANDed
|
||||||
|
with the given mask before they are compared to the given connection track mark.
|
||||||
|
Patterns follow the syntax of DNS names, but additionally allow the wildcard
|
||||||
|
character "*" to be used up to twice per label to match 0 or more characters
|
||||||
|
within that label. Note that the wildcard never matches a dot (e.g., "*.example.com"
|
||||||
|
matches "api.example.com" but not "api.us.example.com"). Patterns must be
|
||||||
|
fully qualified, i.e., consist of at least two labels. The final label must not be
|
||||||
|
fully numeric, and must not be the "local" pseudo-TLD. A pattern must end with at least
|
||||||
|
two literal (non-wildcard) labels.
|
||||||
|
Instead of a pattern, "*" can be specified to disable allowlist filtering
|
||||||
|
for a given Linux connection track mark entirely.
|
||||||
|
.TP
|
||||||
.B \-m, --mx-host=<mx name>[[,<hostname>],<preference>]
|
.B \-m, --mx-host=<mx name>[[,<hostname>],<preference>]
|
||||||
Return an MX record named <mx name> pointing to the given hostname (if
|
Return an MX record named <mx name> pointing to the given hostname (if
|
||||||
given), or
|
given), or
|
||||||
@@ -667,7 +716,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
|
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
|
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
|
.TP
|
||||||
.B --dumpfile=<path/to/file>
|
.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.
|
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.
|
||||||
@@ -866,7 +916,7 @@ compiled in and the kernel must have conntrack support
|
|||||||
included and configured. This option cannot be combined with
|
included and configured. This option cannot be combined with
|
||||||
.B --query-port.
|
.B --query-port.
|
||||||
.TP
|
.TP
|
||||||
.B \-F, --dhcp-range=[tag:<tag>[,tag:<tag>],][set:<tag>,]<start-addr>[,<end-addr>|<mode>][,<netmask>[,<broadcast>]][,<lease time>]
|
.B \-F, --dhcp-range=[tag:<tag>[,tag:<tag>],][set:<tag>,]<start-addr>[,<end-addr>|<mode>[,<netmask>[,<broadcast>]]][,<lease time>]
|
||||||
.TP
|
.TP
|
||||||
.B \-F, --dhcp-range=[tag:<tag>[,tag:<tag>],][set:<tag>,]<start-IPv6addr>[,<end-IPv6addr>|constructor:<interface>][,<mode>][,<prefix-len>][,<lease time>]
|
.B \-F, --dhcp-range=[tag:<tag>[,tag:<tag>],][set:<tag>,]<start-IPv6addr>[,<end-IPv6addr>|constructor:<interface>][,<mode>][,<prefix-len>][,<lease time>]
|
||||||
|
|
||||||
@@ -996,7 +1046,7 @@ is also included, as described in RFC-3775 section 7.3.
|
|||||||
tells dnsmasq to advertise the prefix without the on-link (aka L) bit set.
|
tells dnsmasq to advertise the prefix without the on-link (aka L) bit set.
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B \-G, --dhcp-host=[<hwaddr>][,id:<client_id>|*][,set:<tag>][tag:<tag>][,<ipaddr>][,<hostname>][,<lease_time>][,ignore]
|
.B \-G, --dhcp-host=[<hwaddr>][,id:<client_id>|*][,set:<tag>][,tag:<tag>][,<ipaddr>][,<hostname>][,<lease_time>][,ignore]
|
||||||
Specify per host parameters for the DHCP server. This allows a machine
|
Specify per host parameters for the DHCP server. This allows a machine
|
||||||
with a particular hardware address to be always allocated the same
|
with a particular hardware address to be always allocated the same
|
||||||
hostname, IP address and lease time. A hostname specified like this
|
hostname, IP address and lease time. A hostname specified like this
|
||||||
@@ -1095,7 +1145,10 @@ ignore requests from unknown machines using
|
|||||||
If the host matches only a \fB--dhcp-host\fP directive which cannot
|
If the host matches only a \fB--dhcp-host\fP directive which cannot
|
||||||
be used because it specifies an address on different subnet, the tag "known-othernet" is set.
|
be used because it specifies an address on different subnet, the tag "known-othernet" is set.
|
||||||
|
|
||||||
The tag:<tag> construct filters which dhcp-host directives are used. Tagged directives are used in preference to untagged ones.
|
The tag:<tag> construct filters which dhcp-host directives are used; more than
|
||||||
|
one can be provided, in this case the request must match all of them. Tagged
|
||||||
|
directives are used in preference to untagged ones. Note that one of <hwaddr>,
|
||||||
|
<client_id> or <hostname> still needs to be specified (can be a wildcard).
|
||||||
|
|
||||||
Ethernet addresses (but not client-ids) may have
|
Ethernet addresses (but not client-ids) may have
|
||||||
wildcard bytes, so for example
|
wildcard bytes, so for example
|
||||||
@@ -1126,7 +1179,7 @@ has both wired and wireless interfaces.
|
|||||||
.TP
|
.TP
|
||||||
.B --dhcp-hostsfile=<path>
|
.B --dhcp-hostsfile=<path>
|
||||||
Read DHCP host information from the specified file. If a directory
|
Read DHCP host information from the specified file. If a directory
|
||||||
is given, then read all the files contained in that directory. The file contains
|
is given, then read all the files contained in that directory in alphabetical order. The file contains
|
||||||
information about one host per line. The format of a line is the same
|
information about one host per line. The format of a line is the same
|
||||||
as text to the right of '=' in \fB--dhcp-host\fP. The advantage of storing DHCP host information
|
as text to the right of '=' in \fB--dhcp-host\fP. The advantage of storing DHCP host information
|
||||||
in this file is that it can be changed without re-starting dnsmasq:
|
in this file is that it can be changed without re-starting dnsmasq:
|
||||||
@@ -1134,7 +1187,7 @@ the file will be re-read when dnsmasq receives SIGHUP.
|
|||||||
.TP
|
.TP
|
||||||
.B --dhcp-optsfile=<path>
|
.B --dhcp-optsfile=<path>
|
||||||
Read DHCP option information from the specified file. If a directory
|
Read DHCP option information from the specified file. If a directory
|
||||||
is given, then read all the files contained in that directory. The advantage of
|
is given, then read all the files contained in that directory in alphabetical order. The advantage of
|
||||||
using this option is the same as for \fB--dhcp-hostsfile\fP: the
|
using this option is the same as for \fB--dhcp-hostsfile\fP: the
|
||||||
\fB--dhcp-optsfile\fP will be re-read when dnsmasq receives SIGHUP. Note that
|
\fB--dhcp-optsfile\fP will be re-read when dnsmasq receives SIGHUP. Note that
|
||||||
it is possible to encode the information in a
|
it is possible to encode the information in a
|
||||||
@@ -1149,7 +1202,8 @@ directory, and not an individual file. Changed or new files within
|
|||||||
the directory are read automatically, without the need to send SIGHUP.
|
the directory are read automatically, without the need to send SIGHUP.
|
||||||
If a file is deleted or changed after it has been read by dnsmasq, then the
|
If a file is deleted or changed after it has been read by dnsmasq, then the
|
||||||
host record it contained will remain until dnsmasq receives a SIGHUP, or
|
host record it contained will remain until dnsmasq receives a SIGHUP, or
|
||||||
is restarted; ie host records are only added dynamically.
|
is restarted; ie host records are only added dynamically. The order in which the
|
||||||
|
files in a directory are read is not defined.
|
||||||
.TP
|
.TP
|
||||||
.B --dhcp-optsdir=<path>
|
.B --dhcp-optsdir=<path>
|
||||||
This is equivalent to \fB--dhcp-optsfile\fP, with the differences noted for \fB--dhcp-hostsdir\fP.
|
This is equivalent to \fB--dhcp-optsfile\fP, with the differences noted for \fB--dhcp-hostsdir\fP.
|
||||||
@@ -1394,6 +1448,12 @@ Any number of set: and tag: forms may appear, in any order.
|
|||||||
tag set by another
|
tag set by another
|
||||||
.B --tag-if,
|
.B --tag-if,
|
||||||
the line which sets the tag must precede the one which tests it.
|
the line which sets the tag must precede the one which tests it.
|
||||||
|
|
||||||
|
As an extension, the tag:<tag> clauses support limited wildcard matching,
|
||||||
|
similar to the matching in the \fB--interface\fP directive. This allows, for
|
||||||
|
example, using \fB--tag-if=set:ppp,tag:ppp*\fP to set the tag 'ppp' for all requests
|
||||||
|
received on any matching interface (ppp0, ppp1, etc). This can be used in conjunction
|
||||||
|
with the tag:!<tag> format meaning that no tag matching the wildcard may be set.
|
||||||
.TP
|
.TP
|
||||||
.B \-J, --dhcp-ignore=tag:<tag>[,tag:<tag>]
|
.B \-J, --dhcp-ignore=tag:<tag>[,tag:<tag>]
|
||||||
When all the given tags appear in the tag set ignore the host and do
|
When all the given tags appear in the tag set ignore the host and do
|
||||||
@@ -1574,10 +1634,11 @@ tried. This flag disables this check. Use with caution.
|
|||||||
Extra logging for DHCP: log all the options sent to DHCP clients and
|
Extra logging for DHCP: log all the options sent to DHCP clients and
|
||||||
the tags used to determine them.
|
the tags used to determine them.
|
||||||
.TP
|
.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
|
Suppress logging of the routine operation of these protocols. Errors and
|
||||||
problems will still be logged. \fB--quiet-dhcp\fP and quiet-dhcp6 are
|
problems will still be logged. \fB--quiet-tftp\fP does not consider file not
|
||||||
over-ridden by \fB--log-dhcp\fP.
|
found to be an error. \fB--quiet-dhcp\fP and quiet-dhcp6 are over-ridden by
|
||||||
|
\fB--log-dhcp\fP.
|
||||||
.TP
|
.TP
|
||||||
.B \-l, --dhcp-leasefile=<path>
|
.B \-l, --dhcp-leasefile=<path>
|
||||||
Use the specified file to store DHCP lease information.
|
Use the specified file to store DHCP lease information.
|
||||||
@@ -1875,7 +1936,6 @@ additional flag "local" may be supplied which has the effect of adding
|
|||||||
is identical to
|
is identical to
|
||||||
.B --domain=thekelleys.org.uk,192.168.0.0/24
|
.B --domain=thekelleys.org.uk,192.168.0.0/24
|
||||||
.B --local=/thekelleys.org.uk/ --local=/0.168.192.in-addr.arpa/
|
.B --local=/thekelleys.org.uk/ --local=/0.168.192.in-addr.arpa/
|
||||||
The network size must be 8, 16 or 24 for this to be legal.
|
|
||||||
.TP
|
.TP
|
||||||
.B --dhcp-fqdn
|
.B --dhcp-fqdn
|
||||||
In the default mode, dnsmasq inserts the unqualified names of
|
In the default mode, dnsmasq inserts the unqualified names of
|
||||||
@@ -2379,6 +2439,10 @@ following data is used to populate the authoritative zone.
|
|||||||
.B --mx-host, --srv-host, --dns-rr, --txt-record, --naptr-record, --caa-record,
|
.B --mx-host, --srv-host, --dns-rr, --txt-record, --naptr-record, --caa-record,
|
||||||
as long as the record names are in the authoritative domain.
|
as long as the record names are in the authoritative domain.
|
||||||
.PP
|
.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
|
.B --cname
|
||||||
as long as the record name is in the authoritative domain. If the
|
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
|
target of the CNAME is unqualified, then it is qualified with the
|
||||||
|
|||||||
+731
-579
File diff suppressed because it is too large.
Load diff
+56
-25
@@ -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 nameoffset, axfroffset = 0;
|
||||||
int q, anscount = 0, authcount = 0;
|
int q, anscount = 0, authcount = 0;
|
||||||
struct crec *crecp;
|
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 auth_zone *zone = NULL;
|
||||||
struct addrlist *subnet = NULL;
|
struct addrlist *subnet = NULL;
|
||||||
char *cut;
|
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)
|
if (qclass != C_IN)
|
||||||
{
|
{
|
||||||
auth = 0;
|
auth = 0;
|
||||||
|
out_of_zone = 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,6 +160,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
|||||||
|
|
||||||
if (!zone)
|
if (!zone)
|
||||||
{
|
{
|
||||||
|
out_of_zone = 1;
|
||||||
auth = 0;
|
auth = 0;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -208,7 +210,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
|||||||
if (local_query || in_zone(zone, intr->name, NULL))
|
if (local_query || in_zone(zone, intr->name, NULL))
|
||||||
{
|
{
|
||||||
found = 1;
|
found = 1;
|
||||||
log_query(flag | F_REVERSE | F_CONFIG, intr->name, &addr, NULL);
|
log_query(flag | F_REVERSE | F_CONFIG, intr->name, &addr, NULL, 0);
|
||||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||||
daemon->auth_ttl, NULL,
|
daemon->auth_ttl, NULL,
|
||||||
T_PTR, C_IN, "d", intr->name))
|
T_PTR, C_IN, "d", intr->name))
|
||||||
@@ -232,7 +234,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
|||||||
strcat(name, ".");
|
strcat(name, ".");
|
||||||
strcat(name, zone->domain);
|
strcat(name, zone->domain);
|
||||||
}
|
}
|
||||||
log_query(flag | F_DHCP | F_REVERSE, name, &addr, record_source(crecp->uid));
|
log_query(flag | F_DHCP | F_REVERSE, name, &addr, record_source(crecp->uid), 0);
|
||||||
found = 1;
|
found = 1;
|
||||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||||
daemon->auth_ttl, NULL,
|
daemon->auth_ttl, NULL,
|
||||||
@@ -241,7 +243,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
|||||||
}
|
}
|
||||||
else if (crecp->flags & (F_DHCP | F_HOSTS) && (local_query || in_zone(zone, name, NULL)))
|
else if (crecp->flags & (F_DHCP | F_HOSTS) && (local_query || in_zone(zone, name, NULL)))
|
||||||
{
|
{
|
||||||
log_query(crecp->flags & ~F_FORWARD, name, &addr, record_source(crecp->uid));
|
log_query(crecp->flags & ~F_FORWARD, name, &addr, record_source(crecp->uid), 0);
|
||||||
found = 1;
|
found = 1;
|
||||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||||
daemon->auth_ttl, NULL,
|
daemon->auth_ttl, NULL,
|
||||||
@@ -253,10 +255,21 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
|||||||
|
|
||||||
} while ((crecp = cache_find_by_addr(crecp, &addr, now, flag)));
|
} while ((crecp = cache_find_by_addr(crecp, &addr, now, flag)));
|
||||||
|
|
||||||
|
if (!found && is_rev_synth(flag, &addr, name) && (local_query || in_zone(zone, name, NULL)))
|
||||||
|
{
|
||||||
|
log_query(F_CONFIG | F_REVERSE | flag, name, &addr, NULL, 0);
|
||||||
|
found = 1;
|
||||||
|
|
||||||
|
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||||
|
daemon->auth_ttl, NULL,
|
||||||
|
T_PTR, C_IN, "d", name))
|
||||||
|
anscount++;
|
||||||
|
}
|
||||||
|
|
||||||
if (found)
|
if (found)
|
||||||
nxdomain = 0;
|
nxdomain = 0;
|
||||||
else
|
else
|
||||||
log_query(flag | F_NEG | F_NXDOMAIN | F_REVERSE | (auth ? F_AUTH : 0), NULL, &addr, NULL);
|
log_query(flag | F_NEG | F_NXDOMAIN | F_REVERSE | (auth ? F_AUTH : 0), NULL, &addr, NULL, 0);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -273,6 +286,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
|||||||
|
|
||||||
if (!zone)
|
if (!zone)
|
||||||
{
|
{
|
||||||
|
out_of_zone = 1;
|
||||||
auth = 0;
|
auth = 0;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -286,7 +300,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
|||||||
if (rc == 2 && qtype == T_MX)
|
if (rc == 2 && qtype == T_MX)
|
||||||
{
|
{
|
||||||
found = 1;
|
found = 1;
|
||||||
log_query(F_CONFIG | F_RRNAME, name, NULL, "<MX>");
|
log_query(F_CONFIG | F_RRNAME, name, NULL, "<MX>", 0);
|
||||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
|
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
|
||||||
NULL, T_MX, C_IN, "sd", rec->weight, rec->target))
|
NULL, T_MX, C_IN, "sd", rec->weight, rec->target))
|
||||||
anscount++;
|
anscount++;
|
||||||
@@ -301,7 +315,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
|||||||
if (rc == 2 && qtype == T_SRV)
|
if (rc == 2 && qtype == T_SRV)
|
||||||
{
|
{
|
||||||
found = 1;
|
found = 1;
|
||||||
log_query(F_CONFIG | F_RRNAME, name, NULL, "<SRV>");
|
log_query(F_CONFIG | F_RRNAME, name, NULL, "<SRV>", 0);
|
||||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
|
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
|
||||||
NULL, T_SRV, C_IN, "sssd",
|
NULL, T_SRV, C_IN, "sssd",
|
||||||
rec->priority, rec->weight, rec->srvport, rec->target))
|
rec->priority, rec->weight, rec->srvport, rec->target))
|
||||||
@@ -335,7 +349,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
|||||||
if (rc == 2 && txt->class == qtype)
|
if (rc == 2 && txt->class == qtype)
|
||||||
{
|
{
|
||||||
found = 1;
|
found = 1;
|
||||||
log_query(F_CONFIG | F_RRNAME, name, NULL, querystr(NULL, txt->class));
|
log_query(F_CONFIG | F_RRNAME, name, NULL, NULL, txt->class);
|
||||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
|
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
|
||||||
NULL, txt->class, C_IN, "t", txt->len, txt->txt))
|
NULL, txt->class, C_IN, "t", txt->len, txt->txt))
|
||||||
anscount++;
|
anscount++;
|
||||||
@@ -349,7 +363,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
|||||||
if (rc == 2 && qtype == T_TXT)
|
if (rc == 2 && qtype == T_TXT)
|
||||||
{
|
{
|
||||||
found = 1;
|
found = 1;
|
||||||
log_query(F_CONFIG | F_RRNAME, name, NULL, "<TXT>");
|
log_query(F_CONFIG | F_RRNAME, name, NULL, "<TXT>", 0);
|
||||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
|
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
|
||||||
NULL, T_TXT, C_IN, "t", txt->len, txt->txt))
|
NULL, T_TXT, C_IN, "t", txt->len, txt->txt))
|
||||||
anscount++;
|
anscount++;
|
||||||
@@ -363,7 +377,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
|||||||
if (rc == 2 && qtype == T_NAPTR)
|
if (rc == 2 && qtype == T_NAPTR)
|
||||||
{
|
{
|
||||||
found = 1;
|
found = 1;
|
||||||
log_query(F_CONFIG | F_RRNAME, name, NULL, "<NAPTR>");
|
log_query(F_CONFIG | F_RRNAME, name, NULL, "<NAPTR>", 0);
|
||||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
|
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
|
||||||
NULL, T_NAPTR, C_IN, "sszzzd",
|
NULL, T_NAPTR, C_IN, "sszzzd",
|
||||||
na->order, na->pref, na->flags, na->services, na->regexp, na->replace))
|
na->order, na->pref, na->flags, na->services, na->regexp, na->replace))
|
||||||
@@ -393,7 +407,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
found = 1;
|
found = 1;
|
||||||
log_query(F_FORWARD | F_CONFIG | flag, name, &addrlist->addr, NULL);
|
log_query(F_FORWARD | F_CONFIG | flag, name, &addrlist->addr, NULL, 0);
|
||||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||||
daemon->auth_ttl, NULL, qtype, C_IN,
|
daemon->auth_ttl, NULL, qtype, C_IN,
|
||||||
qtype == T_A ? "4" : "6", &addrlist->addr))
|
qtype == T_A ? "4" : "6", &addrlist->addr))
|
||||||
@@ -401,6 +415,16 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!found && is_name_synthetic(flag, name, &addr) )
|
||||||
|
{
|
||||||
|
nxdomain = 0;
|
||||||
|
|
||||||
|
log_query(F_FORWARD | F_CONFIG | flag, name, &addr, NULL, 0);
|
||||||
|
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||||
|
daemon->auth_ttl, NULL, qtype, C_IN, qtype == T_A ? "4" : "6", &addr))
|
||||||
|
anscount++;
|
||||||
|
}
|
||||||
|
|
||||||
if (!cut)
|
if (!cut)
|
||||||
{
|
{
|
||||||
nxdomain = 0;
|
nxdomain = 0;
|
||||||
@@ -408,8 +432,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
|||||||
if (qtype == T_SOA)
|
if (qtype == T_SOA)
|
||||||
{
|
{
|
||||||
auth = soa = 1; /* inhibits auth section */
|
auth = soa = 1; /* inhibits auth section */
|
||||||
found = 1;
|
log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<SOA>", 0);
|
||||||
log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<SOA>");
|
|
||||||
}
|
}
|
||||||
else if (qtype == T_AXFR)
|
else if (qtype == T_AXFR)
|
||||||
{
|
{
|
||||||
@@ -444,16 +467,14 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
|||||||
soa = 1; /* inhibits auth section */
|
soa = 1; /* inhibits auth section */
|
||||||
ns = 1; /* ensure we include NS records! */
|
ns = 1; /* ensure we include NS records! */
|
||||||
axfr = 1;
|
axfr = 1;
|
||||||
found = 1;
|
|
||||||
axfroffset = nameoffset;
|
axfroffset = nameoffset;
|
||||||
log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<AXFR>");
|
log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<AXFR>", 0);
|
||||||
}
|
}
|
||||||
else if (qtype == T_NS)
|
else if (qtype == T_NS)
|
||||||
{
|
{
|
||||||
auth = 1;
|
auth = 1;
|
||||||
ns = 1; /* inhibits auth section */
|
ns = 1; /* inhibits auth section */
|
||||||
found = 1;
|
log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<NS>", 0);
|
||||||
log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<NS>");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -471,9 +492,8 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
|||||||
(local_query || filter_zone(zone, flag, &(crecp->addr))))
|
(local_query || filter_zone(zone, flag, &(crecp->addr))))
|
||||||
{
|
{
|
||||||
*cut = '.'; /* restore domain part */
|
*cut = '.'; /* restore domain part */
|
||||||
log_query(crecp->flags, name, &crecp->addr, record_source(crecp->uid));
|
log_query(crecp->flags, name, &crecp->addr, record_source(crecp->uid), 0);
|
||||||
*cut = 0; /* remove domain part */
|
*cut = 0; /* remove domain part */
|
||||||
found = 1;
|
|
||||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||||
daemon->auth_ttl, NULL, qtype, C_IN,
|
daemon->auth_ttl, NULL, qtype, C_IN,
|
||||||
qtype == T_A ? "4" : "6", &crecp->addr))
|
qtype == T_A ? "4" : "6", &crecp->addr))
|
||||||
@@ -493,8 +513,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
|||||||
nxdomain = 0;
|
nxdomain = 0;
|
||||||
if ((crecp->flags & flag) && (local_query || filter_zone(zone, flag, &(crecp->addr))))
|
if ((crecp->flags & flag) && (local_query || filter_zone(zone, flag, &(crecp->addr))))
|
||||||
{
|
{
|
||||||
log_query(crecp->flags, name, &crecp->addr, record_source(crecp->uid));
|
log_query(crecp->flags, name, &crecp->addr, record_source(crecp->uid), 0);
|
||||||
found = 1;
|
|
||||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||||
daemon->auth_ttl, NULL, qtype, C_IN,
|
daemon->auth_ttl, NULL, qtype, C_IN,
|
||||||
qtype == T_A ? "4" : "6", &crecp->addr))
|
qtype == T_A ? "4" : "6", &crecp->addr))
|
||||||
@@ -541,7 +560,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
|||||||
|
|
||||||
if (candidate)
|
if (candidate)
|
||||||
{
|
{
|
||||||
log_query(F_CONFIG | F_CNAME, name, NULL, NULL);
|
log_query(F_CONFIG | F_CNAME, name, NULL, NULL, 0);
|
||||||
strcpy(name, candidate->target);
|
strcpy(name, candidate->target);
|
||||||
if (!strchr(name, '.'))
|
if (!strchr(name, '.'))
|
||||||
{
|
{
|
||||||
@@ -559,7 +578,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
|||||||
else if (cache_find_non_terminal(name, now))
|
else if (cache_find_non_terminal(name, now))
|
||||||
nxdomain = 0;
|
nxdomain = 0;
|
||||||
|
|
||||||
log_query(flag | F_NEG | (nxdomain ? F_NXDOMAIN : 0) | F_FORWARD | F_AUTH, name, NULL, NULL);
|
log_query(flag | F_NEG | (nxdomain ? F_NXDOMAIN : 0) | F_FORWARD | F_AUTH, name, NULL, NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -589,7 +608,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
|||||||
if (subnet->prefixlen >= 16 )
|
if (subnet->prefixlen >= 16 )
|
||||||
p += sprintf(p, "%u.", a & 0xff);
|
p += sprintf(p, "%u.", a & 0xff);
|
||||||
a = a >> 8;
|
a = a >> 8;
|
||||||
p += sprintf(p, "%u.in-addr.arpa", a & 0xff);
|
sprintf(p, "%u.in-addr.arpa", a & 0xff);
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -602,7 +621,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
|||||||
int dig = ((unsigned char *)&subnet->addr.addr6)[i>>3];
|
int dig = ((unsigned char *)&subnet->addr.addr6)[i>>3];
|
||||||
p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
|
p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
|
||||||
}
|
}
|
||||||
p += sprintf(p, "ip6.arpa");
|
sprintf(p, "ip6.arpa");
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -855,10 +874,22 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
|||||||
SET_RCODE(header, NXDOMAIN);
|
SET_RCODE(header, NXDOMAIN);
|
||||||
else
|
else
|
||||||
SET_RCODE(header, NOERROR); /* no error */
|
SET_RCODE(header, NOERROR); /* no error */
|
||||||
|
|
||||||
header->ancount = htons(anscount);
|
header->ancount = htons(anscount);
|
||||||
header->nscount = htons(authcount);
|
header->nscount = htons(authcount);
|
||||||
header->arcount = htons(0);
|
header->arcount = htons(0);
|
||||||
|
|
||||||
|
if (!local_query && out_of_zone)
|
||||||
|
{
|
||||||
|
SET_RCODE(header, REFUSED);
|
||||||
|
header->ancount = htons(0);
|
||||||
|
header->nscount = htons(0);
|
||||||
|
addr.log.rcode = REFUSED;
|
||||||
|
addr.log.ede = EDE_NOT_AUTH;
|
||||||
|
log_query(F_UPSTREAM | F_RCODE, "error", &addr, NULL, 0);
|
||||||
|
return resize_packet(header, ansp - (unsigned char *)header, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
/* Advertise our packet size limit in our reply */
|
/* Advertise our packet size limit in our reply */
|
||||||
if (have_pseudoheader)
|
if (have_pseudoheader)
|
||||||
return add_pseudoheader(header, ansp - (unsigned char *)header, (unsigned char *)limit, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0);
|
return add_pseudoheader(header, ansp - (unsigned char *)header, (unsigned char *)limit, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0);
|
||||||
|
|||||||
+1
-1
@@ -52,7 +52,7 @@ void blockdata_init(void)
|
|||||||
|
|
||||||
void blockdata_report(void)
|
void blockdata_report(void)
|
||||||
{
|
{
|
||||||
my_syslog(LOG_INFO, _("pool memory in use %u, max %u, allocated %u"),
|
my_syslog(LOG_INFO, _("pool memory in use %zu, max %zu, allocated %zu"),
|
||||||
blockdata_count * sizeof(struct blockdata),
|
blockdata_count * sizeof(struct blockdata),
|
||||||
blockdata_hwm * sizeof(struct blockdata),
|
blockdata_hwm * sizeof(struct blockdata),
|
||||||
blockdata_alloced * sizeof(struct blockdata));
|
blockdata_alloced * sizeof(struct blockdata));
|
||||||
|
|||||||
+171
-63
@@ -30,50 +30,100 @@ static struct crec *really_insert(char *name, union all_addr *addr, unsigned sho
|
|||||||
time_t now, unsigned long ttl, unsigned int flags);
|
time_t now, unsigned long ttl, unsigned int flags);
|
||||||
|
|
||||||
/* type->string mapping: this is also used by the name-hash function as a mixing table. */
|
/* type->string mapping: this is also used by the name-hash function as a mixing table. */
|
||||||
|
/* taken from https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml */
|
||||||
static const struct {
|
static const struct {
|
||||||
unsigned int type;
|
unsigned int type;
|
||||||
const char * const name;
|
const char * const name;
|
||||||
} typestr[] = {
|
} typestr[] = {
|
||||||
{ 1, "A" },
|
{ 1, "A" }, /* a host address [RFC1035] */
|
||||||
{ 2, "NS" },
|
{ 2, "NS" }, /* an authoritative name server [RFC1035] */
|
||||||
{ 5, "CNAME" },
|
{ 3, "MD" }, /* a mail destination (OBSOLETE - use MX) [RFC1035] */
|
||||||
{ 6, "SOA" },
|
{ 4, "MF" }, /* a mail forwarder (OBSOLETE - use MX) [RFC1035] */
|
||||||
{ 10, "NULL" },
|
{ 5, "CNAME" }, /* the canonical name for an alias [RFC1035] */
|
||||||
{ 11, "WKS" },
|
{ 6, "SOA" }, /* marks the start of a zone of authority [RFC1035] */
|
||||||
{ 12, "PTR" },
|
{ 7, "MB" }, /* a mailbox domain name (EXPERIMENTAL) [RFC1035] */
|
||||||
{ 13, "HINFO" },
|
{ 8, "MG" }, /* a mail group member (EXPERIMENTAL) [RFC1035] */
|
||||||
{ 15, "MX" },
|
{ 9, "MR" }, /* a mail rename domain name (EXPERIMENTAL) [RFC1035] */
|
||||||
{ 16, "TXT" },
|
{ 10, "NULL" }, /* a null RR (EXPERIMENTAL) [RFC1035] */
|
||||||
{ 22, "NSAP" },
|
{ 11, "WKS" }, /* a well known service description [RFC1035] */
|
||||||
{ 23, "NSAP_PTR" },
|
{ 12, "PTR" }, /* a domain name pointer [RFC1035] */
|
||||||
{ 24, "SIG" },
|
{ 13, "HINFO" }, /* host information [RFC1035] */
|
||||||
{ 25, "KEY" },
|
{ 14, "MINFO" }, /* mailbox or mail list information [RFC1035] */
|
||||||
{ 28, "AAAA" },
|
{ 15, "MX" }, /* mail exchange [RFC1035] */
|
||||||
{ 29, "LOC" },
|
{ 16, "TXT" }, /* text strings [RFC1035] */
|
||||||
{ 33, "SRV" },
|
{ 17, "RP" }, /* for Responsible Person [RFC1183] */
|
||||||
{ 35, "NAPTR" },
|
{ 18, "AFSDB" }, /* for AFS Data Base location [RFC1183][RFC5864] */
|
||||||
{ 36, "KX" },
|
{ 19, "X25" }, /* for X.25 PSDN address [RFC1183] */
|
||||||
{ 37, "CERT" },
|
{ 20, "ISDN" }, /* for ISDN address [RFC1183] */
|
||||||
{ 38, "A6" },
|
{ 21, "RT" }, /* for Route Through [RFC1183] */
|
||||||
{ 39, "DNAME" },
|
{ 22, "NSAP" }, /* for NSAP address, NSAP style A record [RFC1706] */
|
||||||
{ 41, "OPT" },
|
{ 23, "NSAP_PTR" }, /* for domain name pointer, NSAP style [RFC1348][RFC1637][RFC1706] */
|
||||||
{ 43, "DS" },
|
{ 24, "SIG" }, /* for security signature [RFC2535][RFC2536][RFC2537][RFC2931][RFC3008][RFC3110][RFC3755][RFC4034] */
|
||||||
{ 46, "RRSIG" },
|
{ 25, "KEY" }, /* for security key [RFC2535][RFC2536][RFC2537][RFC2539][RFC3008][RFC3110][RFC3755][RFC4034] */
|
||||||
{ 47, "NSEC" },
|
{ 26, "PX" }, /* X.400 mail mapping information [RFC2163] */
|
||||||
{ 48, "DNSKEY" },
|
{ 27, "GPOS" }, /* Geographical Position [RFC1712] */
|
||||||
{ 50, "NSEC3" },
|
{ 28, "AAAA" }, /* IP6 Address [RFC3596] */
|
||||||
{ 51, "NSEC3PARAM" },
|
{ 29, "LOC" }, /* Location Information [RFC1876] */
|
||||||
{ 52, "TLSA" },
|
{ 30, "NXT" }, /* Next Domain (OBSOLETE) [RFC2535][RFC3755] */
|
||||||
{ 53, "SMIMEA" },
|
{ 31, "EID" }, /* Endpoint Identifier [Michael_Patton][http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt] 1995-06*/
|
||||||
{ 55, "HIP" },
|
{ 32, "NIMLOC" }, /* Nimrod Locator [1][Michael_Patton][http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt] 1995-06*/
|
||||||
{ 249, "TKEY" },
|
{ 33, "SRV" }, /* Server Selection [1][RFC2782] */
|
||||||
{ 250, "TSIG" },
|
{ 34, "ATMA" }, /* ATM Address [ ATM Forum Technical Committee, "ATM Name System, V2.0", Doc ID: AF-DANS-0152.000, July 2000. Available from and held in escrow by IANA.] */
|
||||||
{ 251, "IXFR" },
|
{ 35, "NAPTR" }, /* Naming Authority Pointer [RFC2168][RFC2915][RFC3403] */
|
||||||
{ 252, "AXFR" },
|
{ 36, "KX" }, /* Key Exchanger [RFC2230] */
|
||||||
{ 253, "MAILB" },
|
{ 37, "CERT" }, /* CERT [RFC4398] */
|
||||||
{ 254, "MAILA" },
|
{ 38, "A6" }, /* A6 (OBSOLETE - use AAAA) [RFC2874][RFC3226][RFC6563] */
|
||||||
{ 255, "ANY" },
|
{ 39, "DNAME" }, /* DNAME [RFC6672] */
|
||||||
{ 257, "CAA" }
|
{ 40, "SINK" }, /* SINK [Donald_E_Eastlake][http://tools.ietf.org/html/draft-eastlake-kitchen-sink] 1997-11*/
|
||||||
|
{ 41, "OPT" }, /* OPT [RFC3225][RFC6891] */
|
||||||
|
{ 42, "APL" }, /* APL [RFC3123] */
|
||||||
|
{ 43, "DS" }, /* Delegation Signer [RFC3658][RFC4034] */
|
||||||
|
{ 44, "SSHFP" }, /* SSH Key Fingerprint [RFC4255] */
|
||||||
|
{ 45, "IPSECKEY" }, /* IPSECKEY [RFC4025] */
|
||||||
|
{ 46, "RRSIG" }, /* RRSIG [RFC3755][RFC4034] */
|
||||||
|
{ 47, "NSEC" }, /* NSEC [RFC3755][RFC4034][RFC9077] */
|
||||||
|
{ 48, "DNSKEY" }, /* DNSKEY [RFC3755][RFC4034] */
|
||||||
|
{ 49, "DHCID" }, /* DHCID [RFC4701] */
|
||||||
|
{ 50, "NSEC3" }, /* NSEC3 [RFC5155][RFC9077] */
|
||||||
|
{ 51, "NSEC3PARAM" }, /* NSEC3PARAM [RFC5155] */
|
||||||
|
{ 52, "TLSA" }, /* TLSA [RFC6698] */
|
||||||
|
{ 53, "SMIMEA" }, /* S/MIME cert association [RFC8162] SMIMEA/smimea-completed-template 2015-12-01*/
|
||||||
|
{ 55, "HIP" }, /* Host Identity Protocol [RFC8005] */
|
||||||
|
{ 56, "NINFO" }, /* NINFO [Jim_Reid] NINFO/ninfo-completed-template 2008-01-21*/
|
||||||
|
{ 57, "RKEY" }, /* RKEY [Jim_Reid] RKEY/rkey-completed-template 2008-01-21*/
|
||||||
|
{ 58, "TALINK" }, /* Trust Anchor LINK [Wouter_Wijngaards] TALINK/talink-completed-template 2010-02-17*/
|
||||||
|
{ 59, "CDS" }, /* Child DS [RFC7344] CDS/cds-completed-template 2011-06-06*/
|
||||||
|
{ 60, "CDNSKEY" }, /* DNSKEY(s) the Child wants reflected in DS [RFC7344] 2014-06-16*/
|
||||||
|
{ 61, "OPENPGPKEY" }, /* OpenPGP Key [RFC7929] OPENPGPKEY/openpgpkey-completed-template 2014-08-12*/
|
||||||
|
{ 62, "CSYNC" }, /* Child-To-Parent Synchronization [RFC7477] 2015-01-27*/
|
||||||
|
{ 63, "ZONEMD" }, /* Message Digest Over Zone Data [RFC8976] ZONEMD/zonemd-completed-template 2018-12-12*/
|
||||||
|
{ 64, "SVCB" }, /* Service Binding [draft-ietf-dnsop-svcb-https-00] SVCB/svcb-completed-template 2020-06-30*/
|
||||||
|
{ 65, "HTTPS" }, /* HTTPS Binding [draft-ietf-dnsop-svcb-https-00] HTTPS/https-completed-template 2020-06-30*/
|
||||||
|
{ 99, "SPF" }, /* [RFC7208] */
|
||||||
|
{ 100, "UINFO" }, /* [IANA-Reserved] */
|
||||||
|
{ 101, "UID" }, /* [IANA-Reserved] */
|
||||||
|
{ 102, "GID" }, /* [IANA-Reserved] */
|
||||||
|
{ 103, "UNSPEC" }, /* [IANA-Reserved] */
|
||||||
|
{ 104, "NID" }, /* [RFC6742] ILNP/nid-completed-template */
|
||||||
|
{ 105, "L32" }, /* [RFC6742] ILNP/l32-completed-template */
|
||||||
|
{ 106, "L64" }, /* [RFC6742] ILNP/l64-completed-template */
|
||||||
|
{ 107, "LP" }, /* [RFC6742] ILNP/lp-completed-template */
|
||||||
|
{ 108, "EUI48" }, /* an EUI-48 address [RFC7043] EUI48/eui48-completed-template 2013-03-27*/
|
||||||
|
{ 109, "EUI64" }, /* an EUI-64 address [RFC7043] EUI64/eui64-completed-template 2013-03-27*/
|
||||||
|
{ 249, "TKEY" }, /* Transaction Key [RFC2930] */
|
||||||
|
{ 250, "TSIG" }, /* Transaction Signature [RFC8945] */
|
||||||
|
{ 251, "IXFR" }, /* incremental transfer [RFC1995] */
|
||||||
|
{ 252, "AXFR" }, /* transfer of an entire zone [RFC1035][RFC5936] */
|
||||||
|
{ 253, "MAILB" }, /* mailbox-related RRs (MB, MG or MR) [RFC1035] */
|
||||||
|
{ 254, "MAILA" }, /* mail agent RRs (OBSOLETE - see MX) [RFC1035] */
|
||||||
|
{ 255, "ANY" }, /* A request for some or all records the server has available [RFC1035][RFC6895][RFC8482] */
|
||||||
|
{ 256, "URI" }, /* URI [RFC7553] URI/uri-completed-template 2011-02-22*/
|
||||||
|
{ 257, "CAA" }, /* Certification Authority Restriction [RFC8659] CAA/caa-completed-template 2011-04-07*/
|
||||||
|
{ 258, "AVC" }, /* Application Visibility and Control [Wolfgang_Riedel] AVC/avc-completed-template 2016-02-26*/
|
||||||
|
{ 259, "DOA" }, /* Digital Object Architecture [draft-durand-doa-over-dns] DOA/doa-completed-template 2017-08-30*/
|
||||||
|
{ 260, "AMTRELAY" }, /* Automatic Multicast Tunneling Relay [RFC8777] AMTRELAY/amtrelay-completed-template 2019-02-06*/
|
||||||
|
{ 32768, "TA" }, /* DNSSEC Trust Authorities [Sam_Weiler][http://cameo.library.cmu.edu/][ Deploying DNSSEC Without a Signed Root. Technical Report 1999-19, Information Networking Institute, Carnegie Mellon University, April 2004.] 2005-12-13*/
|
||||||
|
{ 32769, "DLV" }, /* DNSSEC Lookaside Validation (OBSOLETE) [RFC8749][RFC4431] */
|
||||||
};
|
};
|
||||||
|
|
||||||
static void cache_free(struct crec *crecp);
|
static void cache_free(struct crec *crecp);
|
||||||
@@ -433,7 +483,7 @@ static struct crec *cache_scan_free(char *name, union all_addr *addr, unsigned s
|
|||||||
else if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) &&
|
else if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) &&
|
||||||
(flags & crecp->flags & F_REVERSE) &&
|
(flags & crecp->flags & F_REVERSE) &&
|
||||||
(flags & crecp->flags & (F_IPV4 | F_IPV6)) &&
|
(flags & crecp->flags & (F_IPV4 | F_IPV6)) &&
|
||||||
memcmp(&crecp->addr, addr, addrlen) == 0)
|
addr && memcmp(&crecp->addr, addr, addrlen) == 0)
|
||||||
{
|
{
|
||||||
*up = crecp->hash_next;
|
*up = crecp->hash_next;
|
||||||
cache_unlink(crecp);
|
cache_unlink(crecp);
|
||||||
@@ -488,8 +538,6 @@ struct crec *cache_insert(char *name, union all_addr *addr, unsigned short class
|
|||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
/* Don't log DNSSEC records here, done elsewhere */
|
|
||||||
log_query(flags | F_UPSTREAM, name, addr, NULL);
|
|
||||||
if (daemon->max_cache_ttl != 0 && daemon->max_cache_ttl < ttl)
|
if (daemon->max_cache_ttl != 0 && daemon->max_cache_ttl < ttl)
|
||||||
ttl = daemon->max_cache_ttl;
|
ttl = daemon->max_cache_ttl;
|
||||||
if (daemon->min_cache_ttl != 0 && daemon->min_cache_ttl > ttl)
|
if (daemon->min_cache_ttl != 0 && daemon->min_cache_ttl > ttl)
|
||||||
@@ -1549,6 +1597,7 @@ static void make_non_terminals(struct crec *source)
|
|||||||
if (crecp)
|
if (crecp)
|
||||||
{
|
{
|
||||||
crecp->flags = (source->flags | F_NAMEP) & ~(F_IPV4 | F_IPV6 | F_CNAME | F_SRV | F_DNSKEY | F_DS | F_REVERSE);
|
crecp->flags = (source->flags | F_NAMEP) & ~(F_IPV4 | F_IPV6 | F_CNAME | F_SRV | F_DNSKEY | F_DS | F_REVERSE);
|
||||||
|
if (!(crecp->flags & F_IMMORTAL))
|
||||||
crecp->ttd = source->ttd;
|
crecp->ttd = source->ttd;
|
||||||
crecp->name.namep = name;
|
crecp->name.namep = name;
|
||||||
|
|
||||||
@@ -1602,18 +1651,18 @@ int cache_make_stat(struct txt_record *t)
|
|||||||
case TXT_STAT_SERVERS:
|
case TXT_STAT_SERVERS:
|
||||||
/* sum counts from different records for same server */
|
/* sum counts from different records for same server */
|
||||||
for (serv = daemon->servers; serv; serv = serv->next)
|
for (serv = daemon->servers; serv; serv = serv->next)
|
||||||
serv->flags &= ~SERV_COUNTED;
|
serv->flags &= ~SERV_MARK;
|
||||||
|
|
||||||
for (serv = daemon->servers; serv; serv = serv->next)
|
for (serv = daemon->servers; serv; serv = serv->next)
|
||||||
if (!(serv->flags & SERV_COUNTED))
|
if (!(serv->flags & SERV_MARK))
|
||||||
{
|
{
|
||||||
char *new, *lenp;
|
char *new, *lenp;
|
||||||
int port, newlen, bytes_avail, bytes_needed;
|
int port, newlen, bytes_avail, bytes_needed;
|
||||||
unsigned int queries = 0, failed_queries = 0;
|
unsigned int queries = 0, failed_queries = 0;
|
||||||
for (serv1 = serv; serv1; serv1 = serv1->next)
|
for (serv1 = serv; serv1; serv1 = serv1->next)
|
||||||
if (!(serv1->flags & SERV_COUNTED) && 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;
|
queries += serv1->queries;
|
||||||
failed_queries += serv1->failed_queries;
|
failed_queries += serv1->failed_queries;
|
||||||
}
|
}
|
||||||
@@ -1641,6 +1690,7 @@ int cache_make_stat(struct txt_record *t)
|
|||||||
}
|
}
|
||||||
t->txt = (unsigned char *)buff;
|
t->txt = (unsigned char *)buff;
|
||||||
t->len = p - buff;
|
t->len = p - buff;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1683,17 +1733,17 @@ void dump_cache(time_t now)
|
|||||||
|
|
||||||
/* sum counts from different records for same server */
|
/* sum counts from different records for same server */
|
||||||
for (serv = daemon->servers; serv; serv = serv->next)
|
for (serv = daemon->servers; serv; serv = serv->next)
|
||||||
serv->flags &= ~SERV_COUNTED;
|
serv->flags &= ~SERV_MARK;
|
||||||
|
|
||||||
for (serv = daemon->servers; serv; serv = serv->next)
|
for (serv = daemon->servers; serv; serv = serv->next)
|
||||||
if (!(serv->flags & SERV_COUNTED))
|
if (!(serv->flags & SERV_MARK))
|
||||||
{
|
{
|
||||||
int port;
|
int port;
|
||||||
unsigned int queries = 0, failed_queries = 0;
|
unsigned int queries = 0, failed_queries = 0;
|
||||||
for (serv1 = serv; serv1; serv1 = serv1->next)
|
for (serv1 = serv; serv1; serv1 = serv1->next)
|
||||||
if (!(serv1->flags & SERV_COUNTED) && 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;
|
queries += serv1->queries;
|
||||||
failed_queries += serv1->failed_queries;
|
failed_queries += serv1->failed_queries;
|
||||||
}
|
}
|
||||||
@@ -1806,7 +1856,7 @@ char *record_source(unsigned int index)
|
|||||||
return "<unknown>";
|
return "<unknown>";
|
||||||
}
|
}
|
||||||
|
|
||||||
char *querystr(char *desc, unsigned short type)
|
static char *querystr(char *desc, unsigned short type)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
int len = 10; /* strlen("type=xxxxx") */
|
int len = 10; /* strlen("type=xxxxx") */
|
||||||
@@ -1854,25 +1904,70 @@ char *querystr(char *desc, unsigned short type)
|
|||||||
if (types)
|
if (types)
|
||||||
sprintf(buff, "<%s>", types);
|
sprintf(buff, "<%s>", types);
|
||||||
else
|
else
|
||||||
sprintf(buff, "type=%d", type);
|
sprintf(buff, "<type=%d>", type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return buff ? buff : "";
|
return buff ? buff : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg)
|
static char *edestr(int ede)
|
||||||
{
|
{
|
||||||
char *source, *dest = daemon->addrbuff;
|
switch (ede)
|
||||||
|
{
|
||||||
|
case EDE_OTHER: return "other";
|
||||||
|
case EDE_USUPDNSKEY: return "unsupported DNSKEY algorithm";
|
||||||
|
case EDE_USUPDS: return "unsupported DS digest";
|
||||||
|
case EDE_STALE: return "stale answer";
|
||||||
|
case EDE_FORGED: return "forged";
|
||||||
|
case EDE_DNSSEC_IND: return "DNSSEC indeterminate";
|
||||||
|
case EDE_DNSSEC_BOGUS: return "DNSSEC bogus";
|
||||||
|
case EDE_SIG_EXP: return "DNSSEC signature expired";
|
||||||
|
case EDE_SIG_NYV: return "DNSSEC sig not yet valid";
|
||||||
|
case EDE_NO_DNSKEY: return "DNSKEY missing";
|
||||||
|
case EDE_NO_RRSIG: return "RRSIG missing";
|
||||||
|
case EDE_NO_ZONEKEY: return "no zone key bit set";
|
||||||
|
case EDE_NO_NSEC: return "NSEC(3) missing";
|
||||||
|
case EDE_CACHED_ERR: return "cached error";
|
||||||
|
case EDE_NOT_READY: return "not ready";
|
||||||
|
case EDE_BLOCKED: return "blocked";
|
||||||
|
case EDE_CENSORED: return "censored";
|
||||||
|
case EDE_FILTERED: return "filtered";
|
||||||
|
case EDE_PROHIBITED: return "prohibited";
|
||||||
|
case EDE_STALE_NXD: return "stale NXDOMAIN";
|
||||||
|
case EDE_NOT_AUTH: return "not authoritative";
|
||||||
|
case EDE_NOT_SUP: return "not supported";
|
||||||
|
case EDE_NO_AUTH: return "no reachable authority";
|
||||||
|
case EDE_NETERR: return "network error";
|
||||||
|
case EDE_INVALID_DATA: return "invalid data";
|
||||||
|
default: return "unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg, unsigned short type)
|
||||||
|
{
|
||||||
|
char *source, *dest = arg;
|
||||||
char *verb = "is";
|
char *verb = "is";
|
||||||
|
char *extra = "";
|
||||||
|
|
||||||
if (!option_bool(OPT_LOG))
|
if (!option_bool(OPT_LOG))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
/* build query type string if requested */
|
||||||
|
if(type > 0)
|
||||||
|
arg = querystr(arg, type);
|
||||||
|
|
||||||
|
#ifdef HAVE_DNSSEC
|
||||||
|
if ((flags & F_DNSSECOK) && option_bool(OPT_EXTRALOG))
|
||||||
|
extra = " (DNSSEC signed)";
|
||||||
|
#endif
|
||||||
|
|
||||||
name = sanitise(name);
|
name = sanitise(name);
|
||||||
|
|
||||||
if (addr)
|
if (addr)
|
||||||
{
|
{
|
||||||
|
dest = daemon->addrbuff;
|
||||||
|
|
||||||
if (flags & F_KEYTAG)
|
if (flags & F_KEYTAG)
|
||||||
sprintf(daemon->addrbuff, arg, addr->log.keytag, addr->log.algo, addr->log.digest);
|
sprintf(daemon->addrbuff, arg, addr->log.keytag, addr->log.algo, addr->log.digest);
|
||||||
else if (flags & F_RCODE)
|
else if (flags & F_RCODE)
|
||||||
@@ -1887,14 +1982,19 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg)
|
|||||||
dest = "not implemented";
|
dest = "not implemented";
|
||||||
else
|
else
|
||||||
sprintf(daemon->addrbuff, "%u", rcode);
|
sprintf(daemon->addrbuff, "%u", rcode);
|
||||||
|
|
||||||
|
if (addr->log.ede != EDE_UNSET)
|
||||||
|
{
|
||||||
|
extra = daemon->addrbuff;
|
||||||
|
sprintf(extra, " (EDE: %s)", edestr(addr->log.ede));
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
|
else if (flags & (F_IPV4 | F_IPV6))
|
||||||
inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
|
inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
|
||||||
addr, daemon->addrbuff, ADDRSTRLEN);
|
addr, daemon->addrbuff, ADDRSTRLEN);
|
||||||
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
dest = arg;
|
dest = arg;
|
||||||
|
}
|
||||||
|
|
||||||
if (flags & F_REVERSE)
|
if (flags & F_REVERSE)
|
||||||
{
|
{
|
||||||
@@ -1932,7 +2032,15 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg)
|
|||||||
else if (flags & F_UPSTREAM)
|
else if (flags & F_UPSTREAM)
|
||||||
source = "reply";
|
source = "reply";
|
||||||
else if (flags & F_SECSTAT)
|
else if (flags & F_SECSTAT)
|
||||||
|
{
|
||||||
|
if (addr && addr->log.ede != EDE_UNSET && option_bool(OPT_EXTRALOG))
|
||||||
|
{
|
||||||
|
extra = daemon->addrbuff;
|
||||||
|
sprintf(extra, " (EDE: %s)", edestr(addr->log.ede));
|
||||||
|
}
|
||||||
source = "validation";
|
source = "validation";
|
||||||
|
dest = arg;
|
||||||
|
}
|
||||||
else if (flags & F_AUTH)
|
else if (flags & F_AUTH)
|
||||||
source = "auth";
|
source = "auth";
|
||||||
else if (flags & F_SERVER)
|
else if (flags & F_SERVER)
|
||||||
@@ -1952,7 +2060,7 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg)
|
|||||||
}
|
}
|
||||||
else if (flags & F_IPSET)
|
else if (flags & F_IPSET)
|
||||||
{
|
{
|
||||||
source = "ipset add";
|
source = type ? "ipset add" : "nftset add";
|
||||||
dest = name;
|
dest = name;
|
||||||
name = arg;
|
name = arg;
|
||||||
verb = daemon->addrbuff;
|
verb = daemon->addrbuff;
|
||||||
@@ -1960,19 +2068,19 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg)
|
|||||||
else
|
else
|
||||||
source = "cached";
|
source = "cached";
|
||||||
|
|
||||||
if (strlen(name) == 0)
|
if (name && !name[0])
|
||||||
name = ".";
|
name = ".";
|
||||||
|
|
||||||
if (option_bool(OPT_EXTRALOG))
|
if (option_bool(OPT_EXTRALOG))
|
||||||
{
|
{
|
||||||
if (flags & F_NOEXTRA)
|
if (flags & F_NOEXTRA)
|
||||||
my_syslog(LOG_INFO, "%u %s %s %s %s", daemon->log_display_id, source, name, verb, dest);
|
my_syslog(LOG_INFO, "%u %s %s %s %s%s", daemon->log_display_id, source, name, verb, dest, extra);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int port = prettyprint_addr(daemon->log_source_addr, daemon->addrbuff2);
|
int port = prettyprint_addr(daemon->log_source_addr, daemon->addrbuff2);
|
||||||
my_syslog(LOG_INFO, "%u %s/%u %s %s %s %s", daemon->log_display_id, daemon->addrbuff2, port, source, name, verb, dest);
|
my_syslog(LOG_INFO, "%u %s/%u %s %s %s %s%s", daemon->log_display_id, daemon->addrbuff2, port, source, name, verb, dest, extra);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
my_syslog(LOG_INFO, "%s %s %s %s", source, name, verb, dest);
|
my_syslog(LOG_INFO, "%s %s %s %s%s", source, name, verb, dest, extra);
|
||||||
}
|
}
|
||||||
+9
-1
@@ -115,6 +115,10 @@ HAVE_IPSET
|
|||||||
define this to include the ability to selectively add resolved ip addresses
|
define this to include the ability to selectively add resolved ip addresses
|
||||||
to given ipsets.
|
to given ipsets.
|
||||||
|
|
||||||
|
HAVE_NFTSET
|
||||||
|
define this to include the ability to selectively add resolved ip addresses
|
||||||
|
to given nftables sets.
|
||||||
|
|
||||||
HAVE_AUTH
|
HAVE_AUTH
|
||||||
define this to include the facility to act as an authoritative DNS
|
define this to include the facility to act as an authoritative DNS
|
||||||
server for one or more zones.
|
server for one or more zones.
|
||||||
@@ -192,7 +196,7 @@ RESOLVFILE
|
|||||||
/* #define HAVE_CONNTRACK */
|
/* #define HAVE_CONNTRACK */
|
||||||
/* #define HAVE_CRYPTOHASH */
|
/* #define HAVE_CRYPTOHASH */
|
||||||
/* #define HAVE_DNSSEC */
|
/* #define HAVE_DNSSEC */
|
||||||
|
/* #define HAVE_NFTSET */
|
||||||
|
|
||||||
/* Default locations for important system files. */
|
/* Default locations for important system files. */
|
||||||
|
|
||||||
@@ -420,6 +424,10 @@ static char *compile_opts =
|
|||||||
"no-"
|
"no-"
|
||||||
#endif
|
#endif
|
||||||
"ipset "
|
"ipset "
|
||||||
|
#ifndef HAVE_NFTSET
|
||||||
|
"no-"
|
||||||
|
#endif
|
||||||
|
"nftset "
|
||||||
#ifndef HAVE_AUTH
|
#ifndef HAVE_AUTH
|
||||||
"no-"
|
"no-"
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+20
-15
@@ -114,7 +114,7 @@ static dbus_bool_t add_watch(DBusWatch *watch, void *data)
|
|||||||
w->next = daemon->watches;
|
w->next = daemon->watches;
|
||||||
daemon->watches = w;
|
daemon->watches = w;
|
||||||
|
|
||||||
w = data; /* no warning */
|
(void)data; /* no warning */
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,16 +134,20 @@ static void remove_watch(DBusWatch *watch, void *data)
|
|||||||
up = &(w->next);
|
up = &(w->next);
|
||||||
}
|
}
|
||||||
|
|
||||||
w = data; /* no warning */
|
(void)data; /* no warning */
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dbus_read_servers(DBusMessage *message)
|
static DBusMessage* dbus_read_servers(DBusMessage *message)
|
||||||
{
|
{
|
||||||
DBusMessageIter iter;
|
DBusMessageIter iter;
|
||||||
union mysockaddr addr, source_addr;
|
union mysockaddr addr, source_addr;
|
||||||
char *domain;
|
char *domain;
|
||||||
|
|
||||||
dbus_message_iter_init(message, &iter);
|
if (!dbus_message_iter_init(message, &iter))
|
||||||
|
{
|
||||||
|
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
|
||||||
|
"Failed to initialize dbus message iter");
|
||||||
|
}
|
||||||
|
|
||||||
mark_servers(SERV_FROM_DBUS);
|
mark_servers(SERV_FROM_DBUS);
|
||||||
|
|
||||||
@@ -215,13 +219,14 @@ static void dbus_read_servers(DBusMessage *message)
|
|||||||
domain = NULL;
|
domain = NULL;
|
||||||
|
|
||||||
if (!skip)
|
if (!skip)
|
||||||
add_update_server(SERV_FROM_DBUS, &addr, &source_addr, NULL, domain);
|
add_update_server(SERV_FROM_DBUS, &addr, &source_addr, NULL, domain, NULL);
|
||||||
|
|
||||||
} while (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING);
|
} while (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* unlink and free anything still marked. */
|
/* unlink and free anything still marked. */
|
||||||
cleanup_servers();
|
cleanup_servers();
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_LOOP
|
#ifdef HAVE_LOOP
|
||||||
@@ -276,7 +281,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
|
|||||||
{
|
{
|
||||||
const char *str = NULL;
|
const char *str = NULL;
|
||||||
union mysockaddr addr, source_addr;
|
union mysockaddr addr, source_addr;
|
||||||
int flags = 0;
|
u16 flags = 0;
|
||||||
char interface[IF_NAMESIZE];
|
char interface[IF_NAMESIZE];
|
||||||
char *str_addr, *str_domain = NULL;
|
char *str_addr, *str_domain = NULL;
|
||||||
|
|
||||||
@@ -361,10 +366,6 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
|
|||||||
strcpy(str_addr, str);
|
strcpy(str_addr, str);
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(&addr, 0, sizeof(addr));
|
|
||||||
memset(&source_addr, 0, sizeof(source_addr));
|
|
||||||
memset(&interface, 0, sizeof(interface));
|
|
||||||
|
|
||||||
/* parse the IP address */
|
/* parse the IP address */
|
||||||
if ((addr_err = parse_server(str_addr, &addr, &source_addr, (char *) &interface, &flags)))
|
if ((addr_err = parse_server(str_addr, &addr, &source_addr, (char *) &interface, &flags)))
|
||||||
{
|
{
|
||||||
@@ -392,7 +393,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
|
|||||||
else
|
else
|
||||||
p = NULL;
|
p = NULL;
|
||||||
|
|
||||||
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str_domain);
|
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str_domain, NULL);
|
||||||
} while ((str_domain = p));
|
} while ((str_domain = p));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -407,7 +408,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
|
|||||||
dbus_message_iter_get_basic(&string_iter, &str);
|
dbus_message_iter_get_basic(&string_iter, &str);
|
||||||
dbus_message_iter_next (&string_iter);
|
dbus_message_iter_next (&string_iter);
|
||||||
|
|
||||||
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str);
|
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str, NULL);
|
||||||
} while (dbus_message_iter_get_arg_type(&string_iter) == DBUS_TYPE_STRING);
|
} while (dbus_message_iter_get_arg_type(&string_iter) == DBUS_TYPE_STRING);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -549,6 +550,10 @@ static DBusMessage *dbus_add_lease(DBusMessage* message)
|
|||||||
"Invalid IP address '%s'", ipaddr);
|
"Invalid IP address '%s'", ipaddr);
|
||||||
|
|
||||||
hw_len = parse_hex((char*)hwaddr, dhcp_chaddr, DHCP_CHADDR_MAX, NULL, &hw_type);
|
hw_len = parse_hex((char*)hwaddr, dhcp_chaddr, DHCP_CHADDR_MAX, NULL, &hw_type);
|
||||||
|
if (hw_len < 0)
|
||||||
|
return dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
|
||||||
|
"Invalid HW address '%s'", hwaddr);
|
||||||
|
|
||||||
if (hw_type == 0 && hw_len != 0)
|
if (hw_type == 0 && hw_len != 0)
|
||||||
hw_type = ARPHRD_ETHER;
|
hw_type = ARPHRD_ETHER;
|
||||||
|
|
||||||
@@ -672,7 +677,7 @@ DBusHandlerResult message_handler(DBusConnection *connection,
|
|||||||
#endif
|
#endif
|
||||||
else if (strcmp(method, "SetServers") == 0)
|
else if (strcmp(method, "SetServers") == 0)
|
||||||
{
|
{
|
||||||
dbus_read_servers(message);
|
reply = dbus_read_servers(message);
|
||||||
new_servers = 1;
|
new_servers = 1;
|
||||||
}
|
}
|
||||||
else if (strcmp(method, "SetServersEx") == 0)
|
else if (strcmp(method, "SetServersEx") == 0)
|
||||||
@@ -715,7 +720,7 @@ DBusHandlerResult message_handler(DBusConnection *connection,
|
|||||||
if (new_servers)
|
if (new_servers)
|
||||||
{
|
{
|
||||||
my_syslog(LOG_INFO, _("setting upstream servers from DBus"));
|
my_syslog(LOG_INFO, _("setting upstream servers from DBus"));
|
||||||
check_servers();
|
check_servers(0);
|
||||||
if (option_bool(OPT_RELOAD))
|
if (option_bool(OPT_RELOAD))
|
||||||
clear_cache = 1;
|
clear_cache = 1;
|
||||||
}
|
}
|
||||||
@@ -723,7 +728,7 @@ DBusHandlerResult message_handler(DBusConnection *connection,
|
|||||||
if (clear_cache)
|
if (clear_cache)
|
||||||
clear_cache_and_reload(dnsmasq_time());
|
clear_cache_and_reload(dnsmasq_time());
|
||||||
|
|
||||||
method = user_data; /* no warning */
|
(void)user_data; /* no warning */
|
||||||
|
|
||||||
/* If no reply or no error, return nothing */
|
/* If no reply or no error, return nothing */
|
||||||
if (!reply)
|
if (!reply)
|
||||||
|
|||||||
+36
-1
@@ -79,13 +79,46 @@ ssize_t recv_dhcp_packet(int fd, struct msghdr *msg)
|
|||||||
return (msg->msg_flags & MSG_TRUNC) ? -1 : new_sz;
|
return (msg->msg_flags & MSG_TRUNC) ? -1 : new_sz;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* like match_netid() except that the check can have a trailing * for wildcard */
|
||||||
|
/* started as a direct copy of match_netid() */
|
||||||
|
int match_netid_wild(struct dhcp_netid *check, struct dhcp_netid *pool)
|
||||||
|
{
|
||||||
|
struct dhcp_netid *tmp1;
|
||||||
|
|
||||||
|
for (; check; check = check->next)
|
||||||
|
{
|
||||||
|
const int check_len = strlen(check->net);
|
||||||
|
const int is_wc = (check_len > 0 && check->net[check_len - 1] == '*');
|
||||||
|
|
||||||
|
/* '#' for not is for backwards compat. */
|
||||||
|
if (check->net[0] != '!' && check->net[0] != '#')
|
||||||
|
{
|
||||||
|
for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
|
||||||
|
if (is_wc ? (strncmp(check->net, tmp1->net, check_len-1) == 0) :
|
||||||
|
(strcmp(check->net, tmp1->net) == 0))
|
||||||
|
break;
|
||||||
|
if (!tmp1)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
|
||||||
|
if (is_wc ? (strncmp((check->net)+1, tmp1->net, check_len-2) == 0) :
|
||||||
|
(strcmp((check->net)+1, tmp1->net) == 0))
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
struct dhcp_netid *run_tag_if(struct dhcp_netid *tags)
|
struct dhcp_netid *run_tag_if(struct dhcp_netid *tags)
|
||||||
{
|
{
|
||||||
struct tag_if *exprs;
|
struct tag_if *exprs;
|
||||||
struct dhcp_netid_list *list;
|
struct dhcp_netid_list *list;
|
||||||
|
|
||||||
|
/* this now uses match_netid_wild() above so that tag_if can
|
||||||
|
* be used to set a 'group of interfaces' tag.
|
||||||
|
*/
|
||||||
for (exprs = daemon->tag_if; exprs; exprs = exprs->next)
|
for (exprs = daemon->tag_if; exprs; exprs = exprs->next)
|
||||||
if (match_netid(exprs->tag, tags, 1))
|
if (match_netid_wild(exprs->tag, tags))
|
||||||
for (list = exprs->set; list; list = list->next)
|
for (list = exprs->set; list; list = list->next)
|
||||||
{
|
{
|
||||||
list->list->next = tags;
|
list->list->next = tags;
|
||||||
@@ -620,6 +653,8 @@ static const struct opttab_t {
|
|||||||
{ "client-arch", 93, 2 | OT_DEC },
|
{ "client-arch", 93, 2 | OT_DEC },
|
||||||
{ "client-interface-id", 94, 0 },
|
{ "client-interface-id", 94, 0 },
|
||||||
{ "client-machine-id", 97, 0 },
|
{ "client-machine-id", 97, 0 },
|
||||||
|
{ "posix-timezone", 100, OT_NAME }, /* RFC 4833, Sec. 2 */
|
||||||
|
{ "tzdb-timezone", 101, OT_NAME }, /* RFC 4833, Sec. 2 */
|
||||||
{ "subnet-select", 118, OT_INTERNAL },
|
{ "subnet-select", 118, OT_INTERNAL },
|
||||||
{ "domain-search", 119, OT_RFC1035_NAME },
|
{ "domain-search", 119, OT_RFC1035_NAME },
|
||||||
{ "sip-server", 120, 0 },
|
{ "sip-server", 120, 0 },
|
||||||
|
|||||||
+11
-6
@@ -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 */
|
/* This can fail when, eg, iptables DROPS destination 255.255.255.255 */
|
||||||
if (errno != 0)
|
if (errno != 0)
|
||||||
|
{
|
||||||
|
inet_ntop(AF_INET, &dest.sin_addr, daemon->addrbuff, ADDRSTRLEN);
|
||||||
my_syslog(MS_DHCP | LOG_WARNING, _("Error sending DHCP packet to %s: %s"),
|
my_syslog(MS_DHCP | LOG_WARNING, _("Error sending DHCP packet to %s: %s"),
|
||||||
inet_ntoa(dest.sin_addr), strerror(errno));
|
daemon->addrbuff, strerror(errno));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check against secondary interface addresses */
|
/* 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->start, netmask) &&
|
||||||
is_same_net(addr, context->end, netmask)))
|
is_same_net(addr, context->end, netmask)))
|
||||||
{
|
{
|
||||||
strcpy(daemon->dhcp_buff, inet_ntoa(context->start));
|
inet_ntop(AF_INET, &context->start, daemon->dhcp_buff, DHCP_BUFF_SZ);
|
||||||
strcpy(daemon->dhcp_buff2, inet_ntoa(context->end));
|
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"),
|
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;
|
context->netmask = netmask;
|
||||||
}
|
}
|
||||||
@@ -922,7 +926,7 @@ void dhcp_read_ethers(void)
|
|||||||
|
|
||||||
if (!*cp)
|
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);
|
my_syslog(MS_DHCP | LOG_ERR, _("bad address at %s line %d"), ETHERSFILE, lineno);
|
||||||
continue;
|
continue;
|
||||||
@@ -1097,7 +1101,8 @@ static int relay_upstream4(struct dhcp_relay *relay, struct dhcp_packet *mess,
|
|||||||
if (option_bool(OPT_LOG_OPTS))
|
if (option_bool(OPT_LOG_OPTS))
|
||||||
{
|
{
|
||||||
inet_ntop(AF_INET, &relay->local, daemon->addrbuff, ADDRSTRLEN);
|
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 */
|
/* Save this for replies */
|
||||||
|
|||||||
+1
-1
@@ -292,7 +292,7 @@ void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsi
|
|||||||
if ((maclen = find_mac(&addr, mac, 0, now)) != 0)
|
if ((maclen = find_mac(&addr, mac, 0, now)) != 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
sendto(daemon->icmp6fd, &neigh, sizeof(neigh), 0, &addr.sa, sizeof(addr));
|
while(retry_send(sendto(daemon->icmp6fd, &neigh, sizeof(neigh), 0, &addr.sa, sizeof(addr))));
|
||||||
|
|
||||||
ts.tv_sec = 0;
|
ts.tv_sec = 0;
|
||||||
ts.tv_nsec = 100000000; /* 100ms */
|
ts.tv_nsec = 100000000; /* 100ms */
|
||||||
|
|||||||
@@ -80,10 +80,42 @@
|
|||||||
|
|
||||||
#define EDNS0_OPTION_MAC 65001 /* dyndns.org temporary assignment */
|
#define EDNS0_OPTION_MAC 65001 /* dyndns.org temporary assignment */
|
||||||
#define EDNS0_OPTION_CLIENT_SUBNET 8 /* IANA */
|
#define EDNS0_OPTION_CLIENT_SUBNET 8 /* IANA */
|
||||||
|
#define EDNS0_OPTION_EDE 15 /* IANA - RFC 8914 */
|
||||||
#define EDNS0_OPTION_NOMDEVICEID 65073 /* Nominum temporary assignment */
|
#define EDNS0_OPTION_NOMDEVICEID 65073 /* Nominum temporary assignment */
|
||||||
#define EDNS0_OPTION_NOMCPEID 65074 /* Nominum temporary assignment */
|
#define EDNS0_OPTION_NOMCPEID 65074 /* Nominum temporary assignment */
|
||||||
#define EDNS0_OPTION_UMBRELLA 20292 /* Cisco Umbrella temporary assignment */
|
#define EDNS0_OPTION_UMBRELLA 20292 /* Cisco Umbrella temporary assignment */
|
||||||
|
|
||||||
|
/* RFC-8914 extended errors, negative values are our definitions */
|
||||||
|
#define EDE_UNSET -1 /* No extended DNS error available */
|
||||||
|
#define EDE_OTHER 0 /* Other */
|
||||||
|
#define EDE_USUPDNSKEY 1 /* Unsupported DNSKEY algo */
|
||||||
|
#define EDE_USUPDS 2 /* Unsupported DS Digest */
|
||||||
|
#define EDE_STALE 3 /* Stale answer */
|
||||||
|
#define EDE_FORGED 4 /* Forged answer */
|
||||||
|
#define EDE_DNSSEC_IND 5 /* DNSSEC Indeterminate */
|
||||||
|
#define EDE_DNSSEC_BOGUS 6 /* DNSSEC Bogus */
|
||||||
|
#define EDE_SIG_EXP 7 /* Signature Expired */
|
||||||
|
#define EDE_SIG_NYV 8 /* Signature Not Yet Valid */
|
||||||
|
#define EDE_NO_DNSKEY 9 /* DNSKEY missing */
|
||||||
|
#define EDE_NO_RRSIG 10 /* RRSIGs missing */
|
||||||
|
#define EDE_NO_ZONEKEY 11 /* No Zone Key Bit Set */
|
||||||
|
#define EDE_NO_NSEC 12 /* NSEC Missing */
|
||||||
|
#define EDE_CACHED_ERR 13 /* Cached Error */
|
||||||
|
#define EDE_NOT_READY 14 /* Not Ready */
|
||||||
|
#define EDE_BLOCKED 15 /* Blocked */
|
||||||
|
#define EDE_CENSORED 16 /* Censored */
|
||||||
|
#define EDE_FILTERED 17 /* Filtered */
|
||||||
|
#define EDE_PROHIBITED 18 /* Prohibited */
|
||||||
|
#define EDE_STALE_NXD 19 /* Stale NXDOMAIN */
|
||||||
|
#define EDE_NOT_AUTH 20 /* Not Authoritative */
|
||||||
|
#define EDE_NOT_SUP 21 /* Not Supported */
|
||||||
|
#define EDE_NO_AUTH 22 /* No Reachable Authority */
|
||||||
|
#define EDE_NETERR 23 /* Network error */
|
||||||
|
#define EDE_INVALID_DATA 24 /* Invalid Data */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct dns_header {
|
struct dns_header {
|
||||||
u16 id;
|
u16 id;
|
||||||
u8 hb3,hb4;
|
u8 hb3,hb4;
|
||||||
|
|||||||
+58
-20
@@ -34,7 +34,6 @@ static void poll_resolv(int force, int do_reload, time_t now);
|
|||||||
|
|
||||||
int main (int argc, char **argv)
|
int main (int argc, char **argv)
|
||||||
{
|
{
|
||||||
int bind_fallback = 0;
|
|
||||||
time_t now;
|
time_t now;
|
||||||
struct sigaction sigact;
|
struct sigaction sigact;
|
||||||
struct iname *if_tmp;
|
struct iname *if_tmp;
|
||||||
@@ -59,6 +58,8 @@ int main (int argc, char **argv)
|
|||||||
int did_bind = 0;
|
int did_bind = 0;
|
||||||
struct server *serv;
|
struct server *serv;
|
||||||
char *netlink_warn;
|
char *netlink_warn;
|
||||||
|
#else
|
||||||
|
int bind_fallback = 0;
|
||||||
#endif
|
#endif
|
||||||
#if defined(HAVE_DHCP) || defined(HAVE_DHCP6)
|
#if defined(HAVE_DHCP) || defined(HAVE_DHCP6)
|
||||||
struct dhcp_context *context;
|
struct dhcp_context *context;
|
||||||
@@ -109,7 +110,6 @@ int main (int argc, char **argv)
|
|||||||
daemon->packet_buff_sz = daemon->edns_pktsz + MAXDNAME + RRFIXEDSZ;
|
daemon->packet_buff_sz = daemon->edns_pktsz + MAXDNAME + RRFIXEDSZ;
|
||||||
daemon->packet = safe_malloc(daemon->packet_buff_sz);
|
daemon->packet = safe_malloc(daemon->packet_buff_sz);
|
||||||
|
|
||||||
daemon->addrbuff = safe_malloc(ADDRSTRLEN);
|
|
||||||
if (option_bool(OPT_EXTRALOG))
|
if (option_bool(OPT_EXTRALOG))
|
||||||
daemon->addrbuff2 = safe_malloc(ADDRSTRLEN);
|
daemon->addrbuff2 = safe_malloc(ADDRSTRLEN);
|
||||||
|
|
||||||
@@ -135,6 +135,13 @@ int main (int argc, char **argv)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS)
|
||||||
|
/* CONNTRACK UBUS code uses this buffer, so if not allocated above,
|
||||||
|
we need to allocate it here. */
|
||||||
|
if (option_bool(OPT_CMARK_ALST_EN) && !daemon->workspacename)
|
||||||
|
daemon->workspacename = safe_malloc(MAXDNAME);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_DHCP
|
#ifdef HAVE_DHCP
|
||||||
if (!daemon->lease_file)
|
if (!daemon->lease_file)
|
||||||
{
|
{
|
||||||
@@ -205,8 +212,13 @@ int main (int argc, char **argv)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_CONNTRACK
|
#ifdef HAVE_CONNTRACK
|
||||||
if (option_bool(OPT_CONNTRACK) && (daemon->query_port != 0 || daemon->osport))
|
if (option_bool(OPT_CONNTRACK))
|
||||||
|
{
|
||||||
|
if (daemon->query_port != 0 || daemon->osport)
|
||||||
die (_("cannot use --conntrack AND --query-port"), NULL, EC_BADCONF);
|
die (_("cannot use --conntrack AND --query-port"), NULL, EC_BADCONF);
|
||||||
|
|
||||||
|
need_cap_net_admin = 1;
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
if (option_bool(OPT_CONNTRACK))
|
if (option_bool(OPT_CONNTRACK))
|
||||||
die(_("conntrack support not available: set HAVE_CONNTRACK in src/config.h"), NULL, EC_BADCONF);
|
die(_("conntrack support not available: set HAVE_CONNTRACK in src/config.h"), NULL, EC_BADCONF);
|
||||||
@@ -334,6 +346,16 @@ int main (int argc, char **argv)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_NFTSET
|
||||||
|
if (daemon->nftsets)
|
||||||
|
{
|
||||||
|
nftset_init();
|
||||||
|
# ifdef HAVE_LINUX_NETWORK
|
||||||
|
need_cap_net_admin = 1;
|
||||||
|
# endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(HAVE_LINUX_NETWORK)
|
#if defined(HAVE_LINUX_NETWORK)
|
||||||
netlink_warn = netlink_init();
|
netlink_warn = netlink_init();
|
||||||
#elif defined(HAVE_BSD_NETWORK)
|
#elif defined(HAVE_BSD_NETWORK)
|
||||||
@@ -366,7 +388,7 @@ int main (int argc, char **argv)
|
|||||||
bindtodevice(bound_device, daemon->dhcpfd);
|
bindtodevice(bound_device, daemon->dhcpfd);
|
||||||
did_bind = 1;
|
did_bind = 1;
|
||||||
}
|
}
|
||||||
if (daemon->enable_pxe && bound_device)
|
if (daemon->enable_pxe && bound_device && daemon->pxefd != -1)
|
||||||
{
|
{
|
||||||
bindtodevice(bound_device, daemon->pxefd);
|
bindtodevice(bound_device, daemon->pxefd);
|
||||||
did_bind = 1;
|
did_bind = 1;
|
||||||
@@ -430,8 +452,6 @@ int main (int argc, char **argv)
|
|||||||
#ifdef HAVE_DBUS
|
#ifdef HAVE_DBUS
|
||||||
{
|
{
|
||||||
char *err;
|
char *err;
|
||||||
daemon->dbus = NULL;
|
|
||||||
daemon->watches = NULL;
|
|
||||||
if ((err = dbus_init()))
|
if ((err = dbus_init()))
|
||||||
die(_("DBus error: %s"), err, EC_MISC);
|
die(_("DBus error: %s"), err, EC_MISC);
|
||||||
}
|
}
|
||||||
@@ -443,7 +463,6 @@ int main (int argc, char **argv)
|
|||||||
#ifdef HAVE_UBUS
|
#ifdef HAVE_UBUS
|
||||||
{
|
{
|
||||||
char *err;
|
char *err;
|
||||||
daemon->ubus = NULL;
|
|
||||||
if ((err = ubus_init()))
|
if ((err = ubus_init()))
|
||||||
die(_("UBus error: %s"), err, EC_MISC);
|
die(_("UBus error: %s"), err, EC_MISC);
|
||||||
}
|
}
|
||||||
@@ -912,8 +931,10 @@ int main (int argc, char **argv)
|
|||||||
my_syslog(LOG_WARNING, _("warning: failed to change owner of %s: %s"),
|
my_syslog(LOG_WARNING, _("warning: failed to change owner of %s: %s"),
|
||||||
daemon->log_file, strerror(log_err));
|
daemon->log_file, strerror(log_err));
|
||||||
|
|
||||||
|
#ifndef HAVE_LINUX_NETWORK
|
||||||
if (bind_fallback)
|
if (bind_fallback)
|
||||||
my_syslog(LOG_WARNING, _("setting --bind-interfaces option because of OS limitations"));
|
my_syslog(LOG_WARNING, _("setting --bind-interfaces option because of OS limitations"));
|
||||||
|
#endif
|
||||||
|
|
||||||
if (option_bool(OPT_NOWILD))
|
if (option_bool(OPT_NOWILD))
|
||||||
warn_bound_listeners();
|
warn_bound_listeners();
|
||||||
@@ -1027,7 +1048,7 @@ int main (int argc, char **argv)
|
|||||||
close(err_pipe[1]);
|
close(err_pipe[1]);
|
||||||
|
|
||||||
if (daemon->port != 0)
|
if (daemon->port != 0)
|
||||||
check_servers();
|
check_servers(0);
|
||||||
|
|
||||||
pid = getpid();
|
pid = getpid();
|
||||||
|
|
||||||
@@ -1058,6 +1079,7 @@ int main (int argc, char **argv)
|
|||||||
set_dns_listeners();
|
set_dns_listeners();
|
||||||
|
|
||||||
#ifdef HAVE_DBUS
|
#ifdef HAVE_DBUS
|
||||||
|
if (option_bool(OPT_DBUS))
|
||||||
set_dbus_listeners();
|
set_dbus_listeners();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -1185,28 +1207,44 @@ int main (int argc, char **argv)
|
|||||||
|
|
||||||
#ifdef HAVE_DBUS
|
#ifdef HAVE_DBUS
|
||||||
/* if we didn't create a DBus connection, retry now. */
|
/* if we didn't create a DBus connection, retry now. */
|
||||||
if (option_bool(OPT_DBUS) && !daemon->dbus)
|
if (option_bool(OPT_DBUS))
|
||||||
{
|
{
|
||||||
char *err;
|
if (!daemon->dbus)
|
||||||
if ((err = dbus_init()))
|
{
|
||||||
my_syslog(LOG_WARNING, _("DBus error: %s"), err);
|
char *err = dbus_init();
|
||||||
|
|
||||||
if (daemon->dbus)
|
if (daemon->dbus)
|
||||||
my_syslog(LOG_INFO, _("connected to system DBus"));
|
my_syslog(LOG_INFO, _("connected to system DBus"));
|
||||||
|
else if (err)
|
||||||
|
{
|
||||||
|
my_syslog(LOG_ERR, _("DBus error: %s"), err);
|
||||||
|
reset_option_bool(OPT_DBUS); /* fatal error, stop trying. */
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
check_dbus_listeners();
|
check_dbus_listeners();
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_UBUS
|
#ifdef HAVE_UBUS
|
||||||
/* if we didn't create a UBus connection, retry now. */
|
/* if we didn't create a UBus connection, retry now. */
|
||||||
if (option_bool(OPT_UBUS) && !daemon->ubus)
|
if (option_bool(OPT_UBUS))
|
||||||
{
|
{
|
||||||
char *err;
|
if (!daemon->ubus)
|
||||||
if ((err = ubus_init()))
|
{
|
||||||
my_syslog(LOG_WARNING, _("UBus error: %s"), err);
|
char *err = ubus_init();
|
||||||
|
|
||||||
if (daemon->ubus)
|
if (daemon->ubus)
|
||||||
my_syslog(LOG_INFO, _("connected to system UBus"));
|
my_syslog(LOG_INFO, _("connected to system UBus"));
|
||||||
|
else if (err)
|
||||||
|
{
|
||||||
|
my_syslog(LOG_ERR, _("UBus error: %s"), err);
|
||||||
|
reset_option_bool(OPT_UBUS); /* fatal error, stop trying. */
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
check_ubus_listeners();
|
check_ubus_listeners();
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
check_dns_listeners(now);
|
check_dns_listeners(now);
|
||||||
@@ -1439,7 +1477,7 @@ static void async_event(int pipe, time_t now)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (check)
|
if (check)
|
||||||
check_servers();
|
check_servers(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_DHCP
|
#ifdef HAVE_DHCP
|
||||||
@@ -1550,7 +1588,7 @@ static void async_event(int pipe, time_t now)
|
|||||||
{
|
{
|
||||||
/* block in writes until all done */
|
/* block in writes until all done */
|
||||||
if ((i = fcntl(daemon->helperfd, F_GETFL)) != -1)
|
if ((i = fcntl(daemon->helperfd, F_GETFL)) != -1)
|
||||||
fcntl(daemon->helperfd, F_SETFL, i & ~O_NONBLOCK);
|
while(retry_send(fcntl(daemon->helperfd, F_SETFL, i & ~O_NONBLOCK)));
|
||||||
do {
|
do {
|
||||||
helper_write();
|
helper_write();
|
||||||
} while (!helper_buf_empty() || do_script_run(now));
|
} while (!helper_buf_empty() || do_script_run(now));
|
||||||
@@ -1638,7 +1676,7 @@ static void poll_resolv(int force, int do_reload, time_t now)
|
|||||||
{
|
{
|
||||||
my_syslog(LOG_INFO, _("reading %s"), latest->name);
|
my_syslog(LOG_INFO, _("reading %s"), latest->name);
|
||||||
warned = 0;
|
warned = 0;
|
||||||
check_servers();
|
check_servers(0);
|
||||||
if (option_bool(OPT_RELOAD) && do_reload)
|
if (option_bool(OPT_RELOAD) && do_reload)
|
||||||
clear_cache_and_reload(now);
|
clear_cache_and_reload(now);
|
||||||
}
|
}
|
||||||
@@ -1959,7 +1997,7 @@ static void check_dns_listeners(time_t now)
|
|||||||
attribute from the listening socket.
|
attribute from the listening socket.
|
||||||
Reset that here. */
|
Reset that here. */
|
||||||
if ((flags = fcntl(confd, F_GETFL, 0)) != -1)
|
if ((flags = fcntl(confd, F_GETFL, 0)) != -1)
|
||||||
fcntl(confd, F_SETFL, flags & ~O_NONBLOCK);
|
while(retry_send(fcntl(confd, F_SETFL, flags & ~O_NONBLOCK)));
|
||||||
|
|
||||||
buff = tcp_request(confd, now, &tcp_addr, netmask, auth_dns);
|
buff = tcp_request(confd, now, &tcp_addr, netmask, auth_dns);
|
||||||
|
|
||||||
|
|||||||
+104
-50
@@ -107,6 +107,7 @@ typedef unsigned long long u64;
|
|||||||
#endif
|
#endif
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
@@ -272,7 +273,9 @@ struct event_desc {
|
|||||||
#define OPT_LOG_DEBUG 62
|
#define OPT_LOG_DEBUG 62
|
||||||
#define OPT_UMBRELLA 63
|
#define OPT_UMBRELLA 63
|
||||||
#define OPT_UMBRELLA_DEVID 64
|
#define OPT_UMBRELLA_DEVID 64
|
||||||
#define OPT_LAST 65
|
#define OPT_CMARK_ALST_EN 65
|
||||||
|
#define OPT_QUIET_TFTP 66
|
||||||
|
#define OPT_LAST 67
|
||||||
|
|
||||||
#define OPTION_BITS (sizeof(unsigned int)*8)
|
#define OPTION_BITS (sizeof(unsigned int)*8)
|
||||||
#define OPTION_SIZE ( (OPT_LAST/OPTION_BITS)+((OPT_LAST%OPTION_BITS)!=0) )
|
#define OPTION_SIZE ( (OPT_LAST/OPTION_BITS)+((OPT_LAST%OPTION_BITS)!=0) )
|
||||||
@@ -322,12 +325,14 @@ union all_addr {
|
|||||||
/* for log_query */
|
/* for log_query */
|
||||||
struct {
|
struct {
|
||||||
unsigned short keytag, algo, digest, rcode;
|
unsigned short keytag, algo, digest, rcode;
|
||||||
|
int ede;
|
||||||
} log;
|
} log;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
struct bogus_addr {
|
struct bogus_addr {
|
||||||
struct in_addr addr, mask;
|
int is6, prefix;
|
||||||
|
union all_addr addr;
|
||||||
struct bogus_addr *next;
|
struct bogus_addr *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -525,18 +530,18 @@ union mysockaddr {
|
|||||||
|
|
||||||
|
|
||||||
/* The actual values here matter, since we sort on them to get records in the order
|
/* 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. */
|
IPv6 addr, IPv4 addr, all zero return, resolvconf servers, upstream server, no-data return */
|
||||||
#define SERV_LITERAL_ADDRESS 1 /* addr is the answer, or NoDATA is the answer, depending on the next three flags */
|
#define SERV_LITERAL_ADDRESS 1 /* addr is the answer, or NoDATA is the answer, depending on the next four flags */
|
||||||
#define SERV_ALL_ZEROS 2 /* return all zeros for A and AAAA */
|
#define SERV_USE_RESOLV 2 /* forward this domain in the normal way */
|
||||||
#define SERV_4ADDR 4 /* addr is IPv4 */
|
#define SERV_ALL_ZEROS 4 /* return all zeros for A and AAAA */
|
||||||
#define SERV_6ADDR 8 /* addr is IPv6 */
|
#define SERV_4ADDR 8 /* addr is IPv4 */
|
||||||
#define SERV_HAS_SOURCE 16 /* source address defined */
|
#define SERV_6ADDR 16 /* addr is IPv6 */
|
||||||
#define SERV_FOR_NODOTS 32 /* server for names with no domain part only */
|
#define SERV_HAS_SOURCE 32 /* source address defined */
|
||||||
#define SERV_WARNED_RECURSIVE 64 /* avoid warning spam */
|
#define SERV_FOR_NODOTS 64 /* server for names with no domain part only */
|
||||||
#define SERV_FROM_DBUS 128 /* 1 if source is DBus */
|
#define SERV_WARNED_RECURSIVE 128 /* avoid warning spam */
|
||||||
#define SERV_MARK 256 /* for mark-and-delete */
|
#define SERV_FROM_DBUS 256 /* 1 if source is DBus */
|
||||||
#define SERV_COUNTED 512 /* workspace for log code */
|
#define SERV_MARK 512 /* for mark-and-delete and log code */
|
||||||
#define SERV_USE_RESOLV 1024 /* forward this domain in the normal way */
|
#define SERV_WILDCARD 1024 /* domain has leading '*' */
|
||||||
#define SERV_FROM_RESOLV 2048 /* 1 for servers from resolv, 0 for command line. */
|
#define SERV_FROM_RESOLV 2048 /* 1 for servers from resolv, 0 for command line. */
|
||||||
#define SERV_FROM_FILE 4096 /* read from --servers-file */
|
#define SERV_FROM_FILE 4096 /* read from --servers-file */
|
||||||
#define SERV_LOOP 8192 /* server causes forwarding loop */
|
#define SERV_LOOP 8192 /* server causes forwarding loop */
|
||||||
@@ -564,7 +569,7 @@ struct randfd_list {
|
|||||||
|
|
||||||
|
|
||||||
struct server {
|
struct server {
|
||||||
int flags;
|
u16 flags, domain_len;
|
||||||
char *domain;
|
char *domain;
|
||||||
struct server *next;
|
struct server *next;
|
||||||
int serial, arrayposn;
|
int serial, arrayposn;
|
||||||
@@ -583,33 +588,44 @@ struct server {
|
|||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
/* First three fields must match struct server in next three definitions.. */
|
/* First four fields must match struct server in next three definitions.. */
|
||||||
struct serv_addr4 {
|
struct serv_addr4 {
|
||||||
int flags;
|
u16 flags, domain_len;
|
||||||
char *domain;
|
char *domain;
|
||||||
struct server *next;
|
struct server *next;
|
||||||
struct in_addr addr;
|
struct in_addr addr;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct serv_addr6 {
|
struct serv_addr6 {
|
||||||
int flags;
|
u16 flags, domain_len;
|
||||||
char *domain;
|
char *domain;
|
||||||
struct server *next;
|
struct server *next;
|
||||||
struct in6_addr addr;
|
struct in6_addr addr;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct serv_local {
|
struct serv_local {
|
||||||
int flags;
|
u16 flags, domain_len;
|
||||||
char *domain;
|
char *domain;
|
||||||
struct server *next;
|
struct server *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct rebind_domain {
|
||||||
|
char *domain;
|
||||||
|
struct rebind_domain *next;
|
||||||
|
};
|
||||||
|
|
||||||
struct ipsets {
|
struct ipsets {
|
||||||
char **sets;
|
char **sets;
|
||||||
char *domain;
|
char *domain;
|
||||||
struct ipsets *next;
|
struct ipsets *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct allowlist {
|
||||||
|
u32 mark, mask;
|
||||||
|
char **patterns;
|
||||||
|
struct allowlist *next;
|
||||||
|
};
|
||||||
|
|
||||||
struct irec {
|
struct irec {
|
||||||
union mysockaddr addr;
|
union mysockaddr addr;
|
||||||
struct in_addr netmask; /* only valid for IPv4 */
|
struct in_addr netmask; /* only valid for IPv4 */
|
||||||
@@ -679,17 +695,28 @@ struct hostsfile {
|
|||||||
#define DUMP_BOGUS 0x0040
|
#define DUMP_BOGUS 0x0040
|
||||||
#define DUMP_SEC_BOGUS 0x0080
|
#define DUMP_SEC_BOGUS 0x0080
|
||||||
|
|
||||||
|
|
||||||
/* DNSSEC status values. */
|
/* DNSSEC status values. */
|
||||||
#define STAT_SECURE 1
|
#define STAT_SECURE 0x10000
|
||||||
#define STAT_INSECURE 2
|
#define STAT_INSECURE 0x20000
|
||||||
#define STAT_BOGUS 3
|
#define STAT_BOGUS 0x30000
|
||||||
#define STAT_NEED_DS 4
|
#define STAT_NEED_DS 0x40000
|
||||||
#define STAT_NEED_KEY 5
|
#define STAT_NEED_KEY 0x50000
|
||||||
#define STAT_TRUNCATED 6
|
#define STAT_TRUNCATED 0x60000
|
||||||
#define STAT_SECURE_WILDCARD 7
|
#define STAT_SECURE_WILDCARD 0x70000
|
||||||
#define STAT_OK 8
|
#define STAT_OK 0x80000
|
||||||
#define STAT_ABANDONED 9
|
#define STAT_ABANDONED 0x90000
|
||||||
|
|
||||||
|
#define DNSSEC_FAIL_NYV 0x0001 /* key not yet valid */
|
||||||
|
#define DNSSEC_FAIL_EXP 0x0002 /* key expired */
|
||||||
|
#define DNSSEC_FAIL_INDET 0x0004 /* indetermined */
|
||||||
|
#define DNSSEC_FAIL_NOKEYSUP 0x0008 /* no supported key algo. */
|
||||||
|
#define DNSSEC_FAIL_NOSIG 0x0010 /* No RRsigs */
|
||||||
|
#define DNSSEC_FAIL_NOZONE 0x0020 /* No Zone bit set */
|
||||||
|
#define DNSSEC_FAIL_NONSEC 0x0040 /* No NSEC */
|
||||||
|
#define DNSSEC_FAIL_NODSSUP 0x0080 /* no supported DS algo. */
|
||||||
|
#define DNSSEC_FAIL_NOKEY 0x0100 /* no DNSKEY */
|
||||||
|
|
||||||
|
#define STAT_ISEQUAL(a, b) (((a) & 0xffff0000) == (b))
|
||||||
|
|
||||||
#define FREC_NOREBIND 1
|
#define FREC_NOREBIND 1
|
||||||
#define FREC_CHECKING_DISABLED 2
|
#define FREC_CHECKING_DISABLED 2
|
||||||
@@ -939,10 +966,10 @@ struct dhcp_bridge {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct cond_domain {
|
struct cond_domain {
|
||||||
char *domain, *prefix;
|
char *domain, *prefix; /* prefix is text-prefix on domain name */
|
||||||
struct in_addr start, end;
|
struct in_addr start, end;
|
||||||
struct in6_addr start6, end6;
|
struct in6_addr start6, end6;
|
||||||
int is6, indexed;
|
int is6, indexed, prefixlen;
|
||||||
struct cond_domain *next;
|
struct cond_domain *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1083,9 +1110,13 @@ extern struct daemon {
|
|||||||
char *lease_change_command;
|
char *lease_change_command;
|
||||||
struct iname *if_names, *if_addrs, *if_except, *dhcp_except, *auth_peers, *tftp_interfaces;
|
struct iname *if_names, *if_addrs, *if_except, *dhcp_except, *auth_peers, *tftp_interfaces;
|
||||||
struct bogus_addr *bogus_addr, *ignore_addr;
|
struct bogus_addr *bogus_addr, *ignore_addr;
|
||||||
struct server *servers, *local_domains, **serverarray, *no_rebind;
|
struct server *servers, *servers_tail, *local_domains, **serverarray;
|
||||||
int serverarraysz;
|
struct rebind_domain *no_rebind;
|
||||||
struct ipsets *ipsets;
|
int server_has_wildcard;
|
||||||
|
int serverarraysz, serverarrayhwm;
|
||||||
|
struct ipsets *ipsets, *nftsets;
|
||||||
|
u32 allowlist_mask;
|
||||||
|
struct allowlist *allowlists;
|
||||||
int log_fac; /* log facility */
|
int log_fac; /* log facility */
|
||||||
char *log_file; /* optional log file */
|
char *log_file; /* optional log file */
|
||||||
int max_logs; /* queue limit */
|
int max_logs; /* queue limit */
|
||||||
@@ -1220,9 +1251,8 @@ extern struct daemon {
|
|||||||
/* cache.c */
|
/* cache.c */
|
||||||
void cache_init(void);
|
void cache_init(void);
|
||||||
void next_uid(struct crec *crecp);
|
void next_uid(struct crec *crecp);
|
||||||
void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg);
|
void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg, unsigned short type);
|
||||||
char *record_source(unsigned int index);
|
char *record_source(unsigned int index);
|
||||||
char *querystr(char *desc, unsigned short type);
|
|
||||||
int cache_find_non_terminal(char *name, time_t now);
|
int cache_find_non_terminal(char *name, time_t now);
|
||||||
struct crec *cache_find_by_addr(struct crec *crecp,
|
struct crec *cache_find_by_addr(struct crec *crecp,
|
||||||
union all_addr *addr, time_t now,
|
union all_addr *addr, time_t now,
|
||||||
@@ -1271,10 +1301,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 char *skip_section(unsigned char *ansp, int count, struct dns_header *header, size_t plen);
|
||||||
unsigned int extract_request(struct dns_header *header, size_t qlen,
|
unsigned int extract_request(struct dns_header *header, size_t qlen,
|
||||||
char *name, unsigned short *typep);
|
char *name, unsigned short *typep);
|
||||||
void setup_reply(struct dns_header *header, unsigned int flags);
|
void setup_reply(struct dns_header *header, unsigned int flags, int ede);
|
||||||
int extract_addresses(struct dns_header *header, size_t qlen, char *name,
|
int extract_addresses(struct dns_header *header, size_t qlen, char *name,
|
||||||
time_t now, char **ipsets, int is_sign, int check_rebind,
|
time_t now, struct ipsets *ipsets, struct ipsets *nftsets, int is_sign,
|
||||||
int no_cache_dnssec, int secure, int *doctored);
|
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,
|
size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||||
struct in_addr local_addr, struct in_addr local_netmask,
|
struct in_addr local_addr, struct in_addr local_netmask,
|
||||||
time_t now, int ad_reqd, int do_bit, int have_pseudoheader);
|
time_t now, int ad_reqd, int do_bit, int have_pseudoheader);
|
||||||
@@ -1299,6 +1332,7 @@ int in_zone(struct auth_zone *zone, char *name, char **cut);
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* dnssec.c */
|
/* dnssec.c */
|
||||||
|
#ifdef HAVE_DNSSEC
|
||||||
size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char *name, int class, int type, int edns_pktsz);
|
size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char *name, int class, int type, int edns_pktsz);
|
||||||
int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class);
|
int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class);
|
||||||
int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class);
|
int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class);
|
||||||
@@ -1307,6 +1341,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);
|
int dnskey_keytag(int alg, int flags, unsigned char *key, int keylen);
|
||||||
size_t filter_rrsigs(struct dns_header *header, size_t plen);
|
size_t filter_rrsigs(struct dns_header *header, size_t plen);
|
||||||
int setup_timestamp(void);
|
int setup_timestamp(void);
|
||||||
|
int errflags_to_ede(int status);
|
||||||
|
#endif
|
||||||
|
|
||||||
/* hash_questions.c */
|
/* hash_questions.c */
|
||||||
void hash_questions_init(void);
|
void hash_questions_init(void);
|
||||||
@@ -1340,6 +1376,7 @@ int hostname_issubdomain(char *a, char *b);
|
|||||||
time_t dnsmasq_time(void);
|
time_t dnsmasq_time(void);
|
||||||
int netmask_length(struct in_addr mask);
|
int netmask_length(struct in_addr mask);
|
||||||
int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask);
|
int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask);
|
||||||
|
int is_same_net_prefix(struct in_addr a, struct in_addr b, int prefix);
|
||||||
int is_same_net6(struct in6_addr *a, struct in6_addr *b, int prefixlen);
|
int is_same_net6(struct in6_addr *a, struct in6_addr *b, int prefixlen);
|
||||||
u64 addr6part(struct in6_addr *addr);
|
u64 addr6part(struct in6_addr *addr);
|
||||||
void setaddr6part(struct in6_addr *addr, u64 host);
|
void setaddr6part(struct in6_addr *addr, u64 host);
|
||||||
@@ -1381,7 +1418,7 @@ void set_option_bool(unsigned int opt);
|
|||||||
void reset_option_bool(unsigned int opt);
|
void reset_option_bool(unsigned int opt);
|
||||||
struct hostsfile *expand_filelist(struct hostsfile *list);
|
struct hostsfile *expand_filelist(struct hostsfile *list);
|
||||||
char *parse_server(char *arg, union mysockaddr *addr,
|
char *parse_server(char *arg, union mysockaddr *addr,
|
||||||
union mysockaddr *source_addr, char *interface, int *flags);
|
union mysockaddr *source_addr, char *interface, u16 *flags);
|
||||||
int option_read_dynfile(char *file, int flags);
|
int option_read_dynfile(char *file, int flags);
|
||||||
|
|
||||||
/* forward.c */
|
/* forward.c */
|
||||||
@@ -1402,14 +1439,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);
|
int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifindex, int is_tcp);
|
||||||
void pre_allocate_sfds(void);
|
void pre_allocate_sfds(void);
|
||||||
int reload_servers(char *fname);
|
int reload_servers(char *fname);
|
||||||
void mark_servers(int flag);
|
void check_servers(int no_loop_call);
|
||||||
void cleanup_servers(void);
|
|
||||||
void add_update_server(int flags,
|
|
||||||
union mysockaddr *addr,
|
|
||||||
union mysockaddr *source_addr,
|
|
||||||
const char *interface,
|
|
||||||
const char *domain);
|
|
||||||
void check_servers(void);
|
|
||||||
int enumerate_interfaces(int reset);
|
int enumerate_interfaces(int reset);
|
||||||
void create_wildcard_listeners(void);
|
void create_wildcard_listeners(void);
|
||||||
void create_bound_listeners(int dienow);
|
void create_bound_listeners(int dienow);
|
||||||
@@ -1546,6 +1576,10 @@ char *ubus_init(void);
|
|||||||
void set_ubus_listeners(void);
|
void set_ubus_listeners(void);
|
||||||
void check_ubus_listeners(void);
|
void check_ubus_listeners(void);
|
||||||
void ubus_event_bcast(const char *type, const char *mac, const char *ip, const char *name, const char *interface);
|
void ubus_event_bcast(const char *type, const char *mac, const char *ip, const char *name, const char *interface);
|
||||||
|
# ifdef HAVE_CONNTRACK
|
||||||
|
void ubus_event_bcast_connmark_allowlist_refused(u32 mark, const char *name);
|
||||||
|
void ubus_event_bcast_connmark_allowlist_resolved(u32 mark, const char *pattern, const char *ip, u32 ttl);
|
||||||
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* ipset.c */
|
/* ipset.c */
|
||||||
@@ -1554,6 +1588,19 @@ void ipset_init(void);
|
|||||||
int add_to_ipset(const char *setname, const union all_addr *ipaddr, int flags, int remove);
|
int add_to_ipset(const char *setname, const union all_addr *ipaddr, int flags, int remove);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* nftset.c */
|
||||||
|
#ifdef HAVE_NFTSET
|
||||||
|
void nftset_init(void);
|
||||||
|
int add_to_nftset(const char *setpath, const union all_addr *ipaddr, int flags, int remove);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* pattern.c */
|
||||||
|
#ifdef HAVE_CONNTRACK
|
||||||
|
int is_valid_dns_name(const char *value);
|
||||||
|
int is_valid_dns_name_pattern(const char *value);
|
||||||
|
int is_dns_name_matching_pattern(const char *name, const char *pattern);
|
||||||
|
#endif
|
||||||
|
|
||||||
/* helper.c */
|
/* helper.c */
|
||||||
#if defined(HAVE_SCRIPT)
|
#if defined(HAVE_SCRIPT)
|
||||||
int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd);
|
int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd);
|
||||||
@@ -1726,9 +1773,16 @@ int lookup_domain(char *qdomain, int flags, int *lowout, int *highout);
|
|||||||
int filter_servers(int seed, 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);
|
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,
|
size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header *header,
|
||||||
char *name, int first, int last);
|
char *name, char *limit, int first, int last, int ede);
|
||||||
int server_samegroup(struct server *a, struct server *b);
|
int server_samegroup(struct server *a, struct server *b);
|
||||||
#ifdef HAVE_DNSSEC
|
#ifdef HAVE_DNSSEC
|
||||||
int dnssec_server(struct server *server, char *keyname, int *firstp, int *lastp);
|
int dnssec_server(struct server *server, char *keyname, int *firstp, int *lastp);
|
||||||
#endif
|
#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);
|
||||||
+121
-52
@@ -215,14 +215,6 @@ static int is_check_date(unsigned long curtime)
|
|||||||
return !daemon->dnssec_no_time_check;
|
return !daemon->dnssec_no_time_check;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check whether today/now is between date_start and date_end */
|
|
||||||
static int check_date_range(unsigned long curtime, u32 date_start, u32 date_end)
|
|
||||||
{
|
|
||||||
/* We must explicitly check against wanted values, because of SERIAL_UNDEF */
|
|
||||||
return serial_compare_32(curtime, date_start) == SERIAL_GT
|
|
||||||
&& serial_compare_32(curtime, date_end) == SERIAL_LT;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return bytes of canonicalised rrdata one by one.
|
/* Return bytes of canonicalised rrdata one by one.
|
||||||
Init state->ip with the RR, and state->end with the end of same.
|
Init state->ip with the RR, and state->end with the end of same.
|
||||||
Init state->op to NULL.
|
Init state->op to NULL.
|
||||||
@@ -534,6 +526,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
|
|||||||
struct crec *crecp = NULL;
|
struct crec *crecp = NULL;
|
||||||
u16 *rr_desc = rrfilter_desc(type);
|
u16 *rr_desc = rrfilter_desc(type);
|
||||||
u32 sig_expiration, sig_inception;
|
u32 sig_expiration, sig_inception;
|
||||||
|
int failflags = DNSSEC_FAIL_NOSIG | DNSSEC_FAIL_NYV | DNSSEC_FAIL_EXP | DNSSEC_FAIL_NOKEYSUP;
|
||||||
|
|
||||||
unsigned long curtime = time(0);
|
unsigned long curtime = time(0);
|
||||||
int time_check = is_check_date(curtime);
|
int time_check = is_check_date(curtime);
|
||||||
@@ -558,6 +551,8 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
|
|||||||
char *name_start;
|
char *name_start;
|
||||||
u32 nsigttl, ttl, orig_ttl;
|
u32 nsigttl, ttl, orig_ttl;
|
||||||
|
|
||||||
|
failflags &= ~DNSSEC_FAIL_NOSIG;
|
||||||
|
|
||||||
p = sigs[j];
|
p = sigs[j];
|
||||||
GETLONG(ttl, p);
|
GETLONG(ttl, p);
|
||||||
GETSHORT(rdlen, p); /* rdlen >= 18 checked previously */
|
GETSHORT(rdlen, p); /* rdlen >= 18 checked previously */
|
||||||
@@ -574,9 +569,28 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
|
|||||||
if (!extract_name(header, plen, &p, keyname, 1, 0))
|
if (!extract_name(header, plen, &p, keyname, 1, 0))
|
||||||
return STAT_BOGUS;
|
return STAT_BOGUS;
|
||||||
|
|
||||||
if ((time_check && !check_date_range(curtime, sig_inception, sig_expiration)) ||
|
if (!time_check)
|
||||||
labels > name_labels ||
|
failflags &= ~(DNSSEC_FAIL_NYV | DNSSEC_FAIL_EXP);
|
||||||
!(hash = hash_find(algo_digest_name(algo))) ||
|
else
|
||||||
|
{
|
||||||
|
/* We must explicitly check against wanted values, because of SERIAL_UNDEF */
|
||||||
|
if (serial_compare_32(curtime, sig_inception) == SERIAL_LT)
|
||||||
|
continue;
|
||||||
|
else
|
||||||
|
failflags &= ~DNSSEC_FAIL_NYV;
|
||||||
|
|
||||||
|
if (serial_compare_32(curtime, sig_expiration) == SERIAL_GT)
|
||||||
|
continue;
|
||||||
|
else
|
||||||
|
failflags &= ~DNSSEC_FAIL_EXP;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(hash = hash_find(algo_digest_name(algo))))
|
||||||
|
continue;
|
||||||
|
else
|
||||||
|
failflags &= ~DNSSEC_FAIL_NOKEYSUP;
|
||||||
|
|
||||||
|
if (labels > name_labels ||
|
||||||
!hash_init(hash, &ctx, &digest))
|
!hash_init(hash, &ctx, &digest))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -710,7 +724,8 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
|
|||||||
|
|
||||||
/* namebuff used for workspace above, restore to leave unchanged on exit */
|
/* namebuff used for workspace above, restore to leave unchanged on exit */
|
||||||
p = (unsigned char*)(rrset[0]);
|
p = (unsigned char*)(rrset[0]);
|
||||||
extract_name(header, plen, &p, name, 1, 0);
|
if (!extract_name(header, plen, &p, name, 1, 0))
|
||||||
|
return STAT_BOGUS;
|
||||||
|
|
||||||
if (key)
|
if (key)
|
||||||
{
|
{
|
||||||
@@ -730,7 +745,8 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return STAT_BOGUS;
|
/* If we reach this point, no verifying key was found */
|
||||||
|
return STAT_BOGUS | failflags | DNSSEC_FAIL_NOKEY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -751,17 +767,18 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
|
|||||||
unsigned long ttl, sig_ttl;
|
unsigned long ttl, sig_ttl;
|
||||||
struct blockdata *key;
|
struct blockdata *key;
|
||||||
union all_addr a;
|
union all_addr a;
|
||||||
|
int failflags = DNSSEC_FAIL_NOSIG | DNSSEC_FAIL_NODSSUP | DNSSEC_FAIL_NOZONE | DNSSEC_FAIL_NOKEY;
|
||||||
|
|
||||||
if (ntohs(header->qdcount) != 1 ||
|
if (ntohs(header->qdcount) != 1 ||
|
||||||
RCODE(header) == SERVFAIL || RCODE(header) == REFUSED ||
|
RCODE(header) == SERVFAIL || RCODE(header) == REFUSED ||
|
||||||
!extract_name(header, plen, &p, name, 1, 4))
|
!extract_name(header, plen, &p, name, 1, 4))
|
||||||
return STAT_BOGUS;
|
return STAT_BOGUS | DNSSEC_FAIL_NOKEY;
|
||||||
|
|
||||||
GETSHORT(qtype, p);
|
GETSHORT(qtype, p);
|
||||||
GETSHORT(qclass, p);
|
GETSHORT(qclass, p);
|
||||||
|
|
||||||
if (qtype != T_DNSKEY || qclass != class || ntohs(header->ancount) == 0)
|
if (qtype != T_DNSKEY || qclass != class || ntohs(header->ancount) == 0)
|
||||||
return STAT_BOGUS;
|
return STAT_BOGUS | DNSSEC_FAIL_NOKEY;
|
||||||
|
|
||||||
/* See if we have cached a DS record which validates this key */
|
/* See if we have cached a DS record which validates this key */
|
||||||
if (!(crecp = cache_find_by_name(NULL, name, now, F_DS)))
|
if (!(crecp = cache_find_by_name(NULL, name, now, F_DS)))
|
||||||
@@ -795,14 +812,17 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
|
|||||||
|
|
||||||
GETSHORT(flags, p);
|
GETSHORT(flags, p);
|
||||||
if (*p++ != 3)
|
if (*p++ != 3)
|
||||||
return STAT_BOGUS;
|
return STAT_BOGUS | DNSSEC_FAIL_NOKEY;
|
||||||
algo = *p++;
|
algo = *p++;
|
||||||
keytag = dnskey_keytag(algo, flags, p, rdlen - 4);
|
keytag = dnskey_keytag(algo, flags, p, rdlen - 4);
|
||||||
key = NULL;
|
key = NULL;
|
||||||
|
|
||||||
/* key must have zone key flag set */
|
/* key must have zone key flag set */
|
||||||
if (flags & 0x100)
|
if (flags & 0x100)
|
||||||
|
{
|
||||||
key = blockdata_alloc((char*)p, rdlen - 4);
|
key = blockdata_alloc((char*)p, rdlen - 4);
|
||||||
|
failflags &= ~DNSSEC_FAIL_NOZONE;
|
||||||
|
}
|
||||||
|
|
||||||
p = psave;
|
p = psave;
|
||||||
|
|
||||||
@@ -823,15 +843,23 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
|
|||||||
unsigned char *digest, *ds_digest;
|
unsigned char *digest, *ds_digest;
|
||||||
const struct nettle_hash *hash;
|
const struct nettle_hash *hash;
|
||||||
int sigcnt, rrcnt;
|
int sigcnt, rrcnt;
|
||||||
|
int wire_len;
|
||||||
|
|
||||||
if (recp1->addr.ds.algo == algo &&
|
if (recp1->addr.ds.algo == algo &&
|
||||||
recp1->addr.ds.keytag == keytag &&
|
recp1->addr.ds.keytag == keytag &&
|
||||||
recp1->uid == (unsigned int)class &&
|
recp1->uid == (unsigned int)class)
|
||||||
(hash = hash_find(ds_digest_name(recp1->addr.ds.digest))) &&
|
|
||||||
hash_init(hash, &ctx, &digest))
|
|
||||||
|
|
||||||
{
|
{
|
||||||
int wire_len = to_wire(name);
|
failflags &= ~DNSSEC_FAIL_NOKEY;
|
||||||
|
|
||||||
|
if (!(hash = hash_find(ds_digest_name(recp1->addr.ds.digest))))
|
||||||
|
continue;
|
||||||
|
else
|
||||||
|
failflags &= ~DNSSEC_FAIL_NODSSUP;
|
||||||
|
|
||||||
|
if (!hash_init(hash, &ctx, &digest))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
wire_len = to_wire(name);
|
||||||
|
|
||||||
/* Note that digest may be different between DSs, so
|
/* Note that digest may be different between DSs, so
|
||||||
we can't move this outside the loop. */
|
we can't move this outside the loop. */
|
||||||
@@ -846,15 +874,26 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
|
|||||||
(ds_digest = blockdata_retrieve(recp1->addr.ds.keydata, recp1->addr.ds.keylen, NULL)) &&
|
(ds_digest = blockdata_retrieve(recp1->addr.ds.keydata, recp1->addr.ds.keylen, NULL)) &&
|
||||||
memcmp(ds_digest, digest, recp1->addr.ds.keylen) == 0 &&
|
memcmp(ds_digest, digest, recp1->addr.ds.keylen) == 0 &&
|
||||||
explore_rrset(header, plen, class, T_DNSKEY, name, keyname, &sigcnt, &rrcnt) &&
|
explore_rrset(header, plen, class, T_DNSKEY, name, keyname, &sigcnt, &rrcnt) &&
|
||||||
sigcnt != 0 && rrcnt != 0 &&
|
rrcnt != 0)
|
||||||
validate_rrset(now, header, plen, class, T_DNSKEY, sigcnt, rrcnt, name, keyname,
|
{
|
||||||
NULL, key, rdlen - 4, algo, keytag, &sig_ttl) == STAT_SECURE)
|
if (sigcnt == 0)
|
||||||
|
continue;
|
||||||
|
else
|
||||||
|
failflags &= ~DNSSEC_FAIL_NOSIG;
|
||||||
|
|
||||||
|
rc = validate_rrset(now, header, plen, class, T_DNSKEY, sigcnt, rrcnt, name, keyname,
|
||||||
|
NULL, key, rdlen - 4, algo, keytag, &sig_ttl);
|
||||||
|
|
||||||
|
failflags &= rc;
|
||||||
|
|
||||||
|
if (STAT_ISEQUAL(rc, STAT_SECURE))
|
||||||
{
|
{
|
||||||
valid = 1;
|
valid = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
blockdata_free(key);
|
blockdata_free(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -916,9 +955,9 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
|
|||||||
a.log.keytag = keytag;
|
a.log.keytag = keytag;
|
||||||
a.log.algo = algo;
|
a.log.algo = algo;
|
||||||
if (algo_digest_name(algo))
|
if (algo_digest_name(algo))
|
||||||
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %hu, algo %hu");
|
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %hu, algo %hu", 0);
|
||||||
else
|
else
|
||||||
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %hu, algo %hu (not supported)");
|
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %hu, algo %hu (not supported)", 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -935,8 +974,8 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
|
|||||||
return STAT_OK;
|
return STAT_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DNSKEY");
|
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DNSKEY", 0);
|
||||||
return STAT_BOGUS;
|
return STAT_BOGUS | failflags;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The DNS packet is expected to contain the answer to a DS query
|
/* The DNS packet is expected to contain the answer to a DS query
|
||||||
@@ -971,26 +1010,29 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
|
|||||||
else
|
else
|
||||||
rc = dnssec_validate_reply(now, header, plen, name, keyname, NULL, 0, &neganswer, &nons, &neg_ttl);
|
rc = dnssec_validate_reply(now, header, plen, name, keyname, NULL, 0, &neganswer, &nons, &neg_ttl);
|
||||||
|
|
||||||
if (rc == STAT_INSECURE)
|
if (STAT_ISEQUAL(rc, STAT_INSECURE))
|
||||||
{
|
{
|
||||||
my_syslog(LOG_WARNING, _("Insecure DS reply received for %s, check domain configuration and upstream DNS server DNSSEC support"), name);
|
my_syslog(LOG_WARNING, _("Insecure DS reply received for %s, check domain configuration and upstream DNS server DNSSEC support"), name);
|
||||||
rc = STAT_BOGUS;
|
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS - not secure", 0);
|
||||||
|
return STAT_BOGUS | DNSSEC_FAIL_INDET;
|
||||||
}
|
}
|
||||||
|
|
||||||
p = (unsigned char *)(header+1);
|
p = (unsigned char *)(header+1);
|
||||||
extract_name(header, plen, &p, name, 1, 4);
|
if (!extract_name(header, plen, &p, name, 1, 4))
|
||||||
|
return STAT_BOGUS;
|
||||||
|
|
||||||
p += 4; /* qtype, qclass */
|
p += 4; /* qtype, qclass */
|
||||||
|
|
||||||
/* If the key needed to validate the DS is on the same domain as the DS, we'll
|
/* If the key needed to validate the DS is on the same domain as the DS, we'll
|
||||||
loop getting nowhere. Stop that now. This can happen of the DS answer comes
|
loop getting nowhere. Stop that now. This can happen of the DS answer comes
|
||||||
from the DS's zone, and not the parent zone. */
|
from the DS's zone, and not the parent zone. */
|
||||||
if (rc == STAT_BOGUS || (rc == STAT_NEED_KEY && hostname_isequal(name, keyname)))
|
if (STAT_ISEQUAL(rc, STAT_NEED_KEY) && hostname_isequal(name, keyname))
|
||||||
{
|
{
|
||||||
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS");
|
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS", 0);
|
||||||
return STAT_BOGUS;
|
return STAT_BOGUS;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rc != STAT_SECURE)
|
if (!STAT_ISEQUAL(rc, STAT_SECURE))
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
if (!neganswer)
|
if (!neganswer)
|
||||||
@@ -1042,9 +1084,9 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
|
|||||||
a.log.algo = algo;
|
a.log.algo = algo;
|
||||||
a.log.digest = digest;
|
a.log.digest = digest;
|
||||||
if (ds_digest_name(digest) && algo_digest_name(algo))
|
if (ds_digest_name(digest) && algo_digest_name(algo))
|
||||||
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %hu, algo %hu, digest %hu");
|
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %hu, algo %hu, digest %hu", 0);
|
||||||
else
|
else
|
||||||
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %hu, algo %hu, digest %hu (not supported)");
|
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %hu, algo %hu, digest %hu (not supported)", 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1077,7 +1119,7 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
|
|||||||
|
|
||||||
cache_end_insert();
|
cache_end_insert();
|
||||||
|
|
||||||
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, nons ? "no DS/cut" : "no DS");
|
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, nons ? "no DS/cut" : "no DS", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return STAT_OK;
|
return STAT_OK;
|
||||||
@@ -1854,7 +1896,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
|||||||
|
|
||||||
/* Find all the targets we're looking for answers to.
|
/* Find all the targets we're looking for answers to.
|
||||||
The zeroth array element is for the query, subsequent ones
|
The zeroth array element is for the query, subsequent ones
|
||||||
for CNAME targets, unless the query is for a CNAME. */
|
for CNAME targets, unless the query is for a CNAME or ANY. */
|
||||||
|
|
||||||
if (!expand_workspace(&targets, &target_sz, 0))
|
if (!expand_workspace(&targets, &target_sz, 0))
|
||||||
return STAT_BOGUS;
|
return STAT_BOGUS;
|
||||||
@@ -1873,7 +1915,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
|||||||
if (qtype == T_RRSIG)
|
if (qtype == T_RRSIG)
|
||||||
return STAT_INSECURE;
|
return STAT_INSECURE;
|
||||||
|
|
||||||
if (qtype != T_CNAME)
|
if (qtype != T_CNAME && qtype != T_ANY)
|
||||||
for (j = ntohs(header->ancount); j != 0; j--)
|
for (j = ntohs(header->ancount); j != 0; j--)
|
||||||
{
|
{
|
||||||
if (!(p1 = skip_name(p1, header, plen, 10)))
|
if (!(p1 = skip_name(p1, header, plen, 10)))
|
||||||
@@ -1959,15 +2001,15 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
|||||||
if (check_unsigned && i < ntohs(header->ancount))
|
if (check_unsigned && i < ntohs(header->ancount))
|
||||||
{
|
{
|
||||||
rc = zone_status(name, class1, keyname, now);
|
rc = zone_status(name, class1, keyname, now);
|
||||||
if (rc == STAT_SECURE)
|
if (STAT_ISEQUAL(rc, STAT_SECURE))
|
||||||
rc = STAT_BOGUS;
|
rc = STAT_BOGUS | DNSSEC_FAIL_NOSIG;
|
||||||
if (class)
|
if (class)
|
||||||
*class = class1; /* Class for NEED_DS or NEED_KEY */
|
*class = class1; /* Class for NEED_DS or NEED_KEY */
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
rc = STAT_INSECURE;
|
rc = STAT_INSECURE;
|
||||||
|
|
||||||
if (rc != STAT_INSECURE)
|
if (!STAT_ISEQUAL(rc, STAT_INSECURE))
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1978,7 +2020,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
|||||||
strcpy(daemon->workspacename, keyname);
|
strcpy(daemon->workspacename, keyname);
|
||||||
rc = zone_status(daemon->workspacename, class1, keyname, now);
|
rc = zone_status(daemon->workspacename, class1, keyname, now);
|
||||||
|
|
||||||
if (rc == STAT_BOGUS || rc == STAT_NEED_KEY || rc == STAT_NEED_DS)
|
if (STAT_ISEQUAL(rc, STAT_BOGUS) || STAT_ISEQUAL(rc, STAT_NEED_KEY) || STAT_ISEQUAL(rc, STAT_NEED_DS))
|
||||||
{
|
{
|
||||||
if (class)
|
if (class)
|
||||||
*class = class1; /* Class for NEED_DS or NEED_KEY */
|
*class = class1; /* Class for NEED_DS or NEED_KEY */
|
||||||
@@ -1986,13 +2028,13 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Zone is insecure, don't need to validate RRset */
|
/* Zone is insecure, don't need to validate RRset */
|
||||||
if (rc == STAT_SECURE)
|
if (STAT_ISEQUAL(rc, STAT_SECURE))
|
||||||
{
|
{
|
||||||
unsigned long sig_ttl;
|
unsigned long sig_ttl;
|
||||||
rc = validate_rrset(now, header, plen, class1, type1, sigcnt,
|
rc = validate_rrset(now, header, plen, class1, type1, sigcnt,
|
||||||
rrcnt, name, keyname, &wildname, NULL, 0, 0, 0, &sig_ttl);
|
rrcnt, name, keyname, &wildname, NULL, 0, 0, 0, &sig_ttl);
|
||||||
|
|
||||||
if (rc == STAT_BOGUS || rc == STAT_NEED_KEY || rc == STAT_NEED_DS)
|
if (STAT_ISEQUAL(rc, STAT_BOGUS) || STAT_ISEQUAL(rc, STAT_NEED_KEY) || STAT_ISEQUAL(rc, STAT_NEED_DS))
|
||||||
{
|
{
|
||||||
if (class)
|
if (class)
|
||||||
*class = class1; /* Class for DS or DNSKEY */
|
*class = class1; /* Class for DS or DNSKEY */
|
||||||
@@ -2025,21 +2067,21 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
|||||||
Note that we may not yet have validated the NSEC/NSEC3 RRsets.
|
Note that we may not yet have validated the NSEC/NSEC3 RRsets.
|
||||||
That's not a problem since if the RRsets later fail
|
That's not a problem since if the RRsets later fail
|
||||||
we'll return BOGUS then. */
|
we'll return BOGUS then. */
|
||||||
if (rc == STAT_SECURE_WILDCARD &&
|
if (STAT_ISEQUAL(rc, STAT_SECURE_WILDCARD) &&
|
||||||
!prove_non_existence(header, plen, keyname, name, type1, class1, wildname, NULL, NULL))
|
!prove_non_existence(header, plen, keyname, name, type1, class1, wildname, NULL, NULL))
|
||||||
return STAT_BOGUS;
|
return STAT_BOGUS | DNSSEC_FAIL_NONSEC;
|
||||||
|
|
||||||
rc = STAT_SECURE;
|
rc = STAT_SECURE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rc == STAT_INSECURE)
|
if (STAT_ISEQUAL(rc, STAT_INSECURE))
|
||||||
secure = STAT_INSECURE;
|
secure = STAT_INSECURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* OK, all the RRsets validate, now see if we have a missing answer or CNAME target. */
|
/* OK, all the RRsets validate, now see if we have a missing answer or CNAME target. */
|
||||||
if (secure == STAT_SECURE)
|
if (STAT_ISEQUAL(secure, STAT_SECURE))
|
||||||
for (j = 0; j <targetidx; j++)
|
for (j = 0; j <targetidx; j++)
|
||||||
if ((p2 = targets[j]))
|
if ((p2 = targets[j]))
|
||||||
{
|
{
|
||||||
@@ -2057,16 +2099,16 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
|||||||
{
|
{
|
||||||
/* Empty DS without NSECS */
|
/* Empty DS without NSECS */
|
||||||
if (qtype == T_DS)
|
if (qtype == T_DS)
|
||||||
return STAT_BOGUS;
|
return STAT_BOGUS | DNSSEC_FAIL_NONSEC;
|
||||||
|
|
||||||
if ((rc = zone_status(name, qclass, keyname, now)) != STAT_SECURE)
|
if (STAT_ISEQUAL((rc = zone_status(name, qclass, keyname, now)), STAT_SECURE))
|
||||||
{
|
{
|
||||||
if (class)
|
if (class)
|
||||||
*class = qclass; /* Class for NEED_DS or NEED_KEY */
|
*class = qclass; /* Class for NEED_DS or NEED_KEY */
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
return STAT_BOGUS; /* signed zone, no NSECs */
|
return STAT_BOGUS | DNSSEC_FAIL_NONSEC; /* signed zone, no NSECs */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2130,4 +2172,31 @@ size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int errflags_to_ede(int status)
|
||||||
|
{
|
||||||
|
/* We can end up with more than one flag set for some errors,
|
||||||
|
so this encodes a rough priority so the (eg) No sig is reported
|
||||||
|
before no-unexpired-sig. */
|
||||||
|
|
||||||
|
if (status & DNSSEC_FAIL_NYV)
|
||||||
|
return EDE_SIG_NYV;
|
||||||
|
else if (status & DNSSEC_FAIL_EXP)
|
||||||
|
return EDE_SIG_EXP;
|
||||||
|
else if (status & DNSSEC_FAIL_NOKEYSUP)
|
||||||
|
return EDE_USUPDNSKEY;
|
||||||
|
else if (status & DNSSEC_FAIL_NOZONE)
|
||||||
|
return EDE_NO_ZONEKEY;
|
||||||
|
else if (status & DNSSEC_FAIL_NOKEY)
|
||||||
|
return EDE_NO_DNSKEY;
|
||||||
|
else if (status & DNSSEC_FAIL_NODSSUP)
|
||||||
|
return EDE_USUPDS;
|
||||||
|
else if (status & DNSSEC_FAIL_NONSEC)
|
||||||
|
return EDE_NO_NSEC;
|
||||||
|
else if (status & DNSSEC_FAIL_INDET)
|
||||||
|
return EDE_DNSSEC_IND;
|
||||||
|
else if (status & DNSSEC_FAIL_NOSIG)
|
||||||
|
return EDE_NO_RRSIG;
|
||||||
|
else
|
||||||
|
return EDE_UNSET;
|
||||||
|
}
|
||||||
#endif /* HAVE_DNSSEC */
|
#endif /* HAVE_DNSSEC */
|
||||||
+348
-76
@@ -16,42 +16,64 @@
|
|||||||
|
|
||||||
#include "dnsmasq.h"
|
#include "dnsmasq.h"
|
||||||
|
|
||||||
static int order(char *qdomain, int leading_dot, size_t qlen, struct server *serv);
|
static int order(char *qdomain, size_t qlen, struct server *serv);
|
||||||
static int order_qsort(const void *a, const void *b);
|
static int order_qsort(const void *a, const void *b);
|
||||||
static int order_servers(struct server *s, struct server *s2);
|
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)
|
void build_server_array(void)
|
||||||
{
|
{
|
||||||
struct server *serv;
|
struct server *serv;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|
||||||
for (serv = daemon->servers; serv; serv = serv->next)
|
for (serv = daemon->servers; serv; serv = serv->next)
|
||||||
|
#ifdef HAVE_LOOP
|
||||||
|
if (!(serv->flags & SERV_LOOP))
|
||||||
|
#endif
|
||||||
|
{
|
||||||
count++;
|
count++;
|
||||||
|
if (serv->flags & SERV_WILDCARD)
|
||||||
|
daemon->server_has_wildcard = 1;
|
||||||
|
}
|
||||||
|
|
||||||
for (serv = daemon->local_domains; serv; serv = serv->next)
|
for (serv = daemon->local_domains; serv; serv = serv->next)
|
||||||
|
{
|
||||||
count++;
|
count++;
|
||||||
|
if (serv->flags & SERV_WILDCARD)
|
||||||
|
daemon->server_has_wildcard = 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (count > daemon->serverarraysz)
|
daemon->serverarraysz = count;
|
||||||
|
|
||||||
|
if (count > daemon->serverarrayhwm)
|
||||||
{
|
{
|
||||||
struct server **new;
|
struct server **new;
|
||||||
|
|
||||||
|
count += 10; /* A few extra without re-allocating. */
|
||||||
|
|
||||||
if ((new = whine_malloc(count * sizeof(struct server *))))
|
if ((new = whine_malloc(count * sizeof(struct server *))))
|
||||||
{
|
{
|
||||||
if (daemon->serverarray)
|
if (daemon->serverarray)
|
||||||
free(daemon->serverarray);
|
free(daemon->serverarray);
|
||||||
|
|
||||||
daemon->serverarray = new;
|
daemon->serverarray = new;
|
||||||
daemon->serverarraysz = count;
|
daemon->serverarrayhwm = count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
count = 0;
|
count = 0;
|
||||||
|
|
||||||
for (serv = daemon->servers; serv; serv = serv->next, count++)
|
for (serv = daemon->servers; serv; serv = serv->next)
|
||||||
|
#ifdef HAVE_LOOP
|
||||||
|
if (!(serv->flags & SERV_LOOP))
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
daemon->serverarray[count] = serv;
|
daemon->serverarray[count] = serv;
|
||||||
serv->serial = count;
|
serv->serial = count;
|
||||||
serv->last_server = -1;
|
serv->last_server = -1;
|
||||||
|
count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (serv = daemon->local_domains; serv; serv = serv->next, count++)
|
for (serv = daemon->local_domains; serv; serv = serv->next, count++)
|
||||||
@@ -62,7 +84,7 @@ void build_server_array(void)
|
|||||||
/* servers need the location in the array to find all the whole
|
/* servers need the location in the array to find all the whole
|
||||||
set of equivalent servers from a pointer to a single one. */
|
set of equivalent servers from a pointer to a single one. */
|
||||||
for (count = 0; count < daemon->serverarraysz; count++)
|
for (count = 0; count < daemon->serverarraysz; count++)
|
||||||
if (!(daemon->serverarray[count]->flags & SERV_LITERAL_ADDRESS))
|
if (!(daemon->serverarray[count]->flags & SERV_IS_LOCAL))
|
||||||
daemon->serverarray[count]->arrayposn = count;
|
daemon->serverarray[count]->arrayposn = count;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,22 +97,22 @@ void build_server_array(void)
|
|||||||
A flag of F_DNSSECOK returns a DNSSEC capable server only and
|
A flag of F_DNSSECOK returns a DNSSEC capable server only and
|
||||||
also disables NODOTS servers from consideration.
|
also disables NODOTS servers from consideration.
|
||||||
A flag of F_DOMAINSRV returns a domain-specific server only.
|
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.
|
return 0 if nothing found, 1 otherwise.
|
||||||
*/
|
*/
|
||||||
int lookup_domain(char *qdomain, int flags, int *lowout, int *highout)
|
int lookup_domain(char *domain, int flags, int *lowout, int *highout)
|
||||||
{
|
{
|
||||||
int rc, nodots, leading_dot = 1;
|
int rc, crop_query, nodots;
|
||||||
ssize_t qlen, maxlen;
|
ssize_t qlen;
|
||||||
int try, high, low = 0;
|
int try, high, low = 0;
|
||||||
int nlow = 0, nhigh = 0;
|
int nlow = 0, nhigh = 0;
|
||||||
char *cp;
|
char *cp, *qdomain = domain;
|
||||||
|
|
||||||
/* may be no configured servers. */
|
/* may be no configured servers. */
|
||||||
if (daemon->serverarraysz == 0)
|
if (daemon->serverarraysz == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
maxlen = strlen(daemon->serverarray[0]->domain);
|
|
||||||
|
|
||||||
/* find query length and presence of '.' */
|
/* find query length and presence of '.' */
|
||||||
for (cp = qdomain, nodots = 1, qlen = 0; *cp; qlen++, cp++)
|
for (cp = qdomain, nodots = 1, qlen = 0; *cp; qlen++, cp++)
|
||||||
if (*cp == '.')
|
if (*cp == '.')
|
||||||
@@ -101,73 +123,116 @@ int lookup_domain(char *qdomain, int flags, int *lowout, int *highout)
|
|||||||
if (qlen == 0 || flags & F_DNSSECOK)
|
if (qlen == 0 || flags & F_DNSSECOK)
|
||||||
nodots = 0;
|
nodots = 0;
|
||||||
|
|
||||||
/* No point trying to match more than the largest server domain */
|
|
||||||
if (qlen > maxlen)
|
|
||||||
{
|
|
||||||
qdomain += qlen - maxlen;
|
|
||||||
qlen = maxlen;
|
|
||||||
leading_dot = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Search shorter and shorter RHS substrings for a match */
|
/* Search shorter and shorter RHS substrings for a match */
|
||||||
while (qlen >= 0)
|
while (qlen >= 0)
|
||||||
{
|
{
|
||||||
/* Note that when we chop off a character, all the possible matches
|
/* 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
|
MUST be at a larger index than the nearest failing match with one more
|
||||||
character, since the array is sorted longest to smallest. Hence
|
character, since the array is sorted longest to smallest. Hence
|
||||||
we don't reset low here. */
|
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;
|
high = daemon->serverarraysz;
|
||||||
|
crop_query = 1;
|
||||||
|
|
||||||
/* binary search */
|
/* binary search */
|
||||||
do
|
while (1)
|
||||||
{
|
{
|
||||||
try = (low + high)/2;
|
try = (low + high)/2;
|
||||||
|
|
||||||
if ((rc = order(qdomain, leading_dot, qlen, daemon->serverarray[try])) == 0)
|
if ((rc = order(qdomain, qlen, daemon->serverarray[try])) == 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
{
|
{
|
||||||
if (high == try)
|
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;
|
break;
|
||||||
|
}
|
||||||
high = try;
|
high = try;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (low == try)
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
low = try;
|
low = try;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
while (low != high);
|
|
||||||
|
|
||||||
if (rc == 0)
|
if (rc == 0)
|
||||||
{
|
{
|
||||||
/* We've matched a setting which says to use servers without a domain.
|
int found = 1;
|
||||||
Continue the search with empty query (the last character gets stripped
|
|
||||||
by the loop. */
|
if (daemon->server_has_wildcard)
|
||||||
if (daemon->serverarray[try]->flags & SERV_USE_RESOLV)
|
|
||||||
{
|
{
|
||||||
qdomain += qlen - 1;
|
/* if we have example.com and *example.com we need to check against *example.com,
|
||||||
qlen = 1;
|
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;
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
{
|
|
||||||
|
if (found && filter_servers(try, flags, &nlow, &nhigh))
|
||||||
/* We have a match, but it may only be (say) an IPv6 address, and
|
/* 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
|
if the query wasn't for an AAAA record, it's no good, and we need
|
||||||
to continue generalising */
|
to continue generalising */
|
||||||
if (filter_servers(try, flags, &nlow, &nhigh))
|
{
|
||||||
|
/* We've matched a setting which says to use servers without a domain.
|
||||||
|
Continue the search with empty query */
|
||||||
|
if (daemon->serverarray[nlow]->flags & SERV_USE_RESOLV)
|
||||||
|
crop_query = qlen;
|
||||||
|
else
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (leading_dot)
|
/* crop_query must be at least one always. */
|
||||||
leading_dot = 0;
|
if (crop_query == 0)
|
||||||
else
|
crop_query = 1;
|
||||||
{
|
|
||||||
qlen--;
|
/* strip chars off the query based on the largest possible remaining match,
|
||||||
qdomain++;
|
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,
|
/* domain has no dots, and we have at least one server configured to handle such,
|
||||||
@@ -175,14 +240,9 @@ int lookup_domain(char *qdomain, int flags, int *lowout, int *highout)
|
|||||||
A configured server eg server=/lan/ will take precdence. */
|
A configured server eg server=/lan/ will take precdence. */
|
||||||
if (nodots &&
|
if (nodots &&
|
||||||
(daemon->serverarray[daemon->serverarraysz-1]->flags & SERV_FOR_NODOTS) &&
|
(daemon->serverarray[daemon->serverarraysz-1]->flags & SERV_FOR_NODOTS) &&
|
||||||
(nlow == nhigh || strlen(daemon->serverarray[nlow]->domain) == 0))
|
(nlow == nhigh || daemon->serverarray[nlow]->domain_len == 0))
|
||||||
filter_servers(daemon->serverarraysz-1, flags, &nlow, &nhigh);
|
filter_servers(daemon->serverarraysz-1, flags, &nlow, &nhigh);
|
||||||
|
|
||||||
/* F_DOMAINSRV returns only domain-specific servers, so if we got to a
|
|
||||||
general server, return empty set. */
|
|
||||||
if (nlow != nhigh && (flags & F_DOMAINSRV) && strlen(daemon->serverarray[nlow]->domain) == 0)
|
|
||||||
nlow = nhigh;
|
|
||||||
|
|
||||||
if (lowout)
|
if (lowout)
|
||||||
*lowout = nlow;
|
*lowout = nlow;
|
||||||
|
|
||||||
@@ -217,8 +277,23 @@ int filter_servers(int seed, int flags, int *lowout, int *highout)
|
|||||||
|
|
||||||
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
|
/* Now the servers are on order between low and high, in the order
|
||||||
return zero for both, IPv6 addr, IPv4 addr, no-data return, send upstream.
|
IPv6 addr, IPv4 addr, return zero for both, resolvconf servers, send upstream, no-data return.
|
||||||
|
|
||||||
See which of those match our query in that priority order and narrow (low, high) */
|
See which of those match our query in that priority order and narrow (low, high) */
|
||||||
|
|
||||||
@@ -246,18 +321,35 @@ int filter_servers(int seed, int flags, int *lowout, int *highout)
|
|||||||
{
|
{
|
||||||
nlow = i;
|
nlow = i;
|
||||||
|
|
||||||
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_LITERAL_ADDRESS); i++);
|
/* Short to resolv.conf servers */
|
||||||
|
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_USE_RESOLV); i++);
|
||||||
|
|
||||||
/* --local=/domain/, only return if we don't need a server. */
|
if (i != nlow)
|
||||||
if (i != nlow && !(flags & (F_DNSSECOK | F_DOMAINSRV | F_SERVER)))
|
|
||||||
nhigh = i;
|
nhigh = i;
|
||||||
else
|
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,
|
/* If we want a server that can do DNSSEC, and this one can't,
|
||||||
return nothing. */
|
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))
|
if ((flags & F_DNSSECOK) && !(daemon->serverarray[nlow]->flags & SERV_DO_DNSSEC))
|
||||||
nlow = nhigh;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -283,13 +375,25 @@ int is_local_answer(time_t now, int first, char *name)
|
|||||||
else if (flags & SERV_ALL_ZEROS)
|
else if (flags & SERV_ALL_ZEROS)
|
||||||
rc = F_IPV4 | F_IPV6;
|
rc = F_IPV4 | F_IPV6;
|
||||||
else
|
else
|
||||||
rc = check_for_local_domain(name, now) ? F_NOERR : F_NXDOMAIN;
|
{
|
||||||
|
/* 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;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header *header, char *name, int first, int last)
|
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;
|
int trunc = 0;
|
||||||
unsigned char *p;
|
unsigned char *p;
|
||||||
@@ -297,9 +401,9 @@ size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header
|
|||||||
union all_addr addr;
|
union all_addr addr;
|
||||||
|
|
||||||
if (flags & (F_NXDOMAIN | F_NOERR))
|
if (flags & (F_NXDOMAIN | F_NOERR))
|
||||||
log_query(flags | gotname | F_NEG | F_CONFIG | F_FORWARD, name, NULL, NULL);
|
log_query(flags | gotname | F_NEG | F_CONFIG | F_FORWARD, name, NULL, NULL, 0);
|
||||||
|
|
||||||
setup_reply(header, flags);
|
setup_reply(header, flags, ede);
|
||||||
|
|
||||||
if (!(p = skip_questions(header, size)))
|
if (!(p = skip_questions(header, size)))
|
||||||
return 0;
|
return 0;
|
||||||
@@ -315,8 +419,9 @@ size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header
|
|||||||
addr.addr4 = srv->addr;
|
addr.addr4 = srv->addr;
|
||||||
|
|
||||||
header->ancount = htons(ntohs(header->ancount) + 1);
|
header->ancount = htons(ntohs(header->ancount) + 1);
|
||||||
add_resource_record(header, ((char *)header) + 65536, &trunc, sizeof(struct dns_header), &p, daemon->local_ttl, NULL, T_A, C_IN, "4", &addr);
|
if (!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);
|
return 0;
|
||||||
|
log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV6, name, (union all_addr *)&addr, NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags & gotname & F_IPV6)
|
if (flags & gotname & F_IPV6)
|
||||||
@@ -330,8 +435,8 @@ size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header
|
|||||||
addr.addr6 = srv->addr;
|
addr.addr6 = srv->addr;
|
||||||
|
|
||||||
header->ancount = htons(ntohs(header->ancount) + 1);
|
header->ancount = htons(ntohs(header->ancount) + 1);
|
||||||
add_resource_record(header, ((char *)header) + 65536, &trunc, sizeof(struct dns_header), &p, daemon->local_ttl, NULL, T_AAAA, C_IN, "6", &addr);
|
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);
|
log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV4, name, (union all_addr *)&addr, NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trunc)
|
if (trunc)
|
||||||
@@ -372,20 +477,16 @@ int dnssec_server(struct server *server, char *keyname, int *firstp, int *lastp)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* order by size, then by dictionary order */
|
/* order by size, then by dictionary order */
|
||||||
static int order(char *qdomain, int leading_dot, size_t qlen, struct server *serv)
|
static int order(char *qdomain, size_t qlen, struct server *serv)
|
||||||
{
|
{
|
||||||
size_t dlen = 0;
|
size_t dlen = 0;
|
||||||
int rc;
|
|
||||||
|
|
||||||
/* servers for dotless names always sort last
|
/* servers for dotless names always sort last
|
||||||
searched for name is never dotless. */
|
searched for name is never dotless. */
|
||||||
if (serv->flags & SERV_FOR_NODOTS)
|
if (serv->flags & SERV_FOR_NODOTS)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (leading_dot)
|
dlen = serv->domain_len;
|
||||||
qlen++;
|
|
||||||
|
|
||||||
dlen = strlen(serv->domain);
|
|
||||||
|
|
||||||
if (qlen < dlen)
|
if (qlen < dlen)
|
||||||
return 1;
|
return 1;
|
||||||
@@ -393,22 +494,27 @@ static int order(char *qdomain, int leading_dot, size_t qlen, struct server *ser
|
|||||||
if (qlen > dlen)
|
if (qlen > dlen)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (leading_dot && (rc = '.' - serv->domain[0]) != 0)
|
return strcmp(qdomain, serv->domain);
|
||||||
return rc;
|
|
||||||
|
|
||||||
return strcmp(qdomain, leading_dot ? &serv->domain[1] : serv->domain);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int order_servers(struct server *s1, struct server *s2)
|
static int order_servers(struct server *s1, struct server *s2)
|
||||||
{
|
{
|
||||||
size_t dlen = strlen(s1->domain);
|
int rc;
|
||||||
|
|
||||||
/* need full comparison of dotless servers in
|
/* need full comparison of dotless servers in
|
||||||
order_qsort() and filter_servers() */
|
order_qsort() and filter_servers() */
|
||||||
|
|
||||||
if (s1->flags & SERV_FOR_NODOTS)
|
if (s1->flags & SERV_FOR_NODOTS)
|
||||||
return (s2->flags & SERV_FOR_NODOTS) ? 0 : 1;
|
return (s2->flags & SERV_FOR_NODOTS) ? 0 : 1;
|
||||||
|
|
||||||
return order(s1->domain, 0, dlen, s2);
|
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)
|
static int order_qsort(const void *a, const void *b)
|
||||||
@@ -421,10 +527,12 @@ static int order_qsort(const void *a, const void *b)
|
|||||||
rc = order_servers(s1, s2);
|
rc = order_servers(s1, s2);
|
||||||
|
|
||||||
/* Sort all literal NODATA and local IPV4 or IPV6 responses together,
|
/* Sort all literal NODATA and local IPV4 or IPV6 responses together,
|
||||||
in a very specific order. */
|
in a very specific order. We flip the SERV_LITERAL_ADDRESS bit
|
||||||
|
so the order is IPv6 literal, IPv4 literal, all-zero literal,
|
||||||
|
unqualified servers, upstream server, NXDOMAIN literal. */
|
||||||
if (rc == 0)
|
if (rc == 0)
|
||||||
rc = (s2->flags & (SERV_LITERAL_ADDRESS | SERV_4ADDR | SERV_6ADDR | SERV_ALL_ZEROS)) -
|
rc = ((s2->flags & (SERV_LITERAL_ADDRESS | SERV_4ADDR | SERV_6ADDR | SERV_USE_RESOLV | SERV_ALL_ZEROS)) ^ SERV_LITERAL_ADDRESS) -
|
||||||
(s1->flags & (SERV_LITERAL_ADDRESS | SERV_4ADDR | SERV_6ADDR | SERV_ALL_ZEROS));
|
((s1->flags & (SERV_LITERAL_ADDRESS | SERV_4ADDR | SERV_6ADDR | SERV_USE_RESOLV | SERV_ALL_ZEROS)) ^ SERV_LITERAL_ADDRESS);
|
||||||
|
|
||||||
/* Finally, order by appearance in /etc/resolv.conf etc, for --strict-order */
|
/* Finally, order by appearance in /etc/resolv.conf etc, for --strict-order */
|
||||||
if (rc == 0)
|
if (rc == 0)
|
||||||
@@ -433,3 +541,167 @@ static int order_qsort(const void *a, const void *b)
|
|||||||
|
|
||||||
return rc;
|
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;
|
||||||
|
daemon->servers_tail = serv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
alloc_domain = canonicalise((char *)domain, NULL);
|
||||||
|
|
||||||
|
if (!alloc_domain)
|
||||||
|
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_IS_LOCAL)
|
||||||
|
{
|
||||||
|
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)))
|
||||||
|
{
|
||||||
|
free(alloc_domain);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & SERV_IS_LOCAL)
|
||||||
|
{
|
||||||
|
serv->next = daemon->local_domains;
|
||||||
|
daemon->local_domains = serv;
|
||||||
|
|
||||||
|
if (flags & SERV_4ADDR)
|
||||||
|
((struct serv_addr4*)serv)->addr = local_addr->addr4;
|
||||||
|
|
||||||
|
if (flags & SERV_6ADDR)
|
||||||
|
((struct serv_addr6*)serv)->addr = local_addr->addr6;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memset(serv, 0, sizeof(struct server));
|
||||||
|
|
||||||
|
/* Add to the end of the chain, for order */
|
||||||
|
if (daemon->servers_tail)
|
||||||
|
daemon->servers_tail->next = serv;
|
||||||
|
else
|
||||||
|
daemon->servers = serv;
|
||||||
|
daemon->servers_tail = serv;
|
||||||
|
|
||||||
|
#ifdef HAVE_LOOP
|
||||||
|
serv->uid = rand32();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (interface)
|
||||||
|
safe_strncpy(serv->interface, interface, sizeof(serv->interface));
|
||||||
|
if (addr)
|
||||||
|
serv->addr = *addr;
|
||||||
|
if (source_addr)
|
||||||
|
serv->source_addr = *source_addr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
serv->flags = flags;
|
||||||
|
serv->domain = alloc_domain;
|
||||||
|
serv->domain_len = strlen(alloc_domain);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
+32
-28
@@ -18,8 +18,9 @@
|
|||||||
|
|
||||||
|
|
||||||
static struct cond_domain *search_domain(struct in_addr addr, struct cond_domain *c);
|
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 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)
|
int is_name_synthetic(int flags, char *name, union all_addr *addr)
|
||||||
{
|
{
|
||||||
@@ -135,26 +136,7 @@ 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 (hostname_isequal(c->domain, p+1) && inet_pton(prot, tail, addr))
|
||||||
{
|
found = (prot == AF_INET) ? match_domain(addr->addr4, c) : match_domain6(&addr->addr6, c);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* restore name */
|
/* restore name */
|
||||||
@@ -246,12 +228,20 @@ int is_rev_synth(int flag, union all_addr *addr, char *name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
for (; c; c = c->next)
|
|
||||||
if (!c->is6 &&
|
if (!c->is6 &&
|
||||||
ntohl(addr.s_addr) >= ntohl(c->start.s_addr) &&
|
ntohl(addr.s_addr) >= ntohl(c->start.s_addr) &&
|
||||||
ntohl(addr.s_addr) <= ntohl(c->end.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 (match_domain(addr, c))
|
||||||
return c;
|
return c;
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -267,16 +257,30 @@ char *get_domain(struct in_addr addr)
|
|||||||
return daemon->domain_suffix;
|
return daemon->domain_suffix;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int match_domain6(struct in6_addr *addr, struct cond_domain *c)
|
||||||
static struct cond_domain *search_domain6(struct in6_addr *addr, struct cond_domain *c)
|
|
||||||
{
|
{
|
||||||
u64 addrpart = addr6part(addr);
|
u64 addrpart = addr6part(addr);
|
||||||
|
|
||||||
for (; c; c = c->next)
|
if (c->is6)
|
||||||
if (c->is6 &&
|
{
|
||||||
is_same_net6(addr, &c->start6, 64) &&
|
if (c->prefixlen >= 64)
|
||||||
|
{
|
||||||
|
if (is_same_net6(addr, &c->start6, 64) &&
|
||||||
addrpart >= addr6part(&c->start6) &&
|
addrpart >= addr6part(&c->start6) &&
|
||||||
addrpart <= addr6part(&c->end6))
|
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 (match_domain6(addr, c))
|
||||||
return c;
|
return c;
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|||||||
+326
-108
@@ -119,12 +119,12 @@ static void set_outgoing_mark(struct frec *forward, int fd)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void log_query_mysockaddr(unsigned int flags, char *name, union mysockaddr *addr, char *arg)
|
static void log_query_mysockaddr(unsigned int flags, char *name, union mysockaddr *addr, char *arg, unsigned short type)
|
||||||
{
|
{
|
||||||
if (addr->sa.sa_family == AF_INET)
|
if (addr->sa.sa_family == AF_INET)
|
||||||
log_query(flags | F_IPV4, name, (union all_addr *)&addr->in.sin_addr, arg);
|
log_query(flags | F_IPV4, name, (union all_addr *)&addr->in.sin_addr, arg, type);
|
||||||
else
|
else
|
||||||
log_query(flags | F_IPV6, name, (union all_addr *)&addr->in6.sin6_addr, arg);
|
log_query(flags | F_IPV6, name, (union all_addr *)&addr->in6.sin6_addr, arg, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void server_send(struct server *server, int fd,
|
static void server_send(struct server *server, int fd,
|
||||||
@@ -138,24 +138,24 @@ static void server_send(struct server *server, int fd,
|
|||||||
#ifdef HAVE_DNSSEC
|
#ifdef HAVE_DNSSEC
|
||||||
static void server_send_log(struct server *server, int fd,
|
static void server_send_log(struct server *server, int fd,
|
||||||
const void *header, size_t plen, int dumpflags,
|
const void *header, size_t plen, int dumpflags,
|
||||||
unsigned int logflags, char *name, char *arg)
|
unsigned int logflags, char *name, char *arg,
|
||||||
|
unsigned short type)
|
||||||
{
|
{
|
||||||
#ifdef HAVE_DUMPFILE
|
#ifdef HAVE_DUMPFILE
|
||||||
dump_packet(dumpflags, (void *)header, (size_t)plen, NULL, &server->addr);
|
dump_packet(dumpflags, (void *)header, (size_t)plen, NULL, &server->addr);
|
||||||
#endif
|
#endif
|
||||||
log_query_mysockaddr(logflags, name, &server->addr, arg);
|
log_query_mysockaddr(logflags, name, &server->addr, arg, type);
|
||||||
server_send(server, fd, header, plen, 0);
|
server_send(server, fd, header, plen, 0);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int domain_no_rebind(char *domain)
|
static int domain_no_rebind(char *domain)
|
||||||
{
|
{
|
||||||
struct server *serv;
|
struct rebind_domain *rbd;
|
||||||
int dlen = (int)strlen(domain);
|
size_t tlen, dlen = strlen(domain);
|
||||||
|
|
||||||
/* flags is misused to hold length of domain. */
|
for (rbd = daemon->no_rebind; rbd; rbd = rbd->next)
|
||||||
for (serv = daemon->no_rebind; serv; serv = serv->next)
|
if (dlen >= (tlen = strlen(rbd->domain)) && strcmp(rbd->domain, &domain[dlen - tlen]) == 0)
|
||||||
if (dlen >= serv->flags && strcmp(serv->domain, &domain[dlen - serv->flags]) == 0)
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -178,6 +178,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
|||||||
int subnet, cacheable, forwarded = 0;
|
int subnet, cacheable, forwarded = 0;
|
||||||
size_t edns0_len;
|
size_t edns0_len;
|
||||||
unsigned char *pheader;
|
unsigned char *pheader;
|
||||||
|
int ede = EDE_UNSET;
|
||||||
(void)do_bit;
|
(void)do_bit;
|
||||||
|
|
||||||
if (header->hb4 & HB4_CD)
|
if (header->hb4 & HB4_CD)
|
||||||
@@ -255,26 +256,25 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
|||||||
/* new query */
|
/* new query */
|
||||||
if (!forward)
|
if (!forward)
|
||||||
{
|
{
|
||||||
/* new query */
|
if (lookup_domain(daemon->namebuff, gotname, &first, &last))
|
||||||
|
flags = is_local_answer(now, first, daemon->namebuff);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* no available server. */
|
||||||
|
ede = EDE_NOT_READY;
|
||||||
|
flags = 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* don't forward A or AAAA queries for simple names, except the empty name */
|
/* don't forward A or AAAA queries for simple names, except the empty name */
|
||||||
if (option_bool(OPT_NODOTS_LOCAL) &&
|
if (!flags &&
|
||||||
|
option_bool(OPT_NODOTS_LOCAL) &&
|
||||||
(gotname & (F_IPV4 | F_IPV6)) &&
|
(gotname & (F_IPV4 | F_IPV6)) &&
|
||||||
!strchr(daemon->namebuff, '.') &&
|
!strchr(daemon->namebuff, '.') &&
|
||||||
strlen(daemon->namebuff) != 0)
|
strlen(daemon->namebuff) != 0)
|
||||||
{
|
flags = check_for_local_domain(daemon->namebuff, now) ? F_NOERR : F_NXDOMAIN;
|
||||||
flags = F_NOERR;
|
|
||||||
goto reply;
|
|
||||||
}
|
|
||||||
|
|
||||||
flags = 0;
|
|
||||||
|
|
||||||
/* no available server. */
|
|
||||||
if (!lookup_domain(daemon->namebuff, gotname, &first, &last))
|
|
||||||
goto reply;
|
|
||||||
|
|
||||||
/* Configured answer. */
|
/* Configured answer. */
|
||||||
if ((flags = is_local_answer(now, first, daemon->namebuff)))
|
if (flags || ede == EDE_NOT_READY)
|
||||||
goto reply;
|
goto reply;
|
||||||
|
|
||||||
master = daemon->serverarray[first];
|
master = daemon->serverarray[first];
|
||||||
@@ -340,6 +340,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
|||||||
|
|
||||||
blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
|
blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
|
||||||
plen = forward->stash_len;
|
plen = forward->stash_len;
|
||||||
|
/* get query for logging. */
|
||||||
|
extract_request(header, plen, daemon->namebuff, NULL);
|
||||||
|
|
||||||
if (find_pseudoheader(header, plen, NULL, &pheader, &is_sign, NULL) && !is_sign)
|
if (find_pseudoheader(header, plen, NULL, &pheader, &is_sign, NULL) && !is_sign)
|
||||||
PUTSHORT(SAFE_PKTSZ, pheader);
|
PUTSHORT(SAFE_PKTSZ, pheader);
|
||||||
@@ -493,12 +495,12 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
|||||||
if (!gotname)
|
if (!gotname)
|
||||||
strcpy(daemon->namebuff, "query");
|
strcpy(daemon->namebuff, "query");
|
||||||
log_query_mysockaddr(F_SERVER | F_FORWARD, daemon->namebuff,
|
log_query_mysockaddr(F_SERVER | F_FORWARD, daemon->namebuff,
|
||||||
&srv->addr, NULL);
|
&srv->addr, NULL, 0);
|
||||||
}
|
}
|
||||||
#ifdef HAVE_DNSSEC
|
#ifdef HAVE_DNSSEC
|
||||||
else
|
else
|
||||||
log_query_mysockaddr(F_NOEXTRA | F_DNSSEC, daemon->namebuff, &srv->addr,
|
log_query_mysockaddr(F_NOEXTRA | F_DNSSEC, daemon->namebuff, &srv->addr,
|
||||||
querystr("dnssec-retry", (forward->flags & FREC_DNSKEY_QUERY) ? T_DNSKEY : T_DS));
|
"dnssec-retry", (forward->flags & FREC_DNSKEY_QUERY) ? T_DNSKEY : T_DS);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
srv->queries++;
|
srv->queries++;
|
||||||
@@ -520,15 +522,33 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
|||||||
/* could not send on, prepare to return */
|
/* could not send on, prepare to return */
|
||||||
header->id = htons(forward->frec_src.orig_id);
|
header->id = htons(forward->frec_src.orig_id);
|
||||||
free_frec(forward); /* cancel */
|
free_frec(forward); /* cancel */
|
||||||
|
ede = EDE_NETERR;
|
||||||
|
|
||||||
reply:
|
reply:
|
||||||
if (udpfd != -1)
|
if (udpfd != -1)
|
||||||
{
|
{
|
||||||
if (!(plen = make_local_answer(flags, gotname, plen, header, daemon->namebuff, first, last)))
|
if (!(plen = make_local_answer(flags, gotname, plen, header, daemon->namebuff, limit, first, last, ede)))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (oph)
|
if (oph)
|
||||||
|
{
|
||||||
|
u16 swap = htons((u16)ede);
|
||||||
|
|
||||||
|
if (ede != EDE_UNSET)
|
||||||
|
plen = add_pseudoheader(header, plen, (unsigned char *)limit, daemon->edns_pktsz, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0);
|
||||||
|
else
|
||||||
plen = add_pseudoheader(header, plen, (unsigned char *)limit, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0);
|
plen = add_pseudoheader(header, plen, (unsigned char *)limit, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS)
|
||||||
|
if (option_bool(OPT_CMARK_ALST_EN))
|
||||||
|
{
|
||||||
|
unsigned int mark;
|
||||||
|
int have_mark = get_incoming_mark(udpaddr, dst_addr, /* istcp: */ 0, &mark);
|
||||||
|
if (have_mark && ((u32)mark & daemon->allowlist_mask))
|
||||||
|
report_addresses(header, plen, mark);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
send_from(udpfd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND), (char *)header, plen, udpaddr, dst_addr, dst_iface);
|
send_from(udpfd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND), (char *)header, plen, udpaddr, dst_addr, dst_iface);
|
||||||
}
|
}
|
||||||
@@ -536,12 +556,33 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct ipsets *domain_find_sets(struct ipsets *setlist, const char *domain) {
|
||||||
|
/* Similar algorithm to search_servers. */
|
||||||
|
struct ipsets *ipset_pos, *ret = NULL;
|
||||||
|
unsigned int namelen = strlen(domain);
|
||||||
|
unsigned int matchlen = 0;
|
||||||
|
for (ipset_pos = setlist; ipset_pos; ipset_pos = ipset_pos->next)
|
||||||
|
{
|
||||||
|
unsigned int domainlen = strlen(ipset_pos->domain);
|
||||||
|
const char *matchstart = domain + namelen - domainlen;
|
||||||
|
if (namelen >= domainlen && hostname_isequal(matchstart, ipset_pos->domain) &&
|
||||||
|
(domainlen == 0 || namelen == domainlen || *(matchstart - 1) == '.' ) &&
|
||||||
|
domainlen >= matchlen)
|
||||||
|
{
|
||||||
|
matchlen = domainlen;
|
||||||
|
ret = ipset_pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static size_t process_reply(struct dns_header *header, time_t now, struct server *server, size_t n, int check_rebind,
|
static size_t process_reply(struct dns_header *header, time_t now, struct server *server, size_t n, int check_rebind,
|
||||||
int no_cache, int cache_secure, int bogusanswer, int ad_reqd, int do_bit, int added_pheader,
|
int no_cache, int cache_secure, int bogusanswer, int ad_reqd, int do_bit, int added_pheader,
|
||||||
int check_subnet, union mysockaddr *query_source)
|
int check_subnet, union mysockaddr *query_source, unsigned char *limit, int ede)
|
||||||
{
|
{
|
||||||
unsigned char *pheader, *sizep;
|
unsigned char *pheader, *sizep;
|
||||||
char **sets = 0;
|
struct ipsets *ipsets = NULL, *nftsets = NULL;
|
||||||
int munged = 0, is_sign;
|
int munged = 0, is_sign;
|
||||||
unsigned int rcode = RCODE(header);
|
unsigned int rcode = RCODE(header);
|
||||||
size_t plen;
|
size_t plen;
|
||||||
@@ -552,24 +593,12 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
|||||||
|
|
||||||
#ifdef HAVE_IPSET
|
#ifdef HAVE_IPSET
|
||||||
if (daemon->ipsets && extract_request(header, n, daemon->namebuff, NULL))
|
if (daemon->ipsets && extract_request(header, n, daemon->namebuff, NULL))
|
||||||
{
|
ipsets = domain_find_sets(daemon->ipsets, daemon->namebuff);
|
||||||
/* Similar algorithm to search_servers. */
|
#endif
|
||||||
struct ipsets *ipset_pos;
|
|
||||||
unsigned int namelen = strlen(daemon->namebuff);
|
#ifdef HAVE_NFTSET
|
||||||
unsigned int matchlen = 0;
|
if (daemon->nftsets && extract_request(header, n, daemon->namebuff, NULL))
|
||||||
for (ipset_pos = daemon->ipsets; ipset_pos; ipset_pos = ipset_pos->next)
|
nftsets = domain_find_sets(daemon->nftsets, daemon->namebuff);
|
||||||
{
|
|
||||||
unsigned int domainlen = strlen(ipset_pos->domain);
|
|
||||||
char *matchstart = daemon->namebuff + namelen - domainlen;
|
|
||||||
if (namelen >= domainlen && hostname_isequal(matchstart, ipset_pos->domain) &&
|
|
||||||
(domainlen == 0 || namelen == domainlen || *(matchstart - 1) == '.' ) &&
|
|
||||||
domainlen >= matchlen)
|
|
||||||
{
|
|
||||||
matchlen = domainlen;
|
|
||||||
sets = ipset_pos->sets;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if ((pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign, NULL)))
|
if ((pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign, NULL)))
|
||||||
@@ -593,11 +622,10 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
unsigned short udpsz;
|
|
||||||
|
|
||||||
/* If upstream is advertising a larger UDP packet size
|
/* If upstream is advertising a larger UDP packet size
|
||||||
than we allow, trim it so that we don't get overlarge
|
than we allow, trim it so that we don't get overlarge
|
||||||
requests for the client. We can't do this for signed packets. */
|
requests for the client. We can't do this for signed packets. */
|
||||||
|
unsigned short udpsz;
|
||||||
GETSHORT(udpsz, sizep);
|
GETSHORT(udpsz, sizep);
|
||||||
if (udpsz > daemon->edns_pktsz)
|
if (udpsz > daemon->edns_pktsz)
|
||||||
{
|
{
|
||||||
@@ -634,7 +662,8 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
|||||||
{
|
{
|
||||||
union all_addr a;
|
union all_addr a;
|
||||||
a.log.rcode = rcode;
|
a.log.rcode = rcode;
|
||||||
log_query(F_UPSTREAM | F_RCODE, "error", &a, NULL);
|
a.log.ede = ede;
|
||||||
|
log_query(F_UPSTREAM | F_RCODE, "error", &a, NULL, 0);
|
||||||
|
|
||||||
return resize_packet(header, n, pheader, plen);
|
return resize_packet(header, n, pheader, plen);
|
||||||
}
|
}
|
||||||
@@ -656,14 +685,17 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
|||||||
SET_RCODE(header, NXDOMAIN);
|
SET_RCODE(header, NXDOMAIN);
|
||||||
header->hb3 &= ~HB3_AA;
|
header->hb3 &= ~HB3_AA;
|
||||||
cache_secure = 0;
|
cache_secure = 0;
|
||||||
|
ede = EDE_BLOCKED;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int doctored = 0;
|
int doctored = 0;
|
||||||
|
|
||||||
if (rcode == NXDOMAIN &&
|
if (rcode == NXDOMAIN &&
|
||||||
extract_request(header, n, daemon->namebuff, NULL) &&
|
extract_request(header, n, daemon->namebuff, NULL))
|
||||||
check_for_local_domain(daemon->namebuff, now))
|
{
|
||||||
|
if (check_for_local_domain(daemon->namebuff, now) ||
|
||||||
|
lookup_domain(daemon->namebuff, F_CONFIG, NULL, NULL))
|
||||||
{
|
{
|
||||||
/* if we forwarded a query for a locally known name (because it was for
|
/* if we forwarded a query for a locally known name (because it was for
|
||||||
an unknown type) and the answer is NXDOMAIN, convert that to NODATA,
|
an unknown type) and the answer is NXDOMAIN, convert that to NODATA,
|
||||||
@@ -673,12 +705,14 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
|||||||
SET_RCODE(header, NOERROR);
|
SET_RCODE(header, NOERROR);
|
||||||
cache_secure = 0;
|
cache_secure = 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (extract_addresses(header, n, daemon->namebuff, now, sets, is_sign, check_rebind, no_cache, cache_secure, &doctored))
|
if (extract_addresses(header, n, daemon->namebuff, now, ipsets, nftsets, is_sign, check_rebind, no_cache, cache_secure, &doctored))
|
||||||
{
|
{
|
||||||
my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff);
|
my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff);
|
||||||
munged = 1;
|
munged = 1;
|
||||||
cache_secure = 0;
|
cache_secure = 0;
|
||||||
|
ede = EDE_BLOCKED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (doctored)
|
if (doctored)
|
||||||
@@ -708,7 +742,6 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
|||||||
|
|
||||||
/* do this after extract_addresses. Ensure NODATA reply and remove
|
/* do this after extract_addresses. Ensure NODATA reply and remove
|
||||||
nameserver info. */
|
nameserver info. */
|
||||||
|
|
||||||
if (munged)
|
if (munged)
|
||||||
{
|
{
|
||||||
header->ancount = htons(0);
|
header->ancount = htons(0);
|
||||||
@@ -720,7 +753,15 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
|||||||
/* the bogus-nxdomain stuff, doctor and NXDOMAIN->NODATA munging can all elide
|
/* the bogus-nxdomain stuff, doctor and NXDOMAIN->NODATA munging can all elide
|
||||||
sections of the packet. Find the new length here and put back pseudoheader
|
sections of the packet. Find the new length here and put back pseudoheader
|
||||||
if it was removed. */
|
if it was removed. */
|
||||||
return resize_packet(header, n, pheader, plen);
|
n = resize_packet(header, n, pheader, plen);
|
||||||
|
|
||||||
|
if (pheader && ede != EDE_UNSET)
|
||||||
|
{
|
||||||
|
u16 swap = htons((u16)ede);
|
||||||
|
n = add_pseudoheader(header, n, limit, daemon->edns_pktsz, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_DNSSEC
|
#ifdef HAVE_DNSSEC
|
||||||
@@ -748,7 +789,7 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
|
|||||||
/* As soon as anything returns BOGUS, we stop and unwind, to do otherwise
|
/* As soon as anything returns BOGUS, we stop and unwind, to do otherwise
|
||||||
would invite infinite loops, since the answers to DNSKEY and DS queries
|
would invite infinite loops, since the answers to DNSKEY and DS queries
|
||||||
will not be cached, so they'll be repeated. */
|
will not be cached, so they'll be repeated. */
|
||||||
if (status != STAT_BOGUS && status != STAT_TRUNCATED && status != STAT_ABANDONED)
|
if (!STAT_ISEQUAL(status, STAT_BOGUS) && !STAT_ISEQUAL(status, STAT_TRUNCATED) && !STAT_ISEQUAL(status, STAT_ABANDONED))
|
||||||
{
|
{
|
||||||
if (forward->flags & FREC_DNSKEY_QUERY)
|
if (forward->flags & FREC_DNSKEY_QUERY)
|
||||||
status = dnssec_validate_by_ds(now, header, plen, daemon->namebuff, daemon->keyname, forward->class);
|
status = dnssec_validate_by_ds(now, header, plen, daemon->namebuff, daemon->keyname, forward->class);
|
||||||
@@ -759,7 +800,7 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
|
|||||||
!option_bool(OPT_DNSSEC_IGN_NS) && (forward->sentto->flags & SERV_DO_DNSSEC),
|
!option_bool(OPT_DNSSEC_IGN_NS) && (forward->sentto->flags & SERV_DO_DNSSEC),
|
||||||
NULL, NULL, NULL);
|
NULL, NULL, NULL);
|
||||||
#ifdef HAVE_DUMPFILE
|
#ifdef HAVE_DUMPFILE
|
||||||
if (status == STAT_BOGUS)
|
if (STAT_ISEQUAL(status, STAT_BOGUS))
|
||||||
dump_packet((forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY)) ? DUMP_SEC_BOGUS : DUMP_BOGUS,
|
dump_packet((forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY)) ? DUMP_SEC_BOGUS : DUMP_BOGUS,
|
||||||
header, (size_t)plen, &forward->sentto->addr, NULL);
|
header, (size_t)plen, &forward->sentto->addr, NULL);
|
||||||
#endif
|
#endif
|
||||||
@@ -767,7 +808,7 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
|
|||||||
|
|
||||||
/* Can't validate, as we're missing key data. Put this
|
/* Can't validate, as we're missing key data. Put this
|
||||||
answer aside, whilst we get that. */
|
answer aside, whilst we get that. */
|
||||||
if (status == STAT_NEED_DS || status == STAT_NEED_KEY)
|
if (STAT_ISEQUAL(status, STAT_NEED_DS) || STAT_ISEQUAL(status, STAT_NEED_KEY))
|
||||||
{
|
{
|
||||||
struct frec *new = NULL;
|
struct frec *new = NULL;
|
||||||
int serverind;
|
int serverind;
|
||||||
@@ -786,9 +827,9 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
|
|||||||
/* validate routines leave name of required record in daemon->keyname */
|
/* validate routines leave name of required record in daemon->keyname */
|
||||||
nn = dnssec_generate_query(header, ((unsigned char *) header) + server->edns_pktsz,
|
nn = dnssec_generate_query(header, ((unsigned char *) header) + server->edns_pktsz,
|
||||||
daemon->keyname, forward->class,
|
daemon->keyname, forward->class,
|
||||||
status == STAT_NEED_KEY ? T_DNSKEY : T_DS, server->edns_pktsz);
|
STAT_ISEQUAL(status, STAT_NEED_KEY) ? T_DNSKEY : T_DS, server->edns_pktsz);
|
||||||
|
|
||||||
flags = (status == STAT_NEED_KEY) ? FREC_DNSKEY_QUERY : FREC_DS_QUERY;
|
flags = STAT_ISEQUAL(status, STAT_NEED_KEY) ? FREC_DNSKEY_QUERY : FREC_DS_QUERY;
|
||||||
hash = hash_questions(header, nn, daemon->namebuff);
|
hash = hash_questions(header, nn, daemon->namebuff);
|
||||||
|
|
||||||
if ((new = lookup_frec_by_query(hash, flags, FREC_DNSKEY_QUERY | FREC_DS_QUERY)))
|
if ((new = lookup_frec_by_query(hash, flags, FREC_DNSKEY_QUERY | FREC_DS_QUERY)))
|
||||||
@@ -858,7 +899,7 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
|
|||||||
#endif
|
#endif
|
||||||
server_send_log(server, fd, header, nn, DUMP_SEC_QUERY,
|
server_send_log(server, fd, header, nn, DUMP_SEC_QUERY,
|
||||||
F_NOEXTRA | F_DNSSEC, daemon->keyname,
|
F_NOEXTRA | F_DNSSEC, daemon->keyname,
|
||||||
querystr("dnssec-query", status == STAT_NEED_KEY ? T_DNSKEY : T_DS));
|
"dnssec-query", STAT_ISEQUAL(status, STAT_NEED_KEY) ? T_DNSKEY : T_DS);
|
||||||
server->queries++;
|
server->queries++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1059,6 +1100,7 @@ static void return_reply(time_t now, struct frec *forward, struct dns_header *he
|
|||||||
{
|
{
|
||||||
int check_rebind = 0, no_cache_dnssec = 0, cache_secure = 0, bogusanswer = 0;
|
int check_rebind = 0, no_cache_dnssec = 0, cache_secure = 0, bogusanswer = 0;
|
||||||
size_t nn;
|
size_t nn;
|
||||||
|
int ede = EDE_UNSET;
|
||||||
|
|
||||||
(void)status;
|
(void)status;
|
||||||
|
|
||||||
@@ -1071,36 +1113,40 @@ static void return_reply(time_t now, struct frec *forward, struct dns_header *he
|
|||||||
no_cache_dnssec = 1;
|
no_cache_dnssec = 1;
|
||||||
|
|
||||||
#ifdef HAVE_DNSSEC
|
#ifdef HAVE_DNSSEC
|
||||||
if (status != STAT_OK)
|
if (!STAT_ISEQUAL(status, STAT_OK))
|
||||||
{
|
{
|
||||||
|
/* status is STAT_OK when validation not turned on. */
|
||||||
no_cache_dnssec = 0;
|
no_cache_dnssec = 0;
|
||||||
|
|
||||||
if (status == STAT_TRUNCATED)
|
if (STAT_ISEQUAL(status, STAT_TRUNCATED))
|
||||||
header->hb3 |= HB3_TC;
|
header->hb3 |= HB3_TC;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
char *result, *domain = "result";
|
char *result, *domain = "result";
|
||||||
|
union all_addr a;
|
||||||
|
|
||||||
if (status == STAT_ABANDONED)
|
a.log.ede = ede = errflags_to_ede(status);
|
||||||
|
|
||||||
|
if (STAT_ISEQUAL(status, STAT_ABANDONED))
|
||||||
{
|
{
|
||||||
result = "ABANDONED";
|
result = "ABANDONED";
|
||||||
status = STAT_BOGUS;
|
status = STAT_BOGUS;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
result = (status == STAT_SECURE ? "SECURE" : (status == STAT_INSECURE ? "INSECURE" : "BOGUS"));
|
result = (STAT_ISEQUAL(status, STAT_SECURE) ? "SECURE" : (STAT_ISEQUAL(status, STAT_INSECURE) ? "INSECURE" : "BOGUS"));
|
||||||
|
|
||||||
if (status == STAT_BOGUS && extract_request(header, n, daemon->namebuff, NULL))
|
if (STAT_ISEQUAL(status, STAT_SECURE))
|
||||||
domain = daemon->namebuff;
|
|
||||||
|
|
||||||
log_query(F_SECSTAT, domain, NULL, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status == STAT_SECURE)
|
|
||||||
cache_secure = 1;
|
cache_secure = 1;
|
||||||
else if (status == STAT_BOGUS)
|
else if (STAT_ISEQUAL(status, STAT_BOGUS))
|
||||||
{
|
{
|
||||||
no_cache_dnssec = 1;
|
no_cache_dnssec = 1;
|
||||||
bogusanswer = 1;
|
bogusanswer = 1;
|
||||||
|
|
||||||
|
if (extract_request(header, n, daemon->namebuff, NULL))
|
||||||
|
domain = daemon->namebuff;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_query(F_SECSTAT, domain, &a, result, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -1121,7 +1167,8 @@ static void return_reply(time_t now, struct frec *forward, struct dns_header *he
|
|||||||
|
|
||||||
if ((nn = process_reply(header, now, forward->sentto, (size_t)n, check_rebind, no_cache_dnssec, cache_secure, bogusanswer,
|
if ((nn = process_reply(header, now, forward->sentto, (size_t)n, check_rebind, no_cache_dnssec, cache_secure, bogusanswer,
|
||||||
forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION,
|
forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION,
|
||||||
forward->flags & FREC_ADDED_PHEADER, forward->flags & FREC_HAS_SUBNET, &forward->frec_src.source)))
|
forward->flags & FREC_ADDED_PHEADER, forward->flags & FREC_HAS_SUBNET, &forward->frec_src.source,
|
||||||
|
((unsigned char *)header) + daemon->edns_pktsz, ede)))
|
||||||
{
|
{
|
||||||
struct frec_src *src;
|
struct frec_src *src;
|
||||||
|
|
||||||
@@ -1148,6 +1195,16 @@ static void return_reply(time_t now, struct frec *forward, struct dns_header *he
|
|||||||
dump_packet(DUMP_REPLY, daemon->packet, (size_t)nn, NULL, &src->source);
|
dump_packet(DUMP_REPLY, daemon->packet, (size_t)nn, NULL, &src->source);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS)
|
||||||
|
if (option_bool(OPT_CMARK_ALST_EN))
|
||||||
|
{
|
||||||
|
unsigned int mark;
|
||||||
|
int have_mark = get_incoming_mark(&src->source, &src->dest, /* istcp: */ 0, &mark);
|
||||||
|
if (have_mark && ((u32)mark & daemon->allowlist_mask))
|
||||||
|
report_addresses(header, nn, mark);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
send_from(src->fd, option_bool(OPT_NOWILD) || option_bool (OPT_CLEVERBIND), daemon->packet, nn,
|
send_from(src->fd, option_bool(OPT_NOWILD) || option_bool (OPT_CLEVERBIND), daemon->packet, nn,
|
||||||
&src->source, &src->dest, src->iface);
|
&src->source, &src->dest, src->iface);
|
||||||
|
|
||||||
@@ -1155,7 +1212,7 @@ static void return_reply(time_t now, struct frec *forward, struct dns_header *he
|
|||||||
{
|
{
|
||||||
daemon->log_display_id = src->log_id;
|
daemon->log_display_id = src->log_id;
|
||||||
daemon->log_source_addr = &src->source;
|
daemon->log_source_addr = &src->source;
|
||||||
log_query(F_UPSTREAM, "query", NULL, "duplicate");
|
log_query(F_UPSTREAM, "query", NULL, "duplicate", 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1164,6 +1221,49 @@ static void return_reply(time_t now, struct frec *forward, struct dns_header *he
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef HAVE_CONNTRACK
|
||||||
|
static int is_query_allowed_for_mark(u32 mark, const char *name)
|
||||||
|
{
|
||||||
|
int is_allowable_name, did_validate_name = 0;
|
||||||
|
struct allowlist *allowlists;
|
||||||
|
char **patterns_pos;
|
||||||
|
|
||||||
|
for (allowlists = daemon->allowlists; allowlists; allowlists = allowlists->next)
|
||||||
|
if (allowlists->mark == (mark & daemon->allowlist_mask & allowlists->mask))
|
||||||
|
for (patterns_pos = allowlists->patterns; *patterns_pos; patterns_pos++)
|
||||||
|
{
|
||||||
|
if (!strcmp(*patterns_pos, "*"))
|
||||||
|
return 1;
|
||||||
|
if (!did_validate_name)
|
||||||
|
{
|
||||||
|
is_allowable_name = name ? is_valid_dns_name(name) : 0;
|
||||||
|
did_validate_name = 1;
|
||||||
|
}
|
||||||
|
if (is_allowable_name && is_dns_name_matching_pattern(name, *patterns_pos))
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t answer_disallowed(struct dns_header *header, size_t qlen, u32 mark, const char *name)
|
||||||
|
{
|
||||||
|
unsigned char *p;
|
||||||
|
(void)name;
|
||||||
|
(void)mark;
|
||||||
|
|
||||||
|
#ifdef HAVE_UBUS
|
||||||
|
if (name)
|
||||||
|
ubus_event_bcast_connmark_allowlist_refused(mark, name);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
setup_reply(header, /* flags: */ 0, EDE_BLOCKED);
|
||||||
|
|
||||||
|
if (!(p = skip_questions(header, qlen)))
|
||||||
|
return 0;
|
||||||
|
return p - (unsigned char *)header;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void receive_query(struct listener *listen, time_t now)
|
void receive_query(struct listener *listen, time_t now)
|
||||||
{
|
{
|
||||||
struct dns_header *header = (struct dns_header *)daemon->packet;
|
struct dns_header *header = (struct dns_header *)daemon->packet;
|
||||||
@@ -1175,6 +1275,11 @@ void receive_query(struct listener *listen, time_t now)
|
|||||||
size_t m;
|
size_t m;
|
||||||
ssize_t n;
|
ssize_t n;
|
||||||
int if_index = 0, auth_dns = 0, do_bit = 0, have_pseudoheader = 0;
|
int if_index = 0, auth_dns = 0, do_bit = 0, have_pseudoheader = 0;
|
||||||
|
#ifdef HAVE_CONNTRACK
|
||||||
|
unsigned int mark = 0;
|
||||||
|
int have_mark = 0;
|
||||||
|
int is_single_query = 0, allowed = 1;
|
||||||
|
#endif
|
||||||
#ifdef HAVE_AUTH
|
#ifdef HAVE_AUTH
|
||||||
int local_auth = 0;
|
int local_auth = 0;
|
||||||
#endif
|
#endif
|
||||||
@@ -1404,15 +1509,22 @@ void receive_query(struct listener *listen, time_t now)
|
|||||||
dump_packet(DUMP_QUERY, daemon->packet, (size_t)n, &source_addr, NULL);
|
dump_packet(DUMP_QUERY, daemon->packet, (size_t)n, &source_addr, NULL);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_CONNTRACK
|
||||||
|
if (option_bool(OPT_CMARK_ALST_EN))
|
||||||
|
have_mark = get_incoming_mark(&source_addr, &dst_addr, /* istcp: */ 0, &mark);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (extract_request(header, (size_t)n, daemon->namebuff, &type))
|
if (extract_request(header, (size_t)n, daemon->namebuff, &type))
|
||||||
{
|
{
|
||||||
#ifdef HAVE_AUTH
|
#ifdef HAVE_AUTH
|
||||||
struct auth_zone *zone;
|
struct auth_zone *zone;
|
||||||
#endif
|
#endif
|
||||||
char *types = querystr(auth_dns ? "auth" : "query", type);
|
|
||||||
|
|
||||||
log_query_mysockaddr(F_QUERY | F_FORWARD, daemon->namebuff,
|
log_query_mysockaddr(F_QUERY | F_FORWARD, daemon->namebuff,
|
||||||
&source_addr, types);
|
&source_addr, auth_dns ? "auth" : "query", type);
|
||||||
|
|
||||||
|
#ifdef HAVE_CONNTRACK
|
||||||
|
is_single_query = 1;
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_AUTH
|
#ifdef HAVE_AUTH
|
||||||
/* find queries for zones we're authoritative for, and answer them directly */
|
/* find queries for zones we're authoritative for, and answer them directly */
|
||||||
@@ -1454,20 +1566,59 @@ void receive_query(struct listener *listen, time_t now)
|
|||||||
udp_size = PACKETSZ; /* Sanity check - can't reduce below default. RFC 6891 6.2.3 */
|
udp_size = PACKETSZ; /* Sanity check - can't reduce below default. RFC 6891 6.2.3 */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_CONNTRACK
|
||||||
#ifdef HAVE_AUTH
|
#ifdef HAVE_AUTH
|
||||||
if (auth_dns)
|
if (!auth_dns || local_auth)
|
||||||
|
#endif
|
||||||
|
if (option_bool(OPT_CMARK_ALST_EN) && have_mark && ((u32)mark & daemon->allowlist_mask))
|
||||||
|
allowed = is_query_allowed_for_mark((u32)mark, is_single_query ? daemon->namebuff : NULL);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (0);
|
||||||
|
#ifdef HAVE_CONNTRACK
|
||||||
|
else if (!allowed)
|
||||||
|
{
|
||||||
|
u16 swap = htons(EDE_BLOCKED);
|
||||||
|
|
||||||
|
m = answer_disallowed(header, (size_t)n, (u32)mark, is_single_query ? daemon->namebuff : NULL);
|
||||||
|
|
||||||
|
if (have_pseudoheader && m != 0)
|
||||||
|
m = add_pseudoheader(header, m, ((unsigned char *) header) + udp_size, daemon->edns_pktsz,
|
||||||
|
EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0);
|
||||||
|
|
||||||
|
if (m >= 1)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_DUMPFILE
|
||||||
|
dump_packet(DUMP_REPLY, daemon->packet, m, NULL, &source_addr);
|
||||||
|
#endif
|
||||||
|
send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND),
|
||||||
|
(char *)header, m, &source_addr, &dst_addr, if_index);
|
||||||
|
daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_AUTH
|
||||||
|
else if (auth_dns)
|
||||||
{
|
{
|
||||||
m = answer_auth(header, ((char *) header) + udp_size, (size_t)n, now, &source_addr,
|
m = answer_auth(header, ((char *) header) + udp_size, (size_t)n, now, &source_addr,
|
||||||
local_auth, do_bit, have_pseudoheader);
|
local_auth, do_bit, have_pseudoheader);
|
||||||
if (m >= 1)
|
if (m >= 1)
|
||||||
{
|
{
|
||||||
|
#ifdef HAVE_DUMPFILE
|
||||||
|
dump_packet(DUMP_REPLY, daemon->packet, m, NULL, &source_addr);
|
||||||
|
#endif
|
||||||
|
#if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS)
|
||||||
|
if (local_auth)
|
||||||
|
if (option_bool(OPT_CMARK_ALST_EN) && have_mark && ((u32)mark & daemon->allowlist_mask))
|
||||||
|
report_addresses(header, m, mark);
|
||||||
|
#endif
|
||||||
send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND),
|
send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND),
|
||||||
(char *)header, m, &source_addr, &dst_addr, if_index);
|
(char *)header, m, &source_addr, &dst_addr, if_index);
|
||||||
daemon->metrics[METRIC_DNS_AUTH_ANSWERED]++;
|
daemon->metrics[METRIC_DNS_AUTH_ANSWERED]++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
#endif
|
#endif
|
||||||
|
else
|
||||||
{
|
{
|
||||||
int ad_reqd = do_bit;
|
int ad_reqd = do_bit;
|
||||||
/* RFC 6840 5.7 */
|
/* RFC 6840 5.7 */
|
||||||
@@ -1479,6 +1630,13 @@ void receive_query(struct listener *listen, time_t now)
|
|||||||
|
|
||||||
if (m >= 1)
|
if (m >= 1)
|
||||||
{
|
{
|
||||||
|
#ifdef HAVE_DUMPFILE
|
||||||
|
dump_packet(DUMP_REPLY, daemon->packet, m, NULL, &source_addr);
|
||||||
|
#endif
|
||||||
|
#if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS)
|
||||||
|
if (option_bool(OPT_CMARK_ALST_EN) && have_mark && ((u32)mark & daemon->allowlist_mask))
|
||||||
|
report_addresses(header, m, mark);
|
||||||
|
#endif
|
||||||
send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND),
|
send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND),
|
||||||
(char *)header, m, &source_addr, &dst_addr, if_index);
|
(char *)header, m, &source_addr, &dst_addr, if_index);
|
||||||
daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]++;
|
daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]++;
|
||||||
@@ -1618,16 +1776,16 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
|
|||||||
/* limit the amount of work we do, to avoid cycling forever on loops in the DNS */
|
/* limit the amount of work we do, to avoid cycling forever on loops in the DNS */
|
||||||
if (--(*keycount) == 0)
|
if (--(*keycount) == 0)
|
||||||
new_status = STAT_ABANDONED;
|
new_status = STAT_ABANDONED;
|
||||||
else if (status == STAT_NEED_KEY)
|
else if (STAT_ISEQUAL(status, STAT_NEED_KEY))
|
||||||
new_status = dnssec_validate_by_ds(now, header, n, name, keyname, class);
|
new_status = dnssec_validate_by_ds(now, header, n, name, keyname, class);
|
||||||
else if (status == STAT_NEED_DS)
|
else if (STAT_ISEQUAL(status, STAT_NEED_DS))
|
||||||
new_status = dnssec_validate_ds(now, header, n, name, keyname, class);
|
new_status = dnssec_validate_ds(now, header, n, name, keyname, class);
|
||||||
else
|
else
|
||||||
new_status = dnssec_validate_reply(now, header, n, name, keyname, &class,
|
new_status = dnssec_validate_reply(now, header, n, name, keyname, &class,
|
||||||
!option_bool(OPT_DNSSEC_IGN_NS) && (server->flags & SERV_DO_DNSSEC),
|
!option_bool(OPT_DNSSEC_IGN_NS) && (server->flags & SERV_DO_DNSSEC),
|
||||||
NULL, NULL, NULL);
|
NULL, NULL, NULL);
|
||||||
|
|
||||||
if (new_status != STAT_NEED_DS && new_status != STAT_NEED_KEY)
|
if (!STAT_ISEQUAL(new_status, STAT_NEED_DS) && !STAT_ISEQUAL(new_status, STAT_NEED_KEY))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* Can't validate because we need a key/DS whose name now in keyname.
|
/* Can't validate because we need a key/DS whose name now in keyname.
|
||||||
@@ -1645,7 +1803,7 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
|
|||||||
}
|
}
|
||||||
|
|
||||||
m = dnssec_generate_query(new_header, ((unsigned char *) new_header) + 65536, keyname, class,
|
m = dnssec_generate_query(new_header, ((unsigned char *) new_header) + 65536, keyname, class,
|
||||||
new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS, server->edns_pktsz);
|
STAT_ISEQUAL(new_status, STAT_NEED_KEY) ? T_DNSKEY : T_DS, server->edns_pktsz);
|
||||||
|
|
||||||
if ((start = dnssec_server(server, daemon->keyname, &first, &last)) == -1 ||
|
if ((start = dnssec_server(server, daemon->keyname, &first, &last)) == -1 ||
|
||||||
(m = tcp_talk(first, last, start, packet, m, have_mark, mark, &server)) == 0)
|
(m = tcp_talk(first, last, start, packet, m, have_mark, mark, &server)) == 0)
|
||||||
@@ -1658,13 +1816,13 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
|
|||||||
daemon->log_display_id = ++daemon->log_id;
|
daemon->log_display_id = ++daemon->log_id;
|
||||||
|
|
||||||
log_query_mysockaddr(F_NOEXTRA | F_DNSSEC, keyname, &server->addr,
|
log_query_mysockaddr(F_NOEXTRA | F_DNSSEC, keyname, &server->addr,
|
||||||
querystr("dnssec-query", new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS));
|
"dnssec-query", STAT_ISEQUAL(new_status, STAT_NEED_KEY) ? T_DNSKEY : T_DS);
|
||||||
|
|
||||||
new_status = tcp_key_recurse(now, new_status, new_header, m, class, name, keyname, server, have_mark, mark, keycount);
|
new_status = tcp_key_recurse(now, new_status, new_header, m, class, name, keyname, server, have_mark, mark, keycount);
|
||||||
|
|
||||||
daemon->log_display_id = log_save;
|
daemon->log_display_id = log_save;
|
||||||
|
|
||||||
if (new_status != STAT_OK)
|
if (!STAT_ISEQUAL(new_status, STAT_OK))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1685,6 +1843,9 @@ unsigned char *tcp_request(int confd, time_t now,
|
|||||||
{
|
{
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
int norebind;
|
int norebind;
|
||||||
|
#ifdef HAVE_CONNTRACK
|
||||||
|
int is_single_query = 0, allowed = 1;
|
||||||
|
#endif
|
||||||
#ifdef HAVE_AUTH
|
#ifdef HAVE_AUTH
|
||||||
int local_auth = 0;
|
int local_auth = 0;
|
||||||
#endif
|
#endif
|
||||||
@@ -1711,12 +1872,12 @@ unsigned char *tcp_request(int confd, time_t now,
|
|||||||
int first, last;
|
int first, last;
|
||||||
unsigned int flags = 0;
|
unsigned int flags = 0;
|
||||||
|
|
||||||
if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) == -1)
|
if (!packet || getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) == -1)
|
||||||
return packet;
|
return packet;
|
||||||
|
|
||||||
#ifdef HAVE_CONNTRACK
|
#ifdef HAVE_CONNTRACK
|
||||||
/* Get connection mark of incoming query to set on outgoing connections. */
|
/* Get connection mark of incoming query to set on outgoing connections. */
|
||||||
if (option_bool(OPT_CONNTRACK))
|
if (option_bool(OPT_CONNTRACK) || option_bool(OPT_CMARK_ALST_EN))
|
||||||
{
|
{
|
||||||
union all_addr local;
|
union all_addr local;
|
||||||
|
|
||||||
@@ -1761,6 +1922,8 @@ unsigned char *tcp_request(int confd, time_t now,
|
|||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
|
int ede = EDE_UNSET;
|
||||||
|
|
||||||
if (query_count == TCP_MAX_QUERIES ||
|
if (query_count == TCP_MAX_QUERIES ||
|
||||||
!packet ||
|
!packet ||
|
||||||
!read_write(confd, &c1, 1, 1) || !read_write(confd, &c2, 1, 1) ||
|
!read_write(confd, &c1, 1, 1) || !read_write(confd, &c2, 1, 1) ||
|
||||||
@@ -1791,10 +1954,13 @@ unsigned char *tcp_request(int confd, time_t now,
|
|||||||
#ifdef HAVE_AUTH
|
#ifdef HAVE_AUTH
|
||||||
struct auth_zone *zone;
|
struct auth_zone *zone;
|
||||||
#endif
|
#endif
|
||||||
char *types = querystr(auth_dns ? "auth" : "query", qtype);
|
|
||||||
|
|
||||||
log_query_mysockaddr(F_QUERY | F_FORWARD, daemon->namebuff,
|
log_query_mysockaddr(F_QUERY | F_FORWARD, daemon->namebuff,
|
||||||
&peer_addr, types);
|
&peer_addr, auth_dns ? "auth" : "query", qtype);
|
||||||
|
|
||||||
|
#ifdef HAVE_CONNTRACK
|
||||||
|
is_single_query = 1;
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_AUTH
|
#ifdef HAVE_AUTH
|
||||||
/* find queries for zones we're authoritative for, and answer them directly */
|
/* find queries for zones we're authoritative for, and answer them directly */
|
||||||
@@ -1830,12 +1996,33 @@ unsigned char *tcp_request(int confd, time_t now,
|
|||||||
do_bit = 1; /* do bit */
|
do_bit = 1; /* do bit */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_CONNTRACK
|
||||||
#ifdef HAVE_AUTH
|
#ifdef HAVE_AUTH
|
||||||
if (auth_dns)
|
if (!auth_dns || local_auth)
|
||||||
|
#endif
|
||||||
|
if (option_bool(OPT_CMARK_ALST_EN) && have_mark && ((u32)mark & daemon->allowlist_mask))
|
||||||
|
allowed = is_query_allowed_for_mark((u32)mark, is_single_query ? daemon->namebuff : NULL);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (0);
|
||||||
|
#ifdef HAVE_CONNTRACK
|
||||||
|
else if (!allowed)
|
||||||
|
{
|
||||||
|
u16 swap = htons(EDE_BLOCKED);
|
||||||
|
|
||||||
|
m = answer_disallowed(header, size, (u32)mark, is_single_query ? daemon->namebuff : NULL);
|
||||||
|
|
||||||
|
if (have_pseudoheader && m != 0)
|
||||||
|
m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz,
|
||||||
|
EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_AUTH
|
||||||
|
else if (auth_dns)
|
||||||
m = answer_auth(header, ((char *) header) + 65536, (size_t)size, now, &peer_addr,
|
m = answer_auth(header, ((char *) header) + 65536, (size_t)size, now, &peer_addr,
|
||||||
local_auth, do_bit, have_pseudoheader);
|
local_auth, do_bit, have_pseudoheader);
|
||||||
else
|
|
||||||
#endif
|
#endif
|
||||||
|
else
|
||||||
{
|
{
|
||||||
int ad_reqd = do_bit;
|
int ad_reqd = do_bit;
|
||||||
/* RFC 6840 5.7 */
|
/* RFC 6840 5.7 */
|
||||||
@@ -1854,13 +2041,24 @@ unsigned char *tcp_request(int confd, time_t now,
|
|||||||
struct server *master;
|
struct server *master;
|
||||||
int start;
|
int start;
|
||||||
|
|
||||||
|
if (lookup_domain(daemon->namebuff, gotname, &first, &last))
|
||||||
|
flags = is_local_answer(now, first, daemon->namebuff);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* No configured servers */
|
||||||
|
ede = EDE_NOT_READY;
|
||||||
|
flags = 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* don't forward A or AAAA queries for simple names, except the empty name */
|
/* don't forward A or AAAA queries for simple names, except the empty name */
|
||||||
if (option_bool(OPT_NODOTS_LOCAL) &&
|
if (!flags &&
|
||||||
|
option_bool(OPT_NODOTS_LOCAL) &&
|
||||||
(gotname & (F_IPV4 | F_IPV6)) &&
|
(gotname & (F_IPV4 | F_IPV6)) &&
|
||||||
!strchr(daemon->namebuff, '.') &&
|
!strchr(daemon->namebuff, '.') &&
|
||||||
strlen(daemon->namebuff) != 0)
|
strlen(daemon->namebuff) != 0)
|
||||||
flags = F_NOERR;
|
flags = check_for_local_domain(daemon->namebuff, now) ? F_NOERR : F_NXDOMAIN;
|
||||||
else if (lookup_domain(daemon->namebuff, gotname, &first, &last) && !(flags = is_local_answer(now, first, daemon->namebuff)))
|
|
||||||
|
if (!flags && ede != EDE_NOT_READY)
|
||||||
{
|
{
|
||||||
master = daemon->serverarray[first];
|
master = daemon->serverarray[first];
|
||||||
|
|
||||||
@@ -1890,12 +2088,15 @@ unsigned char *tcp_request(int confd, time_t now,
|
|||||||
|
|
||||||
/* Loop round available servers until we succeed in connecting to one. */
|
/* Loop round available servers until we succeed in connecting to one. */
|
||||||
if ((m = tcp_talk(first, last, start, packet, size, have_mark, mark, &serv)) == 0)
|
if ((m = tcp_talk(first, last, start, packet, size, have_mark, mark, &serv)) == 0)
|
||||||
|
{
|
||||||
|
ede = EDE_NETERR;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
/* get query name again for logging - may have been overwritten */
|
/* get query name again for logging - may have been overwritten */
|
||||||
if (!(gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype)))
|
if (!(gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype)))
|
||||||
strcpy(daemon->namebuff, "query");
|
strcpy(daemon->namebuff, "query");
|
||||||
log_query_mysockaddr(F_SERVER | F_FORWARD, daemon->namebuff, &serv->addr, NULL);
|
log_query_mysockaddr(F_SERVER | F_FORWARD, daemon->namebuff, &serv->addr, NULL, 0);
|
||||||
|
|
||||||
#ifdef HAVE_DNSSEC
|
#ifdef HAVE_DNSSEC
|
||||||
if (option_bool(OPT_DNSSEC_VALID) && !checking_disabled && (master->flags & SERV_DO_DNSSEC))
|
if (option_bool(OPT_DNSSEC_VALID) && !checking_disabled && (master->flags & SERV_DO_DNSSEC))
|
||||||
@@ -1905,27 +2106,29 @@ unsigned char *tcp_request(int confd, time_t now,
|
|||||||
serv, have_mark, mark, &keycount);
|
serv, have_mark, mark, &keycount);
|
||||||
char *result, *domain = "result";
|
char *result, *domain = "result";
|
||||||
|
|
||||||
if (status == STAT_ABANDONED)
|
union all_addr a;
|
||||||
|
a.log.ede = ede = errflags_to_ede(status);
|
||||||
|
|
||||||
|
if (STAT_ISEQUAL(status, STAT_ABANDONED))
|
||||||
{
|
{
|
||||||
result = "ABANDONED";
|
result = "ABANDONED";
|
||||||
status = STAT_BOGUS;
|
status = STAT_BOGUS;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
result = (status == STAT_SECURE ? "SECURE" : (status == STAT_INSECURE ? "INSECURE" : "BOGUS"));
|
result = (STAT_ISEQUAL(status, STAT_SECURE) ? "SECURE" : (STAT_ISEQUAL(status, STAT_INSECURE) ? "INSECURE" : "BOGUS"));
|
||||||
|
|
||||||
if (status == STAT_BOGUS && extract_request(header, m, daemon->namebuff, NULL))
|
if (STAT_ISEQUAL(status, STAT_SECURE))
|
||||||
domain = daemon->namebuff;
|
cache_secure = 1;
|
||||||
|
else if (STAT_ISEQUAL(status, STAT_BOGUS))
|
||||||
log_query(F_SECSTAT, domain, NULL, result);
|
|
||||||
|
|
||||||
if (status == STAT_BOGUS)
|
|
||||||
{
|
{
|
||||||
no_cache_dnssec = 1;
|
no_cache_dnssec = 1;
|
||||||
bogusanswer = 1;
|
bogusanswer = 1;
|
||||||
|
|
||||||
|
if (extract_request(header, m, daemon->namebuff, NULL))
|
||||||
|
domain = daemon->namebuff;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status == STAT_SECURE)
|
log_query(F_SECSTAT, domain, &a, result, 0);
|
||||||
cache_secure = 1;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -1942,7 +2145,7 @@ unsigned char *tcp_request(int confd, time_t now,
|
|||||||
|
|
||||||
m = process_reply(header, now, serv, (unsigned int)m,
|
m = process_reply(header, now, serv, (unsigned int)m,
|
||||||
option_bool(OPT_NO_REBIND) && !norebind, no_cache_dnssec, cache_secure, bogusanswer,
|
option_bool(OPT_NO_REBIND) && !norebind, no_cache_dnssec, cache_secure, bogusanswer,
|
||||||
ad_reqd, do_bit, added_pheader, check_subnet, &peer_addr);
|
ad_reqd, do_bit, added_pheader, check_subnet, &peer_addr, ((unsigned char *)header) + 65536, ede);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1950,17 +2153,32 @@ unsigned char *tcp_request(int confd, time_t now,
|
|||||||
/* In case of local answer or no connections made. */
|
/* In case of local answer or no connections made. */
|
||||||
if (m == 0)
|
if (m == 0)
|
||||||
{
|
{
|
||||||
if (!(m = make_local_answer(flags, gotname, size, header, daemon->namebuff, first, last)))
|
if (!(m = make_local_answer(flags, gotname, size, header, daemon->namebuff,
|
||||||
|
((char *) header) + 65536, first, last, ede)))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (have_pseudoheader)
|
if (have_pseudoheader)
|
||||||
|
{
|
||||||
|
u16 swap = htons((u16)ede);
|
||||||
|
|
||||||
|
if (ede != EDE_UNSET)
|
||||||
|
m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0);
|
||||||
|
else
|
||||||
m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0);
|
m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
check_log_writer(1);
|
check_log_writer(1);
|
||||||
|
|
||||||
*length = htons(m);
|
*length = htons(m);
|
||||||
|
|
||||||
|
#if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS)
|
||||||
|
#ifdef HAVE_AUTH
|
||||||
|
if (!auth_dns || local_auth)
|
||||||
|
#endif
|
||||||
|
if (option_bool(OPT_CMARK_ALST_EN) && have_mark && ((u32)mark & daemon->allowlist_mask))
|
||||||
|
report_addresses(header, m, mark);
|
||||||
|
#endif
|
||||||
if (!read_write(confd, packet, m + sizeof(u16), 0))
|
if (!read_write(confd, packet, m + sizeof(u16), 0))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -2065,7 +2283,7 @@ int allocate_rfd(struct randfd_list **fdlp, struct server *serv)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (j == daemon->numrrand)
|
if (!rfd) /* should be when j == daemon->numrrand */
|
||||||
{
|
{
|
||||||
struct randfd_list *rfl_poll;
|
struct randfd_list *rfl_poll;
|
||||||
|
|
||||||
|
|||||||
@@ -76,9 +76,9 @@ unsigned char *hash_questions(struct dns_header *header, size_t plen, char *name
|
|||||||
|
|
||||||
#else /* HAVE_DNSSEC || HAVE_CRYPTOHASH */
|
#else /* HAVE_DNSSEC || HAVE_CRYPTOHASH */
|
||||||
|
|
||||||
#define SHA256_BLOCK_SIZE 32 // SHA256 outputs a 32 byte digest
|
#define SHA256_BLOCK_SIZE 32 /* SHA256 outputs a 32 byte digest */
|
||||||
typedef unsigned char BYTE; // 8-bit byte
|
typedef unsigned char BYTE; /* 8-bit byte */
|
||||||
typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines
|
typedef unsigned int WORD; /* 32-bit word, change to "long" for 16-bit machines */
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
BYTE data[64];
|
BYTE data[64];
|
||||||
@@ -238,7 +238,7 @@ static void sha256_final(SHA256_CTX *ctx, BYTE hash[])
|
|||||||
|
|
||||||
i = ctx->datalen;
|
i = ctx->datalen;
|
||||||
|
|
||||||
// Pad whatever data is left in the buffer.
|
/* Pad whatever data is left in the buffer. */
|
||||||
if (ctx->datalen < 56)
|
if (ctx->datalen < 56)
|
||||||
{
|
{
|
||||||
ctx->data[i++] = 0x80;
|
ctx->data[i++] = 0x80;
|
||||||
@@ -254,7 +254,7 @@ static void sha256_final(SHA256_CTX *ctx, BYTE hash[])
|
|||||||
memset(ctx->data, 0, 56);
|
memset(ctx->data, 0, 56);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append to the padding the total message's length in bits and transform.
|
/* Append to the padding the total message's length in bits and transform. */
|
||||||
ctx->bitlen += ctx->datalen * 8;
|
ctx->bitlen += ctx->datalen * 8;
|
||||||
ctx->data[63] = ctx->bitlen;
|
ctx->data[63] = ctx->bitlen;
|
||||||
ctx->data[62] = ctx->bitlen >> 8;
|
ctx->data[62] = ctx->bitlen >> 8;
|
||||||
@@ -266,8 +266,8 @@ static void sha256_final(SHA256_CTX *ctx, BYTE hash[])
|
|||||||
ctx->data[56] = ctx->bitlen >> 56;
|
ctx->data[56] = ctx->bitlen >> 56;
|
||||||
sha256_transform(ctx, ctx->data);
|
sha256_transform(ctx, ctx->data);
|
||||||
|
|
||||||
// Since this implementation uses little endian byte ordering and SHA uses big endian,
|
/* Since this implementation uses little endian byte ordering and SHA uses big endian,
|
||||||
// reverse all the bytes when copying the final state to the output hash.
|
reverse all the bytes when copying the final state to the output hash. */
|
||||||
for (i = 0; i < 4; ++i)
|
for (i = 0; i < 4; ++i)
|
||||||
{
|
{
|
||||||
hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff;
|
hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff;
|
||||||
|
|||||||
+8
-3
@@ -236,7 +236,6 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
|
|||||||
else
|
else
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|
||||||
/* stringify MAC into dhcp_buff */
|
/* stringify MAC into dhcp_buff */
|
||||||
p = daemon->dhcp_buff;
|
p = daemon->dhcp_buff;
|
||||||
if (data.hwaddr_type != ARPHRD_ETHER || data.hwaddr_len == 0)
|
if (data.hwaddr_type != ARPHRD_ETHER || data.hwaddr_len == 0)
|
||||||
@@ -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");
|
buf = grab_extradata_lua(buf, end, "relay_address");
|
||||||
else if (data.giaddr.s_addr != 0)
|
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");
|
lua_setfield(lua, -2, "relay_address");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -612,7 +612,12 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
|
|||||||
if (is6)
|
if (is6)
|
||||||
buf = grab_extradata(buf, end, "DNSMASQ_RELAY_ADDRESS", &err);
|
buf = grab_extradata(buf, end, "DNSMASQ_RELAY_ADDRESS", &err);
|
||||||
else
|
else
|
||||||
my_setenv("DNSMASQ_RELAY_ADDRESS", data.giaddr.s_addr != 0 ? inet_ntoa(data.giaddr) : NULL, &err);
|
{
|
||||||
|
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++)
|
for (i = 0; buf; i++)
|
||||||
{
|
{
|
||||||
|
|||||||
+8
-1
@@ -148,13 +148,20 @@ void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revh
|
|||||||
if (!(ah->flags & flag))
|
if (!(ah->flags & flag))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (stat(ah->fname, &buf) == -1 || !(S_ISDIR(buf.st_mode)))
|
if (stat(ah->fname, &buf) == -1)
|
||||||
{
|
{
|
||||||
my_syslog(LOG_ERR, _("bad dynamic directory %s: %s"),
|
my_syslog(LOG_ERR, _("bad dynamic directory %s: %s"),
|
||||||
ah->fname, strerror(errno));
|
ah->fname, strerror(errno));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!(S_ISDIR(buf.st_mode)))
|
||||||
|
{
|
||||||
|
my_syslog(LOG_ERR, _("bad dynamic directory %s: %s"),
|
||||||
|
ah->fname, _("not a directory"));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!(ah->flags & AH_WD_DONE))
|
if (!(ah->flags & AH_WD_DONE))
|
||||||
{
|
{
|
||||||
ah->wd = inotify_add_watch(daemon->inotifyfd, ah->fname, IN_CLOSE_WRITE | IN_MOVED_TO);
|
ah->wd = inotify_add_watch(daemon->inotifyfd, ah->fname, IN_CLOSE_WRITE | IN_MOVED_TO);
|
||||||
|
|||||||
@@ -1021,6 +1021,7 @@ void lease_set_hostname(struct dhcp_lease *lease, const char *name, int auth, ch
|
|||||||
}
|
}
|
||||||
|
|
||||||
kill_name(lease_tmp);
|
kill_name(lease_tmp);
|
||||||
|
lease_tmp->flags |= LEASE_CHANGED; /* run script on change */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-2
@@ -31,11 +31,13 @@ void loop_send_probes()
|
|||||||
identifiable, via the uid. If we see that query back again, then the server is looping, and we should not use it. */
|
identifiable, via the uid. If we see that query back again, then the server is looping, and we should not use it. */
|
||||||
for (serv = daemon->servers; serv; serv = serv->next)
|
for (serv = daemon->servers; serv; serv = serv->next)
|
||||||
if (strlen(serv->domain) == 0 &&
|
if (strlen(serv->domain) == 0 &&
|
||||||
!(serv->flags & (SERV_FOR_NODOTS | SERV_LOOP)))
|
!(serv->flags & (SERV_FOR_NODOTS)))
|
||||||
{
|
{
|
||||||
ssize_t len = loop_make_probe(serv->uid);
|
ssize_t len = loop_make_probe(serv->uid);
|
||||||
int fd;
|
int fd;
|
||||||
|
|
||||||
|
serv->flags &= ~SERV_LOOP;
|
||||||
|
|
||||||
if ((fd = allocate_rfd(&rfds, serv)) == -1)
|
if ((fd = allocate_rfd(&rfds, serv)) == -1)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -101,7 +103,7 @@ int detect_loop(char *query, int type)
|
|||||||
uid == serv->uid)
|
uid == serv->uid)
|
||||||
{
|
{
|
||||||
serv->flags |= SERV_LOOP;
|
serv->flags |= SERV_LOOP;
|
||||||
check_servers(); /* log new state */
|
check_servers(1); /* log new state - don't send more probes. */
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+20
-150
@@ -1494,149 +1494,7 @@ void pre_allocate_sfds(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void mark_servers(int flag)
|
void check_servers(int no_loop_check)
|
||||||
{
|
|
||||||
struct server *serv;
|
|
||||||
|
|
||||||
/* mark everything with argument flag */
|
|
||||||
for (serv = daemon->servers; serv; serv = serv->next)
|
|
||||||
{
|
|
||||||
if (serv->flags & flag)
|
|
||||||
serv->flags |= SERV_MARK;
|
|
||||||
#ifdef HAVE_LOOP
|
|
||||||
/* Give looped servers another chance */
|
|
||||||
serv->flags &= ~SERV_LOOP;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
for (serv = daemon->local_domains; serv; serv = serv->next)
|
|
||||||
if (serv->flags & flag)
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#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;
|
|
||||||
char *domain_str;
|
|
||||||
|
|
||||||
if (!domain)
|
|
||||||
domain = "";
|
|
||||||
|
|
||||||
/* If the server is USE_RESOLV or LITERAL_ADDRES, it lives on the local_domains chain.
|
|
||||||
NOTE that we can get local=/domain/ here, but NOT address=/domain/1.2.3.4 */
|
|
||||||
#define SERV_IS_LOCAL (SERV_USE_RESOLV | SERV_LITERAL_ADDRESS)
|
|
||||||
|
|
||||||
/* See if there is a suitable candidate, and unmark */
|
|
||||||
for (serv = (flags & SERV_IS_LOCAL) ? daemon->local_domains : daemon->servers; serv; serv = serv->next)
|
|
||||||
if ((serv->flags & SERV_MARK) && hostname_isequal(domain, serv->domain))
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (serv)
|
|
||||||
domain_str = serv->domain;
|
|
||||||
else if ((serv = whine_malloc(sizeof (struct server))))
|
|
||||||
{
|
|
||||||
/* Not found, create a new one. */
|
|
||||||
if (!(domain_str = whine_malloc(strlen(domain)+1)))
|
|
||||||
{
|
|
||||||
free(serv);
|
|
||||||
serv = NULL;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
strcpy(domain_str, domain);
|
|
||||||
|
|
||||||
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 (serv)
|
|
||||||
{
|
|
||||||
if (!(flags & SERV_IS_LOCAL))
|
|
||||||
memset(serv, 0, sizeof(struct server));
|
|
||||||
|
|
||||||
serv->flags = flags;
|
|
||||||
serv->domain = domain_str;
|
|
||||||
|
|
||||||
|
|
||||||
if (!(flags & SERV_IS_LOCAL))
|
|
||||||
{
|
|
||||||
serv->queries = serv->failed_queries = 0;
|
|
||||||
#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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void check_servers(void)
|
|
||||||
{
|
{
|
||||||
struct irec *iface;
|
struct irec *iface;
|
||||||
struct server *serv;
|
struct server *serv;
|
||||||
@@ -1644,6 +1502,14 @@ void check_servers(void)
|
|||||||
int port = 0, count;
|
int port = 0, count;
|
||||||
int locals = 0;
|
int locals = 0;
|
||||||
|
|
||||||
|
#ifdef HAVE_LOOP
|
||||||
|
if (!no_loop_check)
|
||||||
|
loop_send_probes();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* clear all marks. */
|
||||||
|
mark_servers(0);
|
||||||
|
|
||||||
/* interface may be new since startup */
|
/* interface may be new since startup */
|
||||||
if (!option_bool(OPT_NOWILD))
|
if (!option_bool(OPT_NOWILD))
|
||||||
enumerate_interfaces(0);
|
enumerate_interfaces(0);
|
||||||
@@ -1720,12 +1586,15 @@ void check_servers(void)
|
|||||||
if (serv->sfd)
|
if (serv->sfd)
|
||||||
serv->sfd->used = 1;
|
serv->sfd->used = 1;
|
||||||
|
|
||||||
|
if (count == SERVERS_LOGGED)
|
||||||
|
my_syslog(LOG_INFO, _("more servers are defined but not logged"));
|
||||||
|
|
||||||
if (++count > SERVERS_LOGGED)
|
if (++count > SERVERS_LOGGED)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (strlen(serv->domain) != 0 || (serv->flags & SERV_FOR_NODOTS))
|
if (strlen(serv->domain) != 0 || (serv->flags & SERV_FOR_NODOTS))
|
||||||
{
|
{
|
||||||
char *s1, *s2, *s3 = "";
|
char *s1, *s2, *s3 = "", *s4 = "";
|
||||||
|
|
||||||
#ifdef HAVE_DNSSEC
|
#ifdef HAVE_DNSSEC
|
||||||
if (option_bool(OPT_DNSSEC_VALID) && !(serv->flags & SERV_DO_DNSSEC))
|
if (option_bool(OPT_DNSSEC_VALID) && !(serv->flags & SERV_DO_DNSSEC))
|
||||||
@@ -1736,9 +1605,9 @@ void check_servers(void)
|
|||||||
else if (strlen(serv->domain) == 0)
|
else if (strlen(serv->domain) == 0)
|
||||||
s1 = _("default"), s2 = "";
|
s1 = _("default"), s2 = "";
|
||||||
else
|
else
|
||||||
s1 = _("domain"), s2 = serv->domain;
|
s1 = _("domain"), s2 = serv->domain, s4 = (serv->flags & SERV_WILDCARD) ? "*" : "";
|
||||||
|
|
||||||
my_syslog(LOG_INFO, _("using nameserver %s#%d for %s %s %s"), daemon->namebuff, port, s1, s2, s3);
|
my_syslog(LOG_INFO, _("using nameserver %s#%d for %s %s%s %s"), daemon->namebuff, port, s1, s4, s2, s3);
|
||||||
}
|
}
|
||||||
#ifdef HAVE_LOOP
|
#ifdef HAVE_LOOP
|
||||||
else if (serv->flags & SERV_LOOP)
|
else if (serv->flags & SERV_LOOP)
|
||||||
@@ -1757,7 +1626,8 @@ void check_servers(void)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
if ((serv->flags & SERV_LITERAL_ADDRESS) &&
|
if ((serv->flags & SERV_LITERAL_ADDRESS) &&
|
||||||
!(serv->flags & (SERV_6ADDR | SERV_4ADDR | SERV_ALL_ZEROS)))
|
!(serv->flags & (SERV_6ADDR | SERV_4ADDR | SERV_ALL_ZEROS)) &&
|
||||||
|
strlen(serv->domain))
|
||||||
{
|
{
|
||||||
count--;
|
count--;
|
||||||
if (++locals <= LOCALS_LOGGED)
|
if (++locals <= LOCALS_LOGGED)
|
||||||
@@ -1786,7 +1656,7 @@ void check_servers(void)
|
|||||||
up = &sfd->next;
|
up = &sfd->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanup_servers();
|
cleanup_servers(); /* remove servers we just deleted. */
|
||||||
build_server_array();
|
build_server_array();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1822,7 +1692,7 @@ int reload_servers(char *fname)
|
|||||||
memset(&addr, 0, sizeof(addr));
|
memset(&addr, 0, sizeof(addr));
|
||||||
memset(&source_addr, 0, sizeof(source_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
|
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||||
source_addr.in.sin_len = addr.in.sin_len = sizeof(source_addr.in);
|
source_addr.in.sin_len = addr.in.sin_len = sizeof(source_addr.in);
|
||||||
@@ -1860,7 +1730,7 @@ int reload_servers(char *fname)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
add_update_server(SERV_FROM_RESOLV, &addr, &source_addr, NULL, NULL);
|
add_update_server(SERV_FROM_RESOLV, &addr, &source_addr, NULL, NULL, NULL);
|
||||||
gotone = 1;
|
gotone = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,94 @@
|
|||||||
|
/* 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"
|
||||||
|
|
||||||
|
#if defined (HAVE_NFTSET) && defined (HAVE_LINUX_NETWORK)
|
||||||
|
|
||||||
|
#include <nftables/libnftables.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
static struct nft_ctx *ctx = NULL;
|
||||||
|
static const char *cmd_add = "add element %s { %s }";
|
||||||
|
static const char *cmd_del = "delete element %s { %s }";
|
||||||
|
|
||||||
|
void nftset_init()
|
||||||
|
{
|
||||||
|
ctx = nft_ctx_new(NFT_CTX_DEFAULT);
|
||||||
|
if (ctx == NULL)
|
||||||
|
die(_("failed to create nftset context"), NULL, EC_MISC);
|
||||||
|
|
||||||
|
/* disable libnftables output */
|
||||||
|
nft_ctx_buffer_error(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
int add_to_nftset(const char *setname, const union all_addr *ipaddr, int flags, int remove)
|
||||||
|
{
|
||||||
|
const char *cmd = remove ? cmd_del : cmd_add;
|
||||||
|
int ret, af = (flags & F_IPV4) ? AF_INET : AF_INET6;
|
||||||
|
size_t new_sz;
|
||||||
|
char *new, *err, *nl;
|
||||||
|
static char *cmd_buf = NULL;
|
||||||
|
static size_t cmd_buf_sz = 0;
|
||||||
|
|
||||||
|
inet_ntop(af, ipaddr, daemon->addrbuff, ADDRSTRLEN);
|
||||||
|
|
||||||
|
if (setname[1] == ' ' && (setname[0] == '4' || setname[0] == '6'))
|
||||||
|
{
|
||||||
|
if (setname[0] == '4' && !(flags & F_IPV4))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (setname[0] == '6' && !(flags & F_IPV6))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
setname += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd_buf_sz == 0)
|
||||||
|
new_sz = 150; /* initial allocation */
|
||||||
|
else
|
||||||
|
new_sz = snprintf(cmd_buf, cmd_buf_sz, cmd, setname, daemon->addrbuff);
|
||||||
|
|
||||||
|
if (new_sz > cmd_buf_sz)
|
||||||
|
{
|
||||||
|
if (!(new = whine_malloc(new_sz + 10)))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (cmd_buf)
|
||||||
|
free(cmd_buf);
|
||||||
|
cmd_buf = new;
|
||||||
|
cmd_buf_sz = new_sz + 10;
|
||||||
|
snprintf(cmd_buf, cmd_buf_sz, cmd, setname, daemon->addrbuff);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = nft_run_cmd_from_buffer(ctx, cmd_buf);
|
||||||
|
err = (char *)nft_ctx_get_error_buffer(ctx);
|
||||||
|
|
||||||
|
if (ret != 0)
|
||||||
|
{
|
||||||
|
/* Log only first line of error return. */
|
||||||
|
if ((nl = strchr(err, '\n')))
|
||||||
|
*nl = 0;
|
||||||
|
my_syslog(LOG_ERR, "nftset %s %s", setname, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
+460
-242
File diff suppressed because it is too large.
Load diff
+386
@@ -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_DEBUG, __VA_ARGS__); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define ASSERT(condition) \
|
||||||
|
do { \
|
||||||
|
if (!(condition)) \
|
||||||
|
my_syslog(LOG_ERR, _("[pattern.c:%d] Assertion failure: %s"), __LINE__, #condition); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether a given string value matches against a glob pattern
|
||||||
|
* which may contain zero-or-more-character wildcards denoted by '*'.
|
||||||
|
*
|
||||||
|
* Based on "Glob Matching Can Be Simple And Fast Too" by Russ Cox,
|
||||||
|
* See https://research.swtch.com/glob
|
||||||
|
*
|
||||||
|
* @param value A string value.
|
||||||
|
* @param num_value_bytes The number of bytes of the string value.
|
||||||
|
* @param pattern A glob pattern.
|
||||||
|
* @param num_pattern_bytes The number of bytes of the glob pattern.
|
||||||
|
*
|
||||||
|
* @return 1 If the provided value matches against the glob pattern.
|
||||||
|
* @return 0 Otherwise.
|
||||||
|
*/
|
||||||
|
static int is_string_matching_glob_pattern(
|
||||||
|
const char *value,
|
||||||
|
size_t num_value_bytes,
|
||||||
|
const char *pattern,
|
||||||
|
size_t num_pattern_bytes)
|
||||||
|
{
|
||||||
|
ASSERT(value);
|
||||||
|
ASSERT(pattern);
|
||||||
|
|
||||||
|
size_t value_index = 0;
|
||||||
|
size_t next_value_index = 0;
|
||||||
|
size_t pattern_index = 0;
|
||||||
|
size_t next_pattern_index = 0;
|
||||||
|
while (value_index < num_value_bytes || pattern_index < num_pattern_bytes)
|
||||||
|
{
|
||||||
|
if (pattern_index < num_pattern_bytes)
|
||||||
|
{
|
||||||
|
char pattern_character = pattern[pattern_index];
|
||||||
|
if ('a' <= pattern_character && pattern_character <= 'z')
|
||||||
|
pattern_character -= 'a' - 'A';
|
||||||
|
if (pattern_character == '*')
|
||||||
|
{
|
||||||
|
/* zero-or-more-character wildcard */
|
||||||
|
/* Try to match at value_index, otherwise restart at value_index + 1 next. */
|
||||||
|
next_pattern_index = pattern_index;
|
||||||
|
pattern_index++;
|
||||||
|
if (value_index < num_value_bytes)
|
||||||
|
next_value_index = value_index + 1;
|
||||||
|
else
|
||||||
|
next_value_index = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* ordinary character */
|
||||||
|
if (value_index < num_value_bytes)
|
||||||
|
{
|
||||||
|
char value_character = value[value_index];
|
||||||
|
if ('a' <= value_character && value_character <= 'z')
|
||||||
|
value_character -= 'a' - 'A';
|
||||||
|
if (value_character == pattern_character)
|
||||||
|
{
|
||||||
|
pattern_index++;
|
||||||
|
value_index++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (next_value_index)
|
||||||
|
{
|
||||||
|
pattern_index = next_pattern_index;
|
||||||
|
value_index = next_value_index;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether a given string value represents a valid DNS name.
|
||||||
|
*
|
||||||
|
* - DNS names must adhere to RFC 1123: 1 to 253 characters in length, consisting of a sequence of labels
|
||||||
|
* delimited by dots ("."). Each label must be 1 to 63 characters in length, contain only
|
||||||
|
* ASCII letters ("a"-"Z"), digits ("0"-"9"), or hyphens ("-") and must not start or end with a hyphen.
|
||||||
|
*
|
||||||
|
* - A valid name must be fully qualified, i.e., consist of at least two labels.
|
||||||
|
* The final label must not be fully numeric, and must not be the "local" pseudo-TLD.
|
||||||
|
*
|
||||||
|
* - Examples:
|
||||||
|
* Valid: "example.com"
|
||||||
|
* Invalid: "ipcamera", "ipcamera.local", "8.8.8.8"
|
||||||
|
*
|
||||||
|
* @param value A string value.
|
||||||
|
*
|
||||||
|
* @return 1 If the provided string value is a valid DNS name.
|
||||||
|
* @return 0 Otherwise.
|
||||||
|
*/
|
||||||
|
int is_valid_dns_name(const char *value)
|
||||||
|
{
|
||||||
|
ASSERT(value);
|
||||||
|
|
||||||
|
size_t num_bytes = 0;
|
||||||
|
size_t num_labels = 0;
|
||||||
|
const char *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
|
||||||
@@ -746,6 +746,8 @@ static int add_lla(int index, unsigned int type, char *mac, size_t maclen, void
|
|||||||
add 7 to round up */
|
add 7 to round up */
|
||||||
int len = (maclen + 9) >> 3;
|
int len = (maclen + 9) >> 3;
|
||||||
unsigned char *p = expand(len << 3);
|
unsigned char *p = expand(len << 3);
|
||||||
|
if (!p)
|
||||||
|
return 1;
|
||||||
memset(p, 0, len << 3);
|
memset(p, 0, len << 3);
|
||||||
*p++ = ICMP6_OPT_SOURCE_MAC;
|
*p++ = ICMP6_OPT_SOURCE_MAC;
|
||||||
*p++ = len;
|
*p++ = len;
|
||||||
|
|||||||
+350
-169
@@ -364,7 +364,7 @@ int private_net(struct in_addr addr, int ban_localhost)
|
|||||||
|
|
||||||
return
|
return
|
||||||
(((ip_addr & 0xFF000000) == 0x7F000000) && ban_localhost) /* 127.0.0.0/8 (loopback) */ ||
|
(((ip_addr & 0xFF000000) == 0x7F000000) && ban_localhost) /* 127.0.0.0/8 (loopback) */ ||
|
||||||
((ip_addr & 0xFF000000) == 0x00000000) /* RFC 5735 section 3. "here" network */ ||
|
(((ip_addr & 0xFF000000) == 0x00000000) && ban_localhost) /* RFC 5735 section 3. "here" network */ ||
|
||||||
((ip_addr & 0xFF000000) == 0x0A000000) /* 10.0.0.0/8 (private) */ ||
|
((ip_addr & 0xFF000000) == 0x0A000000) /* 10.0.0.0/8 (private) */ ||
|
||||||
((ip_addr & 0xFFF00000) == 0xAC100000) /* 172.16.0.0/12 (private) */ ||
|
((ip_addr & 0xFFF00000) == 0xAC100000) /* 172.16.0.0/12 (private) */ ||
|
||||||
((ip_addr & 0xFFFF0000) == 0xC0A80000) /* 192.168.0.0/16 (private) */ ||
|
((ip_addr & 0xFFFF0000) == 0xC0A80000) /* 192.168.0.0/16 (private) */ ||
|
||||||
@@ -375,28 +375,32 @@ int private_net(struct in_addr addr, int ban_localhost)
|
|||||||
((ip_addr & 0xFFFFFFFF) == 0xFFFFFFFF) /* 255.255.255.255/32 (broadcast)*/ ;
|
((ip_addr & 0xFFFFFFFF) == 0xFFFFFFFF) /* 255.255.255.255/32 (broadcast)*/ ;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int private_net6(struct in6_addr *a)
|
static int private_net6(struct in6_addr *a, int ban_localhost)
|
||||||
{
|
{
|
||||||
|
/* Block IPv4-mapped IPv6 addresses in private IPv4 address space */
|
||||||
|
if (IN6_IS_ADDR_V4MAPPED(a))
|
||||||
|
{
|
||||||
|
struct in_addr v4;
|
||||||
|
v4.s_addr = ((const uint32_t *) (a))[3];
|
||||||
|
return private_net(v4, ban_localhost);
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
IN6_IS_ADDR_UNSPECIFIED(a) || /* RFC 6303 4.3 */
|
(IN6_IS_ADDR_UNSPECIFIED(a) && ban_localhost) || /* RFC 6303 4.3 */
|
||||||
IN6_IS_ADDR_LOOPBACK(a) || /* RFC 6303 4.3 */
|
(IN6_IS_ADDR_LOOPBACK(a) && ban_localhost) || /* RFC 6303 4.3 */
|
||||||
IN6_IS_ADDR_LINKLOCAL(a) || /* RFC 6303 4.5 */
|
IN6_IS_ADDR_LINKLOCAL(a) || /* RFC 6303 4.5 */
|
||||||
|
IN6_IS_ADDR_SITELOCAL(a) ||
|
||||||
((unsigned char *)a)[0] == 0xfd || /* RFC 6303 4.4 */
|
((unsigned char *)a)[0] == 0xfd || /* RFC 6303 4.4 */
|
||||||
((u32 *)a)[0] == htonl(0x20010db8); /* RFC 6303 4.6 */
|
((u32 *)a)[0] == htonl(0x20010db8); /* RFC 6303 4.6 */
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *header, size_t qlen, char *name, int *doctored)
|
static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *header, size_t qlen, int *doctored)
|
||||||
{
|
{
|
||||||
int i, qtype, qclass, rdlen;
|
int i, qtype, qclass, rdlen;
|
||||||
|
|
||||||
for (i = count; i != 0; i--)
|
for (i = count; i != 0; i--)
|
||||||
{
|
{
|
||||||
if (name && option_bool(OPT_LOG))
|
if (!(p = skip_name(p, header, qlen, 10)))
|
||||||
{
|
|
||||||
if (!extract_name(header, qlen, &p, name, 1, 10))
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
else if (!(p = skip_name(p, header, qlen, 10)))
|
|
||||||
return 0; /* bad packet */
|
return 0; /* bad packet */
|
||||||
|
|
||||||
GETSHORT(qtype, p);
|
GETSHORT(qtype, p);
|
||||||
@@ -435,34 +439,6 @@ static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (qtype == T_TXT && name && option_bool(OPT_LOG))
|
|
||||||
{
|
|
||||||
unsigned char *p1 = p;
|
|
||||||
if (!CHECK_LEN(header, p1, qlen, rdlen))
|
|
||||||
return 0;
|
|
||||||
while ((p1 - p) < rdlen)
|
|
||||||
{
|
|
||||||
unsigned int i, len = *p1;
|
|
||||||
unsigned char *p2 = p1;
|
|
||||||
if ((p1 + len - p) >= rdlen)
|
|
||||||
return 0; /* bad packet */
|
|
||||||
/* make counted string zero-term and sanitise */
|
|
||||||
for (i = 0; i < len; i++)
|
|
||||||
{
|
|
||||||
if (!isprint((int)*(p2+1)))
|
|
||||||
break;
|
|
||||||
|
|
||||||
*p2 = *(p2+1);
|
|
||||||
p2++;
|
|
||||||
}
|
|
||||||
*p2 = 0;
|
|
||||||
my_syslog(LOG_INFO, "reply %s is %s", name, p1);
|
|
||||||
/* restore */
|
|
||||||
memmove(p1 + 1, p1, i);
|
|
||||||
*p1 = len;
|
|
||||||
p1 += len+1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ADD_RDLEN(header, p, qlen, rdlen))
|
if (!ADD_RDLEN(header, p, qlen, rdlen))
|
||||||
return 0; /* bad packet */
|
return 0; /* bad packet */
|
||||||
@@ -471,7 +447,7 @@ static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int find_soa(struct dns_header *header, size_t qlen, char *name, int *doctored)
|
static int find_soa(struct dns_header *header, size_t qlen, int *doctored)
|
||||||
{
|
{
|
||||||
unsigned char *p;
|
unsigned char *p;
|
||||||
int qtype, qclass, rdlen;
|
int qtype, qclass, rdlen;
|
||||||
@@ -480,7 +456,7 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *doc
|
|||||||
|
|
||||||
/* first move to NS section and find TTL from any SOA section */
|
/* first move to NS section and find TTL from any SOA section */
|
||||||
if (!(p = skip_questions(header, qlen)) ||
|
if (!(p = skip_questions(header, qlen)) ||
|
||||||
!(p = do_doctor(p, ntohs(header->ancount), header, qlen, name, doctored)))
|
!(p = do_doctor(p, ntohs(header->ancount), header, qlen, doctored)))
|
||||||
return 0; /* bad packet */
|
return 0; /* bad packet */
|
||||||
|
|
||||||
for (i = ntohs(header->nscount); i != 0; i--)
|
for (i = ntohs(header->nscount); i != 0; i--)
|
||||||
@@ -516,7 +492,7 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *doc
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* rewrite addresses in additional section too */
|
/* rewrite addresses in additional section too */
|
||||||
if (!do_doctor(p, ntohs(header->arcount), header, qlen, NULL, doctored))
|
if (!do_doctor(p, ntohs(header->arcount), header, qlen, doctored))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (!found_soa)
|
if (!found_soa)
|
||||||
@@ -525,16 +501,50 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *doc
|
|||||||
return minttl;
|
return minttl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Print TXT reply to log */
|
||||||
|
static int print_txt(struct dns_header *header, const size_t qlen, char *name,
|
||||||
|
unsigned char *p, const int ardlen, int secflag)
|
||||||
|
{
|
||||||
|
unsigned char *p1 = p;
|
||||||
|
if (!CHECK_LEN(header, p1, qlen, ardlen))
|
||||||
|
return 0;
|
||||||
|
/* Loop over TXT payload */
|
||||||
|
while ((p1 - p) < ardlen)
|
||||||
|
{
|
||||||
|
unsigned int i, len = *p1;
|
||||||
|
unsigned char *p3 = p1;
|
||||||
|
if ((p1 + len - p) >= ardlen)
|
||||||
|
return 0; /* bad packet */
|
||||||
|
|
||||||
|
/* make counted string zero-term and sanitise */
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
if (!isprint((int)*(p3+1)))
|
||||||
|
break;
|
||||||
|
*p3 = *(p3+1);
|
||||||
|
p3++;
|
||||||
|
}
|
||||||
|
|
||||||
|
*p3 = 0;
|
||||||
|
log_query(secflag | F_FORWARD | F_UPSTREAM, name, NULL, (char*)p1, 0);
|
||||||
|
/* restore */
|
||||||
|
memmove(p1 + 1, p1, i);
|
||||||
|
*p1 = len;
|
||||||
|
p1 += len+1;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* Note that the following code can create CNAME chains that don't point to a real record,
|
/* Note that the following code can create CNAME chains that don't point to a real record,
|
||||||
either because of lack of memory, or lack of SOA records. These are treated by the cache code as
|
either because of lack of memory, or lack of SOA records. These are treated by the cache code as
|
||||||
expired and cleaned out that way.
|
expired and cleaned out that way.
|
||||||
Return 1 if we reject an address because it look like part of dns-rebinding attack. */
|
Return 1 if we reject an address because it look like part of dns-rebinding attack. */
|
||||||
int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t now,
|
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,
|
struct ipsets *ipsets, struct ipsets *nftsets, int is_sign, int check_rebind,
|
||||||
int secure, int *doctored)
|
int no_cache_dnssec, int secure, int *doctored)
|
||||||
{
|
{
|
||||||
unsigned char *p, *p1, *endrr, *namep;
|
unsigned char *p, *p1, *endrr, *namep;
|
||||||
int i, j, qtype, qclass, aqtype, aqclass, ardlen, res, searched_soa = 0;
|
int j, qtype, qclass, aqtype, aqclass, ardlen, res, searched_soa = 0;
|
||||||
unsigned long ttl = 0;
|
unsigned long ttl = 0;
|
||||||
union all_addr addr;
|
union all_addr addr;
|
||||||
#ifdef HAVE_IPSET
|
#ifdef HAVE_IPSET
|
||||||
@@ -542,34 +552,11 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
|||||||
#else
|
#else
|
||||||
(void)ipsets; /* unused */
|
(void)ipsets; /* unused */
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAVE_NFTSET
|
||||||
|
char **nftsets_cur;
|
||||||
cache_start_insert();
|
#else
|
||||||
|
(void)nftsets; /* unused */
|
||||||
/* find_soa is needed for dns_doctor and logging side-effects, so don't call it lazily if there are any. */
|
|
||||||
if (daemon->doctors || option_bool(OPT_LOG) || option_bool(OPT_DNSSEC_VALID))
|
|
||||||
{
|
|
||||||
searched_soa = 1;
|
|
||||||
ttl = find_soa(header, qlen, name, doctored);
|
|
||||||
|
|
||||||
if (*doctored)
|
|
||||||
{
|
|
||||||
if (secure)
|
|
||||||
return 0;
|
|
||||||
#ifdef HAVE_DNSSEC
|
|
||||||
if (option_bool(OPT_DNSSEC_VALID))
|
|
||||||
for (i = 0; i < ntohs(header->ancount); i++)
|
|
||||||
if (daemon->rr_status[i] != 0)
|
|
||||||
return 0;
|
|
||||||
#endif
|
#endif
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* go through the questions. */
|
|
||||||
p = (unsigned char *)(header+1);
|
|
||||||
|
|
||||||
for (i = ntohs(header->qdcount); i != 0; i--)
|
|
||||||
{
|
|
||||||
int found = 0, cname_count = CNAME_CHAIN;
|
int found = 0, cname_count = CNAME_CHAIN;
|
||||||
struct crec *cpp = NULL;
|
struct crec *cpp = NULL;
|
||||||
int flags = RCODE(header) == NXDOMAIN ? F_NXDOMAIN : 0;
|
int flags = RCODE(header) == NXDOMAIN ? F_NXDOMAIN : 0;
|
||||||
@@ -578,24 +565,43 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
|||||||
#endif
|
#endif
|
||||||
unsigned long cttl = ULONG_MAX, attl;
|
unsigned long cttl = ULONG_MAX, attl;
|
||||||
|
|
||||||
namep = p;
|
cache_start_insert();
|
||||||
if (!extract_name(header, qlen, &p, name, 1, 4))
|
|
||||||
|
/* find_soa is needed for dns_doctor side effects, so don't call it lazily if there are any. */
|
||||||
|
if (daemon->doctors || option_bool(OPT_DNSSEC_VALID))
|
||||||
|
{
|
||||||
|
searched_soa = 1;
|
||||||
|
ttl = find_soa(header, qlen, doctored);
|
||||||
|
|
||||||
|
if (*doctored)
|
||||||
|
{
|
||||||
|
if (secure)
|
||||||
|
return 0;
|
||||||
|
#ifdef HAVE_DNSSEC
|
||||||
|
if (option_bool(OPT_DNSSEC_VALID))
|
||||||
|
for (j = 0; j < ntohs(header->ancount); j++)
|
||||||
|
if (daemon->rr_status[j] != 0)
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namep = p = (unsigned char *)(header+1);
|
||||||
|
|
||||||
|
if (ntohs(header->qdcount) != 1 || !extract_name(header, qlen, &p, name, 1, 4))
|
||||||
return 0; /* bad packet */
|
return 0; /* bad packet */
|
||||||
|
|
||||||
GETSHORT(qtype, p);
|
GETSHORT(qtype, p);
|
||||||
GETSHORT(qclass, p);
|
GETSHORT(qclass, p);
|
||||||
|
|
||||||
if (qclass != C_IN)
|
if (qclass != C_IN)
|
||||||
continue;
|
return 0;
|
||||||
|
|
||||||
/* PTRs: we chase CNAMEs here, since we have no way to
|
/* PTRs: we chase CNAMEs here, since we have no way to
|
||||||
represent them in the cache. */
|
represent them in the cache. */
|
||||||
if (qtype == T_PTR)
|
if (qtype == T_PTR)
|
||||||
{
|
{
|
||||||
int name_encoding = in_arpa_name_2_addr(name, &addr);
|
int insert = 1, name_encoding = in_arpa_name_2_addr(name, &addr);
|
||||||
|
|
||||||
if (!name_encoding)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!(flags & F_NXDOMAIN))
|
if (!(flags & F_NXDOMAIN))
|
||||||
{
|
{
|
||||||
@@ -606,15 +612,13 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
|||||||
for (j = 0; j < ntohs(header->ancount); j++)
|
for (j = 0; j < ntohs(header->ancount); j++)
|
||||||
{
|
{
|
||||||
int secflag = 0;
|
int secflag = 0;
|
||||||
unsigned char *tmp = namep;
|
if (!(res = extract_name(header, qlen, &p1, name, 0, 10)))
|
||||||
/* the loop body overwrites the original name, so get it back here. */
|
|
||||||
if (!extract_name(header, qlen, &tmp, name, 1, 0) ||
|
|
||||||
!(res = extract_name(header, qlen, &p1, name, 0, 10)))
|
|
||||||
return 0; /* bad packet */
|
return 0; /* bad packet */
|
||||||
|
|
||||||
GETSHORT(aqtype, p1);
|
GETSHORT(aqtype, p1);
|
||||||
GETSHORT(aqclass, p1);
|
GETSHORT(aqclass, p1);
|
||||||
GETLONG(attl, p1);
|
GETLONG(attl, p1);
|
||||||
|
|
||||||
if ((daemon->max_ttl != 0) && (attl > daemon->max_ttl) && !is_sign)
|
if ((daemon->max_ttl != 0) && (attl > daemon->max_ttl) && !is_sign)
|
||||||
{
|
{
|
||||||
(p1) -= 4;
|
(p1) -= 4;
|
||||||
@@ -629,14 +633,12 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
|||||||
|
|
||||||
if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == T_PTR))
|
if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == T_PTR))
|
||||||
{
|
{
|
||||||
if (!extract_name(header, qlen, &p1, name, 1, 0))
|
|
||||||
return 0;
|
|
||||||
#ifdef HAVE_DNSSEC
|
#ifdef HAVE_DNSSEC
|
||||||
if (option_bool(OPT_DNSSEC_VALID) && daemon->rr_status[j] != 0)
|
if (option_bool(OPT_DNSSEC_VALID) && !no_cache_dnssec && daemon->rr_status[j] != 0)
|
||||||
{
|
{
|
||||||
/* validated RR anywhere in CNAME chain, don't cache. */
|
/* validated RR anywhere in CNAME chain, don't cache. */
|
||||||
if (cname_short || aqtype == T_CNAME)
|
if (cname_short || aqtype == T_CNAME)
|
||||||
return 0;
|
insert = 0;
|
||||||
|
|
||||||
secflag = F_DNSSECOK;
|
secflag = F_DNSSECOK;
|
||||||
/* limit TTL based on signature. */
|
/* limit TTL based on signature. */
|
||||||
@@ -645,6 +647,12 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (aqtype == T_CNAME)
|
||||||
|
log_query(secflag | F_CNAME | F_FORWARD | F_UPSTREAM, name, NULL, NULL, 0);
|
||||||
|
|
||||||
|
if (!extract_name(header, qlen, &p1, name, 1, 0))
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (aqtype == T_CNAME)
|
if (aqtype == T_CNAME)
|
||||||
{
|
{
|
||||||
if (!cname_count--)
|
if (!cname_count--)
|
||||||
@@ -655,8 +663,16 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
|||||||
goto cname_loop;
|
goto cname_loop;
|
||||||
}
|
}
|
||||||
|
|
||||||
cache_insert(name, &addr, C_IN, now, cttl, name_encoding | secflag | F_REVERSE);
|
|
||||||
found = 1;
|
found = 1;
|
||||||
|
|
||||||
|
if (!name_encoding)
|
||||||
|
log_query(secflag | F_FORWARD | F_UPSTREAM, name, NULL, NULL, aqtype);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log_query(name_encoding | secflag | F_REVERSE | F_UPSTREAM, name, &addr, NULL, 0);
|
||||||
|
if (insert)
|
||||||
|
cache_insert(name, &addr, C_IN, now, cttl, name_encoding | secflag | F_REVERSE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
p1 = endrr;
|
p1 = endrr;
|
||||||
@@ -670,17 +686,24 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
|||||||
if (!searched_soa)
|
if (!searched_soa)
|
||||||
{
|
{
|
||||||
searched_soa = 1;
|
searched_soa = 1;
|
||||||
ttl = find_soa(header, qlen, NULL, doctored);
|
ttl = find_soa(header, qlen, doctored);
|
||||||
}
|
}
|
||||||
if (ttl)
|
|
||||||
cache_insert(NULL, &addr, C_IN, now, ttl, name_encoding | F_REVERSE | F_NEG | flags | (secure ? F_DNSSECOK : 0));
|
flags |= F_NEG | (secure ? F_DNSSECOK : 0);
|
||||||
|
if (name_encoding && ttl)
|
||||||
|
{
|
||||||
|
flags |= F_REVERSE | name_encoding;
|
||||||
|
cache_insert(NULL, &addr, C_IN, now, ttl, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
log_query(flags | F_UPSTREAM, name, &addr, NULL, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* everything other than PTR */
|
/* everything other than PTR */
|
||||||
struct crec *newc;
|
struct crec *newc;
|
||||||
int addrlen = 0;
|
int addrlen = 0, insert = 1;
|
||||||
|
|
||||||
if (qtype == T_A)
|
if (qtype == T_A)
|
||||||
{
|
{
|
||||||
@@ -695,7 +718,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
|||||||
else if (qtype == T_SRV)
|
else if (qtype == T_SRV)
|
||||||
flags |= F_SRV;
|
flags |= F_SRV;
|
||||||
else
|
else
|
||||||
continue;
|
insert = 0; /* NOTE: do not cache data from CNAME queries. */
|
||||||
|
|
||||||
cname_loop1:
|
cname_loop1:
|
||||||
if (!(p1 = skip_questions(header, qlen)))
|
if (!(p1 = skip_questions(header, qlen)))
|
||||||
@@ -719,10 +742,17 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
|||||||
GETSHORT(ardlen, p1);
|
GETSHORT(ardlen, p1);
|
||||||
endrr = p1+ardlen;
|
endrr = p1+ardlen;
|
||||||
|
|
||||||
if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == qtype))
|
/* Not what we're looking for? */
|
||||||
|
if (aqclass != C_IN || res == 2)
|
||||||
{
|
{
|
||||||
|
p1 = endrr;
|
||||||
|
if (!CHECK_LEN(header, p1, qlen, 0))
|
||||||
|
return 0; /* bad packet */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef HAVE_DNSSEC
|
#ifdef HAVE_DNSSEC
|
||||||
if (option_bool(OPT_DNSSEC_VALID) && daemon->rr_status[j] != 0)
|
if (option_bool(OPT_DNSSEC_VALID) && !no_cache_dnssec && daemon->rr_status[j] != 0)
|
||||||
{
|
{
|
||||||
secflag = F_DNSSECOK;
|
secflag = F_DNSSECOK;
|
||||||
|
|
||||||
@@ -731,11 +761,16 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
|||||||
attl = daemon->rr_status[j];
|
attl = daemon->rr_status[j];
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (aqtype == T_CNAME)
|
if (aqtype == T_CNAME)
|
||||||
{
|
{
|
||||||
if (!cname_count--)
|
if (!cname_count--)
|
||||||
return 0; /* looped CNAMES */
|
return 0; /* looped CNAMES */
|
||||||
|
|
||||||
|
log_query(secflag | F_CNAME | F_FORWARD | F_UPSTREAM, name, NULL, NULL, 0);
|
||||||
|
|
||||||
|
if (insert)
|
||||||
|
{
|
||||||
if ((newc = cache_insert(name, NULL, C_IN, now, attl, F_CNAME | F_FORWARD | secflag)))
|
if ((newc = cache_insert(name, NULL, C_IN, now, attl, F_CNAME | F_FORWARD | secflag)))
|
||||||
{
|
{
|
||||||
newc->addr.cname.target.cache = NULL;
|
newc->addr.cname.target.cache = NULL;
|
||||||
@@ -751,12 +786,23 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
|||||||
cpp = newc;
|
cpp = newc;
|
||||||
if (attl < cttl)
|
if (attl < cttl)
|
||||||
cttl = attl;
|
cttl = attl;
|
||||||
|
}
|
||||||
|
|
||||||
namep = p1;
|
namep = p1;
|
||||||
if (!extract_name(header, qlen, &p1, name, 1, 0))
|
if (!extract_name(header, qlen, &p1, name, 1, 0))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
if (qtype != T_CNAME)
|
||||||
goto cname_loop1;
|
goto cname_loop1;
|
||||||
|
|
||||||
|
found = 1;
|
||||||
|
}
|
||||||
|
else if (aqtype != qtype)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_DNSSEC
|
||||||
|
if (!option_bool(OPT_DNSSEC_VALID) || aqtype != T_RRSIG)
|
||||||
|
#endif
|
||||||
|
log_query(secflag | F_FORWARD | F_UPSTREAM, name, NULL, NULL, aqtype);
|
||||||
}
|
}
|
||||||
else if (!(flags & F_NXDOMAIN))
|
else if (!(flags & F_NXDOMAIN))
|
||||||
{
|
{
|
||||||
@@ -781,7 +827,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
|||||||
if (!extract_name(header, qlen, &tmp, name, 1, 0))
|
if (!extract_name(header, qlen, &tmp, name, 1, 0))
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
else
|
else if (flags & (F_IPV4 | F_IPV6))
|
||||||
{
|
{
|
||||||
/* copy address into aligned storage */
|
/* copy address into aligned storage */
|
||||||
if (!CHECK_LEN(header, p1, qlen, addrlen))
|
if (!CHECK_LEN(header, p1, qlen, addrlen))
|
||||||
@@ -795,43 +841,27 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
|||||||
private_net(addr.addr4, !option_bool(OPT_LOCAL_REBIND)))
|
private_net(addr.addr4, !option_bool(OPT_LOCAL_REBIND)))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
/* Block IPv4-mapped IPv6 addresses in private IPv4 address space */
|
if ((flags & F_IPV6) &&
|
||||||
if (flags & F_IPV6)
|
private_net6(&addr.addr6, !option_bool(OPT_LOCAL_REBIND)))
|
||||||
{
|
|
||||||
if (IN6_IS_ADDR_V4MAPPED(&addr.addr6))
|
|
||||||
{
|
|
||||||
struct in_addr v4;
|
|
||||||
v4.s_addr = ((const uint32_t *) (&addr.addr6))[3];
|
|
||||||
if (private_net(v4, !option_bool(OPT_LOCAL_REBIND)))
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check for link-local (LL) and site-local (ULA) IPv6 addresses */
|
|
||||||
if (IN6_IS_ADDR_LINKLOCAL(&addr.addr6) ||
|
|
||||||
IN6_IS_ADDR_SITELOCAL(&addr.addr6))
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
/* Check for the IPv6 loopback address (::1) when
|
|
||||||
option rebind-localhost-ok is NOT set */
|
|
||||||
if (!option_bool(OPT_LOCAL_REBIND) &&
|
|
||||||
IN6_IS_ADDR_LOOPBACK(&addr.addr6))
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef HAVE_IPSET
|
#ifdef HAVE_IPSET
|
||||||
if (ipsets && (flags & (F_IPV4 | F_IPV6)))
|
if (ipsets && (flags & (F_IPV4 | F_IPV6)))
|
||||||
{
|
for (ipsets_cur = ipsets->sets; *ipsets_cur; ipsets_cur++)
|
||||||
ipsets_cur = ipsets;
|
if (add_to_ipset(*ipsets_cur, &addr, flags, 0) == 0)
|
||||||
while (*ipsets_cur)
|
log_query((flags & (F_IPV4 | F_IPV6)) | F_IPSET, ipsets->domain, &addr, *ipsets_cur, 1);
|
||||||
{
|
#endif
|
||||||
log_query((flags & (F_IPV4 | F_IPV6)) | F_IPSET, name, &addr, *ipsets_cur);
|
#ifdef HAVE_NFTSET
|
||||||
add_to_ipset(*ipsets_cur++, &addr, flags, 0);
|
if (nftsets && (flags & (F_IPV4 | F_IPV6)))
|
||||||
}
|
for (nftsets_cur = nftsets->sets; *nftsets_cur; nftsets_cur++)
|
||||||
}
|
if (add_to_nftset(*nftsets_cur, &addr, flags, 0) == 0)
|
||||||
|
log_query((flags & (F_IPV4 | F_IPV6)) | F_IPSET, nftsets->domain, &addr, *nftsets_cur, 0);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (insert)
|
||||||
|
{
|
||||||
newc = cache_insert(name, &addr, C_IN, now, attl, flags | F_FORWARD | secflag);
|
newc = cache_insert(name, &addr, C_IN, now, attl, flags | F_FORWARD | secflag);
|
||||||
if (newc && cpp)
|
if (newc && cpp)
|
||||||
{
|
{
|
||||||
@@ -841,6 +871,14 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
|||||||
}
|
}
|
||||||
cpp = NULL;
|
cpp = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (aqtype == T_TXT)
|
||||||
|
{
|
||||||
|
if (!print_txt(header, qlen, name, p1, ardlen, secflag))
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
log_query(flags | F_FORWARD | secflag | F_UPSTREAM, name, &addr, NULL, aqtype);
|
||||||
}
|
}
|
||||||
|
|
||||||
p1 = endrr;
|
p1 = endrr;
|
||||||
@@ -848,18 +886,33 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
|||||||
return 0; /* bad packet */
|
return 0; /* bad packet */
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!found && !option_bool(OPT_NO_NEG))
|
if (!found && (qtype != T_ANY || (flags & F_NXDOMAIN)))
|
||||||
{
|
{
|
||||||
|
if (flags & F_NXDOMAIN)
|
||||||
|
{
|
||||||
|
flags &= ~(F_IPV4 | F_IPV6 | F_SRV);
|
||||||
|
|
||||||
|
/* Can store NXDOMAIN reply to CNAME or ANY query. */
|
||||||
|
if (qtype == T_CNAME || qtype == T_ANY)
|
||||||
|
insert = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_query(F_UPSTREAM | F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0), name, NULL, NULL, 0);
|
||||||
|
|
||||||
if (!searched_soa)
|
if (!searched_soa)
|
||||||
{
|
{
|
||||||
searched_soa = 1;
|
searched_soa = 1;
|
||||||
ttl = find_soa(header, qlen, NULL, doctored);
|
ttl = find_soa(header, qlen, doctored);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If there's no SOA to get the TTL from, but there is a CNAME
|
/* If there's no SOA to get the TTL from, but there is a CNAME
|
||||||
pointing at this, inherit its TTL */
|
pointing at this, inherit its TTL */
|
||||||
if (ttl || cpp)
|
if (insert && !option_bool(OPT_NO_NEG) && (ttl || cpp))
|
||||||
{
|
{
|
||||||
newc = cache_insert(name, NULL, C_IN, now, ttl ? ttl : cttl, F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0));
|
if (ttl == 0)
|
||||||
|
ttl = cttl;
|
||||||
|
|
||||||
|
newc = cache_insert(name, NULL, C_IN, now, ttl, F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0));
|
||||||
if (newc && cpp)
|
if (newc && cpp)
|
||||||
{
|
{
|
||||||
next_uid(newc);
|
next_uid(newc);
|
||||||
@@ -869,7 +922,6 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* Don't put stuff from a truncated packet into the cache.
|
/* Don't put stuff from a truncated packet into the cache.
|
||||||
Don't cache replies from non-recursive nameservers, since we may get a
|
Don't cache replies from non-recursive nameservers, since we may get a
|
||||||
@@ -884,6 +936,92 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS)
|
||||||
|
/* Don't pass control chars and weird escapes to UBus. */
|
||||||
|
static int safe_name(char *name)
|
||||||
|
{
|
||||||
|
unsigned char *r;
|
||||||
|
|
||||||
|
for (r = (unsigned char *)name; *r; r++)
|
||||||
|
if (!isprint((int)*r))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void report_addresses(struct dns_header *header, size_t len, u32 mark)
|
||||||
|
{
|
||||||
|
unsigned char *p, *endrr;
|
||||||
|
int i;
|
||||||
|
unsigned long attl;
|
||||||
|
struct allowlist *allowlists;
|
||||||
|
char **pattern_pos;
|
||||||
|
|
||||||
|
if (RCODE(header) != NOERROR)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (allowlists = daemon->allowlists; allowlists; allowlists = allowlists->next)
|
||||||
|
if (allowlists->mark == (mark & daemon->allowlist_mask & allowlists->mask))
|
||||||
|
for (pattern_pos = allowlists->patterns; *pattern_pos; pattern_pos++)
|
||||||
|
if (!strcmp(*pattern_pos, "*"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!(p = skip_questions(header, len)))
|
||||||
|
return;
|
||||||
|
for (i = ntohs(header->ancount); i != 0; i--)
|
||||||
|
{
|
||||||
|
int aqtype, aqclass, ardlen;
|
||||||
|
|
||||||
|
if (!extract_name(header, len, &p, daemon->namebuff, 1, 10))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!CHECK_LEN(header, p, len, 10))
|
||||||
|
return;
|
||||||
|
GETSHORT(aqtype, p);
|
||||||
|
GETSHORT(aqclass, p);
|
||||||
|
GETLONG(attl, p);
|
||||||
|
GETSHORT(ardlen, p);
|
||||||
|
|
||||||
|
if (!CHECK_LEN(header, p, len, ardlen))
|
||||||
|
return;
|
||||||
|
endrr = p+ardlen;
|
||||||
|
|
||||||
|
if (aqclass == C_IN)
|
||||||
|
{
|
||||||
|
if (aqtype == T_CNAME)
|
||||||
|
{
|
||||||
|
if (!extract_name(header, len, &p, daemon->workspacename, 1, 0))
|
||||||
|
return;
|
||||||
|
if (safe_name(daemon->namebuff) && safe_name(daemon->workspacename))
|
||||||
|
ubus_event_bcast_connmark_allowlist_resolved(mark, daemon->namebuff, daemon->workspacename, attl);
|
||||||
|
}
|
||||||
|
if (aqtype == T_A)
|
||||||
|
{
|
||||||
|
struct in_addr addr;
|
||||||
|
char ip[INET_ADDRSTRLEN];
|
||||||
|
if (ardlen != INADDRSZ)
|
||||||
|
return;
|
||||||
|
memcpy(&addr, p, ardlen);
|
||||||
|
if (inet_ntop(AF_INET, &addr, ip, sizeof ip) && safe_name(daemon->namebuff))
|
||||||
|
ubus_event_bcast_connmark_allowlist_resolved(mark, daemon->namebuff, ip, attl);
|
||||||
|
}
|
||||||
|
else if (aqtype == T_AAAA)
|
||||||
|
{
|
||||||
|
struct in6_addr addr;
|
||||||
|
char ip[INET6_ADDRSTRLEN];
|
||||||
|
if (ardlen != IN6ADDRSZ)
|
||||||
|
return;
|
||||||
|
memcpy(&addr, p, ardlen);
|
||||||
|
if (inet_ntop(AF_INET6, &addr, ip, sizeof ip) && safe_name(daemon->namebuff))
|
||||||
|
ubus_event_bcast_connmark_allowlist_resolved(mark, daemon->namebuff, ip, attl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p = endrr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* If the packet holds exactly one query
|
/* If the packet holds exactly one query
|
||||||
return F_IPV4 or F_IPV6 and leave the name from the query in name */
|
return F_IPV4 or F_IPV6 and leave the name from the query in name */
|
||||||
unsigned int extract_request(struct dns_header *header, size_t qlen, char *name, unsigned short *typep)
|
unsigned int extract_request(struct dns_header *header, size_t qlen, char *name, unsigned short *typep)
|
||||||
@@ -899,6 +1037,9 @@ unsigned int extract_request(struct dns_header *header, size_t qlen, char *name,
|
|||||||
if (ntohs(header->qdcount) != 1 || OPCODE(header) != QUERY)
|
if (ntohs(header->qdcount) != 1 || OPCODE(header) != QUERY)
|
||||||
return 0; /* must be exactly one query. */
|
return 0; /* must be exactly one query. */
|
||||||
|
|
||||||
|
if (!(header->hb3 & HB3_QR) && (ntohs(header->ancount) != 0 || ntohs(header->nscount) != 0))
|
||||||
|
return 0; /* non-standard query. */
|
||||||
|
|
||||||
if (!extract_name(header, qlen, &p, name, 1, 4))
|
if (!extract_name(header, qlen, &p, name, 1, 4))
|
||||||
return 0; /* bad packet */
|
return 0; /* bad packet */
|
||||||
|
|
||||||
@@ -918,17 +1059,19 @@ unsigned int extract_request(struct dns_header *header, size_t qlen, char *name,
|
|||||||
return F_IPV4 | F_IPV6;
|
return F_IPV4 | F_IPV6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_DNSSEC
|
||||||
/* F_DNSSECOK as agument to search_servers() inhibits forwarding
|
/* F_DNSSECOK as agument to search_servers() inhibits forwarding
|
||||||
to servers for domains without a trust anchor. This make the
|
to servers for domains without a trust anchor. This make the
|
||||||
behaviour for DS and DNSKEY queries we forward the same
|
behaviour for DS and DNSKEY queries we forward the same
|
||||||
as for DS and DNSKEY queries we originate. */
|
as for DS and DNSKEY queries we originate. */
|
||||||
if (qtype == T_DS || qtype == T_DNSKEY)
|
if (option_bool(OPT_DNSSEC_VALID) && (qtype == T_DS || qtype == T_DNSKEY))
|
||||||
return F_DNSSECOK;
|
return F_DNSSECOK;
|
||||||
|
#endif
|
||||||
|
|
||||||
return F_QUERY;
|
return F_QUERY;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setup_reply(struct dns_header *header, unsigned int flags)
|
void setup_reply(struct dns_header *header, unsigned int flags, int ede)
|
||||||
{
|
{
|
||||||
/* clear authoritative and truncated flags, set QR flag */
|
/* clear authoritative and truncated flags, set QR flag */
|
||||||
header->hb3 = (header->hb3 & ~(HB3_AA | HB3_TC )) | HB3_QR;
|
header->hb3 = (header->hb3 & ~(HB3_AA | HB3_TC )) | HB3_QR;
|
||||||
@@ -951,7 +1094,8 @@ void setup_reply(struct dns_header *header, unsigned int flags)
|
|||||||
{
|
{
|
||||||
union all_addr a;
|
union all_addr a;
|
||||||
a.log.rcode = REFUSED;
|
a.log.rcode = REFUSED;
|
||||||
log_query(F_CONFIG | F_RCODE, "error", &a, NULL);
|
a.log.ede = ede;
|
||||||
|
log_query(F_CONFIG | F_RCODE, "error", &a, NULL, 0);
|
||||||
SET_RCODE(header, REFUSED);
|
SET_RCODE(header, REFUSED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -997,7 +1141,6 @@ static int check_bad_address(struct dns_header *header, size_t qlen, struct bogu
|
|||||||
int i, qtype, qclass, rdlen;
|
int i, qtype, qclass, rdlen;
|
||||||
unsigned long ttl;
|
unsigned long ttl;
|
||||||
struct bogus_addr *baddrp;
|
struct bogus_addr *baddrp;
|
||||||
struct in_addr addr;
|
|
||||||
|
|
||||||
/* skip over questions */
|
/* skip over questions */
|
||||||
if (!(p = skip_questions(header, qlen)))
|
if (!(p = skip_questions(header, qlen)))
|
||||||
@@ -1019,16 +1162,32 @@ static int check_bad_address(struct dns_header *header, size_t qlen, struct bogu
|
|||||||
if (ttlp)
|
if (ttlp)
|
||||||
*ttlp = ttl;
|
*ttlp = ttl;
|
||||||
|
|
||||||
if (qclass == C_IN && qtype == T_A)
|
if (qclass == C_IN)
|
||||||
{
|
{
|
||||||
|
if (qtype == T_A)
|
||||||
|
{
|
||||||
|
struct in_addr addr;
|
||||||
|
|
||||||
if (!CHECK_LEN(header, p, qlen, INADDRSZ))
|
if (!CHECK_LEN(header, p, qlen, INADDRSZ))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
for (baddrp = baddr; baddrp; baddrp = baddrp->next)
|
|
||||||
{
|
|
||||||
memcpy(&addr, p, INADDRSZ);
|
memcpy(&addr, p, INADDRSZ);
|
||||||
|
|
||||||
if ((addr.s_addr & baddrp->mask.s_addr) == baddrp->addr.s_addr)
|
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;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1296,9 +1455,25 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
|||||||
|
|
||||||
ans = 0; /* have we answered this question */
|
ans = 0; /* have we answered this question */
|
||||||
|
|
||||||
while (--count != 0 && (crecp = cache_find_by_name(NULL, name, now, F_CNAME)))
|
if (qclass == C_IN)
|
||||||
|
while (--count != 0 && (crecp = cache_find_by_name(NULL, name, now, F_CNAME | F_NXDOMAIN)))
|
||||||
{
|
{
|
||||||
char *cname_target = cache_get_cname_target(crecp);
|
char *cname_target;
|
||||||
|
|
||||||
|
if (crecp->flags & F_NXDOMAIN)
|
||||||
|
{
|
||||||
|
if (qtype == T_CNAME)
|
||||||
|
{
|
||||||
|
if (!dryrun)
|
||||||
|
log_query(crecp->flags, name, NULL, record_source(crecp->uid), 0);
|
||||||
|
auth = 0;
|
||||||
|
nxdomain = 1;
|
||||||
|
ans = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
cname_target = cache_get_cname_target(crecp);
|
||||||
|
|
||||||
/* If the client asked for DNSSEC don't use cached data. */
|
/* If the client asked for DNSSEC don't use cached data. */
|
||||||
if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) ||
|
if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) ||
|
||||||
@@ -1312,7 +1487,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
|||||||
|
|
||||||
if (!dryrun)
|
if (!dryrun)
|
||||||
{
|
{
|
||||||
log_query(crecp->flags, name, NULL, record_source(crecp->uid));
|
log_query(crecp->flags, name, NULL, record_source(crecp->uid), 0);
|
||||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||||
crec_ttl(crecp, now), &nameoffset,
|
crec_ttl(crecp, now), &nameoffset,
|
||||||
T_CNAME, C_IN, "d", cname_target))
|
T_CNAME, C_IN, "d", cname_target))
|
||||||
@@ -1323,6 +1498,9 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
|||||||
else
|
else
|
||||||
return 0; /* give up if any cached CNAME in chain can't be used for DNSSEC reasons. */
|
return 0; /* give up if any cached CNAME in chain can't be used for DNSSEC reasons. */
|
||||||
|
|
||||||
|
if (qtype == T_CNAME)
|
||||||
|
break;
|
||||||
|
|
||||||
strcpy(name, cname_target);
|
strcpy(name, cname_target);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1349,7 +1527,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
|||||||
#endif
|
#endif
|
||||||
if (ok)
|
if (ok)
|
||||||
{
|
{
|
||||||
log_query(F_CONFIG | F_RRNAME, name, NULL, "<TXT>");
|
log_query(F_CONFIG | F_RRNAME, name, NULL, "<TXT>", 0);
|
||||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||||
ttl, NULL,
|
ttl, NULL,
|
||||||
T_TXT, t->class, "t", t->len, t->txt))
|
T_TXT, t->class, "t", t->len, t->txt))
|
||||||
@@ -1371,7 +1549,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
|||||||
if (!dryrun)
|
if (!dryrun)
|
||||||
{
|
{
|
||||||
addr.log.rcode = NOTIMP;
|
addr.log.rcode = NOTIMP;
|
||||||
log_query(F_CONFIG | F_RCODE, name, &addr, NULL);
|
log_query(F_CONFIG | F_RCODE, name, &addr, NULL, 0);
|
||||||
}
|
}
|
||||||
ans = 1, sec_data = 0;
|
ans = 1, sec_data = 0;
|
||||||
}
|
}
|
||||||
@@ -1389,7 +1567,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
|||||||
sec_data = 0;
|
sec_data = 0;
|
||||||
if (!dryrun)
|
if (!dryrun)
|
||||||
{
|
{
|
||||||
log_query(F_CONFIG | F_RRNAME, name, NULL, querystr(NULL, t->class));
|
log_query(F_CONFIG | F_RRNAME, name, NULL, NULL, t->class);
|
||||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||||
daemon->local_ttl, NULL,
|
daemon->local_ttl, NULL,
|
||||||
t->class, C_IN, "t", t->len, t->txt))
|
t->class, C_IN, "t", t->len, t->txt))
|
||||||
@@ -1445,7 +1623,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
|||||||
ans = 1;
|
ans = 1;
|
||||||
if (!dryrun)
|
if (!dryrun)
|
||||||
{
|
{
|
||||||
log_query(is_arpa | F_REVERSE | F_CONFIG, intr->name, &addr, NULL);
|
log_query(is_arpa | F_REVERSE | F_CONFIG, intr->name, &addr, NULL, 0);
|
||||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||||
daemon->local_ttl, NULL,
|
daemon->local_ttl, NULL,
|
||||||
T_PTR, C_IN, "d", intr->name))
|
T_PTR, C_IN, "d", intr->name))
|
||||||
@@ -1458,7 +1636,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
|||||||
sec_data = 0;
|
sec_data = 0;
|
||||||
if (!dryrun)
|
if (!dryrun)
|
||||||
{
|
{
|
||||||
log_query(F_CONFIG | F_RRNAME, name, NULL, "<PTR>");
|
log_query(F_CONFIG | F_RRNAME, name, NULL, "<PTR>", 0);
|
||||||
for (ptr = daemon->ptr; ptr; ptr = ptr->next)
|
for (ptr = daemon->ptr; ptr; ptr = ptr->next)
|
||||||
if (hostname_isequal(name, ptr->name) &&
|
if (hostname_isequal(name, ptr->name) &&
|
||||||
add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||||
@@ -1468,7 +1646,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ((crecp = cache_find_by_addr(NULL, &addr, now, is_arpa)))
|
else if (is_arpa && (crecp = cache_find_by_addr(NULL, &addr, now, is_arpa)))
|
||||||
{
|
{
|
||||||
/* Don't use cache when DNSSEC data required, unless we know that
|
/* Don't use cache when DNSSEC data required, unless we know that
|
||||||
the zone is unsigned, which implies that we're doing
|
the zone is unsigned, which implies that we're doing
|
||||||
@@ -1493,7 +1671,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
|||||||
if (crecp->flags & F_NXDOMAIN)
|
if (crecp->flags & F_NXDOMAIN)
|
||||||
nxdomain = 1;
|
nxdomain = 1;
|
||||||
if (!dryrun)
|
if (!dryrun)
|
||||||
log_query(crecp->flags & ~F_FORWARD, name, &addr, NULL);
|
log_query(crecp->flags & ~F_FORWARD, name, &addr, NULL, 0);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1502,7 +1680,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
|||||||
if (!dryrun)
|
if (!dryrun)
|
||||||
{
|
{
|
||||||
log_query(crecp->flags & ~F_FORWARD, cache_get_name(crecp), &addr,
|
log_query(crecp->flags & ~F_FORWARD, cache_get_name(crecp), &addr,
|
||||||
record_source(crecp->uid));
|
record_source(crecp->uid), 0);
|
||||||
|
|
||||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||||
crec_ttl(crecp, now), NULL,
|
crec_ttl(crecp, now), NULL,
|
||||||
@@ -1519,7 +1697,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
|||||||
sec_data = 0;
|
sec_data = 0;
|
||||||
if (!dryrun)
|
if (!dryrun)
|
||||||
{
|
{
|
||||||
log_query(F_CONFIG | F_REVERSE | is_arpa, name, &addr, NULL);
|
log_query(F_CONFIG | F_REVERSE | is_arpa, name, &addr, NULL, 0);
|
||||||
|
|
||||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||||
daemon->local_ttl, NULL,
|
daemon->local_ttl, NULL,
|
||||||
@@ -1528,7 +1706,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (option_bool(OPT_BOGUSPRIV) &&
|
else if (option_bool(OPT_BOGUSPRIV) &&
|
||||||
((is_arpa == F_IPV6 && private_net6(&addr.addr6)) || (is_arpa == F_IPV4 && private_net(addr.addr4, 1))) &&
|
((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))
|
!lookup_domain(name, F_DOMAINSRV, NULL, NULL))
|
||||||
{
|
{
|
||||||
/* if no configured server, not in cache, enabled and private IPV4 address, return NXDOMAIN */
|
/* if no configured server, not in cache, enabled and private IPV4 address, return NXDOMAIN */
|
||||||
@@ -1537,7 +1715,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
|||||||
nxdomain = 1;
|
nxdomain = 1;
|
||||||
if (!dryrun)
|
if (!dryrun)
|
||||||
log_query(F_CONFIG | F_REVERSE | is_arpa | F_NEG | F_NXDOMAIN,
|
log_query(F_CONFIG | F_REVERSE | is_arpa | F_NEG | F_NXDOMAIN,
|
||||||
name, &addr, NULL);
|
name, &addr, NULL, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1592,7 +1770,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
|||||||
if (!dryrun)
|
if (!dryrun)
|
||||||
{
|
{
|
||||||
gotit = 1;
|
gotit = 1;
|
||||||
log_query(F_FORWARD | F_CONFIG | flag, name, &addrlist->addr, NULL);
|
log_query(F_FORWARD | F_CONFIG | flag, name, &addrlist->addr, NULL, 0);
|
||||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||||
daemon->local_ttl, NULL, type, C_IN,
|
daemon->local_ttl, NULL, type, C_IN,
|
||||||
type == T_A ? "4" : "6", &addrlist->addr))
|
type == T_A ? "4" : "6", &addrlist->addr))
|
||||||
@@ -1602,12 +1780,12 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!dryrun && !gotit)
|
if (!dryrun && !gotit)
|
||||||
log_query(F_FORWARD | F_CONFIG | flag | F_NEG, name, NULL, NULL);
|
log_query(F_FORWARD | F_CONFIG | flag | F_NEG, name, NULL, NULL, 0);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((crecp = cache_find_by_name(NULL, name, now, flag | (dryrun ? F_NO_RR : 0))))
|
if ((crecp = cache_find_by_name(NULL, name, now, flag | F_NXDOMAIN | (dryrun ? F_NO_RR : 0))))
|
||||||
{
|
{
|
||||||
int localise = 0;
|
int localise = 0;
|
||||||
|
|
||||||
@@ -1647,7 +1825,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
|||||||
if (crecp->flags & F_NXDOMAIN)
|
if (crecp->flags & F_NXDOMAIN)
|
||||||
nxdomain = 1;
|
nxdomain = 1;
|
||||||
if (!dryrun)
|
if (!dryrun)
|
||||||
log_query(crecp->flags, name, NULL, NULL);
|
log_query(crecp->flags, name, NULL, NULL, 0);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1665,7 +1843,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
|||||||
if (!dryrun)
|
if (!dryrun)
|
||||||
{
|
{
|
||||||
log_query(crecp->flags & ~F_REVERSE, name, &crecp->addr,
|
log_query(crecp->flags & ~F_REVERSE, name, &crecp->addr,
|
||||||
record_source(crecp->uid));
|
record_source(crecp->uid), 0);
|
||||||
|
|
||||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||||
crec_ttl(crecp, now), NULL, type, C_IN,
|
crec_ttl(crecp, now), NULL, type, C_IN,
|
||||||
@@ -1680,7 +1858,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
|||||||
ans = 1, sec_data = 0;
|
ans = 1, sec_data = 0;
|
||||||
if (!dryrun)
|
if (!dryrun)
|
||||||
{
|
{
|
||||||
log_query(F_FORWARD | F_CONFIG | flag, name, &addr, NULL);
|
log_query(F_FORWARD | F_CONFIG | flag, name, &addr, NULL, 0);
|
||||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||||
daemon->local_ttl, NULL, type, C_IN, type == T_A ? "4" : "6", &addr))
|
daemon->local_ttl, NULL, type, C_IN, type == T_A ? "4" : "6", &addr))
|
||||||
anscount++;
|
anscount++;
|
||||||
@@ -1699,7 +1877,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
|||||||
if (!dryrun)
|
if (!dryrun)
|
||||||
{
|
{
|
||||||
int offset;
|
int offset;
|
||||||
log_query(F_CONFIG | F_RRNAME, name, NULL, "<MX>");
|
log_query(F_CONFIG | F_RRNAME, name, NULL, "<MX>", 0);
|
||||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl,
|
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl,
|
||||||
&offset, T_MX, C_IN, "sd", rec->weight, rec->target))
|
&offset, T_MX, C_IN, "sd", rec->weight, rec->target))
|
||||||
{
|
{
|
||||||
@@ -1717,7 +1895,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
|||||||
sec_data = 0;
|
sec_data = 0;
|
||||||
if (!dryrun)
|
if (!dryrun)
|
||||||
{
|
{
|
||||||
log_query(F_CONFIG | F_RRNAME, name, NULL, "<MX>");
|
log_query(F_CONFIG | F_RRNAME, name, NULL, "<MX>", 0);
|
||||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, NULL,
|
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, NULL,
|
||||||
T_MX, C_IN, "sd", 1,
|
T_MX, C_IN, "sd", 1,
|
||||||
option_bool(OPT_SELFMX) ? name : daemon->mxtarget))
|
option_bool(OPT_SELFMX) ? name : daemon->mxtarget))
|
||||||
@@ -1739,7 +1917,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
|||||||
if (!dryrun)
|
if (!dryrun)
|
||||||
{
|
{
|
||||||
int offset;
|
int offset;
|
||||||
log_query(F_CONFIG | F_RRNAME, name, NULL, "<SRV>");
|
log_query(F_CONFIG | F_RRNAME, name, NULL, "<SRV>", 0);
|
||||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl,
|
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl,
|
||||||
&offset, T_SRV, C_IN, "sssd",
|
&offset, T_SRV, C_IN, "sssd",
|
||||||
rec->priority, rec->weight, rec->srvport, rec->target))
|
rec->priority, rec->weight, rec->srvport, rec->target))
|
||||||
@@ -1771,27 +1949,31 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
|||||||
|
|
||||||
if (!found)
|
if (!found)
|
||||||
{
|
{
|
||||||
if ((crecp = cache_find_by_name(NULL, name, now, F_SRV | (dryrun ? F_NO_RR : 0))) &&
|
if ((crecp = cache_find_by_name(NULL, name, now, F_SRV | F_NXDOMAIN | (dryrun ? F_NO_RR : 0))) &&
|
||||||
rd_bit && (!do_bit || (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK))))
|
rd_bit && (!do_bit || (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK))))
|
||||||
|
do
|
||||||
{
|
{
|
||||||
|
/* don't answer wildcard queries with data not from /etc/hosts or dhcp leases, except for NXDOMAIN */
|
||||||
|
if (qtype == T_ANY && !(crecp->flags & (F_NXDOMAIN)))
|
||||||
|
break;
|
||||||
|
|
||||||
if (!(crecp->flags & F_DNSSECOK))
|
if (!(crecp->flags & F_DNSSECOK))
|
||||||
sec_data = 0;
|
sec_data = 0;
|
||||||
|
|
||||||
auth = 0;
|
auth = 0;
|
||||||
found = ans = 1;
|
found = ans = 1;
|
||||||
|
|
||||||
do {
|
|
||||||
if (crecp->flags & F_NEG)
|
if (crecp->flags & F_NEG)
|
||||||
{
|
{
|
||||||
if (crecp->flags & F_NXDOMAIN)
|
if (crecp->flags & F_NXDOMAIN)
|
||||||
nxdomain = 1;
|
nxdomain = 1;
|
||||||
if (!dryrun)
|
if (!dryrun)
|
||||||
log_query(crecp->flags, name, NULL, NULL);
|
log_query(crecp->flags, name, NULL, NULL, 0);
|
||||||
}
|
}
|
||||||
else if (!dryrun)
|
else if (!dryrun)
|
||||||
{
|
{
|
||||||
char *target = blockdata_retrieve(crecp->addr.srv.target, crecp->addr.srv.targetlen, NULL);
|
char *target = blockdata_retrieve(crecp->addr.srv.target, crecp->addr.srv.targetlen, NULL);
|
||||||
log_query(crecp->flags, name, NULL, 0);
|
log_query(crecp->flags, name, NULL, NULL, 0);
|
||||||
|
|
||||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||||
crec_ttl(crecp, now), NULL, T_SRV, C_IN, "sssd",
|
crec_ttl(crecp, now), NULL, T_SRV, C_IN, "sssd",
|
||||||
@@ -1801,14 +1983,13 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
|||||||
}
|
}
|
||||||
} while ((crecp = cache_find_by_name(crecp, name, now, F_SRV)));
|
} while ((crecp = cache_find_by_name(crecp, name, now, F_SRV)));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!found && option_bool(OPT_FILTER) && (qtype == T_SRV || (qtype == T_ANY && strchr(name, '_'))))
|
if (!found && option_bool(OPT_FILTER) && (qtype == T_SRV || (qtype == T_ANY && strchr(name, '_'))))
|
||||||
{
|
{
|
||||||
ans = 1;
|
ans = 1;
|
||||||
sec_data = 0;
|
sec_data = 0;
|
||||||
if (!dryrun)
|
if (!dryrun)
|
||||||
log_query(F_CONFIG | F_NEG, name, NULL, NULL);
|
log_query(F_CONFIG | F_NEG, name, NULL, NULL, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1822,7 +2003,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
|||||||
sec_data = 0;
|
sec_data = 0;
|
||||||
if (!dryrun)
|
if (!dryrun)
|
||||||
{
|
{
|
||||||
log_query(F_CONFIG | F_RRNAME, name, NULL, "<NAPTR>");
|
log_query(F_CONFIG | F_RRNAME, name, NULL, "<NAPTR>", 0);
|
||||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl,
|
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl,
|
||||||
NULL, T_NAPTR, C_IN, "sszzzd",
|
NULL, T_NAPTR, C_IN, "sszzzd",
|
||||||
na->order, na->pref, na->flags, na->services, na->regexp, na->replace))
|
na->order, na->pref, na->flags, na->services, na->regexp, na->replace))
|
||||||
@@ -1839,7 +2020,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
|||||||
ans = 1;
|
ans = 1;
|
||||||
sec_data = 0;
|
sec_data = 0;
|
||||||
if (!dryrun)
|
if (!dryrun)
|
||||||
log_query(F_CONFIG | F_NEG, name, &addr, NULL);
|
log_query(F_CONFIG | F_NEG, name, &addr, NULL, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+43
-21
@@ -372,9 +372,22 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
|||||||
|
|
||||||
if (!context)
|
if (!context)
|
||||||
{
|
{
|
||||||
|
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"),
|
my_syslog(MS_DHCP | LOG_WARNING, _("no address range available for DHCP request %s %s"),
|
||||||
subnet_addr.s_addr ? _("with subnet selector") : _("via"),
|
via, daemon->addrbuff);
|
||||||
subnet_addr.s_addr ? inet_ntoa(subnet_addr) : (mess->giaddr.s_addr ? inet_ntoa(mess->giaddr) : iface_name));
|
|
||||||
return 0;
|
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;
|
struct dhcp_context *context_tmp;
|
||||||
for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)
|
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))
|
if (context_tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
|
||||||
|
{
|
||||||
|
inet_ntop(AF_INET, &context_tmp->netmask, daemon->addrbuff, ADDRSTRLEN);
|
||||||
my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP subnet: %s/%s"),
|
my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP subnet: %s/%s"),
|
||||||
ntohl(mess->xid), daemon->namebuff, inet_ntoa(context_tmp->netmask));
|
ntohl(mess->xid), daemon->namebuff, daemon->addrbuff);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
inet_ntop(AF_INET, &context_tmp->end, daemon->addrbuff, ADDRSTRLEN);
|
||||||
my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP range: %s -- %s"),
|
my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP range: %s -- %s"),
|
||||||
ntohl(mess->xid), daemon->namebuff, inet_ntoa(context_tmp->end));
|
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)
|
config->addr.s_addr == option_addr(opt).s_addr)
|
||||||
{
|
{
|
||||||
prettyprint_time(daemon->dhcp_buff, DECLINE_BACKOFF);
|
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"),
|
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->flags |= CONFIG_DECLINED;
|
||||||
config->decline_time = now;
|
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))
|
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)) &&
|
if ((ltmp = lease_find_by_addr(config->addr)) &&
|
||||||
ltmp != lease &&
|
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,
|
unsigned char *mac = extended_hwaddr(ltmp->hwaddr_type, ltmp->hwaddr_len,
|
||||||
ltmp->hwaddr, ltmp->clid_len, ltmp->clid, &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"),
|
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
|
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)
|
if (context->router.s_addr == config->addr.s_addr)
|
||||||
break;
|
break;
|
||||||
if (tmp)
|
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) &&
|
else if (have_config(config, CONFIG_DECLINED) &&
|
||||||
difftime(now, config->decline_time) < (float)DECLINE_BACKOFF)
|
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
|
else
|
||||||
conf = config->addr;
|
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. */
|
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))
|
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"),
|
my_syslog(MS_DHCP | LOG_INFO, _("abandoning lease to %s of %s"),
|
||||||
print_mac(daemon->namebuff, ltmp->hwaddr, ltmp->hwaddr_len),
|
print_mac(daemon->namebuff, ltmp->hwaddr, ltmp->hwaddr_len),
|
||||||
inet_ntoa(ltmp->addr));
|
daemon->addrbuff);
|
||||||
lease = ltmp;
|
lease = ltmp;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -1674,23 +1695,21 @@ static void add_extradata_opt(struct dhcp_lease *lease, unsigned char *opt)
|
|||||||
static void log_packet(char *type, void *addr, unsigned char *ext_mac,
|
static void log_packet(char *type, void *addr, unsigned char *ext_mac,
|
||||||
int mac_len, char *interface, char *string, char *err, u32 xid)
|
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))
|
if (!err && !option_bool(OPT_LOG_OPTS) && option_bool(OPT_QUIET_DHCP))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* addr may be misaligned */
|
daemon->addrbuff[0] = 0;
|
||||||
if (addr)
|
if (addr)
|
||||||
memcpy(&a, addr, sizeof(a));
|
inet_ntop(AF_INET, addr, daemon->addrbuff, ADDRSTRLEN);
|
||||||
|
|
||||||
print_mac(daemon->namebuff, ext_mac, mac_len);
|
print_mac(daemon->namebuff, ext_mac, mac_len);
|
||||||
|
|
||||||
if(option_bool(OPT_LOG_OPTS))
|
if (option_bool(OPT_LOG_OPTS))
|
||||||
my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s%s %s%s",
|
my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s%s %s%s",
|
||||||
ntohl(xid),
|
ntohl(xid),
|
||||||
type,
|
type,
|
||||||
interface,
|
interface,
|
||||||
addr ? inet_ntoa(a) : "",
|
daemon->addrbuff,
|
||||||
addr ? " " : "",
|
addr ? " " : "",
|
||||||
daemon->namebuff,
|
daemon->namebuff,
|
||||||
string ? string : "",
|
string ? string : "",
|
||||||
@@ -1699,7 +1718,7 @@ static void log_packet(char *type, void *addr, unsigned char *ext_mac,
|
|||||||
my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s%s %s%s",
|
my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s%s %s%s",
|
||||||
type,
|
type,
|
||||||
interface,
|
interface,
|
||||||
addr ? inet_ntoa(a) : "",
|
daemon->addrbuff,
|
||||||
addr ? " " : "",
|
addr ? " " : "",
|
||||||
daemon->namebuff,
|
daemon->namebuff,
|
||||||
string ? string : "",
|
string ? string : "",
|
||||||
@@ -1707,9 +1726,9 @@ static void log_packet(char *type, void *addr, unsigned char *ext_mac,
|
|||||||
|
|
||||||
#ifdef HAVE_UBUS
|
#ifdef HAVE_UBUS
|
||||||
if (!strcmp(type, "DHCPACK"))
|
if (!strcmp(type, "DHCPACK"))
|
||||||
ubus_event_bcast("dhcp.ack", daemon->namebuff, addr ? inet_ntoa(a) : NULL, string ? string : NULL, interface);
|
ubus_event_bcast("dhcp.ack", daemon->namebuff, addr ? daemon->addrbuff : NULL, string, interface);
|
||||||
else if (!strcmp(type, "DHCPRELEASE"))
|
else if (!strcmp(type, "DHCPRELEASE"))
|
||||||
ubus_event_bcast("dhcp.release", daemon->namebuff, addr ? inet_ntoa(a) : NULL, string ? string : NULL, interface);
|
ubus_event_bcast("dhcp.release", daemon->namebuff, addr ? daemon->addrbuff : NULL, string, interface);
|
||||||
#endif
|
#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 (option_bool(OPT_LOG_OPTS))
|
||||||
{
|
{
|
||||||
if (mess->siaddr.s_addr != 0)
|
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)
|
if ((mess->flags & htons(0x8000)) && mess->ciaddr.s_addr == 0)
|
||||||
my_syslog(MS_DHCP | LOG_INFO, _("%u broadcast response"), ntohl(mess->xid));
|
my_syslog(MS_DHCP | LOG_INFO, _("%u broadcast response"), ntohl(mess->xid));
|
||||||
|
|||||||
+2
-1
@@ -600,7 +600,7 @@ void check_tftp_listeners(time_t now)
|
|||||||
/* Wrong source address. See rfc1350 para 4. */
|
/* Wrong source address. See rfc1350 para 4. */
|
||||||
prettyprint_addr(&peer, daemon->addrbuff);
|
prettyprint_addr(&peer, daemon->addrbuff);
|
||||||
len = tftp_err(ERR_TID, daemon->packet, _("ignoring packet from %s (TID mismatch)"), daemon->addrbuff);
|
len = tftp_err(ERR_TID, daemon->packet, _("ignoring packet from %s (TID mismatch)"), daemon->addrbuff);
|
||||||
sendto(transfer->sockfd, daemon->packet, len, 0, &peer.sa, sa_len(&peer));
|
while(retry_send(sendto(transfer->sockfd, daemon->packet, len, 0, &peer.sa, sa_len(&peer))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -761,6 +761,7 @@ static ssize_t tftp_err(int err, char *packet, char *message, char *file)
|
|||||||
len = snprintf(mess->message, MAXMESSAGE, message, file, errstr);
|
len = snprintf(mess->message, MAXMESSAGE, message, file, errstr);
|
||||||
ret += (len < MAXMESSAGE) ? len + 1 : MAXMESSAGE; /* include terminating zero */
|
ret += (len < MAXMESSAGE) ? len + 1 : MAXMESSAGE; /* include terminating zero */
|
||||||
|
|
||||||
|
if (err != ERR_FNF || !option_bool(OPT_QUIET_TFTP))
|
||||||
my_syslog(MS_TFTP | LOG_ERR, "%s", mess->message);
|
my_syslog(MS_TFTP | LOG_ERR, "%s", mess->message);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|||||||
+216
-20
@@ -21,17 +21,44 @@
|
|||||||
#include <libubus.h>
|
#include <libubus.h>
|
||||||
|
|
||||||
static struct blob_buf b;
|
static struct blob_buf b;
|
||||||
static int notify;
|
|
||||||
static int error_logged = 0;
|
static int error_logged = 0;
|
||||||
|
|
||||||
static int ubus_handle_metrics(struct ubus_context *ctx, struct ubus_object *obj,
|
static int ubus_handle_metrics(struct ubus_context *ctx, struct ubus_object *obj,
|
||||||
struct ubus_request_data *req, const char *method,
|
struct ubus_request_data *req, const char *method,
|
||||||
struct blob_attr *msg);
|
struct blob_attr *msg);
|
||||||
|
|
||||||
|
#ifdef HAVE_CONNTRACK
|
||||||
|
enum {
|
||||||
|
SET_CONNMARK_ALLOWLIST_MARK,
|
||||||
|
SET_CONNMARK_ALLOWLIST_MASK,
|
||||||
|
SET_CONNMARK_ALLOWLIST_PATTERNS
|
||||||
|
};
|
||||||
|
static const struct blobmsg_policy set_connmark_allowlist_policy[] = {
|
||||||
|
[SET_CONNMARK_ALLOWLIST_MARK] = {
|
||||||
|
.name = "mark",
|
||||||
|
.type = BLOBMSG_TYPE_INT32
|
||||||
|
},
|
||||||
|
[SET_CONNMARK_ALLOWLIST_MASK] = {
|
||||||
|
.name = "mask",
|
||||||
|
.type = BLOBMSG_TYPE_INT32
|
||||||
|
},
|
||||||
|
[SET_CONNMARK_ALLOWLIST_PATTERNS] = {
|
||||||
|
.name = "patterns",
|
||||||
|
.type = BLOBMSG_TYPE_ARRAY
|
||||||
|
}
|
||||||
|
};
|
||||||
|
static int ubus_handle_set_connmark_allowlist(struct ubus_context *ctx, struct ubus_object *obj,
|
||||||
|
struct ubus_request_data *req, const char *method,
|
||||||
|
struct blob_attr *msg);
|
||||||
|
#endif
|
||||||
|
|
||||||
static void ubus_subscribe_cb(struct ubus_context *ctx, struct ubus_object *obj);
|
static void ubus_subscribe_cb(struct ubus_context *ctx, struct ubus_object *obj);
|
||||||
|
|
||||||
static const struct ubus_method ubus_object_methods[] = {
|
static const struct ubus_method ubus_object_methods[] = {
|
||||||
UBUS_METHOD_NOARG("metrics", ubus_handle_metrics),
|
UBUS_METHOD_NOARG("metrics", ubus_handle_metrics),
|
||||||
|
#ifdef HAVE_CONNTRACK
|
||||||
|
UBUS_METHOD("set_connmark_allowlist", ubus_handle_set_connmark_allowlist, set_connmark_allowlist_policy),
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct ubus_object_type ubus_object_type =
|
static struct ubus_object_type ubus_object_type =
|
||||||
@@ -50,17 +77,16 @@ static void ubus_subscribe_cb(struct ubus_context *ctx, struct ubus_object *obj)
|
|||||||
(void)ctx;
|
(void)ctx;
|
||||||
|
|
||||||
my_syslog(LOG_DEBUG, _("UBus subscription callback: %s subscriber(s)"), obj->has_subscribers ? "1" : "0");
|
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)
|
static void ubus_destroy(struct ubus_context *ubus)
|
||||||
{
|
{
|
||||||
// Forces re-initialization when we're reusing the same definitions later on.
|
|
||||||
ubus_object.id = 0;
|
|
||||||
ubus_object_type.id = 0;
|
|
||||||
|
|
||||||
ubus_free(ubus);
|
ubus_free(ubus);
|
||||||
daemon->ubus = NULL;
|
daemon->ubus = NULL;
|
||||||
|
|
||||||
|
/* Forces re-initialization when we're reusing the same definitions later on. */
|
||||||
|
ubus_object.id = 0;
|
||||||
|
ubus_object_type.id = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ubus_disconnect_cb(struct ubus_context *ubus)
|
static void ubus_disconnect_cb(struct ubus_context *ubus)
|
||||||
@@ -89,7 +115,7 @@ char *ubus_init()
|
|||||||
if (ret)
|
if (ret)
|
||||||
{
|
{
|
||||||
ubus_destroy(ubus);
|
ubus_destroy(ubus);
|
||||||
return ubus_strerror(ret);
|
return (char *)ubus_strerror(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
ubus->connection_lost = ubus_disconnect_cb;
|
ubus->connection_lost = ubus_disconnect_cb;
|
||||||
@@ -145,6 +171,16 @@ void check_ubus_listeners()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define CHECK(stmt) \
|
||||||
|
do { \
|
||||||
|
int e = (stmt); \
|
||||||
|
if (e) \
|
||||||
|
{ \
|
||||||
|
my_syslog(LOG_ERR, _("UBus command failed: %d (%s)"), e, #stmt); \
|
||||||
|
return (UBUS_STATUS_UNKNOWN_ERROR); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
static int ubus_handle_metrics(struct ubus_context *ctx, struct ubus_object *obj,
|
static int ubus_handle_metrics(struct ubus_context *ctx, struct ubus_object *obj,
|
||||||
struct ubus_request_data *req, const char *method,
|
struct ubus_request_data *req, const char *method,
|
||||||
struct blob_attr *msg)
|
struct blob_attr *msg)
|
||||||
@@ -155,36 +191,196 @@ static int ubus_handle_metrics(struct ubus_context *ctx, struct ubus_object *obj
|
|||||||
(void)method;
|
(void)method;
|
||||||
(void)msg;
|
(void)msg;
|
||||||
|
|
||||||
blob_buf_init(&b, BLOBMSG_TYPE_TABLE);
|
CHECK(blob_buf_init(&b, BLOBMSG_TYPE_TABLE));
|
||||||
|
|
||||||
for (i=0; i < __METRIC_MAX; i++)
|
for (i=0; i < __METRIC_MAX; i++)
|
||||||
blobmsg_add_u32(&b, get_metric_name(i), daemon->metrics[i]);
|
CHECK(blobmsg_add_u32(&b, get_metric_name(i), daemon->metrics[i]));
|
||||||
|
|
||||||
return ubus_send_reply(ctx, req, b.head);
|
CHECK(ubus_send_reply(ctx, req, b.head));
|
||||||
|
return UBUS_STATUS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_CONNTRACK
|
||||||
|
static int ubus_handle_set_connmark_allowlist(struct ubus_context *ctx, struct ubus_object *obj,
|
||||||
|
struct ubus_request_data *req, const char *method,
|
||||||
|
struct blob_attr *msg)
|
||||||
|
{
|
||||||
|
const struct blobmsg_policy *policy = set_connmark_allowlist_policy;
|
||||||
|
size_t policy_len = countof(set_connmark_allowlist_policy);
|
||||||
|
struct allowlist *allowlists = NULL, **allowlists_pos;
|
||||||
|
char **patterns = NULL, **patterns_pos;
|
||||||
|
u32 mark, mask = UINT32_MAX;
|
||||||
|
size_t num_patterns = 0;
|
||||||
|
struct blob_attr *tb[policy_len];
|
||||||
|
struct blob_attr *attr;
|
||||||
|
|
||||||
|
if (blobmsg_parse(policy, policy_len, tb, blob_data(msg), blob_len(msg)))
|
||||||
|
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||||
|
|
||||||
|
if (!tb[SET_CONNMARK_ALLOWLIST_MARK])
|
||||||
|
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||||
|
mark = blobmsg_get_u32(tb[SET_CONNMARK_ALLOWLIST_MARK]);
|
||||||
|
if (!mark)
|
||||||
|
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||||
|
|
||||||
|
if (tb[SET_CONNMARK_ALLOWLIST_MASK])
|
||||||
|
{
|
||||||
|
mask = blobmsg_get_u32(tb[SET_CONNMARK_ALLOWLIST_MASK]);
|
||||||
|
if (!mask || (mark & ~mask))
|
||||||
|
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tb[SET_CONNMARK_ALLOWLIST_PATTERNS])
|
||||||
|
{
|
||||||
|
struct blob_attr *head = blobmsg_data(tb[SET_CONNMARK_ALLOWLIST_PATTERNS]);
|
||||||
|
size_t len = blobmsg_data_len(tb[SET_CONNMARK_ALLOWLIST_PATTERNS]);
|
||||||
|
__blob_for_each_attr(attr, head, len)
|
||||||
|
{
|
||||||
|
char *pattern;
|
||||||
|
if (blob_id(attr) != BLOBMSG_TYPE_STRING)
|
||||||
|
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||||
|
if (!(pattern = blobmsg_get_string(attr)))
|
||||||
|
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||||
|
if (strcmp(pattern, "*") && !is_valid_dns_name_pattern(pattern))
|
||||||
|
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||||
|
num_patterns++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (allowlists_pos = &daemon->allowlists; *allowlists_pos; allowlists_pos = &(*allowlists_pos)->next)
|
||||||
|
if ((*allowlists_pos)->mark == mark && (*allowlists_pos)->mask == mask)
|
||||||
|
{
|
||||||
|
struct allowlist *allowlists_next = (*allowlists_pos)->next;
|
||||||
|
for (patterns_pos = (*allowlists_pos)->patterns; *patterns_pos; patterns_pos++)
|
||||||
|
{
|
||||||
|
free(*patterns_pos);
|
||||||
|
*patterns_pos = NULL;
|
||||||
|
}
|
||||||
|
free((*allowlists_pos)->patterns);
|
||||||
|
(*allowlists_pos)->patterns = NULL;
|
||||||
|
free(*allowlists_pos);
|
||||||
|
*allowlists_pos = allowlists_next;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!num_patterns)
|
||||||
|
return UBUS_STATUS_OK;
|
||||||
|
|
||||||
|
patterns = whine_malloc((num_patterns + 1) * sizeof(char *));
|
||||||
|
if (!patterns)
|
||||||
|
goto fail;
|
||||||
|
patterns_pos = patterns;
|
||||||
|
if (tb[SET_CONNMARK_ALLOWLIST_PATTERNS])
|
||||||
|
{
|
||||||
|
struct blob_attr *head = blobmsg_data(tb[SET_CONNMARK_ALLOWLIST_PATTERNS]);
|
||||||
|
size_t len = blobmsg_data_len(tb[SET_CONNMARK_ALLOWLIST_PATTERNS]);
|
||||||
|
__blob_for_each_attr(attr, head, len)
|
||||||
|
{
|
||||||
|
char *pattern;
|
||||||
|
if (!(pattern = blobmsg_get_string(attr)))
|
||||||
|
goto fail;
|
||||||
|
if (!(*patterns_pos = whine_malloc(strlen(pattern) + 1)))
|
||||||
|
goto fail;
|
||||||
|
strcpy(*patterns_pos++, pattern);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allowlists = whine_malloc(sizeof(struct allowlist));
|
||||||
|
if (!allowlists)
|
||||||
|
goto fail;
|
||||||
|
memset(allowlists, 0, sizeof(struct allowlist));
|
||||||
|
allowlists->mark = mark;
|
||||||
|
allowlists->mask = mask;
|
||||||
|
allowlists->patterns = patterns;
|
||||||
|
allowlists->next = daemon->allowlists;
|
||||||
|
daemon->allowlists = allowlists;
|
||||||
|
return UBUS_STATUS_OK;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
if (patterns)
|
||||||
|
{
|
||||||
|
for (patterns_pos = patterns; *patterns_pos; patterns_pos++)
|
||||||
|
{
|
||||||
|
free(*patterns_pos);
|
||||||
|
*patterns_pos = NULL;
|
||||||
|
}
|
||||||
|
free(patterns);
|
||||||
|
patterns = NULL;
|
||||||
|
}
|
||||||
|
if (allowlists)
|
||||||
|
{
|
||||||
|
free(allowlists);
|
||||||
|
allowlists = NULL;
|
||||||
|
}
|
||||||
|
return UBUS_STATUS_UNKNOWN_ERROR;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#undef CHECK
|
||||||
|
|
||||||
|
#define CHECK(stmt) \
|
||||||
|
do { \
|
||||||
|
int e = (stmt); \
|
||||||
|
if (e) \
|
||||||
|
{ \
|
||||||
|
my_syslog(LOG_ERR, _("UBus command failed: %d (%s)"), e, #stmt); \
|
||||||
|
return; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
void ubus_event_bcast(const char *type, const char *mac, const char *ip, const char *name, const char *interface)
|
void ubus_event_bcast(const char *type, const char *mac, const char *ip, const char *name, const char *interface)
|
||||||
{
|
{
|
||||||
struct ubus_context *ubus = (struct ubus_context *)daemon->ubus;
|
struct ubus_context *ubus = (struct ubus_context *)daemon->ubus;
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (!ubus || !notify)
|
if (!ubus || !ubus_object.has_subscribers)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
blob_buf_init(&b, BLOBMSG_TYPE_TABLE);
|
CHECK(blob_buf_init(&b, BLOBMSG_TYPE_TABLE));
|
||||||
if (mac)
|
if (mac)
|
||||||
blobmsg_add_string(&b, "mac", mac);
|
CHECK(blobmsg_add_string(&b, "mac", mac));
|
||||||
if (ip)
|
if (ip)
|
||||||
blobmsg_add_string(&b, "ip", ip);
|
CHECK(blobmsg_add_string(&b, "ip", ip));
|
||||||
if (name)
|
if (name)
|
||||||
blobmsg_add_string(&b, "name", name);
|
CHECK(blobmsg_add_string(&b, "name", name));
|
||||||
if (interface)
|
if (interface)
|
||||||
blobmsg_add_string(&b, "interface", interface);
|
CHECK(blobmsg_add_string(&b, "interface", interface));
|
||||||
|
|
||||||
ret = ubus_notify(ubus, &ubus_object, type, b.head, -1);
|
CHECK(ubus_notify(ubus, &ubus_object, type, b.head, -1));
|
||||||
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;
|
||||||
|
|
||||||
|
if (!ubus || !ubus_object.has_subscribers)
|
||||||
|
return;
|
||||||
|
|
||||||
|
CHECK(blob_buf_init(&b, 0));
|
||||||
|
CHECK(blobmsg_add_u32(&b, "mark", mark));
|
||||||
|
CHECK(blobmsg_add_string(&b, "name", name));
|
||||||
|
|
||||||
|
CHECK(ubus_notify(ubus, &ubus_object, "connmark-allowlist.refused", b.head, -1));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ubus_event_bcast_connmark_allowlist_resolved(u32 mark, const char *name, const char *value, u32 ttl)
|
||||||
|
{
|
||||||
|
struct ubus_context *ubus = (struct ubus_context *)daemon->ubus;
|
||||||
|
|
||||||
|
if (!ubus || !ubus_object.has_subscribers)
|
||||||
|
return;
|
||||||
|
|
||||||
|
CHECK(blob_buf_init(&b, 0));
|
||||||
|
CHECK(blobmsg_add_u32(&b, "mark", mark));
|
||||||
|
CHECK(blobmsg_add_string(&b, "name", name));
|
||||||
|
CHECK(blobmsg_add_string(&b, "value", value));
|
||||||
|
CHECK(blobmsg_add_u32(&b, "ttl", ttl));
|
||||||
|
|
||||||
|
/* Set timeout to allow UBus subscriber to configure firewall rules before returning. */
|
||||||
|
CHECK(ubus_notify(ubus, &ubus_object, "connmark-allowlist.resolved", b.head, /* timeout: */ 1000));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#undef CHECK
|
||||||
|
|
||||||
#endif /* HAVE_UBUS */
|
#endif /* HAVE_UBUS */
|
||||||
+42
-10
@@ -115,7 +115,8 @@ u64 rand64(void)
|
|||||||
return (u64)out[outleft+1] + (((u64)out[outleft]) << 32);
|
return (u64)out[outleft+1] + (((u64)out[outleft]) << 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* returns 2 if names is OK but contains one or more underscores */
|
/* returns 1 if name is OK and ascii printable
|
||||||
|
* returns 2 if name should be processed by IDN */
|
||||||
static int check_name(char *in)
|
static int check_name(char *in)
|
||||||
{
|
{
|
||||||
/* remove trailing .
|
/* remove trailing .
|
||||||
@@ -123,7 +124,9 @@ static int check_name(char *in)
|
|||||||
size_t dotgap = 0, l = strlen(in);
|
size_t dotgap = 0, l = strlen(in);
|
||||||
char c;
|
char c;
|
||||||
int nowhite = 0;
|
int nowhite = 0;
|
||||||
|
int idn_encode = 0;
|
||||||
int hasuscore = 0;
|
int hasuscore = 0;
|
||||||
|
int hasucase = 0;
|
||||||
|
|
||||||
if (l == 0 || l > MAXDNAME) return 0;
|
if (l == 0 || l > MAXDNAME) return 0;
|
||||||
|
|
||||||
@@ -142,22 +145,43 @@ static int check_name(char *in)
|
|||||||
else if (isascii((unsigned char)c) && iscntrl((unsigned char)c))
|
else if (isascii((unsigned char)c) && iscntrl((unsigned char)c))
|
||||||
/* iscntrl only gives expected results for ascii */
|
/* iscntrl only gives expected results for ascii */
|
||||||
return 0;
|
return 0;
|
||||||
#if !defined(HAVE_IDN) && !defined(HAVE_LIBIDN2)
|
|
||||||
else if (!isascii((unsigned char)c))
|
else if (!isascii((unsigned char)c))
|
||||||
|
#if !defined(HAVE_IDN) && !defined(HAVE_LIBIDN2)
|
||||||
return 0;
|
return 0;
|
||||||
|
#else
|
||||||
|
idn_encode = 1;
|
||||||
#endif
|
#endif
|
||||||
else if (c != ' ')
|
else if (c != ' ')
|
||||||
{
|
{
|
||||||
nowhite = 1;
|
nowhite = 1;
|
||||||
|
#if defined(HAVE_LIBIDN2) && (!defined(IDN2_VERSION_NUMBER) || IDN2_VERSION_NUMBER < 0x02000003)
|
||||||
if (c == '_')
|
if (c == '_')
|
||||||
hasuscore = 1;
|
hasuscore = 1;
|
||||||
|
#else
|
||||||
|
(void)hasuscore;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(HAVE_IDN) || defined(HAVE_LIBIDN2)
|
||||||
|
if (c >= 'A' && c <= 'Z')
|
||||||
|
hasucase = 1;
|
||||||
|
#else
|
||||||
|
(void)hasucase;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!nowhite)
|
if (!nowhite)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return hasuscore ? 2 : 1;
|
#if defined(HAVE_LIBIDN2) && (!defined(IDN2_VERSION_NUMBER) || IDN2_VERSION_NUMBER < 0x02000003)
|
||||||
|
/* Older libidn2 strips underscores, so don't do IDN processing
|
||||||
|
if the name has an underscore unless it also has non-ascii characters. */
|
||||||
|
idn_encode = idn_encode || (hasucase && !hasuscore);
|
||||||
|
#else
|
||||||
|
idn_encode = idn_encode || hasucase;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return (idn_encode) ? 2 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Hostnames have a more limited valid charset than domain names
|
/* Hostnames have a more limited valid charset than domain names
|
||||||
@@ -204,12 +228,8 @@ char *canonicalise(char *in, int *nomem)
|
|||||||
if (!(rc = check_name(in)))
|
if (!(rc = check_name(in)))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
#if defined(HAVE_LIBIDN2) && (!defined(IDN2_VERSION_NUMBER) || IDN2_VERSION_NUMBER < 0x02000003)
|
|
||||||
/* older libidn2 strips underscores, so don't do IDN processing
|
|
||||||
if the name has an underscore (check_name() returned 2) */
|
|
||||||
if (rc != 2)
|
|
||||||
#endif
|
|
||||||
#if defined(HAVE_IDN) || defined(HAVE_LIBIDN2)
|
#if defined(HAVE_IDN) || defined(HAVE_LIBIDN2)
|
||||||
|
if (rc == 2)
|
||||||
{
|
{
|
||||||
# ifdef HAVE_LIBIDN2
|
# ifdef HAVE_LIBIDN2
|
||||||
rc = idn2_to_ascii_lz(in, &ret, IDN2_NONTRANSITIONAL);
|
rc = idn2_to_ascii_lz(in, &ret, IDN2_NONTRANSITIONAL);
|
||||||
@@ -234,6 +254,8 @@ char *canonicalise(char *in, int *nomem)
|
|||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
(void)rc;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if ((ret = whine_malloc(strlen(in)+1)))
|
if ((ret = whine_malloc(strlen(in)+1)))
|
||||||
@@ -438,6 +460,16 @@ int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask)
|
|||||||
return (a.s_addr & mask.s_addr) == (b.s_addr & mask.s_addr);
|
return (a.s_addr & mask.s_addr) == (b.s_addr & mask.s_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int is_same_net_prefix(struct in_addr a, struct in_addr b, int prefix)
|
||||||
|
{
|
||||||
|
struct in_addr mask;
|
||||||
|
|
||||||
|
mask.s_addr = htonl(~((1 << (32 - prefix)) - 1));
|
||||||
|
|
||||||
|
return is_same_net(a, b, mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int is_same_net6(struct in6_addr *a, struct in6_addr *b, int prefixlen)
|
int is_same_net6(struct in6_addr *a, struct in6_addr *b, int prefixlen)
|
||||||
{
|
{
|
||||||
int pfbytes = prefixlen >> 3;
|
int pfbytes = prefixlen >> 3;
|
||||||
@@ -518,7 +550,7 @@ void prettyprint_time(char *buf, unsigned int t)
|
|||||||
if ((x = (t/60)%60))
|
if ((x = (t/60)%60))
|
||||||
p += sprintf(&buf[p], "%um", x);
|
p += sprintf(&buf[p], "%um", x);
|
||||||
if ((x = t%60))
|
if ((x = t%60))
|
||||||
p += sprintf(&buf[p], "%us", x);
|
sprintf(&buf[p], "%us", x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -564,7 +596,7 @@ int parse_hex(char *in, unsigned char *out, int maxlen,
|
|||||||
int j, bytes = (1 + (r - in))/2;
|
int j, bytes = (1 + (r - in))/2;
|
||||||
for (j = 0; j < bytes; j++)
|
for (j = 0; j < bytes; j++)
|
||||||
{
|
{
|
||||||
char sav = sav;
|
char sav;
|
||||||
if (j < bytes - 1)
|
if (j < bytes - 1)
|
||||||
{
|
{
|
||||||
sav = in[(j+1)*2];
|
sav = in[(j+1)*2];
|
||||||
|
|||||||
Reference in new issue
Block a user