[Dnsmasq-discuss] [RFC PATCH] Add support for blocking A or AAAA queries per domain
Peter Tirsek
peter at tirsek.com
Fri Jan 6 03:33:39 UTC 2023
This patch extends the `--address` option to accept two new special
address, `!4` and `!6`, which will cause the server to block A or AAAA
queries for the specified domain(s), respectively. This can be useful
in situations where IPv6 connectivity is broken, but only to certain
domains.
Signed-off-by: Peter Tirsek <peter at tirsek.com>
---
I'm not 100% pleased with this diff, so I'm looking for some feedback
and suggestions.
I had hoped to be able to use the existing SERV_ flags in some way and
not have to extend the u16 fields and variables to 32 bits, but apart
from overloading the meaning of some other bit, which is pretty ugly and
unmaintainable in the future, I couldn't immediately see any other way
out of that.
I don't know if there could be some unexpected side effects of extending
the width of the flags field either. Does anyone have some inside
knowledge on that?
Finally, this "Works For Me", but I haven't tested it extensively, and I
haven't tested much for unexpected interactions with other options. I've
mostly tested it with the following options:
dnsmasq --listen-address=127.0.0.1 --port=5301 --no-daemon --log-queries --server 10.0.0.2 --no-resolv --no-hosts --address='/netflix.com/!6'
man/dnsmasq.8 | 9 +++++++++
src/dnsmasq.h | 6 ++++--
src/domain-match.c | 8 +++++---
src/option.c | 27 +++++++++++++++++++++++----
4 files changed, 41 insertions(+), 9 deletions(-)
diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
index 2495ed1..8a79e68 100644
--- a/man/dnsmasq.8
+++ b/man/dnsmasq.8
@@ -570,6 +570,15 @@ its subdomains. This is partly syntactic sugar for \fB--address=/example.com/0.0
and \fB--address=/example.com/::\fP but is also more efficient than including both
as separate configuration lines. Note that NULL addresses normally work in the same way as localhost, so beware that clients looking up these names are likely to end up talking to themselves.
+As a special case, an address specified as \fB!4\fP causes the server to
+return NXDOMAIN for all A (IPv4) queries, but AAAA (IPv6) queries are
+processed as normal. Conversely, specifying \fB!6\fP as the address
+causes AAAA (IPv6) queries to return NXDOMAIN, but A (IPv4) queries are
+processed as normal. This can be useful in situations where IPv6
+connectivity is broken, but only to certain domains. If you want to block
+either A or AAAA records for ALL domains, use the \fB--filter-A\fP or
+\fB--filter-AAAA\fP options instead.
+
Note that the behaviour for queries which don't match the specified address literal changed in version 2.86.
Previous versions, configured with (eg) --address=/example.com/1.2.3.4 and then queried for a RR type other than
A would return a NoData answer. From 2.86, the query is sent upstream. To restore the pre-2.86 behaviour,
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index aaa6d62..6f640f0 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -554,6 +554,7 @@ union mysockaddr {
#define SERV_LOOP 8192 /* server causes forwarding loop */
#define SERV_DO_DNSSEC 16384 /* Validate DNSSEC when using this server */
#define SERV_GOT_TCP 32768 /* Got some data from the TCP connection */
+#define SERV_NXDOMAIN 65536 /* Force NXDOMAIN */
struct serverfd {
int fd;
@@ -576,7 +577,8 @@ struct randfd_list {
struct server {
- u16 flags, domain_len;
+ int flags;
+ u16 domain_len;
char *domain;
struct server *next;
int serial, arrayposn;
@@ -1298,7 +1300,7 @@ struct server_details {
struct addrinfo *hostinfo, *orig_hostinfo;
char *interface, *source, *scope_id, *interface_opt;
int serv_port, source_port, addr_type, scope_index, valid;
- u16 *flags;
+ int *flags;
};
/* cache.c */
diff --git a/src/domain-match.c b/src/domain-match.c
index fe8e25a..0a94fd3 100644
--- a/src/domain-match.c
+++ b/src/domain-match.c
@@ -21,7 +21,7 @@ static int order_qsort(const void *a, const void *b);
static int order_servers(struct server *s, struct server *s2);
/* If the server is USE_RESOLV or LITERAL_ADDRES, it lives on the local_domains chain. */
-#define SERV_IS_LOCAL (SERV_USE_RESOLV | SERV_LITERAL_ADDRESS)
+#define SERV_IS_LOCAL (SERV_USE_RESOLV | SERV_LITERAL_ADDRESS | SERV_NXDOMAIN)
void build_server_array(void)
{
@@ -370,9 +370,11 @@ int is_local_answer(time_t now, int first, char *name)
int flags = 0;
int rc = 0;
- if ((flags = daemon->serverarray[first]->flags) & SERV_LITERAL_ADDRESS)
+ if ((flags = daemon->serverarray[first]->flags) & (SERV_LITERAL_ADDRESS | SERV_NXDOMAIN))
{
- if (flags & SERV_4ADDR)
+ if (flags & SERV_NXDOMAIN)
+ rc = F_NXDOMAIN;
+ else if (flags & SERV_4ADDR)
rc = F_IPV4;
else if (flags & SERV_6ADDR)
rc = F_IPV6;
diff --git a/src/option.c b/src/option.c
index 8e61a6b..538009f 100644
--- a/src/option.c
+++ b/src/option.c
@@ -1095,7 +1095,7 @@ static char *domain_rev4(int from_file, char *server, struct in_addr *addr4, int
int i, j;
char *string;
int msize;
- u16 flags = 0;
+ int flags = 0;
char domain[29]; /* strlen("xxx.yyy.zzz.ttt.in-addr.arpa")+1 */
union mysockaddr serv_addr, source_addr;
char interface[IF_NAMESIZE+1];
@@ -1178,7 +1178,7 @@ static char *domain_rev6(int from_file, char *server, struct in6_addr *addr6, in
int i, j;
char *string;
int msize;
- u16 flags = 0;
+ int flags = 0;
char domain[73]; /* strlen("32*<n.>ip6.arpa")+1 */
union mysockaddr serv_addr, source_addr;
char interface[IF_NAMESIZE+1];
@@ -2966,7 +2966,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
case 'A': /* --address */
{
char *lastdomain = NULL, *domain = "", *cur_domain;
- u16 flags = 0;
+ int flags = 0;
char *err;
union all_addr addr;
union mysockaddr serv_addr, source_addr;
@@ -3002,6 +3002,12 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
/* # as literal address means return zero address for 4 and 6 */
if (strcmp(arg, "#") == 0)
flags = SERV_ALL_ZEROS | SERV_LITERAL_ADDRESS;
+ /* `!4` means return NXDOMAIN for IPv4, but process normally for IPv6 */
+ else if (strcmp(arg, "!4") == 0)
+ flags = SERV_4ADDR | SERV_NXDOMAIN;
+ /* `!6` means return NXDOMAIN for IPv6, but process normally for IPv4 */
+ else if (strcmp(arg, "!6") == 0)
+ flags = SERV_6ADDR | SERV_NXDOMAIN;
else if (inet_pton(AF_INET, arg, &addr.addr4) > 0)
flags = SERV_4ADDR | SERV_LITERAL_ADDRESS;
else if (inet_pton(AF_INET6, arg, &addr.addr6) > 0)
@@ -3019,7 +3025,20 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
flags |= SERV_FROM_FILE;
cur_domain = domain;
- while ((flags & SERV_LITERAL_ADDRESS) || parse_server_next(&sdetails))
+ if (flags & SERV_NXDOMAIN)
+ {
+ while (1)
+ {
+ if (!add_update_server(flags, sdetails.addr, sdetails.source_addr, sdetails.interface, cur_domain, &addr))
+ ret_err(gen_err);
+
+ if (!lastdomain || cur_domain == lastdomain)
+ break;
+
+ cur_domain += strlen(cur_domain) + 1;
+ }
+ }
+ else while ((flags & SERV_LITERAL_ADDRESS) || parse_server_next(&sdetails))
{
cur_domain = domain;
--
2.39.0
More information about the Dnsmasq-discuss
mailing list