mirror of
http://thekelleys.org.uk/git/dnsmasq.git
synced 2026-11-06 15:51:41 +00:00
Compare commits
22
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b1daf44954 | ||
|
|
be291d979d | ||
|
|
6d1edd8d32 | ||
|
|
25ff956c7d | ||
|
|
38179500f8 | ||
|
|
5f7be5f0d6 | ||
|
|
627056febb | ||
|
|
cbd76447fd | ||
|
|
a60a233329 | ||
|
|
a0a3b8ad3e | ||
|
|
d0ae3f5a4d | ||
|
|
6860cf932b | ||
|
|
0276e0805b | ||
|
|
06ff3d8a26 | ||
|
|
1a3b69aa56 | ||
|
|
8237d06ab7 | ||
|
|
1c9f136b57 | ||
|
|
5ab7e4a475 | ||
|
|
3236f358f8 | ||
|
|
4a6550d69a | ||
|
|
ff523d0c67 | ||
|
|
3c93e8eb41 |
No files matched your search
@@ -38,6 +38,44 @@ version 2.86
|
|||||||
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.
|
||||||
|
|
||||||
|
Revise resource handling for number of concurrent DNS queries. This
|
||||||
|
used to have a global limit, but that has a problem when using
|
||||||
|
different servers for different upstream domains. Queries which are
|
||||||
|
routed by domain to an upstream server which is not responding will
|
||||||
|
build up and trigger the limit, which breaks DNS service for
|
||||||
|
all other domains which could be handled by other servers. The
|
||||||
|
change is to make the limit per server-group, where a server group
|
||||||
|
is the set of servers configured for a particular domain. In the
|
||||||
|
common case, where only default servers are declared, there is
|
||||||
|
no effective change.
|
||||||
|
|
||||||
|
Improve efficiency of DNSSEC. The sharing point for DNSSEC RR data
|
||||||
|
used to be when it entered the cache, having been validated. After
|
||||||
|
that queries requiring the KEY or DS records would share the cached
|
||||||
|
values. There is a common case in dual-stack hosts that queries for
|
||||||
|
A and AAAA records for the same domain are made simultaneously.
|
||||||
|
If required keys were not in the cache, this would result in two
|
||||||
|
requests being sent upstream for the same key data (and all the
|
||||||
|
subsequent chain-of-trust queries.) Now we combine these requests
|
||||||
|
and elide the duplicates, resulting in fewer queries upstream
|
||||||
|
and better performance. To keep a better handle on what's
|
||||||
|
going on, the "extra" logging mode has been modified to associate
|
||||||
|
queries and answers for DNSSEC queries in the same way as ordinary
|
||||||
|
queries. The requesting address and port have been removed from
|
||||||
|
DNSSEC logging lines, since this is no longer strictly defined.
|
||||||
|
|
||||||
|
Connection track mark based DNS query filtering. Thanks to
|
||||||
|
Etan Kissling for implementing this It extends query filtering
|
||||||
|
support beyond what is currently possible
|
||||||
|
with the `--ipset` configuration option, by adding support for:
|
||||||
|
1) Specifying allowlists on a per-client basis, based on their
|
||||||
|
associated Linux connection track mark.
|
||||||
|
2) Dynamic configuration of allowlists via Ubus.
|
||||||
|
3) Reporting when a DNS query resolves or is rejected via Ubus.
|
||||||
|
4) DNS name patterns containing wildcards.
|
||||||
|
Disallowed queries are not forwarded; they are rejected
|
||||||
|
with a REFUSED error code.
|
||||||
|
|
||||||
|
|
||||||
version 2.85
|
version 2.85
|
||||||
Fix problem with DNS retries in 2.83/2.84.
|
Fix problem with DNS retries in 2.83/2.84.
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ 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
|
||||||
|
|||||||
+33
-3
@@ -371,7 +371,10 @@ provides service at that name, rather than the default which is
|
|||||||
.TP
|
.TP
|
||||||
.B --enable-ubus[=<service-name>]
|
.B --enable-ubus[=<service-name>]
|
||||||
Enable dnsmasq UBus interface. It sends notifications via UBus on
|
Enable dnsmasq UBus interface. It sends notifications via UBus on
|
||||||
DHCPACK and DHCPRELEASE events. Furthermore it offers metrics.
|
DHCPACK and DHCPRELEASE events. Furthermore it offers metrics
|
||||||
|
and allows configuration of Linux connection track mark based filtering.
|
||||||
|
When DNS query filtering based on Linux connection track marks is enabled
|
||||||
|
UBus notifications are generated for each resolved or filtered DNS query.
|
||||||
Requires that dnsmasq has been built with UBus support. If the service
|
Requires that dnsmasq has been built with UBus support. If the service
|
||||||
name is given, dnsmasq provides service at that namespace, rather than
|
name is given, dnsmasq provides service at that namespace, rather than
|
||||||
the default which is
|
the default which is
|
||||||
@@ -536,6 +539,32 @@ These IP sets must already exist. See
|
|||||||
.BR ipset (8)
|
.BR ipset (8)
|
||||||
for more details.
|
for more details.
|
||||||
.TP
|
.TP
|
||||||
|
.B --connmark-allowlist-enable[=<mask>]
|
||||||
|
Enables filtering of incoming DNS queries with associated Linux connection track marks
|
||||||
|
according to individual allowlists configured via a series of \fB--connmark-allowlist\fP
|
||||||
|
options. Disallowed queries are not forwarded; they are rejected with a REFUSED error code.
|
||||||
|
DNS queries are only allowed if they do not have an associated Linux connection
|
||||||
|
track mark, or if the queried domains match the configured DNS patterns for the
|
||||||
|
associated Linux connection track mark. If no allowlist is configured for a
|
||||||
|
Linux connection track mark, all DNS queries associated with that mark are rejected.
|
||||||
|
If a mask is specified, Linux connection track marks are first bitwise ANDed
|
||||||
|
with the given mask before being processed.
|
||||||
|
.TP
|
||||||
|
.B --connmark-allowlist=<connmark>[/<mask>][,<pattern>[/<pattern>...]]
|
||||||
|
Configures the DNS patterns that are allowed in DNS queries associated with
|
||||||
|
the given Linux connection track mark.
|
||||||
|
If a mask is specified, Linux connection track marks are first bitwise ANDed
|
||||||
|
with the given mask before they are compared to the given connection track mark.
|
||||||
|
Patterns follow the syntax of DNS names, but additionally allow the wildcard
|
||||||
|
character "*" to be used up to twice per label to match 0 or more characters
|
||||||
|
within that label. Note that the wildcard never matches a dot (e.g., "*.example.com"
|
||||||
|
matches "api.example.com" but not "api.us.example.com"). Patterns must be
|
||||||
|
fully qualified, i.e., consist of at least two labels. The final label must not be
|
||||||
|
fully numeric, and must not be the "local" pseudo-TLD. A pattern must end with at least
|
||||||
|
two literal (non-wildcard) labels.
|
||||||
|
Instead of a pattern, "*" can be specified to disable allowlist filtering
|
||||||
|
for a given Linux connection track mark entirely.
|
||||||
|
.TP
|
||||||
.B \-m, --mx-host=<mx name>[[,<hostname>],<preference>]
|
.B \-m, --mx-host=<mx name>[[,<hostname>],<preference>]
|
||||||
Return an MX record named <mx name> pointing to the given hostname (if
|
Return an MX record named <mx name> pointing to the given hostname (if
|
||||||
given), or
|
given), or
|
||||||
@@ -731,7 +760,8 @@ identical queries without forwarding them again.
|
|||||||
Set the maximum number of concurrent DNS queries. The default value is
|
Set the maximum number of concurrent DNS queries. The default value is
|
||||||
150, which should be fine for most setups. The only known situation
|
150, which should be fine for most setups. The only known situation
|
||||||
where this needs to be increased is when using web-server log file
|
where this needs to be increased is when using web-server log file
|
||||||
resolvers, which can generate large numbers of concurrent queries.
|
resolvers, which can generate large numbers of concurrent queries. This
|
||||||
|
parameter actually controls the number of concurrent queries per server group, where a server group is the set of server(s) associated with a single domain. So if a domain has it's own server via --server=/example.com/1.2.3.4 and 1.2.3.4 is not responding, but queries for *.example.com cannot go elsewhere, then other queries will not be affected. On configurations with many such server groups and tight resources, this value may need to be reduced.
|
||||||
.TP
|
.TP
|
||||||
.B --dnssec
|
.B --dnssec
|
||||||
Validate DNS replies and cache DNSSEC data. When forwarding DNS queries, dnsmasq requests the
|
Validate DNS replies and cache DNSSEC data. When forwarding DNS queries, dnsmasq requests the
|
||||||
@@ -875,7 +905,7 @@ in
|
|||||||
.B --dhcp-host
|
.B --dhcp-host
|
||||||
options. If the lease time is given, then leases
|
options. If the lease time is given, then leases
|
||||||
will be given for that length of time. The lease time is in seconds,
|
will be given for that length of time. The lease time is in seconds,
|
||||||
or minutes (eg 45m) or hours (eg 1h) or "infinite". If not given,
|
or minutes (eg 45m) or hours (eg 1h) or days (2d) or weeks (1w) or "infinite". If not given,
|
||||||
the default lease time is one hour for IPv4 and one day for IPv6. The
|
the default lease time is one hour for IPv4 and one day for IPv6. The
|
||||||
minimum lease time is two minutes. For IPv6 ranges, the lease time
|
minimum lease time is two minutes. For IPv6 ranges, the lease time
|
||||||
maybe "deprecated"; this sets the preferred lifetime sent in a DHCP
|
maybe "deprecated"; this sets the preferred lifetime sent in a DHCP
|
||||||
|
|||||||
+4
-2
@@ -1965,12 +1965,14 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg)
|
|||||||
|
|
||||||
if (option_bool(OPT_EXTRALOG))
|
if (option_bool(OPT_EXTRALOG))
|
||||||
{
|
{
|
||||||
int port = prettyprint_addr(daemon->log_source_addr, daemon->addrbuff2);
|
|
||||||
if (flags & F_NOEXTRA)
|
if (flags & F_NOEXTRA)
|
||||||
my_syslog(LOG_INFO, "* %s/%u %s %s %s %s", daemon->addrbuff2, port, source, name, verb, dest);
|
my_syslog(LOG_INFO, "%u %s %s %s %s", daemon->log_display_id, source, name, verb, dest);
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
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", daemon->log_display_id, daemon->addrbuff2, port, source, name, verb, dest);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
my_syslog(LOG_INFO, "%s %s %s %s", source, name, verb, dest);
|
my_syslog(LOG_INFO, "%s %s %s %s", source, name, verb, dest);
|
||||||
}
|
}
|
||||||
+24
-27
@@ -24,7 +24,7 @@ struct daemon *daemon;
|
|||||||
static volatile pid_t pid = 0;
|
static volatile pid_t pid = 0;
|
||||||
static volatile int pipewrite;
|
static volatile int pipewrite;
|
||||||
|
|
||||||
static int set_dns_listeners(time_t now);
|
static void set_dns_listeners(void);
|
||||||
static void check_dns_listeners(time_t now);
|
static void check_dns_listeners(time_t now);
|
||||||
static void sig_handler(int sig);
|
static void sig_handler(int sig);
|
||||||
static void async_event(int pipe, time_t now);
|
static void async_event(int pipe, time_t now);
|
||||||
@@ -135,6 +135,13 @@ int main (int argc, char **argv)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS)
|
||||||
|
/* CONNTRACK UBUS code uses this buffer, so if not allocated above,
|
||||||
|
we need to allocate it here. */
|
||||||
|
if (option_bool(OPT_CMARK_ALST_EN) && !daemon->workspacename)
|
||||||
|
daemon->workspacename = safe_malloc(MAXDNAME);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_DHCP
|
#ifdef HAVE_DHCP
|
||||||
if (!daemon->lease_file)
|
if (!daemon->lease_file)
|
||||||
{
|
{
|
||||||
@@ -442,8 +449,10 @@ int main (int argc, char **argv)
|
|||||||
if (option_bool(OPT_UBUS))
|
if (option_bool(OPT_UBUS))
|
||||||
#ifdef HAVE_UBUS
|
#ifdef HAVE_UBUS
|
||||||
{
|
{
|
||||||
|
char *err;
|
||||||
daemon->ubus = NULL;
|
daemon->ubus = NULL;
|
||||||
ubus_init();
|
if ((err = ubus_init()))
|
||||||
|
die(_("UBus error: %s"), err, EC_MISC);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
die(_("UBus not available: set HAVE_UBUS in src/config.h"), NULL, EC_BADCONF);
|
die(_("UBus not available: set HAVE_UBUS in src/config.h"), NULL, EC_BADCONF);
|
||||||
@@ -1040,16 +1049,10 @@ int main (int argc, char **argv)
|
|||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
int t, timeout = -1;
|
int timeout = -1;
|
||||||
|
|
||||||
poll_reset();
|
poll_reset();
|
||||||
|
|
||||||
/* if we are out of resources, find how long we have to wait
|
|
||||||
for some to come free, we'll loop around then and restart
|
|
||||||
listening for queries */
|
|
||||||
if ((t = set_dns_listeners(now)) != 0)
|
|
||||||
timeout = t * 1000;
|
|
||||||
|
|
||||||
/* Whilst polling for the dbus, or doing a tftp transfer, wake every quarter second */
|
/* Whilst polling for the dbus, or doing a tftp transfer, wake every quarter second */
|
||||||
if (daemon->tftp_trans ||
|
if (daemon->tftp_trans ||
|
||||||
(option_bool(OPT_DBUS) && !daemon->dbus))
|
(option_bool(OPT_DBUS) && !daemon->dbus))
|
||||||
@@ -1059,6 +1062,8 @@ int main (int argc, char **argv)
|
|||||||
else if (is_dad_listeners())
|
else if (is_dad_listeners())
|
||||||
timeout = 1000;
|
timeout = 1000;
|
||||||
|
|
||||||
|
set_dns_listeners();
|
||||||
|
|
||||||
#ifdef HAVE_DBUS
|
#ifdef HAVE_DBUS
|
||||||
set_dbus_listeners();
|
set_dbus_listeners();
|
||||||
#endif
|
#endif
|
||||||
@@ -1199,16 +1204,16 @@ int main (int argc, char **argv)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_UBUS
|
#ifdef HAVE_UBUS
|
||||||
if (option_bool(OPT_UBUS))
|
|
||||||
{
|
|
||||||
/* if we didn't create a UBus connection, retry now. */
|
/* if we didn't create a UBus connection, retry now. */
|
||||||
if (!daemon->ubus)
|
if (option_bool(OPT_UBUS) && !daemon->ubus)
|
||||||
{
|
{
|
||||||
ubus_init();
|
char *err;
|
||||||
|
if ((err = ubus_init()))
|
||||||
|
my_syslog(LOG_WARNING, _("UBus error: %s"), err);
|
||||||
|
if (daemon->ubus)
|
||||||
|
my_syslog(LOG_INFO, _("connected to system UBus"));
|
||||||
}
|
}
|
||||||
|
|
||||||
check_ubus_listeners();
|
check_ubus_listeners();
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
check_dns_listeners(now);
|
check_dns_listeners(now);
|
||||||
@@ -1683,12 +1688,12 @@ void clear_cache_and_reload(time_t now)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static int set_dns_listeners(time_t now)
|
static void set_dns_listeners(void)
|
||||||
{
|
{
|
||||||
struct serverfd *serverfdp;
|
struct serverfd *serverfdp;
|
||||||
struct listener *listener;
|
struct listener *listener;
|
||||||
struct randfd_list *rfl;
|
struct randfd_list *rfl;
|
||||||
int wait = 0, i;
|
int i;
|
||||||
|
|
||||||
#ifdef HAVE_TFTP
|
#ifdef HAVE_TFTP
|
||||||
int tftp = 0;
|
int tftp = 0;
|
||||||
@@ -1701,10 +1706,6 @@ static int set_dns_listeners(time_t now)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* will we be able to get memory? */
|
|
||||||
if (daemon->port != 0)
|
|
||||||
get_new_frec(now, &wait, NULL);
|
|
||||||
|
|
||||||
for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
|
for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
|
||||||
poll_listen(serverfdp->fd, POLLIN);
|
poll_listen(serverfdp->fd, POLLIN);
|
||||||
|
|
||||||
@@ -1723,8 +1724,7 @@ static int set_dns_listeners(time_t now)
|
|||||||
|
|
||||||
for (listener = daemon->listeners; listener; listener = listener->next)
|
for (listener = daemon->listeners; listener; listener = listener->next)
|
||||||
{
|
{
|
||||||
/* only listen for queries if we have resources */
|
if (listener->fd != -1)
|
||||||
if (listener->fd != -1 && wait == 0)
|
|
||||||
poll_listen(listener->fd, POLLIN);
|
poll_listen(listener->fd, POLLIN);
|
||||||
|
|
||||||
/* Only listen for TCP connections when a process slot
|
/* Only listen for TCP connections when a process slot
|
||||||
@@ -1739,15 +1739,12 @@ static int set_dns_listeners(time_t now)
|
|||||||
if (tftp <= daemon->tftp_max && listener->tftpfd != -1)
|
if (tftp <= daemon->tftp_max && listener->tftpfd != -1)
|
||||||
poll_listen(listener->tftpfd, POLLIN);
|
poll_listen(listener->tftpfd, POLLIN);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!option_bool(OPT_DEBUG))
|
if (!option_bool(OPT_DEBUG))
|
||||||
for (i = 0; i < MAX_PROCS; i++)
|
for (i = 0; i < MAX_PROCS; i++)
|
||||||
if (daemon->tcp_pipes[i] != -1)
|
if (daemon->tcp_pipes[i] != -1)
|
||||||
poll_listen(daemon->tcp_pipes[i], POLLIN);
|
poll_listen(daemon->tcp_pipes[i], POLLIN);
|
||||||
|
|
||||||
return wait;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void check_dns_listeners(time_t now)
|
static void check_dns_listeners(time_t now)
|
||||||
@@ -2098,7 +2095,7 @@ int delay_dhcp(time_t start, int sec, int fd, uint32_t addr, unsigned short id)
|
|||||||
poll_reset();
|
poll_reset();
|
||||||
if (fd != -1)
|
if (fd != -1)
|
||||||
poll_listen(fd, POLLIN);
|
poll_listen(fd, POLLIN);
|
||||||
set_dns_listeners(now);
|
set_dns_listeners();
|
||||||
set_log_writer();
|
set_log_writer();
|
||||||
|
|
||||||
#ifdef HAVE_DHCP6
|
#ifdef HAVE_DHCP6
|
||||||
|
|||||||
+35
-10
@@ -272,7 +272,8 @@ 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_LAST 66
|
||||||
|
|
||||||
#define OPTION_BITS (sizeof(unsigned int)*8)
|
#define OPTION_BITS (sizeof(unsigned int)*8)
|
||||||
#define OPTION_SIZE ( (OPT_LAST/OPTION_BITS)+((OPT_LAST%OPTION_BITS)!=0) )
|
#define OPTION_SIZE ( (OPT_LAST/OPTION_BITS)+((OPT_LAST%OPTION_BITS)!=0) )
|
||||||
@@ -564,7 +565,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,23 +584,23 @@ 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;
|
||||||
};
|
};
|
||||||
@@ -610,6 +611,12 @@ struct ipsets {
|
|||||||
struct ipsets *next;
|
struct ipsets *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct allowlist {
|
||||||
|
u32 mark, mask;
|
||||||
|
char **patterns;
|
||||||
|
struct allowlist *next;
|
||||||
|
};
|
||||||
|
|
||||||
struct irec {
|
struct irec {
|
||||||
union mysockaddr addr;
|
union mysockaddr addr;
|
||||||
struct in_addr netmask; /* only valid for IPv4 */
|
struct in_addr netmask; /* only valid for IPv4 */
|
||||||
@@ -690,7 +697,6 @@ struct hostsfile {
|
|||||||
#define STAT_SECURE_WILDCARD 7
|
#define STAT_SECURE_WILDCARD 7
|
||||||
#define STAT_OK 8
|
#define STAT_OK 8
|
||||||
#define STAT_ABANDONED 9
|
#define STAT_ABANDONED 9
|
||||||
#define STAT_INPROGRESS 10
|
|
||||||
|
|
||||||
#define FREC_NOREBIND 1
|
#define FREC_NOREBIND 1
|
||||||
#define FREC_CHECKING_DISABLED 2
|
#define FREC_CHECKING_DISABLED 2
|
||||||
@@ -727,6 +733,7 @@ struct frec {
|
|||||||
struct blockdata *stash; /* Saved reply, whilst we validate */
|
struct blockdata *stash; /* Saved reply, whilst we validate */
|
||||||
size_t stash_len;
|
size_t stash_len;
|
||||||
struct frec *dependent; /* Query awaiting internally-generated DNSKEY or DS query */
|
struct frec *dependent; /* Query awaiting internally-generated DNSKEY or DS query */
|
||||||
|
struct frec *next_dependent; /* list of above. */
|
||||||
struct frec *blocking_query; /* Query which is blocking us. */
|
struct frec *blocking_query; /* Query which is blocking us. */
|
||||||
#endif
|
#endif
|
||||||
struct frec *next;
|
struct frec *next;
|
||||||
@@ -1086,6 +1093,8 @@ extern struct daemon {
|
|||||||
struct server *servers, *local_domains, **serverarray, *no_rebind;
|
struct server *servers, *local_domains, **serverarray, *no_rebind;
|
||||||
int serverarraysz;
|
int serverarraysz;
|
||||||
struct ipsets *ipsets;
|
struct ipsets *ipsets;
|
||||||
|
u32 allowlist_mask;
|
||||||
|
struct allowlist *allowlists;
|
||||||
int log_fac; /* log facility */
|
int log_fac; /* log facility */
|
||||||
char *log_file; /* optional log file */
|
char *log_file; /* optional log file */
|
||||||
int max_logs; /* queue limit */
|
int max_logs; /* queue limit */
|
||||||
@@ -1275,6 +1284,9 @@ void setup_reply(struct dns_header *header, unsigned int flags);
|
|||||||
int extract_addresses(struct dns_header *header, size_t qlen, char *name,
|
int extract_addresses(struct dns_header *header, size_t qlen, char *name,
|
||||||
time_t now, char **ipsets, int is_sign, int check_rebind,
|
time_t now, char **ipsets, int is_sign, int check_rebind,
|
||||||
int no_cache_dnssec, int secure, int *doctored);
|
int no_cache_dnssec, int secure, int *doctored);
|
||||||
|
#if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS)
|
||||||
|
void report_addresses(struct dns_header *header, size_t len, u32 mark);
|
||||||
|
#endif
|
||||||
size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||||
struct in_addr local_addr, struct in_addr local_netmask,
|
struct in_addr local_addr, struct in_addr local_netmask,
|
||||||
time_t now, int ad_reqd, int do_bit, int have_pseudoheader);
|
time_t now, int ad_reqd, int do_bit, int have_pseudoheader);
|
||||||
@@ -1381,7 +1393,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 */
|
||||||
@@ -1390,7 +1402,6 @@ void receive_query(struct listener *listen, time_t now);
|
|||||||
unsigned char *tcp_request(int confd, time_t now,
|
unsigned char *tcp_request(int confd, time_t now,
|
||||||
union mysockaddr *local_addr, struct in_addr netmask, int auth_dns);
|
union mysockaddr *local_addr, struct in_addr netmask, int auth_dns);
|
||||||
void server_gone(struct server *server);
|
void server_gone(struct server *server);
|
||||||
struct frec *get_new_frec(time_t now, int *wait, struct frec *force);
|
|
||||||
int send_from(int fd, int nowild, char *packet, size_t len,
|
int send_from(int fd, int nowild, char *packet, size_t len,
|
||||||
union mysockaddr *to, union all_addr *source,
|
union mysockaddr *to, union all_addr *source,
|
||||||
unsigned int iface);
|
unsigned int iface);
|
||||||
@@ -1543,10 +1554,14 @@ void emit_dbus_signal(int action, struct dhcp_lease *lease, char *hostname);
|
|||||||
|
|
||||||
/* ubus.c */
|
/* ubus.c */
|
||||||
#ifdef HAVE_UBUS
|
#ifdef HAVE_UBUS
|
||||||
void ubus_init(void);
|
char *ubus_init(void);
|
||||||
void set_ubus_listeners(void);
|
void set_ubus_listeners(void);
|
||||||
void check_ubus_listeners(void);
|
void check_ubus_listeners(void);
|
||||||
void ubus_event_bcast(const char *type, const char *mac, const char *ip, const char *name, const char *interface);
|
void ubus_event_bcast(const char *type, const char *mac, const char *ip, const char *name, const char *interface);
|
||||||
|
# ifdef HAVE_CONNTRACK
|
||||||
|
void ubus_event_bcast_connmark_allowlist_refused(u32 mark, const char *name);
|
||||||
|
void ubus_event_bcast_connmark_allowlist_resolved(u32 mark, const char *pattern, const char *ip, u32 ttl);
|
||||||
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* ipset.c */
|
/* ipset.c */
|
||||||
@@ -1555,6 +1570,13 @@ void ipset_init(void);
|
|||||||
int add_to_ipset(const char *setname, const union all_addr *ipaddr, int flags, int remove);
|
int add_to_ipset(const char *setname, const union all_addr *ipaddr, int flags, int remove);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* pattern.c */
|
||||||
|
#ifdef HAVE_CONNTRACK
|
||||||
|
int is_valid_dns_name(const char *value);
|
||||||
|
int is_valid_dns_name_pattern(const char *value);
|
||||||
|
int is_dns_name_matching_pattern(const char *name, const char *pattern);
|
||||||
|
#endif
|
||||||
|
|
||||||
/* helper.c */
|
/* helper.c */
|
||||||
#if defined(HAVE_SCRIPT)
|
#if defined(HAVE_SCRIPT)
|
||||||
int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd);
|
int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd);
|
||||||
@@ -1726,6 +1748,9 @@ void build_server_array(void);
|
|||||||
int lookup_domain(char *qdomain, int flags, int *lowout, int *highout);
|
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,
|
||||||
|
char *name, char *limit, int first, int last);
|
||||||
|
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
|
||||||
|
|||||||
+139
-49
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
#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);
|
||||||
|
|
||||||
@@ -62,7 +62,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_LITERAL_ADDRESS | SERV_USE_RESOLV)))
|
||||||
daemon->serverarray[count]->arrayposn = count;
|
daemon->serverarray[count]->arrayposn = count;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,12 +75,14 @@ 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 *qdomain, 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;
|
||||||
@@ -89,8 +91,6 @@ int lookup_domain(char *qdomain, int flags, int *lowout, int *highout)
|
|||||||
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,56 +101,74 @@ 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.
|
/* We've matched a setting which says to use servers without a domain.
|
||||||
Continue the search with empty query (the last character gets stripped
|
Continue the search with empty query */
|
||||||
by the loop. */
|
|
||||||
if (daemon->serverarray[try]->flags & SERV_USE_RESOLV)
|
if (daemon->serverarray[try]->flags & SERV_USE_RESOLV)
|
||||||
{
|
crop_query = qlen;
|
||||||
qdomain += qlen - 1;
|
|
||||||
qlen = 1;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* 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
|
||||||
@@ -161,13 +179,16 @@ int lookup_domain(char *qdomain, int flags, int *lowout, int *highout)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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. */
|
||||||
}
|
qlen -= crop_query;
|
||||||
|
qdomain += crop_query;
|
||||||
|
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,12 +196,12 @@ 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
|
/* F_DOMAINSRV returns only domain-specific servers, so if we got to a
|
||||||
general server, return empty set. */
|
general server, return empty set. */
|
||||||
if (nlow != nhigh && (flags & F_DOMAINSRV) && strlen(daemon->serverarray[nlow]->domain) == 0)
|
if (nlow != nhigh && (flags & F_DOMAINSRV) && daemon->serverarray[nlow]->domain_len == 0)
|
||||||
nlow = nhigh;
|
nlow = nhigh;
|
||||||
|
|
||||||
if (lowout)
|
if (lowout)
|
||||||
@@ -195,6 +216,11 @@ int lookup_domain(char *qdomain, int flags, int *lowout, int *highout)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Return first server in group of equivalent servers; this is the "master" record. */
|
||||||
|
int server_samegroup(struct server *a, struct server *b)
|
||||||
|
{
|
||||||
|
return order_servers(a, b) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
int filter_servers(int seed, int flags, int *lowout, int *highout)
|
int filter_servers(int seed, int flags, int *lowout, int *highout)
|
||||||
{
|
{
|
||||||
@@ -217,6 +243,14 @@ int filter_servers(int seed, int flags, int *lowout, int *highout)
|
|||||||
|
|
||||||
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) */
|
||||||
|
|
||||||
|
#define SERV_LOCAL_ADDRESS (SERV_6ADDR | SERV_4ADDR | SERV_ALL_ZEROS)
|
||||||
|
|
||||||
|
for (i = nlow; (flags & F_CONFIG) && i < nhigh && (daemon->serverarray[i]->flags & SERV_LOCAL_ADDRESS); i++);
|
||||||
|
|
||||||
|
if (i != nlow)
|
||||||
|
nhigh = i;
|
||||||
|
else
|
||||||
|
{
|
||||||
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_6ADDR); i++);
|
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_6ADDR); i++);
|
||||||
|
|
||||||
if (i != nlow && (flags & F_IPV6))
|
if (i != nlow && (flags & F_IPV6))
|
||||||
@@ -257,6 +291,7 @@ int filter_servers(int seed, int flags, int *lowout, int *highout)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
*lowout = nlow;
|
*lowout = nlow;
|
||||||
*highout = nhigh;
|
*highout = nhigh;
|
||||||
@@ -278,12 +313,75 @@ 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, char *limit, int first, int last)
|
||||||
|
{
|
||||||
|
int trunc = 0;
|
||||||
|
unsigned char *p;
|
||||||
|
int start;
|
||||||
|
union all_addr addr;
|
||||||
|
|
||||||
|
if (flags & (F_NXDOMAIN | F_NOERR))
|
||||||
|
log_query(flags | gotname | F_NEG | F_CONFIG | F_FORWARD, name, NULL, NULL);
|
||||||
|
|
||||||
|
setup_reply(header, flags);
|
||||||
|
|
||||||
|
if (!(p = skip_questions(header, size)))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (flags & gotname & F_IPV4)
|
||||||
|
for (start = first; start != last; start++)
|
||||||
|
{
|
||||||
|
struct serv_addr4 *srv = (struct serv_addr4 *)daemon->serverarray[start];
|
||||||
|
|
||||||
|
if (srv->flags & SERV_ALL_ZEROS)
|
||||||
|
memset(&addr, 0, sizeof(addr));
|
||||||
|
else
|
||||||
|
addr.addr4 = srv->addr;
|
||||||
|
|
||||||
|
header->ancount = htons(ntohs(header->ancount) + 1);
|
||||||
|
add_resource_record(header, limit, &trunc, sizeof(struct dns_header), &p, daemon->local_ttl, NULL, T_A, C_IN, "4", &addr);
|
||||||
|
log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV6, name, (union all_addr *)&addr, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & gotname & F_IPV6)
|
||||||
|
for (start = first; start != last; start++)
|
||||||
|
{
|
||||||
|
struct serv_addr6 *srv = (struct serv_addr6 *)daemon->serverarray[start];
|
||||||
|
|
||||||
|
if (srv->flags & SERV_ALL_ZEROS)
|
||||||
|
memset(&addr, 0, sizeof(addr));
|
||||||
|
else
|
||||||
|
addr.addr6 = srv->addr;
|
||||||
|
|
||||||
|
header->ancount = htons(ntohs(header->ancount) + 1);
|
||||||
|
add_resource_record(header, limit, &trunc, sizeof(struct dns_header), &p, daemon->local_ttl, NULL, T_AAAA, C_IN, "6", &addr);
|
||||||
|
log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV4, name, (union all_addr *)&addr, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trunc)
|
||||||
|
header->hb3 |= HB3_TC;
|
||||||
|
|
||||||
|
return p - (unsigned char *)header;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef HAVE_DNSSEC
|
#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)
|
||||||
{
|
{
|
||||||
@@ -316,20 +414,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;
|
||||||
@@ -337,22 +431,18 @@ 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);
|
|
||||||
|
|
||||||
/* 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);
|
return order(s1->domain, s1->domain_len, s2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int order_qsort(const void *a, const void *b)
|
static int order_qsort(const void *a, const void *b)
|
||||||
|
|||||||
+344
-237
@@ -16,12 +16,15 @@
|
|||||||
|
|
||||||
#include "dnsmasq.h"
|
#include "dnsmasq.h"
|
||||||
|
|
||||||
|
static struct frec *get_new_frec(time_t now, struct server *serv, int force);
|
||||||
static struct frec *lookup_frec(unsigned short id, int fd, void *hash, int *firstp, int *lastp);
|
static struct frec *lookup_frec(unsigned short id, int fd, void *hash, int *firstp, int *lastp);
|
||||||
static struct frec *lookup_frec_by_query(void *hash, unsigned int flags);
|
static struct frec *lookup_frec_by_query(void *hash, unsigned int flags, unsigned int flagmask);
|
||||||
|
|
||||||
static unsigned short get_id(void);
|
static unsigned short get_id(void);
|
||||||
static void free_frec(struct frec *f);
|
static void free_frec(struct frec *f);
|
||||||
static void query_full(time_t now);
|
static void query_full(time_t now, char *domain);
|
||||||
|
|
||||||
|
static void return_reply(time_t now, struct frec *forward, struct dns_header *header, ssize_t n, int status);
|
||||||
|
|
||||||
/* Send a UDP packet with its source address set as "source"
|
/* Send a UDP packet with its source address set as "source"
|
||||||
unless nowild is true, when we just send it with the kernel default */
|
unless nowild is true, when we just send it with the kernel default */
|
||||||
@@ -150,65 +153,13 @@ static int domain_no_rebind(char *domain)
|
|||||||
struct server *serv;
|
struct server *serv;
|
||||||
int dlen = (int)strlen(domain);
|
int dlen = (int)strlen(domain);
|
||||||
|
|
||||||
/* flags is misused to hold length of domain. */
|
|
||||||
for (serv = daemon->no_rebind; serv; serv = serv->next)
|
for (serv = daemon->no_rebind; serv; serv = serv->next)
|
||||||
if (dlen >= serv->flags && strcmp(serv->domain, &domain[dlen - serv->flags]) == 0)
|
if (dlen >= serv->domain_len && strcmp(serv->domain, &domain[dlen - serv->domain_len]) == 0)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header *header, char *name, int first, int last)
|
|
||||||
{
|
|
||||||
int trunc = 0;
|
|
||||||
unsigned char *p;
|
|
||||||
int start;
|
|
||||||
union all_addr addr;
|
|
||||||
|
|
||||||
if (flags & (F_NXDOMAIN | F_NOERR))
|
|
||||||
log_query(flags | gotname | F_NEG | F_CONFIG | F_FORWARD, name, NULL, NULL);
|
|
||||||
|
|
||||||
setup_reply(header, flags);
|
|
||||||
|
|
||||||
if (!(p = skip_questions(header, size)))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (flags & gotname & F_IPV4)
|
|
||||||
for (start = first; start != last; start++)
|
|
||||||
{
|
|
||||||
struct serv_addr4 *srv = (struct serv_addr4 *)daemon->serverarray[start];
|
|
||||||
|
|
||||||
if (srv->flags & SERV_ALL_ZEROS)
|
|
||||||
memset(&addr, 0, sizeof(addr));
|
|
||||||
else
|
|
||||||
addr.addr4 = srv->addr;
|
|
||||||
|
|
||||||
header->ancount = htons(ntohs(header->ancount) + 1);
|
|
||||||
add_resource_record(header, ((char *)header) + 65536, &trunc, sizeof(struct dns_header), &p, daemon->local_ttl, NULL, T_A, C_IN, "4", &addr);
|
|
||||||
log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV6, name, (union all_addr *)&addr, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flags & gotname & F_IPV6)
|
|
||||||
for (start = first; start != last; start++)
|
|
||||||
{
|
|
||||||
struct serv_addr6 *srv = (struct serv_addr6 *)daemon->serverarray[start];
|
|
||||||
|
|
||||||
if (srv->flags & SERV_ALL_ZEROS)
|
|
||||||
memset(&addr, 0, sizeof(addr));
|
|
||||||
else
|
|
||||||
addr.addr6 = srv->addr;
|
|
||||||
|
|
||||||
header->ancount = htons(ntohs(header->ancount) + 1);
|
|
||||||
add_resource_record(header, ((char *)header) + 65536, &trunc, sizeof(struct dns_header), &p, daemon->local_ttl, NULL, T_AAAA, C_IN, "6", &addr);
|
|
||||||
log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV4, name, (union all_addr *)&addr, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trunc)
|
|
||||||
header->hb3 |= HB3_TC;
|
|
||||||
|
|
||||||
return p - (unsigned char *)header;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||||
union all_addr *dst_addr, unsigned int dst_iface,
|
union all_addr *dst_addr, unsigned int dst_iface,
|
||||||
struct dns_header *header, size_t plen, char *limit, time_t now,
|
struct dns_header *header, size_t plen, char *limit, time_t now,
|
||||||
@@ -239,10 +190,17 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
|||||||
fwd_flags |= FREC_DO_QUESTION;
|
fwd_flags |= FREC_DO_QUESTION;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Check for retry on existing query */
|
/* Check for retry on existing query.
|
||||||
|
FREC_DNSKEY and FREC_DS_QUERY are never set in flags, so the test below
|
||||||
|
ensures that no frec created for internal DNSSEC query can be returned here.
|
||||||
|
|
||||||
|
Similarly FREC_NO_CACHE is never set in flags, so a query which is
|
||||||
|
contigent on a particular source address EDNS0 option will never be matched. */
|
||||||
if (forward)
|
if (forward)
|
||||||
old_src = 1;
|
old_src = 1;
|
||||||
else if ((forward = lookup_frec_by_query(hash, fwd_flags)))
|
else if ((forward = lookup_frec_by_query(hash, fwd_flags,
|
||||||
|
FREC_CHECKING_DISABLED | FREC_AD_QUESTION | FREC_DO_QUESTION |
|
||||||
|
FREC_HAS_PHEADER | FREC_DNSKEY_QUERY | FREC_DS_QUERY | FREC_NO_CACHE)))
|
||||||
{
|
{
|
||||||
struct frec_src *src;
|
struct frec_src *src;
|
||||||
|
|
||||||
@@ -270,7 +228,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
|||||||
/* If we've been spammed with many duplicates, return REFUSED. */
|
/* If we've been spammed with many duplicates, return REFUSED. */
|
||||||
if (!daemon->free_frec_src)
|
if (!daemon->free_frec_src)
|
||||||
{
|
{
|
||||||
query_full(now);
|
query_full(now, NULL);
|
||||||
goto reply;
|
goto reply;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,7 +251,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* retry existing query */
|
/* new query */
|
||||||
if (!forward)
|
if (!forward)
|
||||||
{
|
{
|
||||||
/* new query */
|
/* new query */
|
||||||
@@ -320,7 +278,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
|||||||
|
|
||||||
master = daemon->serverarray[first];
|
master = daemon->serverarray[first];
|
||||||
|
|
||||||
if (!(forward = get_new_frec(now, NULL, NULL)))
|
if (!(forward = get_new_frec(now, master, 0)))
|
||||||
goto reply;
|
goto reply;
|
||||||
/* table full - flags == 0, return REFUSED */
|
/* table full - flags == 0, return REFUSED */
|
||||||
|
|
||||||
@@ -381,6 +339,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);
|
||||||
@@ -429,12 +389,12 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
|||||||
forward->flags |= FREC_TEST_PKTSZ;
|
forward->flags |= FREC_TEST_PKTSZ;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We may be resending a DNSSEC query here, for which the below processing is not necessary. */
|
|
||||||
if (!is_dnssec)
|
|
||||||
{
|
|
||||||
/* If a query is retried, use the log_id for the retry when logging the answer. */
|
/* If a query is retried, use the log_id for the retry when logging the answer. */
|
||||||
forward->frec_src.log_id = daemon->log_id;
|
forward->frec_src.log_id = daemon->log_id;
|
||||||
|
|
||||||
|
/* We may be resending a DNSSEC query here, for which the below processing is not necessary. */
|
||||||
|
if (!is_dnssec)
|
||||||
|
{
|
||||||
header->id = htons(forward->new_id);
|
header->id = htons(forward->new_id);
|
||||||
|
|
||||||
plen = add_edns0_config(header, plen, ((unsigned char *)header) + PACKETSZ, &forward->frec_src.source, now, &subnet, &cacheable);
|
plen = add_edns0_config(header, plen, ((unsigned char *)header) + PACKETSZ, &forward->frec_src.source, now, &subnet, &cacheable);
|
||||||
@@ -565,12 +525,22 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
|||||||
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)))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (oph)
|
if (oph)
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -703,8 +673,10 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
|||||||
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,
|
||||||
@@ -714,6 +686,7 @@ 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, sets, is_sign, check_rebind, no_cache, cache_secure, &doctored))
|
||||||
{
|
{
|
||||||
@@ -765,15 +738,14 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_DNSSEC
|
#ifdef HAVE_DNSSEC
|
||||||
static int dnssec_validate(struct frec **forwardp, struct dns_header *header,
|
static void dnssec_validate(struct frec *forward, struct dns_header *header,
|
||||||
ssize_t plen, struct server *server, time_t now)
|
ssize_t plen, int status, time_t now)
|
||||||
{
|
{
|
||||||
int status = 0;
|
daemon->log_display_id = forward->frec_src.log_id;
|
||||||
struct frec *forward = *forwardp;
|
|
||||||
|
|
||||||
/* We've had a reply already, which we're validating. Ignore this duplicate */
|
/* We've had a reply already, which we're validating. Ignore this duplicate */
|
||||||
if (forward->blocking_query)
|
if (forward->blocking_query)
|
||||||
return STAT_INPROGRESS;
|
return;
|
||||||
|
|
||||||
/* Truncated answer can't be validated.
|
/* Truncated answer can't be validated.
|
||||||
If this is an answer to a DNSSEC-generated query, we still
|
If this is an answer to a DNSSEC-generated query, we still
|
||||||
@@ -787,8 +759,6 @@ static int dnssec_validate(struct frec **forwardp, struct dns_header *header,
|
|||||||
if (RCODE(header) == REFUSED)
|
if (RCODE(header) == REFUSED)
|
||||||
status = STAT_ABANDONED;
|
status = STAT_ABANDONED;
|
||||||
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
/* 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. */
|
||||||
@@ -805,7 +775,7 @@ static int dnssec_validate(struct frec **forwardp, struct dns_header *header,
|
|||||||
#ifdef HAVE_DUMPFILE
|
#ifdef HAVE_DUMPFILE
|
||||||
if (status == STAT_BOGUS)
|
if (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, &server->addr, NULL);
|
header, (size_t)plen, &forward->sentto->addr, NULL);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -813,67 +783,78 @@ static int dnssec_validate(struct frec **forwardp, struct dns_header *header,
|
|||||||
answer aside, whilst we get that. */
|
answer aside, whilst we get that. */
|
||||||
if (status == STAT_NEED_DS || status == STAT_NEED_KEY)
|
if (status == STAT_NEED_DS || status == STAT_NEED_KEY)
|
||||||
{
|
{
|
||||||
struct frec *new = NULL, *orig;
|
struct frec *new = NULL;
|
||||||
int serverind;
|
int serverind;
|
||||||
|
struct blockdata *stash;
|
||||||
/* Free any saved query */
|
|
||||||
if (forward->stash)
|
|
||||||
blockdata_free(forward->stash);
|
|
||||||
|
|
||||||
/* Now save reply pending receipt of key data */
|
/* Now save reply pending receipt of key data */
|
||||||
if (!(forward->stash = blockdata_alloc((char *)header, plen)))
|
if ((serverind = dnssec_server(forward->sentto, daemon->keyname, NULL, NULL)) != -1 &&
|
||||||
return STAT_ABANDONED;
|
(stash = blockdata_alloc((char *)header, plen)))
|
||||||
|
{
|
||||||
|
struct server *server = daemon->serverarray[serverind];
|
||||||
|
struct frec *orig;
|
||||||
|
unsigned int flags;
|
||||||
|
void *hash;
|
||||||
|
size_t nn;
|
||||||
|
|
||||||
|
/* validate routines leave name of required record in daemon->keyname */
|
||||||
|
nn = dnssec_generate_query(header, ((unsigned char *) header) + server->edns_pktsz,
|
||||||
|
daemon->keyname, forward->class,
|
||||||
|
status == STAT_NEED_KEY ? T_DNSKEY : T_DS, server->edns_pktsz);
|
||||||
|
|
||||||
|
flags = (status == STAT_NEED_KEY) ? FREC_DNSKEY_QUERY : FREC_DS_QUERY;
|
||||||
|
hash = hash_questions(header, nn, daemon->namebuff);
|
||||||
|
|
||||||
|
if ((new = lookup_frec_by_query(hash, flags, FREC_DNSKEY_QUERY | FREC_DS_QUERY)))
|
||||||
|
{
|
||||||
|
forward->next_dependent = new->dependent;
|
||||||
|
new->dependent = forward;
|
||||||
|
/* Make consistent, only replace query copy with unvalidated answer
|
||||||
|
when we set ->blocking_query. */
|
||||||
|
if (forward->stash)
|
||||||
|
blockdata_free(forward->stash);
|
||||||
|
forward->blocking_query = new;
|
||||||
forward->stash_len = plen;
|
forward->stash_len = plen;
|
||||||
|
forward->stash = stash;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Find the original query that started it all.... */
|
/* Find the original query that started it all.... */
|
||||||
for (orig = forward; orig->dependent; orig = orig->dependent);
|
for (orig = forward; orig->dependent; orig = orig->dependent);
|
||||||
|
|
||||||
/* Make sure we don't expire and free the orig frec during the
|
/* Make sure we don't expire and free the orig frec during the
|
||||||
allocation of a new one. */
|
allocation of a new one: third arg of get_new_frec() does that. */
|
||||||
if (--orig->work_counter == 0 ||
|
if (--orig->work_counter == 0 || !(new = get_new_frec(now, server, 1)))
|
||||||
!(new = get_new_frec(now, NULL, orig)) ||
|
blockdata_free(stash); /* don't leak this on failure. */
|
||||||
(serverind = dnssec_server(server, daemon->keyname, NULL, NULL)) == -1)
|
|
||||||
{
|
|
||||||
status = STAT_ABANDONED;
|
|
||||||
if (new)
|
|
||||||
free_frec(new);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int querytype, fd;
|
int fd;
|
||||||
struct frec *next = new->next;
|
struct frec *next = new->next;
|
||||||
size_t nn;
|
|
||||||
|
|
||||||
server = daemon->serverarray[serverind];
|
|
||||||
|
|
||||||
*new = *forward; /* copy everything, then overwrite */
|
*new = *forward; /* copy everything, then overwrite */
|
||||||
new->next = next;
|
new->next = next;
|
||||||
new->blocking_query = NULL;
|
new->blocking_query = NULL;
|
||||||
|
|
||||||
|
new->frec_src.log_id = daemon->log_display_id = ++daemon->log_id;
|
||||||
new->sentto = server;
|
new->sentto = server;
|
||||||
new->rfds = NULL;
|
new->rfds = NULL;
|
||||||
new->frec_src.next = NULL;
|
new->frec_src.next = NULL;
|
||||||
new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY | FREC_HAS_EXTRADATA);
|
new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY | FREC_HAS_EXTRADATA);
|
||||||
|
new->flags |= flags;
|
||||||
new->forwardall = 0;
|
new->forwardall = 0;
|
||||||
|
|
||||||
|
forward->next_dependent = NULL;
|
||||||
new->dependent = forward; /* to find query awaiting new one. */
|
new->dependent = forward; /* to find query awaiting new one. */
|
||||||
forward->blocking_query = new; /* for garbage cleaning */
|
|
||||||
/* validate routines leave name of required record in daemon->keyname */
|
|
||||||
if (status == STAT_NEED_KEY)
|
|
||||||
{
|
|
||||||
new->flags |= FREC_DNSKEY_QUERY;
|
|
||||||
querytype = T_DNSKEY;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
new->flags |= FREC_DS_QUERY;
|
|
||||||
querytype = T_DS;
|
|
||||||
}
|
|
||||||
|
|
||||||
nn = dnssec_generate_query(header,((unsigned char *) header) + server->edns_pktsz,
|
/* Make consistent, only replace query copy with unvalidated answer
|
||||||
daemon->keyname, forward->class, querytype, server->edns_pktsz);
|
when we set ->blocking_query. */
|
||||||
|
forward->blocking_query = new;
|
||||||
|
if (forward->stash)
|
||||||
|
blockdata_free(forward->stash);
|
||||||
|
forward->stash_len = plen;
|
||||||
|
forward->stash = stash;
|
||||||
|
|
||||||
memcpy(new->hash, hash_questions(header, nn, daemon->namebuff), HASH_SIZE);
|
memcpy(new->hash, hash, HASH_SIZE);
|
||||||
new->new_id = get_id();
|
new->new_id = get_id();
|
||||||
header->id = htons(new->new_id);
|
header->id = htons(new->new_id);
|
||||||
/* Save query for retransmission */
|
/* Save query for retransmission */
|
||||||
@@ -891,28 +872,38 @@ static int dnssec_validate(struct frec **forwardp, 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", querytype));
|
querystr("dnssec-query", status == STAT_NEED_KEY ? T_DNSKEY : T_DS));
|
||||||
server->queries++;
|
server->queries++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return STAT_INPROGRESS;
|
}
|
||||||
|
|
||||||
|
/* sending DNSSEC query failed. */
|
||||||
|
status = STAT_ABANDONED;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Validated original answer, all done. */
|
/* Validated original answer, all done. */
|
||||||
if (!forward->dependent)
|
if (!forward->dependent)
|
||||||
break;
|
return_reply(now, forward, header, plen, status);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* validated subsidiary query/queries, (and cached result)
|
||||||
|
pop that and return to the previous query/queries we were working on. */
|
||||||
|
struct frec *prev, *nxt = forward->dependent;
|
||||||
|
|
||||||
/* validated subsidiary query, (and cached result)
|
|
||||||
pop that and return to the previous query we were working on. */
|
|
||||||
struct frec *prev = forward->dependent;
|
|
||||||
free_frec(forward);
|
free_frec(forward);
|
||||||
*forwardp = forward = prev;
|
|
||||||
forward->blocking_query = NULL; /* already gone */
|
|
||||||
blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
|
|
||||||
plen = forward->stash_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
return status;
|
while ((prev = nxt))
|
||||||
|
{
|
||||||
|
/* ->next_dependent will have changed after return from recursive call below. */
|
||||||
|
nxt = prev->next_dependent;
|
||||||
|
prev->blocking_query = NULL; /* already gone */
|
||||||
|
blockdata_retrieve(prev->stash, prev->stash_len, (void *)header);
|
||||||
|
dnssec_validate(prev, header, prev->stash_len, status, now);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -926,11 +917,9 @@ void reply_query(int fd, time_t now)
|
|||||||
struct frec *forward;
|
struct frec *forward;
|
||||||
socklen_t addrlen = sizeof(serveraddr);
|
socklen_t addrlen = sizeof(serveraddr);
|
||||||
ssize_t n = recvfrom(fd, daemon->packet, daemon->packet_buff_sz, 0, &serveraddr.sa, &addrlen);
|
ssize_t n = recvfrom(fd, daemon->packet, daemon->packet_buff_sz, 0, &serveraddr.sa, &addrlen);
|
||||||
size_t nn;
|
|
||||||
struct server *server;
|
struct server *server;
|
||||||
void *hash;
|
void *hash;
|
||||||
int first, last, c;
|
int first, last, c;
|
||||||
int check_rebind = 0, no_cache_dnssec = 0, cache_secure = 0, bogusanswer = 0;
|
|
||||||
|
|
||||||
/* packet buffer overwritten */
|
/* packet buffer overwritten */
|
||||||
daemon->srv_save = NULL;
|
daemon->srv_save = NULL;
|
||||||
@@ -995,10 +984,14 @@ void reply_query(int fd, time_t now)
|
|||||||
unsigned short udp_size = PACKETSZ; /* default if no EDNS0 */
|
unsigned short udp_size = PACKETSZ; /* default if no EDNS0 */
|
||||||
size_t plen;
|
size_t plen;
|
||||||
int is_sign;
|
int is_sign;
|
||||||
|
size_t nn = 0;
|
||||||
|
|
||||||
#ifdef HAVE_DNSSEC
|
#ifdef HAVE_DNSSEC
|
||||||
/* DNSSEC queries have a copy of the original query stashed. */
|
/* DNSSEC queries have a copy of the original query stashed.
|
||||||
if (forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY))
|
The query MAY have got a good answer, and be awaiting
|
||||||
|
the results of further queries, in which case
|
||||||
|
The Stash contains something else and we don't need to retry anyway. */
|
||||||
|
if ((forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY)) && !forward->blocking_query)
|
||||||
{
|
{
|
||||||
blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
|
blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
|
||||||
nn = forward->stash_len;
|
nn = forward->stash_len;
|
||||||
@@ -1007,8 +1000,6 @@ void reply_query(int fd, time_t now)
|
|||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
nn = 0;
|
|
||||||
|
|
||||||
/* recreate query from reply */
|
/* recreate query from reply */
|
||||||
if ((pheader = find_pseudoheader(header, (size_t)n, &plen, &udpsz, &is_sign, NULL)))
|
if ((pheader = find_pseudoheader(header, (size_t)n, &plen, &udpsz, &is_sign, NULL)))
|
||||||
GETSHORT(udp_size, udpsz);
|
GETSHORT(udp_size, udpsz);
|
||||||
@@ -1066,25 +1057,36 @@ void reply_query(int fd, time_t now)
|
|||||||
my_syslog(LOG_WARNING, _("reducing DNS packet size for nameserver %s to %d"), daemon->addrbuff, SAFE_PKTSZ);
|
my_syslog(LOG_WARNING, _("reducing DNS packet size for nameserver %s to %d"), daemon->addrbuff, SAFE_PKTSZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
forward->sentto = server;
|
||||||
|
|
||||||
|
#ifdef HAVE_DNSSEC
|
||||||
|
if ((forward->sentto->flags & SERV_DO_DNSSEC) &&
|
||||||
|
option_bool(OPT_DNSSEC_VALID) &&
|
||||||
|
!(forward->flags & FREC_CHECKING_DISABLED))
|
||||||
|
dnssec_validate(forward, header, n, STAT_OK, now);
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
return_reply(now, forward, header, n, STAT_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void return_reply(time_t now, struct frec *forward, struct dns_header *header, ssize_t n, int status)
|
||||||
|
{
|
||||||
|
int check_rebind = 0, no_cache_dnssec = 0, cache_secure = 0, bogusanswer = 0;
|
||||||
|
size_t nn;
|
||||||
|
|
||||||
|
(void)status;
|
||||||
|
|
||||||
|
daemon->log_display_id = forward->frec_src.log_id;
|
||||||
|
daemon->log_source_addr = &forward->frec_src.source;
|
||||||
|
|
||||||
/* Don't cache replies where DNSSEC validation was turned off, either
|
/* Don't cache replies where DNSSEC validation was turned off, either
|
||||||
the upstream server told us so, or the original query specified it. */
|
the upstream server told us so, or the original query specified it. */
|
||||||
if ((header->hb4 & HB4_CD) || (forward->flags & FREC_CHECKING_DISABLED))
|
if ((header->hb4 & HB4_CD) || (forward->flags & FREC_CHECKING_DISABLED))
|
||||||
no_cache_dnssec = 1;
|
no_cache_dnssec = 1;
|
||||||
|
|
||||||
#ifdef HAVE_DNSSEC
|
#ifdef HAVE_DNSSEC
|
||||||
if ((forward->sentto->flags & SERV_DO_DNSSEC) &&
|
if (status != STAT_OK)
|
||||||
option_bool(OPT_DNSSEC_VALID) &&
|
|
||||||
!(forward->flags & FREC_CHECKING_DISABLED))
|
|
||||||
{
|
{
|
||||||
/* Note that the value of forward may change here:
|
|
||||||
we can start with a DNSSEC query reply and
|
|
||||||
return with a query that was suspended pending
|
|
||||||
that DNSSEC query. */
|
|
||||||
int status = dnssec_validate(&forward, header, n, server, now);
|
|
||||||
|
|
||||||
if (status == STAT_INPROGRESS)
|
|
||||||
return;
|
|
||||||
|
|
||||||
no_cache_dnssec = 0;
|
no_cache_dnssec = 0;
|
||||||
|
|
||||||
if (status == STAT_TRUNCATED)
|
if (status == STAT_TRUNCATED)
|
||||||
@@ -1131,7 +1133,7 @@ void reply_query(int fd, time_t now)
|
|||||||
if (forward->flags & FREC_NO_CACHE)
|
if (forward->flags & FREC_NO_CACHE)
|
||||||
no_cache_dnssec = 1;
|
no_cache_dnssec = 1;
|
||||||
|
|
||||||
if ((nn = process_reply(header, now, server, (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)))
|
||||||
{
|
{
|
||||||
@@ -1160,6 +1162,16 @@ void reply_query(int fd, time_t now)
|
|||||||
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);
|
||||||
|
|
||||||
@@ -1176,6 +1188,47 @@ void reply_query(int fd, time_t now)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#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;
|
||||||
|
|
||||||
|
#ifdef HAVE_UBUS
|
||||||
|
if (name)
|
||||||
|
ubus_event_bcast_connmark_allowlist_refused(mark, name);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
setup_reply(header, /* flags: */ 0);
|
||||||
|
|
||||||
|
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;
|
||||||
@@ -1187,6 +1240,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
|
||||||
@@ -1416,6 +1474,11 @@ 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
|
||||||
@@ -1426,6 +1489,10 @@ void receive_query(struct listener *listen, time_t now)
|
|||||||
log_query_mysockaddr(F_QUERY | F_FORWARD, daemon->namebuff,
|
log_query_mysockaddr(F_QUERY | F_FORWARD, daemon->namebuff,
|
||||||
&source_addr, types);
|
&source_addr, types);
|
||||||
|
|
||||||
|
#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 */
|
||||||
if (!auth_dns && !option_bool(OPT_LOCALISE))
|
if (!auth_dns && !option_bool(OPT_LOCALISE))
|
||||||
@@ -1466,20 +1533,50 @@ 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)
|
||||||
|
{
|
||||||
|
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, 0, NULL, 0, do_bit, 0);
|
||||||
|
|
||||||
|
if (m >= 1)
|
||||||
|
{
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
|
#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 */
|
||||||
@@ -1491,6 +1588,10 @@ void receive_query(struct listener *listen, time_t now)
|
|||||||
|
|
||||||
if (m >= 1)
|
if (m >= 1)
|
||||||
{
|
{
|
||||||
|
#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]++;
|
||||||
@@ -1520,6 +1621,7 @@ static ssize_t tcp_talk(int first, int last, int start, unsigned char *packet,
|
|||||||
(void)have_mark;
|
(void)have_mark;
|
||||||
|
|
||||||
memcpy(hash, hash_questions(header, (unsigned int)qsize, daemon->namebuff), HASH_SIZE);
|
memcpy(hash, hash_questions(header, (unsigned int)qsize, daemon->namebuff), HASH_SIZE);
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
int data_sent = 0;
|
int data_sent = 0;
|
||||||
@@ -1624,6 +1726,7 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
|
|||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
size_t m;
|
size_t m;
|
||||||
|
int log_save;
|
||||||
|
|
||||||
/* 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)
|
||||||
@@ -1664,11 +1767,16 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log_save = daemon->log_display_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));
|
querystr("dnssec-query", 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;
|
||||||
|
|
||||||
if (new_status != STAT_OK)
|
if (new_status != STAT_OK)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1690,6 +1798,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
|
||||||
@@ -1721,7 +1832,7 @@ unsigned char *tcp_request(int confd, time_t now,
|
|||||||
|
|
||||||
#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;
|
||||||
|
|
||||||
@@ -1801,6 +1912,10 @@ unsigned char *tcp_request(int confd, time_t now,
|
|||||||
log_query_mysockaddr(F_QUERY | F_FORWARD, daemon->namebuff,
|
log_query_mysockaddr(F_QUERY | F_FORWARD, daemon->namebuff,
|
||||||
&peer_addr, types);
|
&peer_addr, types);
|
||||||
|
|
||||||
|
#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 */
|
||||||
if (!auth_dns && !option_bool(OPT_LOCALISE))
|
if (!auth_dns && !option_bool(OPT_LOCALISE))
|
||||||
@@ -1835,12 +1950,30 @@ 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)
|
||||||
|
{
|
||||||
|
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, 0, NULL, 0, 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 */
|
||||||
@@ -1864,13 +1997,8 @@ unsigned char *tcp_request(int confd, time_t now,
|
|||||||
(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 = F_NOERR;
|
||||||
break;;
|
else if (lookup_domain(daemon->namebuff, gotname, &first, &last) && !(flags = is_local_answer(now, first, daemon->namebuff)))
|
||||||
}
|
|
||||||
|
|
||||||
/* Configured answer or no available server. */
|
|
||||||
if (lookup_domain(daemon->namebuff, gotname, &first, &last) && !(flags = is_local_answer(now, first, daemon->namebuff)))
|
|
||||||
{
|
{
|
||||||
master = daemon->serverarray[first];
|
master = daemon->serverarray[first];
|
||||||
|
|
||||||
@@ -1960,7 +2088,8 @@ 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)))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (have_pseudoheader)
|
if (have_pseudoheader)
|
||||||
@@ -1971,6 +2100,13 @@ unsigned char *tcp_request(int confd, time_t now,
|
|||||||
|
|
||||||
*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;
|
||||||
}
|
}
|
||||||
@@ -1978,29 +2114,6 @@ unsigned char *tcp_request(int confd, time_t now,
|
|||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static struct frec *allocate_frec(time_t now)
|
|
||||||
{
|
|
||||||
struct frec *f;
|
|
||||||
|
|
||||||
if ((f = (struct frec *)whine_malloc(sizeof(struct frec))))
|
|
||||||
{
|
|
||||||
f->next = daemon->frec_list;
|
|
||||||
f->time = now;
|
|
||||||
f->sentto = NULL;
|
|
||||||
f->rfds = NULL;
|
|
||||||
f->flags = 0;
|
|
||||||
#ifdef HAVE_DNSSEC
|
|
||||||
f->dependent = NULL;
|
|
||||||
f->blocking_query = NULL;
|
|
||||||
f->stash = NULL;
|
|
||||||
#endif
|
|
||||||
daemon->frec_list = f;
|
|
||||||
}
|
|
||||||
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* return a UDP socket bound to a random port, have to cope with straying into
|
/* return a UDP socket bound to a random port, have to cope with straying into
|
||||||
occupied port nos and reserved ones. */
|
occupied port nos and reserved ones. */
|
||||||
static int random_sock(struct server *s)
|
static int random_sock(struct server *s)
|
||||||
@@ -2215,30 +2328,46 @@ static void free_frec(struct frec *f)
|
|||||||
|
|
||||||
/* Anything we're waiting on is pointless now, too */
|
/* Anything we're waiting on is pointless now, too */
|
||||||
if (f->blocking_query)
|
if (f->blocking_query)
|
||||||
|
{
|
||||||
|
struct frec *n, **up;
|
||||||
|
|
||||||
|
/* unlink outselves from the blocking query's dependents list. */
|
||||||
|
for (n = f->blocking_query->dependent, up = &f->blocking_query->dependent; n; n = n->next_dependent)
|
||||||
|
if (n == f)
|
||||||
|
{
|
||||||
|
*up = n->next_dependent;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
up = &n->next_dependent;
|
||||||
|
|
||||||
|
/* If we were the only/last dependent, free the blocking query too. */
|
||||||
|
if (!f->blocking_query->dependent)
|
||||||
free_frec(f->blocking_query);
|
free_frec(f->blocking_query);
|
||||||
|
}
|
||||||
|
|
||||||
f->blocking_query = NULL;
|
f->blocking_query = NULL;
|
||||||
f->dependent = NULL;
|
f->dependent = NULL;
|
||||||
|
f->next_dependent = NULL;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* if wait==NULL return a free or older than TIMEOUT record.
|
/* Impose an absolute
|
||||||
else return *wait zero if one available, or *wait is delay to
|
|
||||||
when the oldest in-use record will expire. Impose an absolute
|
|
||||||
limit of 4*TIMEOUT before we wipe things (for random sockets).
|
limit of 4*TIMEOUT before we wipe things (for random sockets).
|
||||||
If force is non-NULL, always return a result, even if we have
|
If force is set, always return a result, even if we have
|
||||||
to allocate above the limit, and never free the record pointed
|
to allocate above the limit, and don'y free any records.
|
||||||
to by the force argument. */
|
This is set when allocating for DNSSEC to avoid cutting off
|
||||||
struct frec *get_new_frec(time_t now, int *wait, struct frec *force)
|
the branch we are sitting on. */
|
||||||
|
static struct frec *get_new_frec(time_t now, struct server *master, int force)
|
||||||
{
|
{
|
||||||
struct frec *f, *oldest, *target;
|
struct frec *f, *oldest, *target;
|
||||||
int count;
|
int count;
|
||||||
|
|
||||||
if (wait)
|
/* look for free records, garbage collect old records and count number in use by our server-group. */
|
||||||
*wait = 0;
|
for (f = daemon->frec_list, oldest = NULL, target = NULL, count = 0; f; f = f->next)
|
||||||
|
{
|
||||||
for (f = daemon->frec_list, oldest = NULL, target = NULL, count = 0; f; f = f->next, count++)
|
|
||||||
if (!f->sentto)
|
if (!f->sentto)
|
||||||
target = f;
|
target = f;
|
||||||
else
|
else
|
||||||
@@ -2247,7 +2376,7 @@ struct frec *get_new_frec(time_t now, int *wait, struct frec *force)
|
|||||||
/* Don't free DNSSEC sub-queries here, as we may end up with
|
/* Don't free DNSSEC sub-queries here, as we may end up with
|
||||||
dangling references to them. They'll go when their "real" query
|
dangling references to them. They'll go when their "real" query
|
||||||
is freed. */
|
is freed. */
|
||||||
if (!f->dependent && f != force)
|
if (!f->dependent && !force)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
if (difftime(now, f->time) >= 4*TIMEOUT)
|
if (difftime(now, f->time) >= 4*TIMEOUT)
|
||||||
@@ -2255,64 +2384,51 @@ struct frec *get_new_frec(time_t now, int *wait, struct frec *force)
|
|||||||
free_frec(f);
|
free_frec(f);
|
||||||
target = f;
|
target = f;
|
||||||
}
|
}
|
||||||
|
else if (!oldest || difftime(f->time, oldest->time) <= 0)
|
||||||
|
|
||||||
if (!oldest || difftime(f->time, oldest->time) <= 0)
|
|
||||||
oldest = f;
|
oldest = f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target)
|
if (f->sentto && ((int)difftime(now, f->time)) < TIMEOUT && server_samegroup(f->sentto, master))
|
||||||
{
|
count++;
|
||||||
target->time = now;
|
|
||||||
return target;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* can't find empty one, use oldest if there is one
|
if (!force && count >= daemon->ftabsize)
|
||||||
and it's older than timeout */
|
|
||||||
if (!force && oldest && ((int)difftime(now, oldest->time)) >= TIMEOUT)
|
|
||||||
{
|
{
|
||||||
/* keep stuff for twice timeout if we can by allocating a new
|
query_full(now, master->domain);
|
||||||
record instead */
|
|
||||||
if (difftime(now, oldest->time) < 2*TIMEOUT &&
|
|
||||||
count <= daemon->ftabsize &&
|
|
||||||
(f = allocate_frec(now)))
|
|
||||||
return f;
|
|
||||||
|
|
||||||
if (!wait)
|
|
||||||
{
|
|
||||||
free_frec(oldest);
|
|
||||||
oldest->time = now;
|
|
||||||
}
|
|
||||||
return oldest;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* none available, calculate time 'till oldest record expires */
|
|
||||||
if (!force && count > daemon->ftabsize)
|
|
||||||
{
|
|
||||||
if (oldest && wait)
|
|
||||||
*wait = oldest->time + (time_t)TIMEOUT - now;
|
|
||||||
|
|
||||||
query_full(now);
|
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(f = allocate_frec(now)) && wait)
|
if (!target && oldest && ((int)difftime(now, oldest->time)) >= TIMEOUT)
|
||||||
/* wait one second on malloc failure */
|
{
|
||||||
*wait = 1;
|
/* can't find empty one, use oldest if there is one and it's older than timeout */
|
||||||
|
free_frec(oldest);
|
||||||
return f; /* OK if malloc fails and this is NULL */
|
target = oldest;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void query_full(time_t now)
|
if (!target && (target = (struct frec *)whine_malloc(sizeof(struct frec))))
|
||||||
|
{
|
||||||
|
target->next = daemon->frec_list;
|
||||||
|
daemon->frec_list = target;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target)
|
||||||
|
target->time = now;
|
||||||
|
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void query_full(time_t now, char *domain)
|
||||||
{
|
{
|
||||||
static time_t last_log = 0;
|
static time_t last_log = 0;
|
||||||
|
|
||||||
if ((int)difftime(now, last_log) > 5)
|
if ((int)difftime(now, last_log) > 5)
|
||||||
{
|
{
|
||||||
last_log = now;
|
last_log = now;
|
||||||
|
if (!domain || strlen(domain) == 0)
|
||||||
my_syslog(LOG_WARNING, _("Maximum number of concurrent DNS queries reached (max: %d)"), daemon->ftabsize);
|
my_syslog(LOG_WARNING, _("Maximum number of concurrent DNS queries reached (max: %d)"), daemon->ftabsize);
|
||||||
|
else
|
||||||
|
my_syslog(LOG_WARNING, _("Maximum number of concurrent DNS queries to %s reached (max: %d)"), domain, daemon->ftabsize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2349,22 +2465,13 @@ static struct frec *lookup_frec(unsigned short id, int fd, void *hash, int *firs
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct frec *lookup_frec_by_query(void *hash, unsigned int flags)
|
static struct frec *lookup_frec_by_query(void *hash, unsigned int flags, unsigned int flagmask)
|
||||||
{
|
{
|
||||||
struct frec *f;
|
struct frec *f;
|
||||||
|
|
||||||
/* FREC_DNSKEY and FREC_DS_QUERY are never set in flags, so the test below
|
|
||||||
ensures that no frec created for internal DNSSEC query can be returned here.
|
|
||||||
|
|
||||||
Similarly FREC_NO_CACHE is never set in flags, so a query which is
|
|
||||||
contigent on a particular source address EDNS0 option will never be matched. */
|
|
||||||
|
|
||||||
#define FLAGMASK (FREC_CHECKING_DISABLED | FREC_AD_QUESTION | FREC_DO_QUESTION \
|
|
||||||
| FREC_HAS_PHEADER | FREC_DNSKEY_QUERY | FREC_DS_QUERY | FREC_NO_CACHE)
|
|
||||||
|
|
||||||
for(f = daemon->frec_list; f; f = f->next)
|
for(f = daemon->frec_list; f; f = f->next)
|
||||||
if (f->sentto &&
|
if (f->sentto &&
|
||||||
(f->flags & FLAGMASK) == flags &&
|
(f->flags & flagmask) == flags &&
|
||||||
memcmp(hash, f->hash, HASH_SIZE) == 0)
|
memcmp(hash, f->hash, HASH_SIZE) == 0)
|
||||||
return f;
|
return f;
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1617,7 +1617,7 @@ void add_update_server(int flags,
|
|||||||
|
|
||||||
serv->flags = flags;
|
serv->flags = flags;
|
||||||
serv->domain = domain_str;
|
serv->domain = domain_str;
|
||||||
|
serv->domain_len = strlen(domain_str);
|
||||||
|
|
||||||
if (!(flags & SERV_IS_LOCAL))
|
if (!(flags & SERV_IS_LOCAL))
|
||||||
{
|
{
|
||||||
|
|||||||
+149
-5
@@ -171,6 +171,8 @@ struct myoption {
|
|||||||
#define LOPT_DYNHOST 362
|
#define LOPT_DYNHOST 362
|
||||||
#define LOPT_LOG_DEBUG 363
|
#define LOPT_LOG_DEBUG 363
|
||||||
#define LOPT_UMBRELLA 364
|
#define LOPT_UMBRELLA 364
|
||||||
|
#define LOPT_CMARK_ALST_EN 365
|
||||||
|
#define LOPT_CMARK_ALST 366
|
||||||
|
|
||||||
#ifdef HAVE_GETOPT_LONG
|
#ifdef HAVE_GETOPT_LONG
|
||||||
static const struct option opts[] =
|
static const struct option opts[] =
|
||||||
@@ -324,6 +326,8 @@ static const struct myoption opts[] =
|
|||||||
{ "auth-sec-servers", 1, 0, LOPT_AUTHSFS },
|
{ "auth-sec-servers", 1, 0, LOPT_AUTHSFS },
|
||||||
{ "auth-peer", 1, 0, LOPT_AUTHPEER },
|
{ "auth-peer", 1, 0, LOPT_AUTHPEER },
|
||||||
{ "ipset", 1, 0, LOPT_IPSET },
|
{ "ipset", 1, 0, LOPT_IPSET },
|
||||||
|
{ "connmark-allowlist-enable", 2, 0, LOPT_CMARK_ALST_EN },
|
||||||
|
{ "connmark-allowlist", 1, 0, LOPT_CMARK_ALST },
|
||||||
{ "synth-domain", 1, 0, LOPT_SYNTH },
|
{ "synth-domain", 1, 0, LOPT_SYNTH },
|
||||||
{ "dnssec", 0, 0, LOPT_SEC_VALID },
|
{ "dnssec", 0, 0, LOPT_SEC_VALID },
|
||||||
{ "trust-anchor", 1, 0, LOPT_TRUST_ANCHOR },
|
{ "trust-anchor", 1, 0, LOPT_TRUST_ANCHOR },
|
||||||
@@ -508,6 +512,8 @@ static struct {
|
|||||||
{ LOPT_AUTHSFS, ARG_DUP, "<NS>[,<NS>...]", gettext_noop("Secondary authoritative nameservers for forward domains"), NULL },
|
{ LOPT_AUTHSFS, ARG_DUP, "<NS>[,<NS>...]", gettext_noop("Secondary authoritative nameservers for forward domains"), NULL },
|
||||||
{ LOPT_AUTHPEER, ARG_DUP, "<ipaddr>[,<ipaddr>...]", gettext_noop("Peers which are allowed to do zone transfer"), NULL },
|
{ LOPT_AUTHPEER, ARG_DUP, "<ipaddr>[,<ipaddr>...]", gettext_noop("Peers which are allowed to do zone transfer"), NULL },
|
||||||
{ LOPT_IPSET, ARG_DUP, "/<domain>[/<domain>...]/<ipset>...", gettext_noop("Specify ipsets to which matching domains should be added"), NULL },
|
{ LOPT_IPSET, ARG_DUP, "/<domain>[/<domain>...]/<ipset>...", gettext_noop("Specify ipsets to which matching domains should be added"), NULL },
|
||||||
|
{ LOPT_CMARK_ALST_EN, ARG_ONE, "[=<mask>]", gettext_noop("Enable filtering of DNS queries with connection-track marks."), NULL },
|
||||||
|
{ LOPT_CMARK_ALST, ARG_DUP, "<connmark>[/<mask>][,<pattern>[/<pattern>...]]", gettext_noop("Set allowed DNS patterns for a connection-track mark."), NULL },
|
||||||
{ LOPT_SYNTH, ARG_DUP, "<domain>,<range>,[<prefix>]", gettext_noop("Specify a domain and address range for synthesised names"), NULL },
|
{ LOPT_SYNTH, ARG_DUP, "<domain>,<range>,[<prefix>]", gettext_noop("Specify a domain and address range for synthesised names"), NULL },
|
||||||
{ LOPT_SEC_VALID, OPT_DNSSEC_VALID, NULL, gettext_noop("Activate DNSSEC validation"), NULL },
|
{ LOPT_SEC_VALID, OPT_DNSSEC_VALID, NULL, gettext_noop("Activate DNSSEC validation"), NULL },
|
||||||
{ LOPT_TRUST_ANCHOR, ARG_DUP, "<domain>,[<class>],...", gettext_noop("Specify trust anchor key digest."), NULL },
|
{ LOPT_TRUST_ANCHOR, ARG_DUP, "<domain>,[<class>],...", gettext_noop("Specify trust anchor key digest."), NULL },
|
||||||
@@ -685,13 +691,16 @@ static int atoi_check(char *a, int *res)
|
|||||||
|
|
||||||
static int strtoul_check(char *a, u32 *res)
|
static int strtoul_check(char *a, u32 *res)
|
||||||
{
|
{
|
||||||
|
unsigned long x;
|
||||||
|
|
||||||
if (!numeric_check(a))
|
if (!numeric_check(a))
|
||||||
return 0;
|
return 0;
|
||||||
*res = strtoul(a, NULL, 10);
|
x = strtoul(a, NULL, 10);
|
||||||
if (errno == ERANGE) {
|
if (errno || x > UINT32_MAX) {
|
||||||
errno = 0;
|
errno = 0;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
*res = (u32)x;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -812,7 +821,7 @@ static char *parse_mysockaddr(char *arg, union mysockaddr *addr)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_addr, char *interface, int *flags)
|
char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_addr, char *interface, u16 *flags)
|
||||||
{
|
{
|
||||||
int source_port = 0, serv_port = NAMESERVER_PORT;
|
int source_port = 0, serv_port = NAMESERVER_PORT;
|
||||||
char *portno, *source;
|
char *portno, *source;
|
||||||
@@ -2617,7 +2626,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
|||||||
comma = split_chr(arg, '/');
|
comma = split_chr(arg, '/');
|
||||||
new = opt_malloc(sizeof(struct serv_local));
|
new = opt_malloc(sizeof(struct serv_local));
|
||||||
new->domain = opt_string_alloc(arg);
|
new->domain = opt_string_alloc(arg);
|
||||||
new->flags = strlen(arg);
|
new->domain_len = strlen(arg);
|
||||||
new->next = daemon->no_rebind;
|
new->next = daemon->no_rebind;
|
||||||
daemon->no_rebind = new;
|
daemon->no_rebind = new;
|
||||||
arg = comma;
|
arg = comma;
|
||||||
@@ -2634,7 +2643,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
|||||||
size_t size;
|
size_t size;
|
||||||
char *lastdomain = NULL, *domain = "";
|
char *lastdomain = NULL, *domain = "";
|
||||||
char *alloc_domain;
|
char *alloc_domain;
|
||||||
int flags = 0;
|
u16 flags = 0;
|
||||||
char *err;
|
char *err;
|
||||||
struct in_addr addr4;
|
struct in_addr addr4;
|
||||||
struct in6_addr addr6;
|
struct in6_addr addr6;
|
||||||
@@ -2647,6 +2656,9 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
|||||||
char *last;
|
char *last;
|
||||||
|
|
||||||
arg++;
|
arg++;
|
||||||
|
/* elide leading dots - they are implied in the search algorithm */
|
||||||
|
while (*arg == '.') arg++;
|
||||||
|
|
||||||
domain = lastdomain = arg;
|
domain = lastdomain = arg;
|
||||||
|
|
||||||
while ((last = split_chr(arg, '/')))
|
while ((last = split_chr(arg, '/')))
|
||||||
@@ -2730,6 +2742,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
|||||||
}
|
}
|
||||||
|
|
||||||
new->domain = alloc_domain;
|
new->domain = alloc_domain;
|
||||||
|
new->domain_len = strlen(alloc_domain);
|
||||||
|
|
||||||
/* server=//1.2.3.4 is special. */
|
/* server=//1.2.3.4 is special. */
|
||||||
if (strlen(domain) == 0 && lastdomain)
|
if (strlen(domain) == 0 && lastdomain)
|
||||||
@@ -2751,6 +2764,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
|||||||
new = opt_malloc(size);
|
new = opt_malloc(size);
|
||||||
memcpy(new, last, size);
|
memcpy(new, last, size);
|
||||||
new->domain = alloc_domain;
|
new->domain = alloc_domain;
|
||||||
|
new->domain_len = strlen(alloc_domain);
|
||||||
|
|
||||||
if (flags & (SERV_USE_RESOLV | SERV_LITERAL_ADDRESS))
|
if (flags & (SERV_USE_RESOLV | SERV_LITERAL_ADDRESS))
|
||||||
{
|
{
|
||||||
new->next = daemon->local_domains;
|
new->next = daemon->local_domains;
|
||||||
@@ -2877,6 +2892,135 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
case LOPT_CMARK_ALST_EN: /* --connmark-allowlist-enable */
|
||||||
|
#ifndef HAVE_CONNTRACK
|
||||||
|
ret_err(_("recompile with HAVE_CONNTRACK defined to enable connmark-allowlist directives"));
|
||||||
|
break;
|
||||||
|
#else
|
||||||
|
{
|
||||||
|
u32 mask = UINT32_MAX;
|
||||||
|
|
||||||
|
if (arg)
|
||||||
|
if (!strtoul_check(arg, &mask) || mask < 1)
|
||||||
|
ret_err(gen_err);
|
||||||
|
|
||||||
|
set_option_bool(OPT_CMARK_ALST_EN);
|
||||||
|
daemon->allowlist_mask = mask;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
case LOPT_CMARK_ALST: /* --connmark-allowlist */
|
||||||
|
#ifndef HAVE_CONNTRACK
|
||||||
|
ret_err(_("recompile with HAVE_CONNTRACK defined to enable connmark-allowlist directives"));
|
||||||
|
break;
|
||||||
|
#else
|
||||||
|
{
|
||||||
|
struct allowlist *allowlists;
|
||||||
|
char **patterns, **patterns_pos;
|
||||||
|
u32 mark, mask = UINT32_MAX;
|
||||||
|
size_t num_patterns = 0;
|
||||||
|
|
||||||
|
char *c, *m = NULL;
|
||||||
|
char *separator;
|
||||||
|
unhide_metas(arg);
|
||||||
|
if (!arg)
|
||||||
|
ret_err(gen_err);
|
||||||
|
c = arg;
|
||||||
|
if (*c < '0' || *c > '9')
|
||||||
|
ret_err(gen_err);
|
||||||
|
while (*c && *c != ',')
|
||||||
|
{
|
||||||
|
if (*c == '/')
|
||||||
|
{
|
||||||
|
if (m)
|
||||||
|
ret_err(gen_err);
|
||||||
|
*c = '\0';
|
||||||
|
m = ++c;
|
||||||
|
}
|
||||||
|
if (*c < '0' || *c > '9')
|
||||||
|
ret_err(gen_err);
|
||||||
|
c++;
|
||||||
|
}
|
||||||
|
separator = c;
|
||||||
|
if (!*separator)
|
||||||
|
break;
|
||||||
|
while (c && *c)
|
||||||
|
{
|
||||||
|
char *end = strchr(++c, '/');
|
||||||
|
if (end)
|
||||||
|
*end = '\0';
|
||||||
|
if (strcmp(c, "*") && !is_valid_dns_name_pattern(c))
|
||||||
|
ret_err(gen_err);
|
||||||
|
if (end)
|
||||||
|
*end = '/';
|
||||||
|
if (num_patterns >= UINT16_MAX - 1)
|
||||||
|
ret_err(gen_err);
|
||||||
|
num_patterns++;
|
||||||
|
c = end;
|
||||||
|
}
|
||||||
|
|
||||||
|
*separator = '\0';
|
||||||
|
if (!strtoul_check(arg, &mark) || mark < 1 || mark > UINT32_MAX)
|
||||||
|
ret_err(gen_err);
|
||||||
|
if (m)
|
||||||
|
if (!strtoul_check(m, &mask) || mask < 1 || mask > UINT32_MAX || (mark & ~mask))
|
||||||
|
ret_err(gen_err);
|
||||||
|
if (num_patterns)
|
||||||
|
*separator = ',';
|
||||||
|
for (allowlists = daemon->allowlists; allowlists; allowlists = allowlists->next)
|
||||||
|
if (allowlists->mark == mark && allowlists->mask == mask)
|
||||||
|
ret_err(gen_err);
|
||||||
|
|
||||||
|
patterns = opt_malloc((num_patterns + 1) * sizeof(char *));
|
||||||
|
if (!patterns)
|
||||||
|
goto fail_cmark_allowlist;
|
||||||
|
patterns_pos = patterns;
|
||||||
|
c = separator;
|
||||||
|
while (c && *c)
|
||||||
|
{
|
||||||
|
char *end = strchr(++c, '/');
|
||||||
|
if (end)
|
||||||
|
*end = '\0';
|
||||||
|
if (!(*patterns_pos++ = opt_string_alloc(c)))
|
||||||
|
goto fail_cmark_allowlist;
|
||||||
|
if (end)
|
||||||
|
*end = '/';
|
||||||
|
c = end;
|
||||||
|
}
|
||||||
|
*patterns_pos++ = NULL;
|
||||||
|
|
||||||
|
allowlists = opt_malloc(sizeof(struct allowlist));
|
||||||
|
if (!allowlists)
|
||||||
|
goto fail_cmark_allowlist;
|
||||||
|
memset(allowlists, 0, sizeof(struct allowlist));
|
||||||
|
allowlists->mark = mark;
|
||||||
|
allowlists->mask = mask;
|
||||||
|
allowlists->patterns = patterns;
|
||||||
|
allowlists->next = daemon->allowlists;
|
||||||
|
daemon->allowlists = allowlists;
|
||||||
|
break;
|
||||||
|
|
||||||
|
fail_cmark_allowlist:
|
||||||
|
if (patterns)
|
||||||
|
{
|
||||||
|
for (patterns_pos = patterns; *patterns_pos; patterns_pos++)
|
||||||
|
{
|
||||||
|
free(*patterns_pos);
|
||||||
|
*patterns_pos = NULL;
|
||||||
|
}
|
||||||
|
free(patterns);
|
||||||
|
patterns = NULL;
|
||||||
|
}
|
||||||
|
if (allowlists)
|
||||||
|
{
|
||||||
|
free(allowlists);
|
||||||
|
allowlists = NULL;
|
||||||
|
}
|
||||||
|
ret_err(gen_err);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
case 'c': /* --cache-size */
|
case 'c': /* --cache-size */
|
||||||
{
|
{
|
||||||
int size;
|
int size;
|
||||||
|
|||||||
+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_WARNING, __VA_ARGS__); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define ASSERT(condition) \
|
||||||
|
do { \
|
||||||
|
if (!(condition)) \
|
||||||
|
LOG("[pattern.c:%d] Assertion failure: %s", __LINE__, #condition); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether a given string value matches against a glob pattern
|
||||||
|
* which may contain zero-or-more-character wildcards denoted by '*'.
|
||||||
|
*
|
||||||
|
* Based on "Glob Matching Can Be Simple And Fast Too" by Russ Cox,
|
||||||
|
* See https://research.swtch.com/glob
|
||||||
|
*
|
||||||
|
* @param value A string value.
|
||||||
|
* @param num_value_bytes The number of bytes of the string value.
|
||||||
|
* @param pattern A glob pattern.
|
||||||
|
* @param num_pattern_bytes The number of bytes of the glob pattern.
|
||||||
|
*
|
||||||
|
* @return 1 If the provided value matches against the glob pattern.
|
||||||
|
* @return 0 Otherwise.
|
||||||
|
*/
|
||||||
|
static int is_string_matching_glob_pattern(
|
||||||
|
const char *value,
|
||||||
|
size_t num_value_bytes,
|
||||||
|
const char *pattern,
|
||||||
|
size_t num_pattern_bytes)
|
||||||
|
{
|
||||||
|
ASSERT(value);
|
||||||
|
ASSERT(pattern);
|
||||||
|
|
||||||
|
size_t value_index = 0;
|
||||||
|
size_t next_value_index = 0;
|
||||||
|
size_t pattern_index = 0;
|
||||||
|
size_t next_pattern_index = 0;
|
||||||
|
while (value_index < num_value_bytes || pattern_index < num_pattern_bytes)
|
||||||
|
{
|
||||||
|
if (pattern_index < num_pattern_bytes)
|
||||||
|
{
|
||||||
|
char pattern_character = pattern[pattern_index];
|
||||||
|
if ('a' <= pattern_character && pattern_character <= 'z')
|
||||||
|
pattern_character -= 'a' - 'A';
|
||||||
|
if (pattern_character == '*')
|
||||||
|
{
|
||||||
|
// zero-or-more-character wildcard
|
||||||
|
// Try to match at value_index, otherwise restart at value_index + 1 next.
|
||||||
|
next_pattern_index = pattern_index;
|
||||||
|
pattern_index++;
|
||||||
|
if (value_index < num_value_bytes)
|
||||||
|
next_value_index = value_index + 1;
|
||||||
|
else
|
||||||
|
next_value_index = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// ordinary character
|
||||||
|
if (value_index < num_value_bytes)
|
||||||
|
{
|
||||||
|
char value_character = value[value_index];
|
||||||
|
if ('a' <= value_character && value_character <= 'z')
|
||||||
|
value_character -= 'a' - 'A';
|
||||||
|
if (value_character == pattern_character)
|
||||||
|
{
|
||||||
|
pattern_index++;
|
||||||
|
value_index++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (next_value_index)
|
||||||
|
{
|
||||||
|
pattern_index = next_pattern_index;
|
||||||
|
value_index = next_value_index;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether a given string value represents a valid DNS name.
|
||||||
|
*
|
||||||
|
* - DNS names must adhere to RFC 1123: 1 to 253 characters in length, consisting of a sequence of labels
|
||||||
|
* delimited by dots ("."). Each label must be 1 to 63 characters in length, contain only
|
||||||
|
* ASCII letters ("a"-"Z"), digits ("0"-"9"), or hyphens ("-") and must not start or end with a hyphen.
|
||||||
|
*
|
||||||
|
* - A valid name must be fully qualified, i.e., consist of at least two labels.
|
||||||
|
* The final label must not be fully numeric, and must not be the "local" pseudo-TLD.
|
||||||
|
*
|
||||||
|
* - Examples:
|
||||||
|
* Valid: "example.com"
|
||||||
|
* Invalid: "ipcamera", "ipcamera.local", "8.8.8.8"
|
||||||
|
*
|
||||||
|
* @param value A string value.
|
||||||
|
*
|
||||||
|
* @return 1 If the provided string value is a valid DNS name.
|
||||||
|
* @return 0 Otherwise.
|
||||||
|
*/
|
||||||
|
int is_valid_dns_name(const char *value)
|
||||||
|
{
|
||||||
|
ASSERT(value);
|
||||||
|
|
||||||
|
size_t num_bytes = 0;
|
||||||
|
size_t num_labels = 0;
|
||||||
|
const char *label = NULL;
|
||||||
|
int is_label_numeric = 1;
|
||||||
|
for (const char *c = value;; c++)
|
||||||
|
{
|
||||||
|
if (*c &&
|
||||||
|
*c != '-' && *c != '.' &&
|
||||||
|
(*c < '0' || *c > '9') &&
|
||||||
|
(*c < 'A' || *c > 'Z') &&
|
||||||
|
(*c < 'a' || *c > 'z'))
|
||||||
|
{
|
||||||
|
LOG("Invalid DNS name: Invalid character %c.", *c);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (*c)
|
||||||
|
num_bytes++;
|
||||||
|
if (!label)
|
||||||
|
{
|
||||||
|
if (!*c || *c == '.')
|
||||||
|
{
|
||||||
|
LOG("Invalid DNS name: Empty label.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (*c == '-')
|
||||||
|
{
|
||||||
|
LOG("Invalid DNS name: Label starts with hyphen.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
label = c;
|
||||||
|
}
|
||||||
|
if (*c && *c != '.')
|
||||||
|
{
|
||||||
|
if (*c < '0' || *c > '9')
|
||||||
|
is_label_numeric = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (c[-1] == '-')
|
||||||
|
{
|
||||||
|
LOG("Invalid DNS name: Label ends with hyphen.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
size_t num_label_bytes = (size_t) (c - label);
|
||||||
|
if (num_label_bytes > 63)
|
||||||
|
{
|
||||||
|
LOG("Invalid DNS name: Label is too long (%zu).", num_label_bytes);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
num_labels++;
|
||||||
|
if (!*c)
|
||||||
|
{
|
||||||
|
if (num_labels < 2)
|
||||||
|
{
|
||||||
|
LOG("Invalid DNS name: Not enough labels (%zu).", num_labels);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (is_label_numeric)
|
||||||
|
{
|
||||||
|
LOG("Invalid DNS name: Final label is fully numeric.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (num_label_bytes == 5 &&
|
||||||
|
(label[0] == 'l' || label[0] == 'L') &&
|
||||||
|
(label[1] == 'o' || label[1] == 'O') &&
|
||||||
|
(label[2] == 'c' || label[2] == 'C') &&
|
||||||
|
(label[3] == 'a' || label[3] == 'A') &&
|
||||||
|
(label[4] == 'l' || label[4] == 'L'))
|
||||||
|
{
|
||||||
|
LOG("Invalid DNS name: \"local\" pseudo-TLD.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (num_bytes < 1 || num_bytes > 253)
|
||||||
|
{
|
||||||
|
LOG("DNS name has invalid length (%zu).", num_bytes);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
label = NULL;
|
||||||
|
is_label_numeric = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether a given string value represents a valid DNS name pattern.
|
||||||
|
*
|
||||||
|
* - DNS names must adhere to RFC 1123: 1 to 253 characters in length, consisting of a sequence of labels
|
||||||
|
* delimited by dots ("."). Each label must be 1 to 63 characters in length, contain only
|
||||||
|
* ASCII letters ("a"-"Z"), digits ("0"-"9"), or hyphens ("-") and must not start or end with a hyphen.
|
||||||
|
*
|
||||||
|
* - Patterns follow the syntax of DNS names, but additionally allow the wildcard character "*" to be used up to
|
||||||
|
* twice per label to match 0 or more characters within that label. Note that the wildcard never matches a dot
|
||||||
|
* (e.g., "*.example.com" matches "api.example.com" but not "api.us.example.com").
|
||||||
|
*
|
||||||
|
* - A valid name or pattern must be fully qualified, i.e., consist of at least two labels.
|
||||||
|
* The final label must not be fully numeric, and must not be the "local" pseudo-TLD.
|
||||||
|
* A pattern must end with at least two literal (non-wildcard) labels.
|
||||||
|
*
|
||||||
|
* - Examples:
|
||||||
|
* Valid: "example.com", "*.example.com", "video*.example.com", "api*.*.example.com", "*-prod-*.example.com"
|
||||||
|
* Invalid: "ipcamera", "ipcamera.local", "*", "*.com", "8.8.8.8"
|
||||||
|
*
|
||||||
|
* @param value A string value.
|
||||||
|
*
|
||||||
|
* @return 1 If the provided string value is a valid DNS name pattern.
|
||||||
|
* @return 0 Otherwise.
|
||||||
|
*/
|
||||||
|
int is_valid_dns_name_pattern(const char *value)
|
||||||
|
{
|
||||||
|
ASSERT(value);
|
||||||
|
|
||||||
|
size_t num_bytes = 0;
|
||||||
|
size_t num_labels = 0;
|
||||||
|
const char *label = NULL;
|
||||||
|
int is_label_numeric = 1;
|
||||||
|
size_t num_wildcards = 0;
|
||||||
|
int previous_label_has_wildcard = 1;
|
||||||
|
for (const char *c = value;; c++)
|
||||||
|
{
|
||||||
|
if (*c &&
|
||||||
|
*c != '*' && // Wildcard.
|
||||||
|
*c != '-' && *c != '.' &&
|
||||||
|
(*c < '0' || *c > '9') &&
|
||||||
|
(*c < 'A' || *c > 'Z') &&
|
||||||
|
(*c < 'a' || *c > 'z'))
|
||||||
|
{
|
||||||
|
LOG("Invalid DNS name pattern: Invalid character %c.", *c);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (*c && *c != '*')
|
||||||
|
num_bytes++;
|
||||||
|
if (!label)
|
||||||
|
{
|
||||||
|
if (!*c || *c == '.')
|
||||||
|
{
|
||||||
|
LOG("Invalid DNS name pattern: Empty label.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (*c == '-')
|
||||||
|
{
|
||||||
|
LOG("Invalid DNS name pattern: Label starts with hyphen.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
label = c;
|
||||||
|
}
|
||||||
|
if (*c && *c != '.')
|
||||||
|
{
|
||||||
|
if (*c < '0' || *c > '9')
|
||||||
|
is_label_numeric = 0;
|
||||||
|
if (*c == '*')
|
||||||
|
{
|
||||||
|
if (num_wildcards >= 2)
|
||||||
|
{
|
||||||
|
LOG("Invalid DNS name pattern: Wildcard character used more than twice per label.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
num_wildcards++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (c[-1] == '-')
|
||||||
|
{
|
||||||
|
LOG("Invalid DNS name pattern: Label ends with hyphen.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
size_t num_label_bytes = (size_t) (c - label) - num_wildcards;
|
||||||
|
if (num_label_bytes > 63)
|
||||||
|
{
|
||||||
|
LOG("Invalid DNS name pattern: Label is too long (%zu).", num_label_bytes);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
num_labels++;
|
||||||
|
if (!*c)
|
||||||
|
{
|
||||||
|
if (num_labels < 2)
|
||||||
|
{
|
||||||
|
LOG("Invalid DNS name pattern: Not enough labels (%zu).", num_labels);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (num_wildcards != 0 || previous_label_has_wildcard)
|
||||||
|
{
|
||||||
|
LOG("Invalid DNS name pattern: Wildcard within final two labels.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (is_label_numeric)
|
||||||
|
{
|
||||||
|
LOG("Invalid DNS name pattern: Final label is fully numeric.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (num_label_bytes == 5 &&
|
||||||
|
(label[0] == 'l' || label[0] == 'L') &&
|
||||||
|
(label[1] == 'o' || label[1] == 'O') &&
|
||||||
|
(label[2] == 'c' || label[2] == 'C') &&
|
||||||
|
(label[3] == 'a' || label[3] == 'A') &&
|
||||||
|
(label[4] == 'l' || label[4] == 'L'))
|
||||||
|
{
|
||||||
|
LOG("Invalid DNS name pattern: \"local\" pseudo-TLD.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (num_bytes < 1 || num_bytes > 253)
|
||||||
|
{
|
||||||
|
LOG("DNS name pattern has invalid length after removing wildcards (%zu).", num_bytes);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
label = NULL;
|
||||||
|
is_label_numeric = 1;
|
||||||
|
previous_label_has_wildcard = num_wildcards != 0;
|
||||||
|
num_wildcards = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether a given DNS name matches against a DNS name pattern.
|
||||||
|
*
|
||||||
|
* @param name A valid DNS name.
|
||||||
|
* @param pattern A valid DNS name pattern.
|
||||||
|
*
|
||||||
|
* @return 1 If the provided DNS name matches against the DNS name pattern.
|
||||||
|
* @return 0 Otherwise.
|
||||||
|
*/
|
||||||
|
int is_dns_name_matching_pattern(const char *name, const char *pattern)
|
||||||
|
{
|
||||||
|
ASSERT(name);
|
||||||
|
ASSERT(is_valid_dns_name(name));
|
||||||
|
ASSERT(pattern);
|
||||||
|
ASSERT(is_valid_dns_name_pattern(pattern));
|
||||||
|
|
||||||
|
const char *n = name;
|
||||||
|
const char *p = pattern;
|
||||||
|
|
||||||
|
do {
|
||||||
|
const char *name_label = n;
|
||||||
|
while (*n && *n != '.')
|
||||||
|
n++;
|
||||||
|
const char *pattern_label = p;
|
||||||
|
while (*p && *p != '.')
|
||||||
|
p++;
|
||||||
|
if (!is_string_matching_glob_pattern(
|
||||||
|
name_label, (size_t) (n - name_label),
|
||||||
|
pattern_label, (size_t) (p - pattern_label)))
|
||||||
|
break;
|
||||||
|
if (*n)
|
||||||
|
n++;
|
||||||
|
if (*p)
|
||||||
|
p++;
|
||||||
|
} while (*n && *p);
|
||||||
|
|
||||||
|
return !*n && !*p;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
+88
-1
@@ -884,6 +884,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)
|
||||||
@@ -896,7 +982,8 @@ unsigned int extract_request(struct dns_header *header, size_t qlen, char *name,
|
|||||||
|
|
||||||
*name = 0; /* return empty name if no query found. */
|
*name = 0; /* return empty name if no query found. */
|
||||||
|
|
||||||
if (ntohs(header->qdcount) != 1 || OPCODE(header) != QUERY)
|
if (ntohs(header->qdcount) != 1 || OPCODE(header) != QUERY ||
|
||||||
|
ntohs(header->ancount) != 0 || ntohs(header->nscount) != 0)
|
||||||
return 0; /* must be exactly one query. */
|
return 0; /* must be exactly one query. */
|
||||||
|
|
||||||
if (!extract_name(header, qlen, &p, name, 1, 4))
|
if (!extract_name(header, qlen, &p, name, 1, 4))
|
||||||
|
|||||||
+188
-21
@@ -28,10 +28,38 @@ static int ubus_handle_metrics(struct ubus_context *ctx, struct ubus_object *obj
|
|||||||
struct ubus_request_data *req, const char *method,
|
struct ubus_request_data *req, const char *method,
|
||||||
struct blob_attr *msg);
|
struct blob_attr *msg);
|
||||||
|
|
||||||
|
#ifdef HAVE_CONNTRACK
|
||||||
|
enum {
|
||||||
|
SET_CONNMARK_ALLOWLIST_MARK,
|
||||||
|
SET_CONNMARK_ALLOWLIST_MASK,
|
||||||
|
SET_CONNMARK_ALLOWLIST_PATTERNS
|
||||||
|
};
|
||||||
|
static const struct blobmsg_policy set_connmark_allowlist_policy[] = {
|
||||||
|
[SET_CONNMARK_ALLOWLIST_MARK] = {
|
||||||
|
.name = "mark",
|
||||||
|
.type = BLOBMSG_TYPE_INT32
|
||||||
|
},
|
||||||
|
[SET_CONNMARK_ALLOWLIST_MASK] = {
|
||||||
|
.name = "mask",
|
||||||
|
.type = BLOBMSG_TYPE_INT32
|
||||||
|
},
|
||||||
|
[SET_CONNMARK_ALLOWLIST_PATTERNS] = {
|
||||||
|
.name = "patterns",
|
||||||
|
.type = BLOBMSG_TYPE_ARRAY
|
||||||
|
}
|
||||||
|
};
|
||||||
|
static int ubus_handle_set_connmark_allowlist(struct ubus_context *ctx, struct ubus_object *obj,
|
||||||
|
struct ubus_request_data *req, const char *method,
|
||||||
|
struct blob_attr *msg);
|
||||||
|
#endif
|
||||||
|
|
||||||
static void ubus_subscribe_cb(struct ubus_context *ctx, struct ubus_object *obj);
|
static void ubus_subscribe_cb(struct ubus_context *ctx, struct ubus_object *obj);
|
||||||
|
|
||||||
static const struct ubus_method ubus_object_methods[] = {
|
static const struct ubus_method ubus_object_methods[] = {
|
||||||
UBUS_METHOD_NOARG("metrics", ubus_handle_metrics),
|
UBUS_METHOD_NOARG("metrics", ubus_handle_metrics),
|
||||||
|
#ifdef HAVE_CONNTRACK
|
||||||
|
UBUS_METHOD("set_connmark_allowlist", ubus_handle_set_connmark_allowlist, set_connmark_allowlist_policy),
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct ubus_object_type ubus_object_type =
|
static struct ubus_object_type ubus_object_type =
|
||||||
@@ -76,42 +104,27 @@ static void ubus_disconnect_cb(struct ubus_context *ubus)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ubus_init()
|
char *ubus_init()
|
||||||
{
|
{
|
||||||
struct ubus_context *ubus = NULL;
|
struct ubus_context *ubus = NULL;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
ubus = ubus_connect(NULL);
|
if (!(ubus = ubus_connect(NULL)))
|
||||||
if (!ubus)
|
return NULL;
|
||||||
{
|
|
||||||
if (!error_logged)
|
|
||||||
{
|
|
||||||
my_syslog(LOG_ERR, _("Cannot initialize UBus: connection failed"));
|
|
||||||
error_logged = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ubus_destroy(ubus);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ubus_object.name = daemon->ubus_name;
|
ubus_object.name = daemon->ubus_name;
|
||||||
ret = ubus_add_object(ubus, &ubus_object);
|
ret = ubus_add_object(ubus, &ubus_object);
|
||||||
if (ret)
|
if (ret)
|
||||||
{
|
{
|
||||||
if (!error_logged)
|
|
||||||
{
|
|
||||||
my_syslog(LOG_ERR, _("Cannot add object to UBus: %s"), ubus_strerror(ret));
|
|
||||||
error_logged = 1;
|
|
||||||
}
|
|
||||||
ubus_destroy(ubus);
|
ubus_destroy(ubus);
|
||||||
return;
|
return (char *)ubus_strerror(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
ubus->connection_lost = ubus_disconnect_cb;
|
ubus->connection_lost = ubus_disconnect_cb;
|
||||||
daemon->ubus = ubus;
|
daemon->ubus = ubus;
|
||||||
error_logged = 0;
|
error_logged = 0;
|
||||||
|
|
||||||
my_syslog(LOG_INFO, _("Connected to system UBus"));
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_ubus_listeners()
|
void set_ubus_listeners()
|
||||||
@@ -178,6 +191,122 @@ static int ubus_handle_metrics(struct ubus_context *ctx, struct ubus_object *obj
|
|||||||
return ubus_send_reply(ctx, req, b.head);
|
return ubus_send_reply(ctx, req, b.head);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_CONNTRACK
|
||||||
|
static int ubus_handle_set_connmark_allowlist(struct ubus_context *ctx, struct ubus_object *obj,
|
||||||
|
struct ubus_request_data *req, const char *method,
|
||||||
|
struct blob_attr *msg)
|
||||||
|
{
|
||||||
|
const struct blobmsg_policy *policy = set_connmark_allowlist_policy;
|
||||||
|
size_t policy_len = countof(set_connmark_allowlist_policy);
|
||||||
|
struct allowlist *allowlists = NULL, **allowlists_pos;
|
||||||
|
char **patterns = NULL, **patterns_pos;
|
||||||
|
u32 mark, mask = UINT32_MAX;
|
||||||
|
size_t num_patterns = 0;
|
||||||
|
struct blob_attr *tb[policy_len];
|
||||||
|
struct blob_attr *attr;
|
||||||
|
|
||||||
|
if (blobmsg_parse(policy, policy_len, tb, blob_data(msg), blob_len(msg)))
|
||||||
|
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||||
|
|
||||||
|
if (!tb[SET_CONNMARK_ALLOWLIST_MARK])
|
||||||
|
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||||
|
mark = blobmsg_get_u32(tb[SET_CONNMARK_ALLOWLIST_MARK]);
|
||||||
|
if (!mark)
|
||||||
|
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||||
|
|
||||||
|
if (tb[SET_CONNMARK_ALLOWLIST_MASK])
|
||||||
|
{
|
||||||
|
mask = blobmsg_get_u32(tb[SET_CONNMARK_ALLOWLIST_MASK]);
|
||||||
|
if (!mask || (mark & ~mask))
|
||||||
|
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tb[SET_CONNMARK_ALLOWLIST_PATTERNS])
|
||||||
|
{
|
||||||
|
struct blob_attr *head = blobmsg_data(tb[SET_CONNMARK_ALLOWLIST_PATTERNS]);
|
||||||
|
size_t len = blobmsg_data_len(tb[SET_CONNMARK_ALLOWLIST_PATTERNS]);
|
||||||
|
__blob_for_each_attr(attr, head, len)
|
||||||
|
{
|
||||||
|
char *pattern;
|
||||||
|
if (blob_id(attr) != BLOBMSG_TYPE_STRING)
|
||||||
|
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||||
|
if (!(pattern = blobmsg_get_string(attr)))
|
||||||
|
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||||
|
if (strcmp(pattern, "*") && !is_valid_dns_name_pattern(pattern))
|
||||||
|
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||||
|
num_patterns++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (allowlists_pos = &daemon->allowlists; *allowlists_pos; allowlists_pos = &(*allowlists_pos)->next)
|
||||||
|
if ((*allowlists_pos)->mark == mark && (*allowlists_pos)->mask == mask)
|
||||||
|
{
|
||||||
|
struct allowlist *allowlists_next = (*allowlists_pos)->next;
|
||||||
|
for (patterns_pos = (*allowlists_pos)->patterns; *patterns_pos; patterns_pos++)
|
||||||
|
{
|
||||||
|
free(*patterns_pos);
|
||||||
|
*patterns_pos = NULL;
|
||||||
|
}
|
||||||
|
free((*allowlists_pos)->patterns);
|
||||||
|
(*allowlists_pos)->patterns = NULL;
|
||||||
|
free(*allowlists_pos);
|
||||||
|
*allowlists_pos = allowlists_next;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!num_patterns)
|
||||||
|
return UBUS_STATUS_OK;
|
||||||
|
|
||||||
|
patterns = whine_malloc((num_patterns + 1) * sizeof(char *));
|
||||||
|
if (!patterns)
|
||||||
|
goto fail;
|
||||||
|
patterns_pos = patterns;
|
||||||
|
if (tb[SET_CONNMARK_ALLOWLIST_PATTERNS])
|
||||||
|
{
|
||||||
|
struct blob_attr *head = blobmsg_data(tb[SET_CONNMARK_ALLOWLIST_PATTERNS]);
|
||||||
|
size_t len = blobmsg_data_len(tb[SET_CONNMARK_ALLOWLIST_PATTERNS]);
|
||||||
|
__blob_for_each_attr(attr, head, len)
|
||||||
|
{
|
||||||
|
char *pattern;
|
||||||
|
if (!(pattern = blobmsg_get_string(attr)))
|
||||||
|
goto fail;
|
||||||
|
if (!(*patterns_pos = whine_malloc(strlen(pattern) + 1)))
|
||||||
|
goto fail;
|
||||||
|
strcpy(*patterns_pos++, pattern);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allowlists = whine_malloc(sizeof(struct allowlist));
|
||||||
|
if (!allowlists)
|
||||||
|
goto fail;
|
||||||
|
memset(allowlists, 0, sizeof(struct allowlist));
|
||||||
|
allowlists->mark = mark;
|
||||||
|
allowlists->mask = mask;
|
||||||
|
allowlists->patterns = patterns;
|
||||||
|
allowlists->next = daemon->allowlists;
|
||||||
|
daemon->allowlists = allowlists;
|
||||||
|
return UBUS_STATUS_OK;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
if (patterns)
|
||||||
|
{
|
||||||
|
for (patterns_pos = patterns; *patterns_pos; patterns_pos++)
|
||||||
|
{
|
||||||
|
free(*patterns_pos);
|
||||||
|
*patterns_pos = NULL;
|
||||||
|
}
|
||||||
|
free(patterns);
|
||||||
|
patterns = NULL;
|
||||||
|
}
|
||||||
|
if (allowlists)
|
||||||
|
{
|
||||||
|
free(allowlists);
|
||||||
|
allowlists = NULL;
|
||||||
|
}
|
||||||
|
return UBUS_STATUS_UNKNOWN_ERROR;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void ubus_event_bcast(const char *type, const char *mac, const char *ip, const char *name, const char *interface)
|
void ubus_event_bcast(const char *type, const char *mac, const char *ip, const char *name, const char *interface)
|
||||||
{
|
{
|
||||||
struct ubus_context *ubus = (struct ubus_context *)daemon->ubus;
|
struct ubus_context *ubus = (struct ubus_context *)daemon->ubus;
|
||||||
@@ -197,9 +326,47 @@ void ubus_event_bcast(const char *type, const char *mac, const char *ip, const c
|
|||||||
blobmsg_add_string(&b, "interface", interface);
|
blobmsg_add_string(&b, "interface", interface);
|
||||||
|
|
||||||
ret = ubus_notify(ubus, &ubus_object, type, b.head, -1);
|
ret = ubus_notify(ubus, &ubus_object, type, b.head, -1);
|
||||||
if (!ret)
|
if (ret)
|
||||||
my_syslog(LOG_ERR, _("Failed to send UBus event: %s"), ubus_strerror(ret));
|
my_syslog(LOG_ERR, _("Failed to send UBus event: %s"), ubus_strerror(ret));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_CONNTRACK
|
||||||
|
void ubus_event_bcast_connmark_allowlist_refused(u32 mark, const char *name)
|
||||||
|
{
|
||||||
|
struct ubus_context *ubus = (struct ubus_context *)daemon->ubus;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!ubus || !notify)
|
||||||
|
return;
|
||||||
|
|
||||||
|
blob_buf_init(&b, 0);
|
||||||
|
blobmsg_add_u32(&b, "mark", mark);
|
||||||
|
blobmsg_add_string(&b, "name", name);
|
||||||
|
|
||||||
|
ret = ubus_notify(ubus, &ubus_object, "connmark-allowlist.refused", b.head, -1);
|
||||||
|
if (ret)
|
||||||
|
my_syslog(LOG_ERR, _("Failed to send UBus event: %s"), ubus_strerror(ret));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ubus_event_bcast_connmark_allowlist_resolved(u32 mark, const char *name, const char *value, u32 ttl)
|
||||||
|
{
|
||||||
|
struct ubus_context *ubus = (struct ubus_context *)daemon->ubus;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!ubus || !notify)
|
||||||
|
return;
|
||||||
|
|
||||||
|
blob_buf_init(&b, 0);
|
||||||
|
blobmsg_add_u32(&b, "mark", mark);
|
||||||
|
blobmsg_add_string(&b, "name", name);
|
||||||
|
blobmsg_add_string(&b, "value", value);
|
||||||
|
blobmsg_add_u32(&b, "ttl", ttl);
|
||||||
|
|
||||||
|
ret = ubus_notify(ubus, &ubus_object, "connmark-allowlist.resolved", b.head, /* timeout: */ 1000);
|
||||||
|
if (ret)
|
||||||
|
my_syslog(LOG_ERR, _("Failed to send UBus event: %s"), ubus_strerror(ret));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#endif /* HAVE_UBUS */
|
#endif /* HAVE_UBUS */
|
||||||
Reference in new issue
Block a user