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
|
||||||
|
|||||||
+5
-3
@@ -1965,11 +1965,13 @@ 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
|
||||||
my_syslog(LOG_INFO, "%u %s/%u %s %s %s %s", daemon->log_display_id, daemon->addrbuff2, port, source, name, verb, dest);
|
{
|
||||||
|
int port = prettyprint_addr(daemon->log_source_addr, daemon->addrbuff2);
|
||||||
|
my_syslog(LOG_INFO, "%u %s/%u %s %s %s %s", 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);
|
||||||
|
|||||||
+1
-1
@@ -23,7 +23,7 @@
|
|||||||
#define SAFE_PKTSZ 1280 /* "go anywhere" UDP packet size */
|
#define SAFE_PKTSZ 1280 /* "go anywhere" UDP packet size */
|
||||||
#define KEYBLOCK_LEN 40 /* choose to minimise fragmentation when storing DNSSEC keys */
|
#define KEYBLOCK_LEN 40 /* choose to minimise fragmentation when storing DNSSEC keys */
|
||||||
#define DNSSEC_WORK 50 /* Max number of queries to validate one question */
|
#define DNSSEC_WORK 50 /* Max number of queries to validate one question */
|
||||||
#define TIMEOUT 10 /* drop UDP queries after TIMEOUT seconds */
|
#define TIMEOUT 10 /* drop UDP queries after TIMEOUT seconds */
|
||||||
#define FORWARD_TEST 50 /* try all servers every 50 queries */
|
#define FORWARD_TEST 50 /* try all servers every 50 queries */
|
||||||
#define FORWARD_TIME 20 /* or 20 seconds */
|
#define FORWARD_TIME 20 /* or 20 seconds */
|
||||||
#define UDP_TEST_TIME 60 /* How often to reset our idea of max packet size. */
|
#define UDP_TEST_TIME 60 /* How often to reset our idea of max packet size. */
|
||||||
|
|||||||
+28
-31
@@ -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
|
||||||
@@ -1195,20 +1200,20 @@ int main (int argc, char **argv)
|
|||||||
if (daemon->dbus)
|
if (daemon->dbus)
|
||||||
my_syslog(LOG_INFO, _("connected to system DBus"));
|
my_syslog(LOG_INFO, _("connected to system DBus"));
|
||||||
}
|
}
|
||||||
check_dbus_listeners();
|
check_dbus_listeners();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_UBUS
|
#ifdef HAVE_UBUS
|
||||||
if (option_bool(OPT_UBUS))
|
/* if we didn't create a UBus connection, retry now. */
|
||||||
|
if (option_bool(OPT_UBUS) && !daemon->ubus)
|
||||||
{
|
{
|
||||||
/* if we didn't create a UBus connection, retry now. */
|
char *err;
|
||||||
if (!daemon->ubus)
|
if ((err = ubus_init()))
|
||||||
{
|
my_syslog(LOG_WARNING, _("UBus error: %s"), err);
|
||||||
ubus_init();
|
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
|
||||||
|
|||||||
+159
-69
@@ -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)
|
||||||
break;
|
{
|
||||||
|
/* qdomain is longer or same length as longest domain, and try == 0
|
||||||
|
crop the query to the longest domain. */
|
||||||
|
crop_query = qlen - daemon->serverarray[try]->domain_len;
|
||||||
|
break;
|
||||||
|
}
|
||||||
high = try;
|
high = try;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (low == try)
|
if (low == try)
|
||||||
break;
|
{
|
||||||
|
/* try now points to the last domain that sorts before the query, so
|
||||||
|
we know that a substring of the query shorter than it is required to match, so
|
||||||
|
find the largest domain that's shorter than try. Note that just going to
|
||||||
|
try+1 is not optimal, consider searching bbb in (aaa,ccc,bb). try will point
|
||||||
|
to aaa, since ccc sorts after bbb, but the first domain that has a chance to
|
||||||
|
match is bb. So find the length of the first domain later than try which is
|
||||||
|
is shorter than it.
|
||||||
|
There's a nasty edge case when qdomain sorts before _any_ of the
|
||||||
|
server domains, where try _doesn't point_ to the last domain that sorts
|
||||||
|
before the query, since no such domain exists. In that case, the loop
|
||||||
|
exits via the rc < 0 && high == try path above and this code is
|
||||||
|
not executed. */
|
||||||
|
ssize_t len, old = daemon->serverarray[try]->domain_len;
|
||||||
|
while (++try != daemon->serverarraysz)
|
||||||
|
{
|
||||||
|
if (old != (len = daemon->serverarray[try]->domain_len))
|
||||||
|
{
|
||||||
|
crop_query = qlen - len;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
low = try;
|
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,42 +243,51 @@ 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) */
|
||||||
|
|
||||||
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_6ADDR); i++);
|
#define SERV_LOCAL_ADDRESS (SERV_6ADDR | SERV_4ADDR | SERV_ALL_ZEROS)
|
||||||
|
|
||||||
if (i != nlow && (flags & F_IPV6))
|
for (i = nlow; (flags & F_CONFIG) && i < nhigh && (daemon->serverarray[i]->flags & SERV_LOCAL_ADDRESS); i++);
|
||||||
|
|
||||||
|
if (i != nlow)
|
||||||
nhigh = i;
|
nhigh = i;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
nlow = i;
|
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_6ADDR); i++);
|
||||||
|
|
||||||
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_4ADDR); i++);
|
if (i != nlow && (flags & F_IPV6))
|
||||||
|
|
||||||
if (i != nlow && (flags & F_IPV4))
|
|
||||||
nhigh = i;
|
nhigh = i;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
nlow = i;
|
nlow = i;
|
||||||
|
|
||||||
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_ALL_ZEROS); i++);
|
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_4ADDR); i++);
|
||||||
|
|
||||||
if (i != nlow && (flags & (F_IPV4 | F_IPV6)))
|
if (i != nlow && (flags & F_IPV4))
|
||||||
nhigh = i;
|
nhigh = i;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
nlow = i;
|
nlow = i;
|
||||||
|
|
||||||
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_LITERAL_ADDRESS); i++);
|
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_ALL_ZEROS); i++);
|
||||||
|
|
||||||
/* --local=/domain/, only return if we don't need a server. */
|
if (i != nlow && (flags & (F_IPV4 | F_IPV6)))
|
||||||
if (i != nlow && !(flags & (F_DNSSECOK | F_DOMAINSRV | F_SERVER)))
|
|
||||||
nhigh = i;
|
nhigh = i;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
nlow = i;
|
nlow = i;
|
||||||
/* If we want a server that can do DNSSEC, and this one can't,
|
|
||||||
return nothing. */
|
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_LITERAL_ADDRESS); i++);
|
||||||
if ((flags & F_DNSSECOK) && !(daemon->serverarray[nlow]->flags & SERV_DO_DNSSEC))
|
|
||||||
nlow = nhigh;
|
/* --local=/domain/, only return if we don't need a server. */
|
||||||
|
if (i != nlow && !(flags & (F_DNSSECOK | F_DOMAINSRV | F_SERVER)))
|
||||||
|
nhigh = i;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nlow = i;
|
||||||
|
/* If we want a server that can do DNSSEC, and this one can't,
|
||||||
|
return nothing. */
|
||||||
|
if ((flags & F_DNSSECOK) && !(daemon->serverarray[nlow]->flags & SERV_DO_DNSSEC))
|
||||||
|
nlow = 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
|
||||||
|
order_qsort() and filter_servers() */
|
||||||
|
|
||||||
/* need full comparison of dotless servers in
|
if (s1->flags & SERV_FOR_NODOTS)
|
||||||
order_qsort() and filter_servers() */
|
|
||||||
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)
|
||||||
|
|||||||
+392
-285
File diff suppressed because it is too large.
Load diff
+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