From f4cd2b4e98287e25e6a44d220a3c357831e4d4d7 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Wed, 15 Mar 2023 21:31:12 +0100 Subject: [PATCH 01/29] Put version.ftl also behind new no-ident config option Signed-off-by: DL6ER --- src/dnsmasq/option.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/dnsmasq/option.c b/src/dnsmasq/option.c index 96c094c6..caea766b 100644 --- a/src/dnsmasq/option.c +++ b/src/dnsmasq/option.c @@ -5879,12 +5879,10 @@ void read_opts(int argc, char **argv, char *compile_opts) add_txt("servers.bind", NULL, TXT_STAT_SERVERS); /* Pi-hole modification */ add_txt("privacylevel.pihole", NULL, TXT_PRIVACYLEVEL); + add_txt("version.FTL", (char*)get_FTL_version(), 0 ); /************************/ } #endif - /******** Pi-hole modification ********/ - add_txt("version.FTL", (char*)get_FTL_version(), 0 ); - /**************************************/ /* port might not be known when the address is parsed - fill in here */ if (daemon->servers) From e343086ca5af73f9b9e6ecefeb88b70adbef801b Mon Sep 17 00:00:00 2001 From: Taylor R Campbell Date: Sat, 25 Feb 2023 15:00:30 +0000 Subject: [PATCH 02/29] Avoid undefined behaviour with the ctype(3) functions. As defined in the C standard: In all cases the argument is an int, the value of which shall be representable as an unsigned char or shall equal the value of the macro EOF. If the argument has any other value, the behavior is undefined. This is because they're designed to work with the int values returned by getc or fgetc; they need extra work to handle a char value. If EOF is -1 (as it almost always is), with 8-bit bytes, the allowed inputs to the ctype(3) functions are: {-1, 0, 1, 2, 3, ..., 255}. However, on platforms where char is signed, such as x86 with the usual ABI, code like char *arg = ...; ... isspace(*arg) ... may pass in values in the range: {-128, -127, -126, ..., -2, -1, 0, 1, ..., 127}. This has two problems: 1. Inputs in the set {-128, -127, -126, ..., -2} are forbidden. 2. The non-EOF byte 0xff is conflated with the value EOF = -1, so even though the input is not forbidden, it may give the wrong answer. Casting char to int first before passing the result to ctype(3) doesn't help: inputs like -128 are unchanged by this cast. It is necessary to cast char inputs to unsigned char first; you can then cast to int if you like but there's no need because the functions will always convert the argument to int by definition. So the above fragment needs to be: char *arg = ...; ... isspace((unsigned char)*arg) ... This patch inserts unsigned char casts where necessary, and changes int casts to unsigned char casts where the input is char. I left alone int casts where the input is unsigned char already -- they're not immediately harmful, although they would have the effect of suppressing some compiler warnings if the input is ever changed to be char instead of unsigned char, so it might be better to remove those casts too. I also left alone calls where the input is int to begin with because it came from getc; casting to unsigned char here would be wrong, of course. Signed-off-by: DL6ER --- src/dnsmasq/dhcp-common.c | 6 +++--- src/dnsmasq/dhcp.c | 6 +++--- src/dnsmasq/loop.c | 2 +- src/dnsmasq/option.c | 8 ++++---- src/dnsmasq/rfc1035.c | 2 +- src/dnsmasq/rfc2131.c | 2 +- src/dnsmasq/tftp.c | 2 +- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/dnsmasq/dhcp-common.c b/src/dnsmasq/dhcp-common.c index 1b6cc849..7e2abef4 100644 --- a/src/dnsmasq/dhcp-common.c +++ b/src/dnsmasq/dhcp-common.c @@ -838,7 +838,7 @@ char *option_string(int prot, unsigned int opt, unsigned char *val, int opt_len, for (i = 0, j = 0; i < opt_len && j < buf_len ; i++) { char c = val[i]; - if (isprint((int)c)) + if (isprint((unsigned char)c)) buf[j++] = c; } #ifdef HAVE_DHCP6 @@ -852,7 +852,7 @@ char *option_string(int prot, unsigned int opt, unsigned char *val, int opt_len, for (k = i + 1; k < opt_len && k < l && j < buf_len ; k++) { char c = val[k]; - if (isprint((int)c)) + if (isprint((unsigned char)c)) buf[j++] = c; } i = l; @@ -873,7 +873,7 @@ char *option_string(int prot, unsigned int opt, unsigned char *val, int opt_len, for (k = 0; k < len && j < buf_len; k++) { char c = *p++; - if (isprint((int)c)) + if (isprint((unsigned char)c)) buf[j++] = c; } i += len +2; diff --git a/src/dnsmasq/dhcp.c b/src/dnsmasq/dhcp.c index 42d819f0..e5783918 100644 --- a/src/dnsmasq/dhcp.c +++ b/src/dnsmasq/dhcp.c @@ -916,14 +916,14 @@ void dhcp_read_ethers(void) lineno++; - while (strlen(buff) > 0 && isspace((int)buff[strlen(buff)-1])) + while (strlen(buff) > 0 && isspace((unsigned char)buff[strlen(buff)-1])) buff[strlen(buff)-1] = 0; if ((*buff == '#') || (*buff == '+') || (*buff == 0)) continue; - for (ip = buff; *ip && !isspace((int)*ip); ip++); - for(; *ip && isspace((int)*ip); ip++) + for (ip = buff; *ip && !isspace((unsigned char)*ip); ip++); + for(; *ip && isspace((unsigned char)*ip); ip++) *ip = 0; if (!*ip || parse_hex(buff, hwaddr, ETHER_ADDR_LEN, NULL, NULL) != ETHER_ADDR_LEN) { diff --git a/src/dnsmasq/loop.c b/src/dnsmasq/loop.c index cd4855e2..19bfae0d 100644 --- a/src/dnsmasq/loop.c +++ b/src/dnsmasq/loop.c @@ -92,7 +92,7 @@ int detect_loop(char *query, int type) return 0; for (i = 0; i < 8; i++) - if (!isxdigit(query[i])) + if (!isxdigit((unsigned char)query[i])) return 0; uid = strtol(query, NULL, 16); diff --git a/src/dnsmasq/option.c b/src/dnsmasq/option.c index caea766b..71304a98 100644 --- a/src/dnsmasq/option.c +++ b/src/dnsmasq/option.c @@ -2755,7 +2755,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma ret_err(gen_err); for (p = arg; *p; p++) - if (!isxdigit((int)*p)) + if (!isxdigit((unsigned char)*p)) ret_err(gen_err); set_option_bool(OPT_UMBRELLA_DEVID); @@ -4840,7 +4840,7 @@ err: new->target = target; new->ttl = ttl; - for (arg += arglen+1; *arg && isspace(*arg); arg++); + for (arg += arglen+1; *arg && isspace((unsigned char)*arg); arg++); } break; @@ -5231,7 +5231,7 @@ err: unhide_metas(keyhex); /* 4034: "Whitespace is allowed within digits" */ for (cp = keyhex; *cp; ) - if (isspace(*cp)) + if (isspace((unsigned char)*cp)) for (cp1 = cp; *cp1; cp1++) *cp1 = *(cp1+1); else @@ -5319,7 +5319,7 @@ static void read_file(char *file, FILE *f, int hard_opt, int from_script) memmove(p, p+1, strlen(p+1)+1); } - if (isspace(*p)) + if (isspace((unsigned char)*p)) { *p = ' '; white = 1; diff --git a/src/dnsmasq/rfc1035.c b/src/dnsmasq/rfc1035.c index 9b5f2650..09f9cc4c 100644 --- a/src/dnsmasq/rfc1035.c +++ b/src/dnsmasq/rfc1035.c @@ -520,7 +520,7 @@ static int print_txt(struct dns_header *header, const size_t qlen, char *name, /* make counted string zero-term and sanitise */ for (i = 0; i < len; i++) { - if (!isprint((int)*(p3+1))) + if (!isprint((unsigned char)*(p3+1))) break; *p3 = *(p3+1); p3++; diff --git a/src/dnsmasq/rfc2131.c b/src/dnsmasq/rfc2131.c index 17e97b52..5190982d 100644 --- a/src/dnsmasq/rfc2131.c +++ b/src/dnsmasq/rfc2131.c @@ -1678,7 +1678,7 @@ static int sanitise(unsigned char *opt, char *buf) for (i = option_len(opt); i > 0; i--) { char c = *p++; - if (isprint((int)c)) + if (isprint((unsigned char)c)) *buf++ = c; } *buf = 0; /* add terminator */ diff --git a/src/dnsmasq/tftp.c b/src/dnsmasq/tftp.c index 0861f37c..8e1dc4ae 100644 --- a/src/dnsmasq/tftp.c +++ b/src/dnsmasq/tftp.c @@ -405,7 +405,7 @@ void tftp_request(struct listener *listen, time_t now) if (*p == '\\') *p = '/'; else if (option_bool(OPT_TFTP_LC)) - *p = tolower(*p); + *p = tolower((unsigned char)*p); strcpy(daemon->namebuff, "/"); if (prefix) From d7883c53dde444683db1d2af8a2a8a9c7ce4d47f Mon Sep 17 00:00:00 2001 From: Dominik Derigs Date: Fri, 3 Mar 2023 18:05:26 +0100 Subject: [PATCH 03/29] Fix --rev-server option. It was broken in 1db9943c6879c160a5fbef885d5ceadd3668b74d when resolving upstream servers by name was extended to --rev-server without accounting for the fact that re-using one and the same upstream server for each of the x.y.z.in-addr.arpa is actually a wanted feature Signed-off-by: DL6ER --- src/dnsmasq/option.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/dnsmasq/option.c b/src/dnsmasq/option.c index 71304a98..8f738995 100644 --- a/src/dnsmasq/option.c +++ b/src/dnsmasq/option.c @@ -1163,6 +1163,9 @@ static char *domain_rev4(int from_file, char *server, struct in_addr *addr4, int } else { + /* Always reset server as valid here, so we can add the same upstream + server address multiple times for each x.y.z.in-addr.arpa */ + sdetails.valid = 1; while (parse_server_next(&sdetails)) { if ((string = parse_server_addr(&sdetails))) @@ -1248,6 +1251,9 @@ static char *domain_rev6(int from_file, char *server, struct in6_addr *addr6, in } else { + /* Always reset server as valid here, so we can add the same upstream + server address multiple times for each x.y.z.ip6.arpa */ + sdetails.valid = 1; while (parse_server_next(&sdetails)) { if ((string = parse_server_addr(&sdetails))) From 4f2fd40c7dec92a80678bb412f05910314b44e1e Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Mon, 6 Mar 2023 23:00:58 +0000 Subject: [PATCH 04/29] Fix possible SEGV when no servers defined. If there exists a --address=// or --server=//# configuration but no upstream server config unqualified by domain then when a query which doesnt match the domain is recieved it will use the qualfied server config and in the process possibly make an out-of-bounds memory access. Thanks to Daniel Danzberger for spotting the bug. Signed-off-by: DL6ER --- src/dnsmasq/domain-match.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/dnsmasq/domain-match.c b/src/dnsmasq/domain-match.c index fe8e25a3..9cc51e68 100644 --- a/src/dnsmasq/domain-match.c +++ b/src/dnsmasq/domain-match.c @@ -253,9 +253,10 @@ int lookup_domain(char *domain, int flags, int *lowout, int *highout) if (highout) *highout = nhigh; - if (nlow == nhigh) + /* qlen == -1 when we failed to match even an empty query, if there are no default servers. */ + if (nlow == nhigh || qlen == -1) return 0; - + return 1; } From e5c5a34dd772d1b421fc95e4195c0e2c88f5294d Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Tue, 7 Mar 2023 22:07:46 +0000 Subject: [PATCH 05/29] Set the default maximum DNS UDP packet size to 1232. http://www.dnsflagday.net/2020/ refers. Thanks to Xiang Li for the prompt. Signed-off-by: DL6ER --- src/dnsmasq/config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dnsmasq/config.h b/src/dnsmasq/config.h index 3ee9be1f..12562901 100644 --- a/src/dnsmasq/config.h +++ b/src/dnsmasq/config.h @@ -19,7 +19,7 @@ #define CHILD_LIFETIME 300 /* secs 'till terminated (RFC1035 suggests > 120s) */ #define TCP_MAX_QUERIES 100 /* Maximum number of queries per incoming TCP connection */ #define TCP_BACKLOG 32 /* kernel backlog limit for TCP connections */ -#define EDNS_PKTSZ 4096 /* default max EDNS.0 UDP packet from RFC5625 */ +#define EDNS_PKTSZ 1232 /* default max EDNS.0 UDP packet from from /dnsflagday.net/2020 */ #define SAFE_PKTSZ 1232 /* "go anywhere" UDP packet size, see https://dnsflagday.net/2020/ */ #define KEYBLOCK_LEN 40 /* choose to minimise fragmentation when storing DNSSEC keys */ #define DNSSEC_WORK 50 /* Max number of queries to validate one question */ From 16b711dd1c9b7e3ad49b9bc0bf1a7cecd93f9443 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Tue, 7 Mar 2023 22:46:44 +0000 Subject: [PATCH 06/29] Generalise cached NXDOMAIN replies. We can cache an NXDOMAIN reply to a query for any RRTYPE and reply from a cached NXDOMAIN to any RRTYPE. Signed-off-by: DL6ER --- src/dnsmasq/rfc1035.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/dnsmasq/rfc1035.c b/src/dnsmasq/rfc1035.c index 09f9cc4c..3b4cc340 100644 --- a/src/dnsmasq/rfc1035.c +++ b/src/dnsmasq/rfc1035.c @@ -907,9 +907,8 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t { flags &= ~(F_IPV4 | F_IPV6 | F_SRV); - /* Can store NXDOMAIN reply to CNAME or ANY query. */ - if (qtype == T_CNAME || qtype == T_ANY) - insert = 1; + /* Can store NXDOMAIN reply for any qtype. */ + insert = 1; } log_query(F_UPSTREAM | F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0), name, NULL, NULL, 0); @@ -2109,7 +2108,22 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, } if (!ans) - return 0; /* failed to answer a question */ + { + /* We may know that the domain doesn't exist for any RRtype. */ + if ((crecp = cache_find_by_name(NULL, name, now, F_NXDOMAIN))) + { + ans = nxdomain = 1; + auth = 0; + + if (!(crecp->flags & F_DNSSECOK)) + sec_data = 0; + + if (!dryrun) + log_query(F_NXDOMAIN | F_NEG, name, NULL, NULL, 0); + } + else + return 0; /* failed to answer a question */ + } } if (dryrun) From e57b84be66cae1ca368428cf4ddef04f3c9929c2 Mon Sep 17 00:00:00 2001 From: Clayton Craft Date: Wed, 8 Mar 2023 15:35:05 +0000 Subject: [PATCH 07/29] Allow configuring filter-A/AAAA via dbus. Signed-off-by: DL6ER --- src/dnsmasq/dbus.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/dnsmasq/dbus.c b/src/dnsmasq/dbus.c index fd5d1ca6..4366b7ea 100644 --- a/src/dnsmasq/dbus.c +++ b/src/dnsmasq/dbus.c @@ -52,6 +52,12 @@ const char* introspection_xml_template = " \n" " \n" " \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" " \n" " \n" " \n" @@ -817,6 +823,14 @@ DBusHandlerResult message_handler(DBusConnection *connection, { reply = dbus_set_bool(message, OPT_FILTER, "filterwin2k"); } + else if (strcmp(method, "SetFilterA") == 0) + { + reply = dbus_set_bool(message, OPT_FILTER_A, "filter-A"); + } + else if (strcmp(method, "SetFilterAAAA") == 0) + { + reply = dbus_set_bool(message, OPT_FILTER_AAAA, "filter-AAAA"); + } else if (strcmp(method, "SetLocaliseQueriesOption") == 0) { reply = dbus_set_bool(message, OPT_LOCALISE, "localise-queries"); From 8d130417f47bfb3c29326ca2afe1c17e589e4cd4 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Wed, 15 Mar 2023 21:12:55 +0000 Subject: [PATCH 08/29] Fix DHCPv6 "use multicast" response which previously failed to set the message type correctly. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Thanks to Petr Menšík for spotting the problem. Signed-off-by: DL6ER --- src/dnsmasq/rfc3315.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/dnsmasq/rfc3315.c b/src/dnsmasq/rfc3315.c index 87544816..477df91c 100644 --- a/src/dnsmasq/rfc3315.c +++ b/src/dnsmasq/rfc3315.c @@ -353,7 +353,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu put_opt6_short(DHCP6USEMULTI); put_opt6_string("Use multicast"); end_opt6(o1); - return 1; + goto done; } /* match vendor and user class options */ @@ -1277,12 +1277,14 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu } + log_tags(tagif, state->xid); + + done: /* Fill in the message type. Note that we store the offset, not a direct pointer, since the packet memory may have been reallocated. */ ((unsigned char *)(daemon->outpacket.iov_base))[start_msg] = outmsgtype; - log_tags(tagif, state->xid); log6_opts(0, state->xid, daemon->outpacket.iov_base + start_opts, daemon->outpacket.iov_base + save_counter(-1)); return 1; From cf20043aabde030009661ccdee92d73899032865 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Thu, 16 Mar 2023 15:16:17 +0000 Subject: [PATCH 09/29] Remove limitation on --dynamic-host. Dynamic-host was implemented to ignore interface addresses with /32 (or /128 for IPv6) prefix lengths, since they are not useful for synthesising addresses. Due to a bug before 2.88, this didn't work for IPv4, and some have used --dynamic-host=example.com,0.0.0.0,eth0 to do the equivalent of --interface-name for such interfaces. When the bug was fixed in 2.88 these uses broke. Since this behaviour seems to violate the principle of least surprise, and since the 2.88 fix is breaking existing imstallations, this commit removes the check on /32 and /128 prefix lengths to solve both problems. Signed-off-by: DL6ER --- src/dnsmasq/network.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/dnsmasq/network.c b/src/dnsmasq/network.c index f5900a7b..7217495b 100644 --- a/src/dnsmasq/network.c +++ b/src/dnsmasq/network.c @@ -361,13 +361,8 @@ static int iface_allowed(struct iface_param *param, int if_index, char *label, struct in_addr newaddr = addr->in.sin_addr; if (int_name->flags & INP4) - { - if (netmask.s_addr == 0xffffffff) - continue; - - newaddr.s_addr = (addr->in.sin_addr.s_addr & netmask.s_addr) | - (int_name->proto4.s_addr & ~netmask.s_addr); - } + newaddr.s_addr = (addr->in.sin_addr.s_addr & netmask.s_addr) | + (int_name->proto4.s_addr & ~netmask.s_addr); /* check for duplicates. */ for (lp = int_name->addr; lp; lp = lp->next) @@ -400,10 +395,6 @@ static int iface_allowed(struct iface_param *param, int if_index, char *label, { int i; - /* No sense in doing /128. */ - if (prefixlen == 128) - continue; - for (i = 0; i < 16; i++) { int bits = ((i+1)*8) - prefixlen; From e08f118bba456c67b0dd6e266e5699cf9e423980 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 19 Mar 2023 11:36:42 +0100 Subject: [PATCH 10/29] Add .codespellignore file to fix spell-checker action Signed-off-by: DL6ER --- .codespellignore | 4 ---- .github/.codespellignore | 4 ++++ .github/workflows/codespell.yml | 4 ++-- src/edns0.c | 2 +- src/log.c | 8 ++++---- src/regex_r.h | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) delete mode 100644 .codespellignore create mode 100644 .github/.codespellignore diff --git a/.codespellignore b/.codespellignore deleted file mode 100644 index 5530bfc9..00000000 --- a/.codespellignore +++ /dev/null @@ -1,4 +0,0 @@ -ede -edn -nd -tre \ No newline at end of file diff --git a/.github/.codespellignore b/.github/.codespellignore new file mode 100644 index 00000000..48b6bd42 --- /dev/null +++ b/.github/.codespellignore @@ -0,0 +1,4 @@ +ssudo +tre +ede +nd diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml index 7b0553a6..e4960f5e 100644 --- a/.github/workflows/codespell.yml +++ b/.github/workflows/codespell.yml @@ -15,5 +15,5 @@ jobs: name: Spell-Checking uses: codespell-project/actions-codespell@master with: - ignore_words_file: .codespellignore - skip: ./src/database/sqlite3.c,./src/database/sqlite3.h,./src/database/shell.c,./src/lua,./src/dnsmasq,./src/tre-regex + ignore_words_file: .github/.codespellignore + skip: ./src/database/sqlite3.c,./src/database/sqlite3.h,./src/database/shell.c,./src/lua,./src/dnsmasq,./src/tre-regex,./.git,./test/libs diff --git a/src/edns0.c b/src/edns0.c index 3dbaef1c..35860381 100644 --- a/src/edns0.c +++ b/src/edns0.c @@ -21,7 +21,7 @@ // EDNS(0) Client Subnet [Optional, RFC7871] #define EDNS0_ECS EDNS0_OPTION_CLIENT_SUBNET -// EDN(0) COOKIE [Standard, RFC7873] +// EDNS(0) COOKIE [Standard, RFC7873] #define EDNS0_COOKIE 10 // EDNS(0) MAC address [NOT STANDARDIZED] diff --git a/src/log.c b/src/log.c index 30e22fb4..293ba3eb 100644 --- a/src/log.c +++ b/src/log.c @@ -333,13 +333,13 @@ const char __attribute__ ((const)) *get_ordinal_suffix(unsigned int number) // If the tens digit is not equal to 1, then the following table could be used: switch (number % 10) { - case 1: // If the units digit is 1: This is written after the number "st" + case 1: // If the units digit is 1: This is written after the number "1st" return "st"; - case 2: // If the units digit is 2: This is written after the number "nd" + case 2: // If the units digit is 2: This is written after the number "2nd" return "nd"; - case 3: // If the units digit is 3: This is written after the number "rd" + case 3: // If the units digit is 3: This is written after the number "3rd" return "rd"; - default: // If the units digit is 0 or 4-9: This is written after the number "th" + default: // If the units digit is 0 or 4-9: This is written after the number "9th" return "th"; } // For example: 2nd, 7th, 20th, 23rd, 52nd, 135th, 301st BUT 311th (covered above) diff --git a/src/regex_r.h b/src/regex_r.h index 7be349b6..35e9a60c 100644 --- a/src/regex_r.h +++ b/src/regex_r.h @@ -15,7 +15,7 @@ extern const char *regextype[]; -// Use TRE instead of GNU regex library (compiled into FTL itself) +// Use TRE-Engine instead of GNU/MUSL regex libraries #define USE_TRE_REGEX #ifdef USE_TRE_REGEX From 017e086c1cff27b114e224bf62564817ae1a8ede Mon Sep 17 00:00:00 2001 From: MichaIng Date: Sun, 5 Mar 2023 14:16:42 +0100 Subject: [PATCH 11/29] Add RISC-V 64-bit support and builds Signed-off-by: MichaIng --- .github/workflows/build.yml | 4 +++- src/struct_size.c | 3 +++ test/arch_test.sh | 6 ++++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 50470690..65dafe0a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -53,7 +53,7 @@ jobs: needs: smoke-tests - container: ghcr.io/pi-hole/ftl-build:v1.23-${{ matrix.arch }} + container: ghcr.io/pi-hole/ftl-build:v1.26-${{ matrix.arch }} strategy: fail-fast: false @@ -81,6 +81,8 @@ jobs: bin_name: pihole-FTL-armv8-linux-gnueabihf - arch: aarch64 bin_name: pihole-FTL-aarch64-linux-gnu + - arch: riscv64 + bin_name: pihole-FTL-riscv64-linux-gnu env: CI_ARCH: ${{ matrix.arch }}${{ matrix.arch_extra }} diff --git a/src/struct_size.c b/src/struct_size.c index ae640c14..fd7f6fb8 100644 --- a/src/struct_size.c +++ b/src/struct_size.c @@ -29,6 +29,9 @@ int check_one_struct(const char *struct_name, const size_t found_size, const siz #elif defined(__arm__) const size_t expected_size = size32; const char *arch = "arm"; +#elif defined(__riscv) && __riscv_xlen == 64 + const size_t expected_size = size64; + const char *arch = "riscv64"; #else const size_t expected_size = 0; const char *arch = NULL; diff --git a/test/arch_test.sh b/test/arch_test.sh index 6a8ef0cd..3ec0046e 100644 --- a/test/arch_test.sh +++ b/test/arch_test.sh @@ -147,6 +147,12 @@ elif [[ "${CI_ARCH}" == "armv8a" ]]; then check_CPU_arch "v8" check_FP_arch "VFPv3-D16" +elif [[ "${CI_ARCH}" == "riscv64" ]]; then + + check_machine "ELF64" "RISC-V" + check_libs "[libm.so.6] [libc.so.6] [ld-linux-riscv64-lp64d.so.1]" + check_file "ELF 64-bit LSB pie executable, UCB RISC-V, RVC, double-float ABI, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-riscv64-lp64d.so.1, for GNU/Linux 4.15.0, with debug_info, not stripped" + else echo "Invalid job ${CI_ARCH}" From fe0339a3a2b69b52fead96cda6de7327e505e24f Mon Sep 17 00:00:00 2001 From: DL6ER Date: Wed, 22 Mar 2023 21:55:23 +0100 Subject: [PATCH 12/29] Update dnsmasq version to pi-hole-v2.89-9461807 Signed-off-by: DL6ER --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d468618a..6bf4dedd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,6 @@ cmake_minimum_required(VERSION 2.8.12) project(PIHOLE_FTL C) -set(DNSMASQ_VERSION pi-hole-v2.89) +set(DNSMASQ_VERSION pi-hole-v2.89-9461807) add_subdirectory(src) From ddd56d70db8179cb03aee1f54f000651c82ca3a3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Mar 2023 20:56:24 +0000 Subject: [PATCH 13/29] Bump actions/checkout from 3.3.0 to 3.4.0 Bumps [actions/checkout](https://github.com/actions/checkout) from 3.3.0 to 3.4.0. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3.3.0...v3.4.0) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yml | 6 +++--- .github/workflows/codespell.yml | 2 +- .github/workflows/sync-back-to-dev.yml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 65dafe0a..12620838 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,7 +24,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3.3.0 + uses: actions/checkout@v3.4.0 - name: "Calculate required variables" id: variables @@ -90,7 +90,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3.3.0 + uses: actions/checkout@v3.4.0 - name: "Fix ownership of repository" run: chown -R root . @@ -133,7 +133,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3.3.0 + uses: actions/checkout@v3.4.0 - name: Get Binaries built in previous jobs uses: actions/download-artifact@v3.0.2 diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml index e4960f5e..6ba65017 100644 --- a/.github/workflows/codespell.yml +++ b/.github/workflows/codespell.yml @@ -10,7 +10,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3.3.0 + uses: actions/checkout@v3.4.0 - name: Spell-Checking uses: codespell-project/actions-codespell@master diff --git a/.github/workflows/sync-back-to-dev.yml b/.github/workflows/sync-back-to-dev.yml index 89b6323f..b8ecdb71 100644 --- a/.github/workflows/sync-back-to-dev.yml +++ b/.github/workflows/sync-back-to-dev.yml @@ -11,7 +11,7 @@ jobs: name: Syncing branches steps: - name: Checkout - uses: actions/checkout@v3.3.0 + uses: actions/checkout@v3.4.0 - name: Opening pull request run: gh pr create -B development -H master --title 'Sync master back into development' --body 'Created by Github action' --label 'internal' env: From 094f33a8c28a284b963dde241b78a6872df9d717 Mon Sep 17 00:00:00 2001 From: Samu Voutilainen Date: Fri, 24 Mar 2023 06:48:31 +0200 Subject: [PATCH 14/29] Correct declaration for query_blocked(). Fixes build on OpenSUSE Tumbleweed. Signed-off-by: Samu Voutilainen --- src/dnsmasq_interface.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dnsmasq_interface.c b/src/dnsmasq_interface.c index 608801b8..aae62d2a 100644 --- a/src/dnsmasq_interface.c +++ b/src/dnsmasq_interface.c @@ -62,7 +62,7 @@ static void _query_set_reply(const unsigned int flags, const enum reply_type rep static bool _FTL_check_blocking(int queryID, int domainID, int clientID, const char* file, const int line); static unsigned long converttimeval(const struct timeval time) __attribute__((const)); static enum query_status detect_blocked_IP(const unsigned short flags, const union all_addr *addr, const queriesData *query, const domainsData *domain); -static void query_blocked(queriesData* query, domainsData* domain, clientsData* client, const unsigned char new_status); +static void query_blocked(queriesData* query, domainsData* domain, clientsData* client, const enum query_status new_status); static void FTL_forwarded(const unsigned int flags, const char *name, const union all_addr *addr, unsigned short port, const int id, const char* file, const int line); static void FTL_reply(const unsigned int flags, const char *name, const union all_addr *addr, const char* arg, const int id, const char* file, const int line); static void FTL_upstream_error(const union all_addr *addr, const unsigned int flags, const int id, const char* file, const int line); From 2f29edb8be4850beb339effea45b0fa91ab1cd92 Mon Sep 17 00:00:00 2001 From: Samu Voutilainen Date: Fri, 24 Mar 2023 06:52:21 +0200 Subject: [PATCH 15/29] Correct declaration for blockingstatus variable. Signed-off-by: Samu Voutilainen --- src/setupVars.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/setupVars.h b/src/setupVars.h index 7588be64..8a1c346b 100644 --- a/src/setupVars.h +++ b/src/setupVars.h @@ -20,6 +20,6 @@ char* find_equals(const char* s) __attribute__((pure)); void trim_whitespace(char *string); void check_blocking_status(void); -extern unsigned char blockingstatus; +extern enum blocking_status blockingstatus; #endif //SETUPVARS_H From 28ec1f04c45e0f254d701f3826787c584a4090d6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 25 Mar 2023 10:56:46 +0000 Subject: [PATCH 16/29] Bump actions/stale from 7.0.0 to 8.0.0 Bumps [actions/stale](https://github.com/actions/stale) from 7.0.0 to 8.0.0. - [Release notes](https://github.com/actions/stale/releases) - [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/stale/compare/v7.0.0...v8.0.0) --- updated-dependencies: - dependency-name: actions/stale dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/stale.yml | 2 +- .github/workflows/stale_pr.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 9c3c9829..e183ce17 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -13,7 +13,7 @@ jobs: issues: write steps: - - uses: actions/stale@v7.0.0 + - uses: actions/stale@v8.0.0 with: repo-token: ${{ secrets.GITHUB_TOKEN }} days-before-stale: 30 diff --git a/.github/workflows/stale_pr.yml b/.github/workflows/stale_pr.yml index 7c753d7d..e9230b2c 100644 --- a/.github/workflows/stale_pr.yml +++ b/.github/workflows/stale_pr.yml @@ -17,7 +17,7 @@ jobs: pull-requests: write steps: - - uses: actions/stale@v7.0.0 + - uses: actions/stale@v8.0.0 with: repo-token: ${{ secrets.GITHUB_TOKEN }} # Do not automatically mark PR/issue as stale From 5ad479b6289ff715f0ce4fa3ce16824e55d0f126 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 25 Mar 2023 10:56:49 +0000 Subject: [PATCH 17/29] Bump actions/checkout from 3.4.0 to 3.5.0 Bumps [actions/checkout](https://github.com/actions/checkout) from 3.4.0 to 3.5.0. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3.4.0...v3.5.0) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yml | 6 +++--- .github/workflows/codespell.yml | 2 +- .github/workflows/sync-back-to-dev.yml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 12620838..4815fc1d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,7 +24,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3.4.0 + uses: actions/checkout@v3.5.0 - name: "Calculate required variables" id: variables @@ -90,7 +90,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3.4.0 + uses: actions/checkout@v3.5.0 - name: "Fix ownership of repository" run: chown -R root . @@ -133,7 +133,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3.4.0 + uses: actions/checkout@v3.5.0 - name: Get Binaries built in previous jobs uses: actions/download-artifact@v3.0.2 diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml index 6ba65017..a04908f3 100644 --- a/.github/workflows/codespell.yml +++ b/.github/workflows/codespell.yml @@ -10,7 +10,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3.4.0 + uses: actions/checkout@v3.5.0 - name: Spell-Checking uses: codespell-project/actions-codespell@master diff --git a/.github/workflows/sync-back-to-dev.yml b/.github/workflows/sync-back-to-dev.yml index b8ecdb71..79111ddd 100644 --- a/.github/workflows/sync-back-to-dev.yml +++ b/.github/workflows/sync-back-to-dev.yml @@ -11,7 +11,7 @@ jobs: name: Syncing branches steps: - name: Checkout - uses: actions/checkout@v3.4.0 + uses: actions/checkout@v3.5.0 - name: Opening pull request run: gh pr create -B development -H master --title 'Sync master back into development' --body 'Created by Github action' --label 'internal' env: From 68242de9e66c7d1e370d169d8f9b81f4f919afa7 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Mon, 20 Mar 2023 15:16:29 +0000 Subject: [PATCH 18/29] Improve cache use with --filter-A and --filter-AAAA If --filter-AAAA is set and we have cached entry for the domain in question fpr any RR type that allows us to return a NODATA reply when --filter-AAAA is set without going upstream. Similarly for --filter-A. Signed-off-by: DL6ER --- src/dnsmasq/rfc1035.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/dnsmasq/rfc1035.c b/src/dnsmasq/rfc1035.c index 3b4cc340..16b23252 100644 --- a/src/dnsmasq/rfc1035.c +++ b/src/dnsmasq/rfc1035.c @@ -1938,6 +1938,25 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, anscount++; } } + else if (((flag & F_IPV4) && option_bool(OPT_FILTER_A)) || ((flag & F_IPV6) && option_bool(OPT_FILTER_AAAA))) + { + /* We don't have a cached answer and when we get an answer from upstream we're going to + filter it anyway. If we have a cached answer for the domain for another RRtype then + that may be enough to tell us if the answer should be NODATA and save the round trip. + Cached NXDOMAIN has already been handled, so here we look for any record for the domain, + since its existence allows us to return a NODATA answer. Note that we never set the AD flag, + since we didn't authentucate the record. We do set the AA flag since this answer comes from + local config. */ + + if (cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_SRV)) + { + ans = 1; + sec_data = 0; + + if (!dryrun) + log_query(F_NEG | F_CONFIG | flag, name, NULL, NULL, 0); + } + } } if (qtype == T_MX || qtype == T_ANY) @@ -1948,6 +1967,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, { ans = found = 1; sec_data = 0; + if (!dryrun) { int offset; From 875d5184ba74a62e044c882e3469cad3d572a722 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Mon, 20 Mar 2023 17:14:17 +0000 Subject: [PATCH 19/29] More --filter-AAAA caching improvements. Cache answers before filtering and filter coming out of the cache. Signed-off-by: DL6ER --- src/dnsmasq/forward.c | 19 +++++++++---------- src/dnsmasq/rfc1035.c | 38 +++++++++++++++++++++++++++++--------- 2 files changed, 38 insertions(+), 19 deletions(-) diff --git a/src/dnsmasq/forward.c b/src/dnsmasq/forward.c index c1070067..aa432b63 100644 --- a/src/dnsmasq/forward.c +++ b/src/dnsmasq/forward.c @@ -826,16 +826,6 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server } } - /* Before extract_addresses() */ - if (rcode == NOERROR) - { - if (option_bool(OPT_FILTER_A)) - n = rrfilter(header, n, RRFILTER_A); - - if (option_bool(OPT_FILTER_AAAA)) - n = rrfilter(header, n, RRFILTER_AAAA); - } - switch (extract_addresses(header, n, daemon->namebuff, now, ipsets, nftsets, is_sign, check_rebind, no_cache, cache_secure, &doctored)) { case 1: @@ -872,6 +862,15 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server break; } + if (rcode == NOERROR) + { + if (option_bool(OPT_FILTER_A)) + n = rrfilter(header, n, RRFILTER_A); + + if (option_bool(OPT_FILTER_AAAA)) + n = rrfilter(header, n, RRFILTER_AAAA); + } + if (doctored) cache_secure = 0; } diff --git a/src/dnsmasq/rfc1035.c b/src/dnsmasq/rfc1035.c index 16b23252..80ab6fcf 100644 --- a/src/dnsmasq/rfc1035.c +++ b/src/dnsmasq/rfc1035.c @@ -893,7 +893,18 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t return 2; } else - log_query(flags | F_FORWARD | secflag | F_UPSTREAM, name, &addr, NULL, aqtype); + { + int negflag = F_UPSTREAM; + + /* We're filtering this RRtype. It will be removed from the + returned packet in process_reply() but gets cached here anyway + and will be filtered again on the way out of the cache. Here, + we just need to alter the logging. */ + if (((flags & F_IPV4) && option_bool(OPT_FILTER_A)) || ((flags & F_IPV6) && option_bool(OPT_FILTER_AAAA))) + negflag = F_NEG | F_CONFIG; + + log_query(negflag | flags | F_FORWARD | secflag, name, &addr, NULL, aqtype); + } } p1 = endrr; @@ -1876,8 +1887,21 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, if (!(crecp->flags & F_DNSSECOK)) sec_data = 0; - - if (crecp->flags & F_NEG) + + if (!(crecp->flags & (F_HOSTS | F_DHCP))) + auth = 0; + + if ((((flag & F_IPV4) && option_bool(OPT_FILTER_A)) || ((flag & F_IPV6) && option_bool(OPT_FILTER_AAAA))) && + !(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG | F_NEG))) + { + /* We have a cached answer but we're filtering it. */ + ans = 1; + sec_data = 0; + + if (!dryrun) + log_query(F_NEG | F_CONFIG | flag, name, NULL, NULL, 0); + } + else if (crecp->flags & F_NEG) { ans = 1; auth = 0; @@ -1897,9 +1921,6 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, !is_same_net(crecp->addr.addr4, local_addr, local_netmask)) continue; - if (!(crecp->flags & (F_HOSTS | F_DHCP))) - auth = 0; - ans = 1; if (!dryrun) { @@ -1945,13 +1966,12 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, that may be enough to tell us if the answer should be NODATA and save the round trip. Cached NXDOMAIN has already been handled, so here we look for any record for the domain, since its existence allows us to return a NODATA answer. Note that we never set the AD flag, - since we didn't authentucate the record. We do set the AA flag since this answer comes from - local config. */ + since we didn't authentucate the record. */ if (cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_SRV)) { ans = 1; - sec_data = 0; + sec_data = auth = 0; if (!dryrun) log_query(F_NEG | F_CONFIG | flag, name, NULL, NULL, 0); From 3c40a9846b55f903fa77a9ca0901c729f1d9da7f Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Mon, 20 Mar 2023 18:32:14 +0000 Subject: [PATCH 20/29] Add EDE "filtered" extended error when --filter-A or --filter-AAAA act. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If a NODATA answer is returned instead of actual data for A or AAAA queries because of the existence of --filter-A or --filter-AAAA config options, then mark the replies with an EDE "filtered" tag. Basic patch by Petr Menšík, tweaked by Simon Kelley to apply onto the preceding caching patches. Signed-off-by: DL6ER --- src/dnsmasq/dnsmasq.h | 4 +-- src/dnsmasq/edns0.c | 2 +- src/dnsmasq/forward.c | 64 +++++++++++++++++++++++++++++------------- src/dnsmasq/rfc1035.c | 14 +++++++-- src/dnsmasq/rrfilter.c | 46 +++++++++++++++--------------- 5 files changed, 83 insertions(+), 47 deletions(-) diff --git a/src/dnsmasq/dnsmasq.h b/src/dnsmasq/dnsmasq.h index 2883b8d6..1c0119c2 100644 --- a/src/dnsmasq/dnsmasq.h +++ b/src/dnsmasq/dnsmasq.h @@ -1388,7 +1388,7 @@ void report_addresses(struct dns_header *header, size_t len, u32 mark); size_t answer_request(struct dns_header *header, char *limit, size_t qlen, struct in_addr local_addr, struct in_addr local_netmask, time_t now, int ad_reqd, int do_bit, int have_pseudoheader, - int *stale); + int *stale, int *filtered); int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name, time_t now); int check_for_ignored_address(struct dns_header *header, size_t qlen); @@ -1844,7 +1844,7 @@ void poll_listen(int fd, short event); int do_poll(int timeout); /* rrfilter.c */ -size_t rrfilter(struct dns_header *header, size_t plen, int mode); +size_t rrfilter(struct dns_header *header, size_t *plen, int mode); u16 *rrfilter_desc(int type); int expand_workspace(unsigned char ***wkspc, int *szp, int new); /* modes. */ diff --git a/src/dnsmasq/edns0.c b/src/dnsmasq/edns0.c index c498eb12..567101b5 100644 --- a/src/dnsmasq/edns0.c +++ b/src/dnsmasq/edns0.c @@ -178,7 +178,7 @@ size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *l memcpy(buff, datap, rdlen); /* now, delete OPT RR */ - plen = rrfilter(header, plen, RRFILTER_EDNS0); + rrfilter(header, &plen, RRFILTER_EDNS0); /* Now, force addition of a new one */ p = NULL; diff --git a/src/dnsmasq/forward.c b/src/dnsmasq/forward.c index aa432b63..604e2f95 100644 --- a/src/dnsmasq/forward.c +++ b/src/dnsmasq/forward.c @@ -734,7 +734,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server if (added_pheader) { /* client didn't send EDNS0, we added one, strip it off before returning answer. */ - n = rrfilter(header, n, RRFILTER_EDNS0); + rrfilter(header, &n, RRFILTER_EDNS0); pheader = NULL; } else @@ -864,11 +864,16 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server if (rcode == NOERROR) { + size_t modified = 0; + if (option_bool(OPT_FILTER_A)) - n = rrfilter(header, n, RRFILTER_A); + modified = rrfilter(header, &n, RRFILTER_A); if (option_bool(OPT_FILTER_AAAA)) - n = rrfilter(header, n, RRFILTER_AAAA); + modified += rrfilter(header, &n, RRFILTER_AAAA); + + if (modified > 0) + ede = EDE_FILTERED; } if (doctored) @@ -892,7 +897,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server /* If the requestor didn't set the DO bit, don't return DNSSEC info. */ if (!do_bit) - n = rrfilter(header, n, RRFILTER_DNSSEC); + rrfilter(header, &n, RRFILTER_DNSSEC); } #endif @@ -1866,7 +1871,7 @@ void receive_query(struct listener *listen, time_t now) #endif else { - int stale; + int stale, filtered; int ad_reqd = do_bit; u16 hb3 = header->hb3, hb4 = header->hb4; int fd = listen->fd; @@ -1906,17 +1911,28 @@ void receive_query(struct listener *listen, time_t now) /**********************************************/ m = answer_request(header, ((char *) header) + udp_size, (size_t)n, - dst_addr_4, netmask, now, ad_reqd, do_bit, have_pseudoheader, &stale); + dst_addr_4, netmask, now, ad_reqd, do_bit, have_pseudoheader, &stale, &filtered); if (m >= 1) { - if (stale && have_pseudoheader) + if (have_pseudoheader) { - u16 swap = htons(EDE_STALE); + int ede = EDE_UNSET; - m = add_pseudoheader(header, m, ((unsigned char *) header) + udp_size, daemon->edns_pktsz, - EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0); + if (filtered) + ede = EDE_FILTERED; + else if (stale) + ede = EDE_STALE; + + if (ede != EDE_UNSET) + { + u16 swap = htons(ede); + + m = add_pseudoheader(header, m, ((unsigned char *) header) + udp_size, daemon->edns_pktsz, + EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0); + } } + #ifdef HAVE_DUMPFILE dump_packet_udp(DUMP_REPLY, daemon->packet, m, NULL, &source_addr, listen->fd); #endif @@ -2186,7 +2202,7 @@ unsigned char *tcp_request(int confd, time_t now, unsigned char *pheader; unsigned int mark = 0; int have_mark = 0; - int first, last, stale, do_stale = 0; + int first, last, filtered, stale, do_stale = 0; unsigned int flags = 0; u16 hb3, hb4; @@ -2419,7 +2435,7 @@ unsigned char *tcp_request(int confd, time_t now, else /* m > 0 if answered from cache */ m = answer_request(header, ((char *) header) + 65536, (size_t)size, - dst_addr_4, netmask, now, ad_reqd, do_bit, have_pseudoheader, &stale); + dst_addr_4, netmask, now, ad_reqd, do_bit, have_pseudoheader, &stale, &filtered); /* Do this by steam now we're not in the select() loop */ check_log_writer(1); @@ -2561,13 +2577,23 @@ unsigned char *tcp_request(int confd, time_t now, m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0); } } - else if (stale) - { - u16 swap = htons((u16)EDE_STALE); - - m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0); - } - + else + { + ede = EDE_UNSET; + + if (filtered) + ede = EDE_FILTERED; + else if (stale) + ede = EDE_STALE; + + if (ede != EDE_UNSET) + { + u16 swap = htons((u16)ede); + + m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0); + } + } + check_log_writer(1); *length = htons(m); diff --git a/src/dnsmasq/rfc1035.c b/src/dnsmasq/rfc1035.c index 80ab6fcf..61a35ea4 100644 --- a/src/dnsmasq/rfc1035.c +++ b/src/dnsmasq/rfc1035.c @@ -1432,7 +1432,7 @@ static int cache_validated(const struct crec *crecp) size_t answer_request(struct dns_header *header, char *limit, size_t qlen, struct in_addr local_addr, struct in_addr local_netmask, time_t now, int ad_reqd, int do_bit, int have_pseudoheader, - int *stale) + int *stale, int *filtered) { char *name = daemon->namebuff; unsigned char *p, *ansp; @@ -1450,6 +1450,9 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, if (stale) *stale = 0; + + if (filtered) + *filtered = 0; /* never answer queries with RD unset, to avoid cache snooping. */ if (ntohs(header->ancount) != 0 || @@ -1718,8 +1721,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, /* don't answer wildcard queries with data not from /etc/hosts or dhcp leases */ if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP))) continue; - - + if (!(crecp->flags & F_DNSSECOK)) sec_data = 0; @@ -1900,6 +1902,9 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, if (!dryrun) log_query(F_NEG | F_CONFIG | flag, name, NULL, NULL, 0); + + if (filtered) + *filtered = 1; } else if (crecp->flags & F_NEG) { @@ -1975,6 +1980,9 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, if (!dryrun) log_query(F_NEG | F_CONFIG | flag, name, NULL, NULL, 0); + + if (filtered) + *filtered = 1; } } } diff --git a/src/dnsmasq/rrfilter.c b/src/dnsmasq/rrfilter.c index 42d9c210..3a5547a2 100644 --- a/src/dnsmasq/rrfilter.c +++ b/src/dnsmasq/rrfilter.c @@ -156,41 +156,43 @@ static int check_rrs(unsigned char *p, struct dns_header *header, size_t plen, i } -/* mode may be remove EDNS0 or DNSSEC RRs or remove A or AAAA from answer section. */ -size_t rrfilter(struct dns_header *header, size_t plen, int mode) +/* mode may be remove EDNS0 or DNSSEC RRs or remove A or AAAA from answer section. + * returns number of modified records. */ +size_t rrfilter(struct dns_header *header, size_t *plen, int mode) { static unsigned char **rrs = NULL; static int rr_sz = 0; unsigned char *p = (unsigned char *)(header+1); - int i, rdlen, qtype, qclass, rr_found, chop_an, chop_ns, chop_ar; + size_t rr_found = 0; + int i, rdlen, qtype, qclass, chop_an, chop_ns, chop_ar; if (ntohs(header->qdcount) != 1 || - !(p = skip_name(p, header, plen, 4))) - return plen; + !(p = skip_name(p, header, *plen, 4))) + return 0; GETSHORT(qtype, p); GETSHORT(qclass, p); /* First pass, find pointers to start and end of all the records we wish to elide: records added for DNSSEC, unless explicitly queried for */ - for (rr_found = 0, chop_ns = 0, chop_an = 0, chop_ar = 0, i = 0; + for (chop_ns = 0, chop_an = 0, chop_ar = 0, i = 0; i < ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount); i++) { unsigned char *pstart = p; int type, class; - if (!(p = skip_name(p, header, plen, 10))) - return plen; + if (!(p = skip_name(p, header, *plen, 10))) + return rr_found; GETSHORT(type, p); GETSHORT(class, p); p += 4; /* TTL */ GETSHORT(rdlen, p); - if (!ADD_RDLEN(header, p, plen, rdlen)) - return plen; + if (!ADD_RDLEN(header, p, *plen, rdlen)) + return rr_found; if (mode == RRFILTER_EDNS0) /* EDNS */ { @@ -225,7 +227,7 @@ size_t rrfilter(struct dns_header *header, size_t plen, int mode) } if (!expand_workspace(&rrs, &rr_sz, rr_found + 1)) - return plen; + return rr_found; rrs[rr_found++] = pstart; rrs[rr_found++] = p; @@ -240,7 +242,7 @@ size_t rrfilter(struct dns_header *header, size_t plen, int mode) /* Nothing to do. */ if (rr_found == 0) - return plen; + return rr_found; /* Second pass, look for pointers in names in the records we're keeping and make sure they don't point to records we're going to elide. This is theoretically possible, but unlikely. If @@ -248,38 +250,38 @@ size_t rrfilter(struct dns_header *header, size_t plen, int mode) p = (unsigned char *)(header+1); /* question first */ - if (!check_name(&p, header, plen, 0, rrs, rr_found)) - return plen; + if (!check_name(&p, header, *plen, 0, rrs, rr_found)) + return rr_found; p += 4; /* qclass, qtype */ /* Now answers and NS */ - if (!check_rrs(p, header, plen, 0, rrs, rr_found)) - return plen; + if (!check_rrs(p, header, *plen, 0, rrs, rr_found)) + return rr_found; /* Third pass, actually fix up pointers in the records */ p = (unsigned char *)(header+1); - check_name(&p, header, plen, 1, rrs, rr_found); + check_name(&p, header, *plen, 1, rrs, rr_found); p += 4; /* qclass, qtype */ - check_rrs(p, header, plen, 1, rrs, rr_found); + check_rrs(p, header, *plen, 1, rrs, rr_found); /* Fourth pass, elide records */ - for (p = rrs[0], i = 1; i < rr_found; i += 2) + for (p = rrs[0], i = 1; (unsigned)i < rr_found; i += 2) { unsigned char *start = rrs[i]; - unsigned char *end = (i != rr_found - 1) ? rrs[i+1] : ((unsigned char *)header) + plen; + unsigned char *end = ((unsigned)i != rr_found - 1) ? rrs[i+1] : ((unsigned char *)header) + *plen; memmove(p, start, end-start); p += end-start; } - plen = p - (unsigned char *)header; + *plen = p - (unsigned char *)header; header->ancount = htons(ntohs(header->ancount) - chop_an); header->nscount = htons(ntohs(header->nscount) - chop_ns); header->arcount = htons(ntohs(header->arcount) - chop_ar); - return plen; + return rr_found; } /* This is used in the DNSSEC code too, hence it's exported */ From fe95fa5511f735ba16dbfa1a69284e7587534557 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Mon, 20 Mar 2023 23:11:38 +0000 Subject: [PATCH 21/29] Fold F_NOERR and F_DNSSEC to make space for new F_RR. Signed-off-by: DL6ER --- src/dnsmasq/cache.c | 2 +- src/dnsmasq/dnsmasq.h | 7 ++++++- src/dnsmasq/forward.c | 6 +++--- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/dnsmasq/cache.c b/src/dnsmasq/cache.c index 0816cb54..e724757c 100644 --- a/src/dnsmasq/cache.c +++ b/src/dnsmasq/cache.c @@ -2175,7 +2175,7 @@ void _log_query(unsigned int flags, char *name, union all_addr *addr, char *arg, } else if (flags & F_AUTH) source = "auth"; - else if (flags & F_DNSSEC) + else if (flags & F_NOERR) { source = arg; verb = "to"; diff --git a/src/dnsmasq/dnsmasq.h b/src/dnsmasq/dnsmasq.h index 1c0119c2..e772f676 100644 --- a/src/dnsmasq/dnsmasq.h +++ b/src/dnsmasq/dnsmasq.h @@ -334,6 +334,11 @@ union all_addr { unsigned short keytag, algo, digest, rcode; int ede; } log; + /* for arbitrary RR record. */ + struct { + struct blockdata *rrdata; + u16 rrtype; + } rr; }; @@ -507,7 +512,7 @@ struct crec { #define F_QUERY (1u<<19) #define F_NOERR (1u<<20) #define F_AUTH (1u<<21) -#define F_DNSSEC (1u<<22) +#define F_RR (1u<<22) #define F_KEYTAG (1u<<23) #define F_SECSTAT (1u<<24) #define F_NO_RR (1u<<25) diff --git a/src/dnsmasq/forward.c b/src/dnsmasq/forward.c index 604e2f95..c2bde77f 100644 --- a/src/dnsmasq/forward.c +++ b/src/dnsmasq/forward.c @@ -555,7 +555,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, } #ifdef HAVE_DNSSEC else - log_query_mysockaddr(F_NOEXTRA | F_DNSSEC | F_SERVER, daemon->namebuff, &srv->addr, + log_query_mysockaddr(F_NOEXTRA | F_NOERR | F_SERVER, daemon->namebuff, &srv->addr, (forward->flags & FREC_DNSKEY_QUERY) ? "dnssec-retry[DNSKEY]" : "dnssec-retry[DS]", 0); #endif @@ -1089,7 +1089,7 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header, #ifdef HAVE_DUMPFILE dump_packet_udp(DUMP_SEC_QUERY, (void *)header, (size_t)nn, NULL, &server->addr, fd); #endif - log_query_mysockaddr(F_NOEXTRA | F_DNSSEC | F_SERVER, daemon->keyname, &server->addr, + log_query_mysockaddr(F_NOEXTRA | F_NOERR | F_SERVER, daemon->keyname, &server->addr, STAT_ISEQUAL(status, STAT_NEED_KEY) ? "dnssec-query[DNSKEY]" : "dnssec-query[DS]", 0); return; } @@ -2148,7 +2148,7 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si log_save = daemon->log_display_id; daemon->log_display_id = ++daemon->log_id; - log_query_mysockaddr(F_NOEXTRA | F_DNSSEC | F_SERVER, keyname, &server->addr, + log_query_mysockaddr(F_NOEXTRA | F_NOERR | F_SERVER, keyname, &server->addr, STAT_ISEQUAL(status, STAT_NEED_KEY) ? "dnssec-query[DNSKEY]" : "dnssec-query[DS]", 0); new_status = tcp_key_recurse(now, new_status, new_header, m, class, name, keyname, server, have_mark, mark, keycount); From ee9564ccc0ad3fa2e79dcbb8d0944fd82d56bd63 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Fri, 31 Mar 2023 07:58:05 +0200 Subject: [PATCH 22/29] Apply necessasry changes to FTL due to most recent dnsmasq commit Signed-off-by: DL6ER --- src/dnsmasq_interface.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dnsmasq_interface.c b/src/dnsmasq_interface.c index aae62d2a..cab1a960 100644 --- a/src/dnsmasq_interface.c +++ b/src/dnsmasq_interface.c @@ -95,7 +95,7 @@ static struct { static union mysockaddr last_server = {{ 0 }}; unsigned char* pihole_privacylevel = &config.privacylevel; -const char *flagnames[] = {"F_IMMORTAL ", "F_NAMEP ", "F_REVERSE ", "F_FORWARD ", "F_DHCP ", "F_NEG ", "F_HOSTS ", "F_IPV4 ", "F_IPV6 ", "F_BIGNAME ", "F_NXDOMAIN ", "F_CNAME ", "F_DNSKEY ", "F_CONFIG ", "F_DS ", "F_DNSSECOK ", "F_UPSTREAM ", "F_RRNAME ", "F_SERVER ", "F_QUERY ", "F_NOERR ", "F_AUTH ", "F_DNSSEC ", "F_KEYTAG ", "F_SECSTAT ", "F_NO_RR ", "F_IPSET ", "F_NOEXTRA ", "F_SERVFAIL", "F_RCODE", "F_SRV", "F_STALE" }; +const char *flagnames[] = {"F_IMMORTAL ", "F_NAMEP ", "F_REVERSE ", "F_FORWARD ", "F_DHCP ", "F_NEG ", "F_HOSTS ", "F_IPV4 ", "F_IPV6 ", "F_BIGNAME ", "F_NXDOMAIN ", "F_CNAME ", "F_DNSKEY ", "F_CONFIG ", "F_DS ", "F_DNSSECOK ", "F_UPSTREAM ", "F_RRNAME ", "F_SERVER ", "F_QUERY ", "F_NOERR ", "F_AUTH ", "F_RR ", "F_KEYTAG ", "F_SECSTAT ", "F_NO_RR ", "F_IPSET ", "F_NOEXTRA ", "F_SERVFAIL", "F_RCODE", "F_SRV", "F_STALE" }; void FTL_hook(unsigned int flags, const char *name, union all_addr *addr, char *arg, int id, unsigned short type, const char* file, const int line) { @@ -122,7 +122,7 @@ void FTL_hook(unsigned int flags, const char *name, union all_addr *addr, char * else if(flags & F_RCODE && name && strcasecmp(name, "error") == 0) // upstream sent something different than NOERROR or NXDOMAIN FTL_upstream_error(addr, flags, id, path, line); - else if(flags & F_NOEXTRA && flags & F_DNSSEC) + else if(flags & F_NOEXTRA && flags & F_NOERR) { // This is a new DNSSEC query (dnssec-query[DS]) if(!config.show_dnssec) From 4c2090c9a805b9feca1498a662048cb5cc28b8b7 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Thu, 23 Mar 2023 17:15:35 +0000 Subject: [PATCH 23/29] Add --cache-rr to enable caching of arbitrary RR types. Signed-off-by: DL6ER --- src/dnsmasq/blockdata.c | 123 ++++++++++++++++++++++-------- src/dnsmasq/cache.c | 45 +++++++++-- src/dnsmasq/dnsmasq.c | 14 +--- src/dnsmasq/dnsmasq.h | 20 ++++- src/dnsmasq/dnssec.c | 87 ++-------------------- src/dnsmasq/option.c | 37 +++++++++- src/dnsmasq/rfc1035.c | 160 ++++++++++++++++++++++++++++++++-------- src/dnsmasq/rrfilter.c | 87 ++++++++++++++++++++-- src/dnsmasq/util.c | 17 ++++- 9 files changed, 416 insertions(+), 174 deletions(-) diff --git a/src/dnsmasq/blockdata.c b/src/dnsmasq/blockdata.c index 4c26155f..56698c70 100644 --- a/src/dnsmasq/blockdata.c +++ b/src/dnsmasq/blockdata.c @@ -19,7 +19,7 @@ static struct blockdata *keyblock_free; static unsigned int blockdata_count, blockdata_hwm, blockdata_alloced; -static void blockdata_expand(int n) +static void add_blocks(int n) { struct blockdata *new = whine_malloc(n * sizeof(struct blockdata)); @@ -47,7 +47,7 @@ void blockdata_init(void) /* Note that daemon->cachesize is enforced to have non-zero size if OPT_DNSSEC_VALID is set */ if (option_bool(OPT_DNSSEC_VALID)) - blockdata_expand(daemon->cachesize); + add_blocks(daemon->cachesize); } void blockdata_report(void) @@ -58,50 +58,61 @@ void blockdata_report(void) blockdata_alloced * sizeof(struct blockdata)); } +static struct blockdata *new_block(void) +{ + struct blockdata *block; + + if (!keyblock_free) + add_blocks(50); + + if (keyblock_free) + { + block = keyblock_free; + keyblock_free = block->next; + blockdata_count++; + if (blockdata_hwm < blockdata_count) + blockdata_hwm = blockdata_count; + block->next = NULL; + return block; + } + + return NULL; +} + static struct blockdata *blockdata_alloc_real(int fd, char *data, size_t len) { struct blockdata *block, *ret = NULL; struct blockdata **prev = &ret; size_t blen; - while (len > 0) + do { - if (!keyblock_free) - blockdata_expand(50); - - if (keyblock_free) - { - block = keyblock_free; - keyblock_free = block->next; - blockdata_count++; - } - else + if (!(block = new_block())) { /* failed to alloc, free partial chain */ blockdata_free(ret); return NULL; } - - if (blockdata_hwm < blockdata_count) - blockdata_hwm = blockdata_count; + + if ((blen = len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len) > 0) + { + if (data) + { + memcpy(block->key, data, blen); + data += blen; + } + else if (!read_write(fd, block->key, blen, 1)) + { + /* failed read free partial chain */ + blockdata_free(ret); + return NULL; + } + } - blen = len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len; - if (data) - { - memcpy(block->key, data, blen); - data += blen; - } - else if (!read_write(fd, block->key, blen, 1)) - { - /* failed read free partial chain */ - blockdata_free(ret); - return NULL; - } len -= blen; *prev = block; prev = &block->next; - block->next = NULL; - } + } while (len != 0); return ret; } @@ -111,6 +122,58 @@ struct blockdata *blockdata_alloc(char *data, size_t len) return blockdata_alloc_real(0, data, len); } +/* Add data to the end of the block. + newlen is length of new data, NOT total new length. + Use blockdata_alloc(NULL, 0) to make empty block to add to. */ +int blockdata_expand(struct blockdata *block, size_t oldlen, char *data, size_t newlen) +{ + struct blockdata *b; + + /* find size of current final block */ + for (b = block; oldlen > KEYBLOCK_LEN && b; b = b->next, oldlen -= KEYBLOCK_LEN); + + /* chain to short for length, something is broken */ + if (oldlen > KEYBLOCK_LEN) + { + blockdata_free(block); + return 0; + } + + while (1) + { + struct blockdata *new; + size_t blocksize = KEYBLOCK_LEN - oldlen; + size_t size = (newlen <= blocksize) ? newlen : blocksize; + + if (size != 0) + { + memcpy(&b->key[oldlen], data, size); + data += size; + newlen -= size; + } + + /* full blocks from now on. */ + oldlen = 0; + + if (newlen == 0) + break; + + if ((new = new_block())) + { + b->next = new; + b = new; + } + else + { + /* failed to alloc, free partial chain */ + blockdata_free(block); + return 0; + } + } + + return 1; +} + void blockdata_free(struct blockdata *blocks) { struct blockdata *tmp; diff --git a/src/dnsmasq/cache.c b/src/dnsmasq/cache.c index e724757c..4c8d9645 100644 --- a/src/dnsmasq/cache.c +++ b/src/dnsmasq/cache.c @@ -30,6 +30,7 @@ static void make_non_terminals(struct crec *source); static struct crec *really_insert(char *name, union all_addr *addr, unsigned short class, time_t now, unsigned long ttl, unsigned int flags); static void dump_cache_entry(struct crec *cache, time_t now); +static char *querystr(char *desc, unsigned short type); /* type->string mapping: this is also used by the name-hash function as a mixing table. */ /* taken from https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml */ @@ -134,6 +135,17 @@ static void cache_link(struct crec *crecp); void rehash(int size); static void cache_hash(struct crec *crecp); +unsigned short rrtype(char *in) +{ + int i; + + for (i = 0; i < (sizeof(typestr)/sizeof(typestr[0])); i++) + if (strcasecmp(in, typestr[i].name) == 0) + return typestr[i].type; + + return 0; +} + void next_uid(struct crec *crecp) { static unsigned int uid = 0; @@ -266,6 +278,8 @@ static void cache_blockdata_free(struct crec *crecp) { if (crecp->flags & F_SRV) blockdata_free(crecp->addr.srv.target); + else if (crecp->flags & F_RR) + blockdata_free(crecp->addr.rr.rrdata); #ifdef HAVE_DNSSEC else if (crecp->flags & F_DNSKEY) blockdata_free(crecp->addr.key.keydata); @@ -460,7 +474,8 @@ static struct crec *cache_scan_free(char *name, union all_addr *addr, unsigned s { /* Don't delete DNSSEC in favour of a CNAME, they can co-exist */ if ((flags & crecp->flags & (F_IPV4 | F_IPV6 | F_SRV | F_NXDOMAIN)) || - (((crecp->flags | flags) & F_CNAME) && !(crecp->flags & (F_DNSKEY | F_DS)))) + (((crecp->flags | flags) & F_CNAME) && !(crecp->flags & (F_DNSKEY | F_DS))) || + ((crecp->flags & flags & F_RR) && addr->rr.rrtype == crecp->addr.rr.rrtype)) { if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) return crecp; @@ -777,7 +792,7 @@ void cache_end_insert(void) read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->ttd, sizeof(new_chain->ttd), 0); read_write(daemon->pipe_to_parent, (unsigned char *)&flags, sizeof(flags), 0); - if (flags & (F_IPV4 | F_IPV6 | F_DNSKEY | F_DS | F_SRV)) + if (flags & (F_IPV4 | F_IPV6 | F_DNSKEY | F_DS | F_SRV | F_RR)) read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->addr, sizeof(new_chain->addr), 0); if (flags & F_SRV) { @@ -785,6 +800,12 @@ void cache_end_insert(void) if (!(flags & F_NEG)) blockdata_write(new_chain->addr.srv.target, new_chain->addr.srv.targetlen, daemon->pipe_to_parent); } + if (flags & F_RR) + { + /* A negative RR entry is possible and has no data, obviously. */ + if (!(flags & F_NEG)) + blockdata_write(new_chain->addr.rr.rrdata, new_chain->addr.rr.datalen, daemon->pipe_to_parent); + } #ifdef HAVE_DNSSEC if (flags & F_DNSKEY) { @@ -849,16 +870,18 @@ int cache_recv_insert(time_t now, int fd) ttl = difftime(ttd, now); - if (flags & (F_IPV4 | F_IPV6 | F_DNSKEY | F_DS | F_SRV)) + if (flags & (F_IPV4 | F_IPV6 | F_DNSKEY | F_DS | F_SRV | F_RR)) { unsigned short class = C_IN; - + if (!read_write(fd, (unsigned char *)&addr, sizeof(addr), 1)) return 0; - + if ((flags & F_SRV) && !(flags & F_NEG) && !(addr.srv.target = blockdata_read(fd, addr.srv.targetlen))) return 0; - + + if ((flags & F_RR) && !(flags & F_NEG) && !(addr.rr.rrdata = blockdata_read(fd, addr.rr.datalen))) + return 0; #ifdef HAVE_DNSSEC if (flags & F_DNSKEY) { @@ -1588,7 +1611,7 @@ static void make_non_terminals(struct crec *source) if (!is_outdated_cname_pointer(crecp) && (crecp->flags & F_FORWARD) && (crecp->flags & type) && - !(crecp->flags & (F_IPV4 | F_IPV6 | F_CNAME | F_SRV | F_DNSKEY | F_DS)) && + !(crecp->flags & (F_IPV4 | F_IPV6 | F_CNAME | F_SRV | F_DNSKEY | F_DS | F_RR)) && hostname_isequal(name, cache_get_name(crecp))) { *up = crecp->hash_next; @@ -1645,7 +1668,7 @@ static void make_non_terminals(struct crec *source) if (crecp) { - crecp->flags = (source->flags | F_NAMEP) & ~(F_IPV4 | F_IPV6 | F_CNAME | F_SRV | F_DNSKEY | F_DS | F_REVERSE); + crecp->flags = (source->flags | F_NAMEP) & ~(F_IPV4 | F_IPV6 | F_CNAME | F_SRV | F_RR | F_DNSKEY | F_DS | F_REVERSE); if (!(crecp->flags & F_IMMORTAL)) crecp->ttd = source->ttd; crecp->name.namep = name; @@ -1799,6 +1822,8 @@ static void dump_cache_entry(struct crec *cache, time_t now) blockdata_retrieve(cache->addr.srv.target, targetlen, a + len); a[len + targetlen] = 0; } + else if (cache->flags & F_RR) + sprintf(a, "%s", querystr(NULL, cache->addr.rr.rrtype)); #ifdef HAVE_DNSSEC else if (cache->flags & F_DS) { @@ -1827,6 +1852,8 @@ static void dump_cache_entry(struct crec *cache, time_t now) t = "C"; else if (cache->flags & F_SRV) t = "V"; + else if (cache->flags & F_RR) + t = "T"; #ifdef HAVE_DNSSEC else if (cache->flags & F_DS) t = "S"; @@ -2124,6 +2151,8 @@ void _log_query(unsigned int flags, char *name, union all_addr *addr, char *arg, sprintf(portstring, "#%u", type); } } + else if (flags & F_RR) + dest = querystr(NULL, addr->rr.rrtype); else dest = arg; } diff --git a/src/dnsmasq/dnsmasq.c b/src/dnsmasq/dnsmasq.c index 59bd4c15..28ff4f7d 100644 --- a/src/dnsmasq/dnsmasq.c +++ b/src/dnsmasq/dnsmasq.c @@ -132,17 +132,11 @@ int main_dnsmasq (int argc, char **argv) { /* Note that both /000 and '.' are allowed within labels. These get represented in presentation format using NAME_ESCAPE as an escape - character when in DNSSEC mode. - In theory, if all the characters in a name were /000 or + character. In theory, if all the characters in a name were /000 or '.' or NAME_ESCAPE then all would have to be escaped, so the - presentation format would be twice as long as the spec. - - daemon->namebuff was previously allocated by the option-reading - code before we knew if we're in DNSSEC mode, so reallocate here. */ - free(daemon->namebuff); - daemon->namebuff = safe_malloc(MAXDNAME * 2); - daemon->keyname = safe_malloc(MAXDNAME * 2); - daemon->workspacename = safe_malloc(MAXDNAME * 2); + presentation format would be twice as long as the spec. */ + daemon->keyname = safe_malloc((MAXDNAME * 2) + 1); + daemon->workspacename = safe_malloc((MAXDNAME * 2) + 1); /* one char flag per possible RR in answer section (may get extended). */ daemon->rr_status_sz = 64; daemon->rr_status = safe_malloc(sizeof(*daemon->rr_status) * daemon->rr_status_sz); diff --git a/src/dnsmasq/dnsmasq.h b/src/dnsmasq/dnsmasq.h index e772f676..71fbf745 100644 --- a/src/dnsmasq/dnsmasq.h +++ b/src/dnsmasq/dnsmasq.h @@ -282,7 +282,8 @@ struct event_desc { #define OPT_STRIP_MAC 70 #define OPT_NORR 71 #define OPT_NO_IDENT 72 -#define OPT_LAST 73 +#define OPT_CACHE_RR 73 +#define OPT_LAST 74 #define OPTION_BITS (sizeof(unsigned int)*8) #define OPTION_SIZE ( (OPT_LAST/OPTION_BITS)+((OPT_LAST%OPTION_BITS)!=0) ) @@ -337,7 +338,7 @@ union all_addr { /* for arbitrary RR record. */ struct { struct blockdata *rrdata; - u16 rrtype; + unsigned short rrtype, datalen; } rr; }; @@ -668,6 +669,11 @@ struct iname { struct iname *next; }; +struct rrlist { + unsigned short rr; + struct rrlist *next; +}; + /* subnet parameters from command line */ struct mysubnet { union mysockaddr addr; @@ -1133,6 +1139,7 @@ extern struct daemon { struct naptr *naptr; struct txt_record *txt, *rr; struct ptr_record *ptr; + struct rrlist *cache_rr, filter_rr; struct host_record *host_records, *host_records_tail; struct cname *cnames; struct auth_zone *auth_zones; @@ -1314,6 +1321,7 @@ struct server_details { /* cache.c */ void cache_init(void); +unsigned short rrtype(char *in); void next_uid(struct crec *crecp); /********************************************* Pi-hole modification ***********************************************/ #define log_query(flags,name,addr,arg,type) _log_query(flags, name, addr, arg, type, __FILE__, __LINE__) @@ -1364,6 +1372,8 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, void blockdata_init(void); void blockdata_report(void); struct blockdata *blockdata_alloc(char *data, size_t len); +int blockdata_expand(struct blockdata *block, size_t oldlen, + char *data, size_t newlen); void *blockdata_retrieve(struct blockdata *block, size_t len, void *data); struct blockdata *blockdata_read(int fd, size_t len); void blockdata_write(struct blockdata *block, size_t len, int fd); @@ -1445,6 +1455,7 @@ void rand_init(void); unsigned short rand16(void); u32 rand32(void); u64 rand64(void); +int rr_on_list(struct rrlist *list, unsigned short rr); int legal_hostname(char *name); char *canonicalise(char *in, int *nomem); unsigned char *do_rfc1035_name(unsigned char *p, char *sval, char *limit); @@ -1850,13 +1861,16 @@ int do_poll(int timeout); /* rrfilter.c */ size_t rrfilter(struct dns_header *header, size_t *plen, int mode); -u16 *rrfilter_desc(int type); +short *rrfilter_desc(int type); int expand_workspace(unsigned char ***wkspc, int *szp, int new); +int to_wire(char *name); +void from_wire(char *name); /* modes. */ #define RRFILTER_EDNS0 0 #define RRFILTER_DNSSEC 1 #define RRFILTER_A 2 #define RRFILTER_AAAA 3 + /* edns0.c */ unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t *len, unsigned char **p, int *is_sign, int *is_last); diff --git a/src/dnsmasq/dnssec.c b/src/dnsmasq/dnssec.c index 219ba9af..aa196ebf 100644 --- a/src/dnsmasq/dnssec.c +++ b/src/dnsmasq/dnssec.c @@ -24,81 +24,6 @@ #define SERIAL_LT -1 #define SERIAL_GT 1 -/* Convert from presentation format to wire format, in place. - Also map UC -> LC. - Note that using extract_name to get presentation format - then calling to_wire() removes compression and maps case, - thus generating names in canonical form. - Calling to_wire followed by from_wire is almost an identity, - except that the UC remains mapped to LC. - - Note that both /000 and '.' are allowed within labels. These get - represented in presentation format using NAME_ESCAPE as an escape - character. In theory, if all the characters in a name were /000 or - '.' or NAME_ESCAPE then all would have to be escaped, so the - presentation format would be twice as long as the spec (1024). - The buffers are all declared as 2049 (allowing for the trailing zero) - for this reason. -*/ -static int to_wire(char *name) -{ - unsigned char *l, *p, *q, term; - int len; - - for (l = (unsigned char*)name; *l != 0; l = p) - { - for (p = l; *p != '.' && *p != 0; p++) - if (*p >= 'A' && *p <= 'Z') - *p = *p - 'A' + 'a'; - else if (*p == NAME_ESCAPE) - { - for (q = p; *q; q++) - *q = *(q+1); - (*p)--; - } - term = *p; - - if ((len = p - l) != 0) - memmove(l+1, l, len); - *l = len; - - p++; - - if (term == 0) - *p = 0; - } - - return l + 1 - (unsigned char *)name; -} - -/* Note: no compression allowed in input. */ -static void from_wire(char *name) -{ - unsigned char *l, *p, *last; - int len; - - for (last = (unsigned char *)name; *last != 0; last += *last+1); - - for (l = (unsigned char *)name; *l != 0; l += len+1) - { - len = *l; - memmove(l, l+1, len); - for (p = l; p < l + len; p++) - if (*p == '.' || *p == 0 || *p == NAME_ESCAPE) - { - memmove(p+1, p, 1 + last - p); - len++; - *p++ = NAME_ESCAPE; - (*p)++; - } - - l[len] = '.'; - } - - if ((char *)l != name) - *(l-1) = 0; -} - /* Input in presentation format */ static int count_labels(char *name) { @@ -225,7 +150,7 @@ static int is_check_date(unsigned long curtime) On returning 0, the end has been reached. */ struct rdata_state { - u16 *desc; + short *desc; size_t c; unsigned char *end, *ip, *op; char *buff; @@ -246,7 +171,7 @@ static int get_rdata(struct dns_header *header, size_t plen, struct rdata_state { d = *(state->desc); - if (d == (u16)-1) + if (d == -1) { /* all the bytes to the end. */ if ((state->c = state->end - state->ip) != 0) @@ -294,7 +219,7 @@ static int get_rdata(struct dns_header *header, size_t plen, struct rdata_state /* Bubble sort the RRset into the canonical order. */ -static int sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int rrsetidx, +static int sort_rrset(struct dns_header *header, size_t plen, short *rr_desc, int rrsetidx, unsigned char **rrset, char *buff1, char *buff2) { int swap, i, j; @@ -331,7 +256,7 @@ static int sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int is the identity function and we can compare the RRs directly. If not we compare the canonicalised RRs one byte at a time. */ - if (*rr_desc == (u16)-1) + if (*rr_desc == -1) { int rdmin = rdlen1 > rdlen2 ? rdlen2 : rdlen1; int cmp = memcmp(state1.ip, state2.ip, rdmin); @@ -524,7 +449,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in unsigned char *p; int rdlen, j, name_labels, algo, labels, key_tag; struct crec *crecp = NULL; - u16 *rr_desc = rrfilter_desc(type); + short *rr_desc = rrfilter_desc(type); u32 sig_expiration, sig_inception; int failflags = DNSSEC_FAIL_NOSIG | DNSSEC_FAIL_NYV | DNSSEC_FAIL_EXP | DNSSEC_FAIL_NOKEYSUP; @@ -671,7 +596,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in If canonicalisation is not needed, a simple insertion into the hash works. */ - if (*rr_desc == (u16)-1) + if (*rr_desc == -1) { len = htons(rdlen); hash->update(ctx, 2, (unsigned char *)&len); diff --git a/src/dnsmasq/option.c b/src/dnsmasq/option.c index 8f738995..5b5fba90 100644 --- a/src/dnsmasq/option.c +++ b/src/dnsmasq/option.c @@ -190,6 +190,7 @@ struct myoption { #define LOPT_STALE_CACHE 377 #define LOPT_NORR 378 #define LOPT_NO_IDENT 379 +#define LOPT_CACHE_RR 380 #ifdef HAVE_GETOPT_LONG static const struct option opts[] = @@ -243,6 +244,7 @@ static const struct myoption opts[] = { "local-ttl", 1, 0, 'T' }, { "no-negcache", 0, 0, 'N' }, { "no-round-robin", 0, 0, LOPT_NORR }, + { "cache-rr", 1, 0, LOPT_CACHE_RR }, { "addn-hosts", 1, 0, 'H' }, { "hostsdir", 1, 0, LOPT_HOST_INOTIFY }, { "query-port", 1, 0, 'Q' }, @@ -570,13 +572,14 @@ static struct { { LOPT_DHCPTTL, ARG_ONE, "", gettext_noop("Set TTL in DNS responses with DHCP-derived addresses."), NULL }, { LOPT_REPLY_DELAY, ARG_ONE, "", gettext_noop("Delay DHCP replies for at least number of seconds."), NULL }, { LOPT_RAPID_COMMIT, OPT_RAPID_COMMIT, NULL, gettext_noop("Enables DHCPv4 Rapid Commit option."), NULL }, - { LOPT_DUMPFILE, ARG_ONE, "", gettext_noop("Path to debug packet dump file"), NULL }, - { LOPT_DUMPMASK, ARG_ONE, "", gettext_noop("Mask which packets to dump"), NULL }, + { LOPT_DUMPFILE, ARG_ONE, "", gettext_noop("Path to debug packet dump file."), NULL }, + { LOPT_DUMPMASK, ARG_ONE, "", gettext_noop("Mask which packets to dump."), NULL }, { LOPT_SCRIPT_TIME, OPT_LEASE_RENEW, NULL, gettext_noop("Call dhcp-script when lease expiry changes."), NULL }, { LOPT_UMBRELLA, ARG_ONE, "[=]", gettext_noop("Send Cisco Umbrella identifiers including remote IP."), NULL }, { LOPT_QUIET_TFTP, OPT_QUIET_TFTP, NULL, gettext_noop("Do not log routine TFTP."), NULL }, { LOPT_NORR, OPT_NORR, NULL, gettext_noop("Suppress round-robin ordering of DNS records."), NULL }, { LOPT_NO_IDENT, OPT_NO_IDENT, NULL, gettext_noop("Do not add CHAOS TXT records."), NULL }, + { LOPT_CACHE_RR, ARG_DUP, "RRtype", gettext_noop("Cache this DNS resource record type."), NULL }, { 0, 0, NULL, NULL, NULL } }; @@ -3469,6 +3472,27 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma } } break; + + case LOPT_CACHE_RR: + while (1) { + int type; + struct rrlist *new; + + comma = split(arg); + if (!atoi_check(arg, &type) && (type = rrtype(arg)) == 0) + ret_err(_("bad RR type")); + + new = opt_malloc(sizeof(struct rrlist)); + new->rr = type; + + new->next = daemon->cache_rr; + daemon->cache_rr = new; + + if (!comma) break; + arg = comma; + } + break; + #ifdef HAVE_DHCP case 'X': /* --dhcp-lease-max */ @@ -5737,10 +5761,15 @@ void read_opts(int argc, char **argv, char *compile_opts) { size_t argbuf_size = MAXDNAME; char *argbuf = opt_malloc(argbuf_size); - char *buff = opt_malloc(MAXDNAME); + /* Note that both /000 and '.' are allowed within labels. These get + represented in presentation format using NAME_ESCAPE as an escape + character. In theory, if all the characters in a name were /000 or + '.' or NAME_ESCAPE then all would have to be escaped, so the + presentation format would be twice as long as the spec. */ + char *buff = opt_malloc((MAXDNAME * 2) + 1); int option, testmode = 0; char *arg, *conffile = NULL; - + opterr = 0; daemon = opt_malloc(sizeof(struct daemon)); diff --git a/src/dnsmasq/rfc1035.c b/src/dnsmasq/rfc1035.c index 61a35ea4..f8a05294 100644 --- a/src/dnsmasq/rfc1035.c +++ b/src/dnsmasq/rfc1035.c @@ -90,23 +90,14 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp, if (isExtract) { unsigned char c = *p; -#ifdef HAVE_DNSSEC - if (option_bool(OPT_DNSSEC_VALID)) + + if (c == 0 || c == '.' || c == NAME_ESCAPE) { - if (c == 0 || c == '.' || c == NAME_ESCAPE) - { - *cp++ = NAME_ESCAPE; - *cp++ = c+1; - } - else - *cp++ = c; + *cp++ = NAME_ESCAPE; + *cp++ = c+1; } else -#endif - if (c != 0 && c != '.') - *cp++ = c; - else - return 0; + *cp++ = c; } else { @@ -119,10 +110,9 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp, cp++; if (c1 >= 'A' && c1 <= 'Z') c1 += 'a' - 'A'; -#ifdef HAVE_DNSSEC - if (option_bool(OPT_DNSSEC_VALID) && c1 == NAME_ESCAPE) + + if (c1 == NAME_ESCAPE) c1 = (*cp++)-1; -#endif if (c2 >= 'A' && c2 <= 'Z') c2 += 'a' - 'A'; @@ -503,12 +493,10 @@ static int find_soa(struct dns_header *header, size_t qlen, int *doctored) } /* Print TXT reply to log */ -static int print_txt(struct dns_header *header, const size_t qlen, char *name, - unsigned char *p, const int ardlen, int secflag) +static int log_txt(char *name, unsigned char *p, const int ardlen, int secflag) { unsigned char *p1 = p; - if (!CHECK_LEN(header, p1, qlen, ardlen)) - return 0; + /* Loop over TXT payload */ while ((p1 - p) < ardlen) { @@ -527,7 +515,7 @@ static int print_txt(struct dns_header *header, const size_t qlen, char *name, } *p3 = 0; - log_query(secflag | F_FORWARD | F_UPSTREAM, name, NULL, (char*)p1, 0); + log_query(secflag | F_FORWARD, name, NULL, (char*)p1, 0); /* restore */ memmove(p1 + 1, p1, i); *p1 = len; @@ -720,6 +708,8 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t } else if (qtype == T_SRV) flags |= F_SRV; + else if (qtype != T_CNAME && rr_on_list(daemon->cache_rr, qtype)) + flags |= F_RR; else insert = 0; /* NOTE: do not cache data from CNAME queries. */ @@ -817,7 +807,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t #ifdef HAVE_DNSSEC if (!option_bool(OPT_DNSSEC_VALID) || aqtype != T_RRSIG) #endif - log_query(secflag | F_FORWARD | F_UPSTREAM, name, NULL, NULL, aqtype); + log_query(secflag | F_FORWARD | F_UPSTREAM | F_RRNAME, name, NULL, NULL, aqtype); } else if (!(flags & F_NXDOMAIN)) { @@ -842,6 +832,64 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t if (!extract_name(header, qlen, &tmp, name, 1, 0)) return 2; } + else if (flags & F_RR) + { + short desc, *rrdesc = rrfilter_desc(aqtype); + unsigned char *tmp = namep; + + if (!CHECK_LEN(header, p1, qlen, ardlen)) + return 2; /* bad packet */ + addr.rr.rrtype = aqtype; + addr.rr.datalen = 0; + + /* The RR data may include names, and those names may include + compression, which will be rendered meaningless when + copied into another packet. + Here we go through a description of the packet type to + find the names, and extract them to a c-string and then + re-encode them to standalone DNS format without compression. */ + if (!(addr.rr.rrdata = blockdata_alloc(NULL, 0))) + return 0; + do + { + desc = *rrdesc++; + + if (desc == -1) + { + /* Copy the rest of the RR and end. */ + if (!blockdata_expand(addr.rr.rrdata, addr.rr.datalen, (char *)p1, endrr - p1)) + return 0; + addr.rr.datalen += endrr - p1; + } + else if (desc == 0) + { + /* Name, extract it then re-encode. */ + int len; + + if (!extract_name(header, qlen, &p1, name, 1, 0)) + return 2; + + len = to_wire(name); + if (!blockdata_expand(addr.rr.rrdata, addr.rr.datalen, name, len)) + return 0; + addr.rr.datalen += len; + } + else + { + /* desc is length of a block of data to be used as-is */ + if (desc > endrr - p1) + desc = endrr - p1; + if (!blockdata_expand(addr.rr.rrdata, addr.rr.datalen, (char *)p1, desc)) + return 0; + addr.rr.datalen += desc; + p1 += desc; + } + } while (desc != -1); + + /* we overwrote the original name, so get it back here. */ + if (!extract_name(header, qlen, &tmp, name, 1, 0)) + return 2; + } else if (flags & (F_IPV4 | F_IPV6)) { /* copy address into aligned storage */ @@ -889,8 +937,10 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t if (aqtype == T_TXT) { - if (!print_txt(header, qlen, name, p1, ardlen, secflag)) - return 2; + if (!CHECK_LEN(header, p1, qlen, ardlen)) + return 2; + + log_txt(name, p1, ardlen, secflag | F_UPSTREAM); } else { @@ -916,7 +966,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t { if (flags & F_NXDOMAIN) { - flags &= ~(F_IPV4 | F_IPV6 | F_SRV); + flags &= ~(F_IPV4 | F_IPV6 | F_SRV | F_RR); /* Can store NXDOMAIN reply for any qtype. */ insert = 1; @@ -937,7 +987,10 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t if (ttl == 0) ttl = cttl; - newc = cache_insert(name, NULL, C_IN, now, ttl, F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0)); + if (flags & F_RR) + addr.rr.rrtype = qtype; + + newc = cache_insert(name, &addr, C_IN, now, ttl, F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0)); if (newc && cpp) { next_uid(newc); @@ -2072,7 +2125,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, if (!found) { if ((crecp = cache_find_by_name(NULL, name, now, F_SRV | F_NXDOMAIN | (dryrun ? F_NO_RR : 0))) && - rd_bit && (!do_bit || (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK)))) + rd_bit && (!do_bit || cache_validated(crecp))) do { int stale_flag = 0; @@ -2153,8 +2206,57 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, if (!dryrun) log_query(F_CONFIG | F_NEG, name, &addr, NULL, 0); } - } + if (!ans && qtype != T_ANY) + { + if ((crecp = cache_find_by_name(NULL, name, now, F_RR | F_NXDOMAIN | (dryrun ? F_NO_RR : 0))) && + rd_bit && (!do_bit || cache_validated(crecp))) + do + { + int stale_flag = 0; + + if (crecp->addr.rr.rrtype == qtype) + { + if (crec_isstale(crecp, now)) + { + if (stale) + *stale = 1; + + stale_flag = F_STALE; + } + + if (!(crecp->flags & F_DNSSECOK)) + sec_data = 0; + + auth = 0; + ans = 1; + + if (!dryrun) + { + char *rrdata = NULL; + + if (!(crecp->flags & F_NEG)) + { + rrdata = blockdata_retrieve(crecp->addr.rr.rrdata, crecp->addr.rr.datalen, NULL); + + if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, + crec_ttl(crecp, now), NULL, qtype, C_IN, "t", + crecp->addr.rr.datalen, rrdata)) + anscount++; + } + + /* log after cache insertion as log_txt mangles rrdata */ + if (qtype == T_TXT && !(crecp->flags & F_NEG)) + log_txt(name, (unsigned char *)rrdata, crecp->addr.rr.datalen, crecp->flags & F_DNSSECOK); + else + log_query(stale_flag | crecp->flags, name, &crecp->addr, NULL, 0); + } + } + } while ((crecp = cache_find_by_name(crecp, name, now, F_RR))); + } + } + + if (!ans) { /* We may know that the domain doesn't exist for any RRtype. */ diff --git a/src/dnsmasq/rrfilter.c b/src/dnsmasq/rrfilter.c index 3a5547a2..e4c56cb5 100644 --- a/src/dnsmasq/rrfilter.c +++ b/src/dnsmasq/rrfilter.c @@ -136,9 +136,9 @@ static int check_rrs(unsigned char *p, struct dns_header *header, size_t plen, i if (class == C_IN) { - u16 *d; + short *d; - for (pp = p, d = rrfilter_desc(type); *d != (u16)-1; d++) + for (pp = p, d = rrfilter_desc(type); *d != -1; d++) { if (*d != 0) pp += *d; @@ -285,7 +285,7 @@ size_t rrfilter(struct dns_header *header, size_t *plen, int mode) } /* This is used in the DNSSEC code too, hence it's exported */ -u16 *rrfilter_desc(int type) +short *rrfilter_desc(int type) { /* List of RRtypes which include domains in the data. 0 -> domain @@ -296,7 +296,7 @@ u16 *rrfilter_desc(int type) anything which needs no mangling. */ - static u16 rr_desc[] = + static short rr_desc[] = { T_NS, 0, -1, T_MD, 0, -1, @@ -321,10 +321,10 @@ u16 *rrfilter_desc(int type) 0, -1 /* wildcard/catchall */ }; - u16 *p = rr_desc; + short *p = rr_desc; while (*p != type && *p != 0) - while (*p++ != (u16)-1); + while (*p++ != -1); return p+1; } @@ -352,3 +352,78 @@ int expand_workspace(unsigned char ***wkspc, int *szp, int new) return 1; } + +/* Convert from presentation format to wire format, in place. + Also map UC -> LC. + Note that using extract_name to get presentation format + then calling to_wire() removes compression and maps case, + thus generating names in canonical form. + Calling to_wire followed by from_wire is almost an identity, + except that the UC remains mapped to LC. + + Note that both /000 and '.' are allowed within labels. These get + represented in presentation format using NAME_ESCAPE as an escape + character. In theory, if all the characters in a name were /000 or + '.' or NAME_ESCAPE then all would have to be escaped, so the + presentation format would be twice as long as the spec (1024). + The buffers are all declared as 2049 (allowing for the trailing zero) + for this reason. +*/ +int to_wire(char *name) +{ + unsigned char *l, *p, *q, term; + int len; + + for (l = (unsigned char*)name; *l != 0; l = p) + { + for (p = l; *p != '.' && *p != 0; p++) + if (*p >= 'A' && *p <= 'Z') + *p = *p - 'A' + 'a'; + else if (*p == NAME_ESCAPE) + { + for (q = p; *q; q++) + *q = *(q+1); + (*p)--; + } + term = *p; + + if ((len = p - l) != 0) + memmove(l+1, l, len); + *l = len; + + p++; + + if (term == 0) + *p = 0; + } + + return l + 1 - (unsigned char *)name; +} + +/* Note: no compression allowed in input. */ +void from_wire(char *name) +{ + unsigned char *l, *p, *last; + int len; + + for (last = (unsigned char *)name; *last != 0; last += *last+1); + + for (l = (unsigned char *)name; *l != 0; l += len+1) + { + len = *l; + memmove(l, l+1, len); + for (p = l; p < l + len; p++) + if (*p == '.' || *p == 0 || *p == NAME_ESCAPE) + { + memmove(p+1, p, 1 + last - p); + len++; + *p++ = NAME_ESCAPE; + (*p)++; + } + + l[len] = '.'; + } + + if ((char *)l != name) + *(l-1) = 0; +} diff --git a/src/dnsmasq/util.c b/src/dnsmasq/util.c index e0ce67d3..073d7ada 100644 --- a/src/dnsmasq/util.c +++ b/src/dnsmasq/util.c @@ -115,6 +115,19 @@ u64 rand64(void) return (u64)out[outleft+1] + (((u64)out[outleft]) << 32); } +int rr_on_list(struct rrlist *list, unsigned short rr) +{ + while (list) + { + if (list->rr == rr) + return 1; + + list = list->next; + } + + return 0; +} + /* returns 1 if name is OK and ascii printable * returns 2 if name should be processed by IDN */ static int check_name(char *in) @@ -280,11 +293,9 @@ unsigned char *do_rfc1035_name(unsigned char *p, char *sval, char *limit) if (limit && p + 1 > (unsigned char*)limit) return NULL; -#ifdef HAVE_DNSSEC - if (option_bool(OPT_DNSSEC_VALID) && *sval == NAME_ESCAPE) + if (*sval == NAME_ESCAPE) *p++ = (*(++sval))-1; else -#endif *p++ = *sval; } From bc523726baac8f1a55588b725f8322c44a468a82 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Fri, 31 Mar 2023 08:02:41 +0200 Subject: [PATCH 24/29] Apply necessasry changes to FTL due to most recent dnsmasq patch Signed-off-by: DL6ER --- src/dnsmasq/cache.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dnsmasq/cache.c b/src/dnsmasq/cache.c index 4c8d9645..69f9f421 100644 --- a/src/dnsmasq/cache.c +++ b/src/dnsmasq/cache.c @@ -30,7 +30,7 @@ static void make_non_terminals(struct crec *source); static struct crec *really_insert(char *name, union all_addr *addr, unsigned short class, time_t now, unsigned long ttl, unsigned int flags); static void dump_cache_entry(struct crec *cache, time_t now); -static char *querystr(char *desc, unsigned short type); +char *querystr(char *desc, unsigned short type); /* type->string mapping: this is also used by the name-hash function as a mixing table. */ /* taken from https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml */ From 4288757e440fd1e48fa1d752beb33900556c518c Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Tue, 28 Mar 2023 18:24:22 +0100 Subject: [PATCH 25/29] Remove code for caching SRV. Function replaced by the ability to cache any RR type. For backwards compatibilty SRV records are always on the list of cacheable RR-types. Signed-off-by: DL6ER --- src/dnsmasq/cache.c | 43 ++++-------------- src/dnsmasq/dnsmasq.h | 8 +--- src/dnsmasq/forward.c | 6 +-- src/dnsmasq/option.c | 6 +-- src/dnsmasq/rfc1035.c | 102 ++++++++---------------------------------- 5 files changed, 35 insertions(+), 130 deletions(-) diff --git a/src/dnsmasq/cache.c b/src/dnsmasq/cache.c index 69f9f421..44c5212a 100644 --- a/src/dnsmasq/cache.c +++ b/src/dnsmasq/cache.c @@ -137,7 +137,7 @@ static void cache_hash(struct crec *crecp); unsigned short rrtype(char *in) { - int i; + unsigned int i; for (i = 0; i < (sizeof(typestr)/sizeof(typestr[0])); i++) if (strcasecmp(in, typestr[i].name) == 0) @@ -276,9 +276,7 @@ static void cache_blockdata_free(struct crec *crecp) { if (!(crecp->flags & F_NEG)) { - if (crecp->flags & F_SRV) - blockdata_free(crecp->addr.srv.target); - else if (crecp->flags & F_RR) + if (crecp->flags & F_RR) blockdata_free(crecp->addr.rr.rrdata); #ifdef HAVE_DNSSEC else if (crecp->flags & F_DNSKEY) @@ -473,7 +471,7 @@ static struct crec *cache_scan_free(char *name, union all_addr *addr, unsigned s if ((crecp->flags & F_FORWARD) && hostname_isequal(cache_get_name(crecp), name)) { /* Don't delete DNSSEC in favour of a CNAME, they can co-exist */ - if ((flags & crecp->flags & (F_IPV4 | F_IPV6 | F_SRV | F_NXDOMAIN)) || + if ((flags & crecp->flags & (F_IPV4 | F_IPV6 | F_RR | F_NXDOMAIN)) || (((crecp->flags | flags) & F_CNAME) && !(crecp->flags & (F_DNSKEY | F_DS))) || ((crecp->flags & flags & F_RR) && addr->rr.rrtype == crecp->addr.rr.rrtype)) { @@ -792,14 +790,9 @@ void cache_end_insert(void) read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->ttd, sizeof(new_chain->ttd), 0); read_write(daemon->pipe_to_parent, (unsigned char *)&flags, sizeof(flags), 0); - if (flags & (F_IPV4 | F_IPV6 | F_DNSKEY | F_DS | F_SRV | F_RR)) + if (flags & (F_IPV4 | F_IPV6 | F_DNSKEY | F_DS | F_RR)) read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->addr, sizeof(new_chain->addr), 0); - if (flags & F_SRV) - { - /* A negative SRV entry is possible and has no data, obviously. */ - if (!(flags & F_NEG)) - blockdata_write(new_chain->addr.srv.target, new_chain->addr.srv.targetlen, daemon->pipe_to_parent); - } + if (flags & F_RR) { /* A negative RR entry is possible and has no data, obviously. */ @@ -870,16 +863,13 @@ int cache_recv_insert(time_t now, int fd) ttl = difftime(ttd, now); - if (flags & (F_IPV4 | F_IPV6 | F_DNSKEY | F_DS | F_SRV | F_RR)) + if (flags & (F_IPV4 | F_IPV6 | F_DNSKEY | F_DS | F_RR)) { unsigned short class = C_IN; if (!read_write(fd, (unsigned char *)&addr, sizeof(addr), 1)) return 0; - if ((flags & F_SRV) && !(flags & F_NEG) && !(addr.srv.target = blockdata_read(fd, addr.srv.targetlen))) - return 0; - if ((flags & F_RR) && !(flags & F_NEG) && !(addr.rr.rrdata = blockdata_read(fd, addr.rr.datalen))) return 0; #ifdef HAVE_DNSSEC @@ -1611,7 +1601,7 @@ static void make_non_terminals(struct crec *source) if (!is_outdated_cname_pointer(crecp) && (crecp->flags & F_FORWARD) && (crecp->flags & type) && - !(crecp->flags & (F_IPV4 | F_IPV6 | F_CNAME | F_SRV | F_DNSKEY | F_DS | F_RR)) && + !(crecp->flags & (F_IPV4 | F_IPV6 | F_CNAME | F_DNSKEY | F_DS | F_RR)) && hostname_isequal(name, cache_get_name(crecp))) { *up = crecp->hash_next; @@ -1668,7 +1658,7 @@ static void make_non_terminals(struct crec *source) if (crecp) { - crecp->flags = (source->flags | F_NAMEP) & ~(F_IPV4 | F_IPV6 | F_CNAME | F_SRV | F_RR | F_DNSKEY | F_DS | F_REVERSE); + crecp->flags = (source->flags | F_NAMEP) & ~(F_IPV4 | F_IPV6 | F_CNAME | F_RR | F_DNSKEY | F_DS | F_REVERSE); if (!(crecp->flags & F_IMMORTAL)) crecp->ttd = source->ttd; crecp->name.namep = name; @@ -1811,17 +1801,6 @@ static void dump_cache_entry(struct crec *cache, time_t now) p += sprintf(p, "%-30.30s ", sanitise(n)); if ((cache->flags & F_CNAME) && !is_outdated_cname_pointer(cache)) a = sanitise(cache_get_cname_target(cache)); - else if ((cache->flags & F_SRV) && !(cache->flags & F_NEG)) - { - int targetlen = cache->addr.srv.targetlen; - ssize_t len = sprintf(a, "%u %u %u ", cache->addr.srv.priority, - cache->addr.srv.weight, cache->addr.srv.srvport); - - if (targetlen > (40 - len)) - targetlen = 40 - len; - blockdata_retrieve(cache->addr.srv.target, targetlen, a + len); - a[len + targetlen] = 0; - } else if (cache->flags & F_RR) sprintf(a, "%s", querystr(NULL, cache->addr.rr.rrtype)); #ifdef HAVE_DNSSEC @@ -1850,8 +1829,6 @@ static void dump_cache_entry(struct crec *cache, time_t now) t = "6"; else if (cache->flags & F_CNAME) t = "C"; - else if (cache->flags & F_SRV) - t = "V"; else if (cache->flags & F_RR) t = "T"; #ifdef HAVE_DNSSEC @@ -2179,8 +2156,6 @@ void _log_query(unsigned int flags, char *name, union all_addr *addr, char *arg, } else if (flags & F_CNAME) dest = ""; - else if (flags & F_SRV) - dest = ""; else if (flags & F_RRNAME) dest = arg; @@ -2204,7 +2179,7 @@ void _log_query(unsigned int flags, char *name, union all_addr *addr, char *arg, } else if (flags & F_AUTH) source = "auth"; - else if (flags & F_NOERR) + else if (flags & F_DNSSEC) { source = arg; verb = "to"; diff --git a/src/dnsmasq/dnsmasq.h b/src/dnsmasq/dnsmasq.h index 71fbf745..0ffaec68 100644 --- a/src/dnsmasq/dnsmasq.h +++ b/src/dnsmasq/dnsmasq.h @@ -326,10 +326,6 @@ union all_addr { unsigned char algo; unsigned char digest; } ds; - struct { - struct blockdata *target; - unsigned short targetlen, srvport, priority, weight; - } srv; /* for log_query */ struct { unsigned short keytag, algo, digest, rcode; @@ -513,7 +509,7 @@ struct crec { #define F_QUERY (1u<<19) #define F_NOERR (1u<<20) #define F_AUTH (1u<<21) -#define F_RR (1u<<22) +#define F_DNSSEC (1u<<22) #define F_KEYTAG (1u<<23) #define F_SECSTAT (1u<<24) #define F_NO_RR (1u<<25) @@ -521,7 +517,7 @@ struct crec { #define F_NOEXTRA (1u<<27) #define F_DOMAINSRV (1u<<28) #define F_RCODE (1u<<29) -#define F_SRV (1u<<30) +#define F_RR (1u<<30) #define F_STALE (1u<<31) #define UID_NONE 0 diff --git a/src/dnsmasq/forward.c b/src/dnsmasq/forward.c index c2bde77f..604e2f95 100644 --- a/src/dnsmasq/forward.c +++ b/src/dnsmasq/forward.c @@ -555,7 +555,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, } #ifdef HAVE_DNSSEC else - log_query_mysockaddr(F_NOEXTRA | F_NOERR | F_SERVER, daemon->namebuff, &srv->addr, + log_query_mysockaddr(F_NOEXTRA | F_DNSSEC | F_SERVER, daemon->namebuff, &srv->addr, (forward->flags & FREC_DNSKEY_QUERY) ? "dnssec-retry[DNSKEY]" : "dnssec-retry[DS]", 0); #endif @@ -1089,7 +1089,7 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header, #ifdef HAVE_DUMPFILE dump_packet_udp(DUMP_SEC_QUERY, (void *)header, (size_t)nn, NULL, &server->addr, fd); #endif - log_query_mysockaddr(F_NOEXTRA | F_NOERR | F_SERVER, daemon->keyname, &server->addr, + log_query_mysockaddr(F_NOEXTRA | F_DNSSEC | F_SERVER, daemon->keyname, &server->addr, STAT_ISEQUAL(status, STAT_NEED_KEY) ? "dnssec-query[DNSKEY]" : "dnssec-query[DS]", 0); return; } @@ -2148,7 +2148,7 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si log_save = daemon->log_display_id; daemon->log_display_id = ++daemon->log_id; - log_query_mysockaddr(F_NOEXTRA | F_NOERR | F_SERVER, keyname, &server->addr, + log_query_mysockaddr(F_NOEXTRA | F_DNSSEC | F_SERVER, keyname, &server->addr, STAT_ISEQUAL(status, STAT_NEED_KEY) ? "dnssec-query[DNSKEY]" : "dnssec-query[DS]", 0); new_status = tcp_key_recurse(now, new_status, new_header, m, class, name, keyname, server, have_mark, mark, keycount); diff --git a/src/dnsmasq/option.c b/src/dnsmasq/option.c index 5b5fba90..9e2f48e7 100644 --- a/src/dnsmasq/option.c +++ b/src/dnsmasq/option.c @@ -3450,7 +3450,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma break; } - case LOPT_FAST_RETRY: + case LOPT_FAST_RETRY: /* --fast-dns-retry */ daemon->fast_retry_timeout = TIMEOUT; if (!arg) @@ -3473,7 +3473,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma } break; - case LOPT_CACHE_RR: + case LOPT_CACHE_RR: /* --cache-rr */ while (1) { int type; struct rrlist *new; @@ -5188,7 +5188,7 @@ err: break; } - case LOPT_STALE_CACHE: + case LOPT_STALE_CACHE: /* --use-stale-cache */ { int max_expiry = STALE_CACHE_EXPIRY; if (arg) diff --git a/src/dnsmasq/rfc1035.c b/src/dnsmasq/rfc1035.c index f8a05294..8d704cdd 100644 --- a/src/dnsmasq/rfc1035.c +++ b/src/dnsmasq/rfc1035.c @@ -706,9 +706,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t addrlen = IN6ADDRSZ; flags |= F_IPV6; } - else if (qtype == T_SRV) - flags |= F_SRV; - else if (qtype != T_CNAME && rr_on_list(daemon->cache_rr, qtype)) + else if (qtype != T_CNAME && (qtype == T_SRV || rr_on_list(daemon->cache_rr, qtype))) flags |= F_RR; else insert = 0; /* NOTE: do not cache data from CNAME queries. */ @@ -813,26 +811,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t { found = 1; - if (flags & F_SRV) - { - unsigned char *tmp = namep; - - if (!CHECK_LEN(header, p1, qlen, 6)) - return 2; /* bad packet */ - GETSHORT(addr.srv.priority, p1); - GETSHORT(addr.srv.weight, p1); - GETSHORT(addr.srv.srvport, p1); - if (!extract_name(header, qlen, &p1, name, 1, 0)) - return 2; - addr.srv.targetlen = strlen(name) + 1; /* include terminating zero */ - if (!(addr.srv.target = blockdata_alloc(name, addr.srv.targetlen))) - return 0; - - /* we overwrote the original name, so get it back here. */ - if (!extract_name(header, qlen, &tmp, name, 1, 0)) - return 2; - } - else if (flags & F_RR) + if (flags & F_RR) { short desc, *rrdesc = rrfilter_desc(aqtype); unsigned char *tmp = namep; @@ -966,7 +945,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t { if (flags & F_NXDOMAIN) { - flags &= ~(F_IPV4 | F_IPV6 | F_SRV | F_RR); + flags &= ~(F_IPV4 | F_IPV6 | F_RR); /* Can store NXDOMAIN reply for any qtype. */ insert = 1; @@ -2026,7 +2005,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, since its existence allows us to return a NODATA answer. Note that we never set the AD flag, since we didn't authentucate the record. */ - if (cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_SRV)) + if (cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_RR)) { ans = 1; sec_data = auth = 0; @@ -2081,13 +2060,12 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, if (qtype == T_SRV || qtype == T_ANY) { - int found = 0; struct mx_srv_record *move = NULL, **up = &daemon->mxnames; for (rec = daemon->mxnames; rec; rec = rec->next) if (rec->issrv && hostname_isequal(name, rec->name)) { - found = ans = 1; + ans = 1; sec_data = 0; if (!dryrun) { @@ -2121,60 +2099,6 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, *up = move; move->next = NULL; } - - if (!found) - { - if ((crecp = cache_find_by_name(NULL, name, now, F_SRV | F_NXDOMAIN | (dryrun ? F_NO_RR : 0))) && - rd_bit && (!do_bit || cache_validated(crecp))) - do - { - int stale_flag = 0; - - if (crec_isstale(crecp, now)) - { - if (stale) - *stale = 1; - - stale_flag = F_STALE; - } - /* don't answer wildcard queries with data not from /etc/hosts or dhcp leases, except for NXDOMAIN */ - if (qtype == T_ANY && !(crecp->flags & (F_NXDOMAIN))) - break; - - if (!(crecp->flags & F_DNSSECOK)) - sec_data = 0; - - auth = 0; - found = ans = 1; - - if (crecp->flags & F_NEG) - { - if (crecp->flags & F_NXDOMAIN) - nxdomain = 1; - if (!dryrun) - log_query(stale_flag | crecp->flags, name, NULL, NULL, 0); - } - else if (!dryrun) - { - char *target = blockdata_retrieve(crecp->addr.srv.target, crecp->addr.srv.targetlen, NULL); - log_query(stale_flag | crecp->flags, name, NULL, NULL, 0); - - if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, - crec_ttl(crecp, now), NULL, T_SRV, C_IN, "sssd", - crecp->addr.srv.priority, crecp->addr.srv.weight, crecp->addr.srv.srvport, - target)) - anscount++; - } - } while ((crecp = cache_find_by_name(crecp, name, now, F_SRV))); - } - - if (!found && option_bool(OPT_FILTER) && (qtype == T_SRV || (qtype == T_ANY && strchr(name, '_')))) - { - ans = 1; - sec_data = 0; - if (!dryrun) - log_query(F_CONFIG | F_NEG, name, NULL, NULL, 0); - } } if (qtype == T_NAPTR || qtype == T_ANY) @@ -2227,7 +2151,10 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, if (!(crecp->flags & F_DNSSECOK)) sec_data = 0; - + + if (crecp->flags & F_NXDOMAIN) + nxdomain = 1; + auth = 0; ans = 1; @@ -2259,9 +2186,16 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, if (!ans) { - /* We may know that the domain doesn't exist for any RRtype. */ - if ((crecp = cache_find_by_name(NULL, name, now, F_NXDOMAIN))) + if (option_bool(OPT_FILTER) && (qtype == T_SRV || (qtype == T_ANY && strchr(name, '_')))) { + ans = 1; + sec_data = 0; + if (!dryrun) + log_query(F_CONFIG | F_NEG, name, NULL, NULL, 0); + } + else if ((crecp = cache_find_by_name(NULL, name, now, F_NXDOMAIN))) + { + /* We may know that the domain doesn't exist for any RRtype. */ ans = nxdomain = 1; auth = 0; From 91eaa62e2f474a4399086ae53d1c5b606b9b59d9 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Wed, 29 Mar 2023 22:43:21 +0100 Subject: [PATCH 26/29] Add filtering of arbitrary RR-types. Signed-off-by: DL6ER --- src/dnsmasq/dbus.c | 18 ++++++++- src/dnsmasq/dnsmasq.c | 2 +- src/dnsmasq/dnsmasq.h | 19 ++++----- src/dnsmasq/forward.c | 15 +------ src/dnsmasq/option.c | 41 ++++++++++++++----- src/dnsmasq/rfc1035.c | 89 ++++++++++++++++++------------------------ src/dnsmasq/rrfilter.c | 5 +-- 7 files changed, 99 insertions(+), 90 deletions(-) diff --git a/src/dnsmasq/dbus.c b/src/dnsmasq/dbus.c index 4366b7ea..4512b8ec 100644 --- a/src/dnsmasq/dbus.c +++ b/src/dnsmasq/dbus.c @@ -825,11 +825,25 @@ DBusHandlerResult message_handler(DBusConnection *connection, } else if (strcmp(method, "SetFilterA") == 0) { - reply = dbus_set_bool(message, OPT_FILTER_A, "filter-A"); + static int done = 0; + static struct rrlist list = { T_A, NULL }; + + if (!done) + { + list.next = daemon->filter_rr; + daemon->filter_rr = &list; + } } else if (strcmp(method, "SetFilterAAAA") == 0) { - reply = dbus_set_bool(message, OPT_FILTER_AAAA, "filter-AAAA"); + static int done = 0; + static struct rrlist list = { T_AAAA, NULL }; + + if (!done) + { + list.next = daemon->filter_rr; + daemon->filter_rr = &list; + } } else if (strcmp(method, "SetLocaliseQueriesOption") == 0) { diff --git a/src/dnsmasq/dnsmasq.c b/src/dnsmasq/dnsmasq.c index 28ff4f7d..ec1f2836 100644 --- a/src/dnsmasq/dnsmasq.c +++ b/src/dnsmasq/dnsmasq.c @@ -147,7 +147,7 @@ int main_dnsmasq (int argc, char **argv) /* 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); + daemon->workspacename = safe_malloc((MAXDNAME * 2) + 1); #endif #ifdef HAVE_DHCP diff --git a/src/dnsmasq/dnsmasq.h b/src/dnsmasq/dnsmasq.h index 0ffaec68..a1256616 100644 --- a/src/dnsmasq/dnsmasq.h +++ b/src/dnsmasq/dnsmasq.h @@ -276,14 +276,12 @@ struct event_desc { #define OPT_UMBRELLA_DEVID 64 #define OPT_CMARK_ALST_EN 65 #define OPT_QUIET_TFTP 66 -#define OPT_FILTER_A 67 -#define OPT_FILTER_AAAA 68 -#define OPT_STRIP_ECS 69 -#define OPT_STRIP_MAC 70 -#define OPT_NORR 71 -#define OPT_NO_IDENT 72 -#define OPT_CACHE_RR 73 -#define OPT_LAST 74 +#define OPT_STRIP_ECS 67 +#define OPT_STRIP_MAC 68 +#define OPT_NORR 69 +#define OPT_NO_IDENT 70 +#define OPT_CACHE_RR 71 +#define OPT_LAST 72 #define OPTION_BITS (sizeof(unsigned int)*8) #define OPTION_SIZE ( (OPT_LAST/OPTION_BITS)+((OPT_LAST%OPTION_BITS)!=0) ) @@ -1135,7 +1133,7 @@ extern struct daemon { struct naptr *naptr; struct txt_record *txt, *rr; struct ptr_record *ptr; - struct rrlist *cache_rr, filter_rr; + struct rrlist *cache_rr, *filter_rr; struct host_record *host_records, *host_records_tail; struct cname *cnames; struct auth_zone *auth_zones; @@ -1864,8 +1862,7 @@ void from_wire(char *name); /* modes. */ #define RRFILTER_EDNS0 0 #define RRFILTER_DNSSEC 1 -#define RRFILTER_A 2 -#define RRFILTER_AAAA 3 +#define RRFILTER_CONF 2 /* edns0.c */ unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, diff --git a/src/dnsmasq/forward.c b/src/dnsmasq/forward.c index 604e2f95..45de97b1 100644 --- a/src/dnsmasq/forward.c +++ b/src/dnsmasq/forward.c @@ -862,19 +862,8 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server break; } - if (rcode == NOERROR) - { - size_t modified = 0; - - if (option_bool(OPT_FILTER_A)) - modified = rrfilter(header, &n, RRFILTER_A); - - if (option_bool(OPT_FILTER_AAAA)) - modified += rrfilter(header, &n, RRFILTER_AAAA); - - if (modified > 0) - ede = EDE_FILTERED; - } + if (rcode == NOERROR && rrfilter(header, &n, RRFILTER_CONF) > 0) + ede = EDE_FILTERED; if (doctored) cache_secure = 0; diff --git a/src/dnsmasq/option.c b/src/dnsmasq/option.c index 9e2f48e7..058fd13e 100644 --- a/src/dnsmasq/option.c +++ b/src/dnsmasq/option.c @@ -191,6 +191,7 @@ struct myoption { #define LOPT_NORR 378 #define LOPT_NO_IDENT 379 #define LOPT_CACHE_RR 380 +#define LOPT_FILTER_RR 381 #ifdef HAVE_GETOPT_LONG static const struct option opts[] = @@ -230,6 +231,7 @@ static const struct myoption opts[] = { "filterwin2k", 0, 0, 'f' }, { "filter-A", 0, 0, LOPT_FILTER_A }, { "filter-AAAA", 0, 0, LOPT_FILTER_AAAA }, + { "filter-rr", 1, 0, LOPT_FILTER_RR }, { "pid-file", 2, 0, 'x' }, { "strict-order", 0, 0, 'o' }, { "server", 1, 0, 'S' }, @@ -409,8 +411,9 @@ static struct { { 'e', OPT_SELFMX, NULL, gettext_noop("Return self-pointing MX records for local hosts."), NULL }, { 'E', OPT_EXPAND, NULL, gettext_noop("Expand simple names in /etc/hosts with domain-suffix."), NULL }, { 'f', OPT_FILTER, NULL, gettext_noop("Don't forward spurious DNS requests from Windows hosts."), NULL }, - { LOPT_FILTER_A, OPT_FILTER_A, NULL, gettext_noop("Don't include IPv4 addresses in DNS answers."), NULL }, - { LOPT_FILTER_AAAA, OPT_FILTER_AAAA, NULL, gettext_noop("Don't include IPv6 addresses in DNS answers."), NULL }, + { LOPT_FILTER_A, ARG_DUP, NULL, gettext_noop("Don't include IPv4 addresses in DNS answers."), NULL }, + { LOPT_FILTER_AAAA, ARG_DUP, NULL, gettext_noop("Don't include IPv6 addresses in DNS answers."), NULL }, + { LOPT_FILTER_RR, ARG_DUP, "", gettext_noop("Don't include resource records of the given type in DNS answers."), NULL }, { 'F', ARG_DUP, ",...", gettext_noop("Enable DHCP in the range given with lease duration."), NULL }, { 'g', ARG_ONE, "", gettext_noop("Change to this group after startup (defaults to %s)."), CHGRP }, { 'G', ARG_DUP, "", gettext_noop("Set address or hostname for a specified machine."), NULL }, @@ -579,7 +582,7 @@ static struct { { LOPT_QUIET_TFTP, OPT_QUIET_TFTP, NULL, gettext_noop("Do not log routine TFTP."), NULL }, { LOPT_NORR, OPT_NORR, NULL, gettext_noop("Suppress round-robin ordering of DNS records."), NULL }, { LOPT_NO_IDENT, OPT_NO_IDENT, NULL, gettext_noop("Do not add CHAOS TXT records."), NULL }, - { LOPT_CACHE_RR, ARG_DUP, "RRtype", gettext_noop("Cache this DNS resource record type."), NULL }, + { LOPT_CACHE_RR, ARG_DUP, "", gettext_noop("Cache this DNS resource record type."), NULL }, { 0, 0, NULL, NULL, NULL } }; @@ -3474,19 +3477,39 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma break; case LOPT_CACHE_RR: /* --cache-rr */ + case LOPT_FILTER_RR: /* --filter-rr */ + case LOPT_FILTER_A: /* --filter-A */ + case LOPT_FILTER_AAAA: /* --filter-AAAA */ while (1) { int type; struct rrlist *new; - - comma = split(arg); - if (!atoi_check(arg, &type) && (type = rrtype(arg)) == 0) - ret_err(_("bad RR type")); + comma = NULL; + + if (option == LOPT_FILTER_A) + type = T_A; + else if (option == LOPT_FILTER_AAAA) + type = T_AAAA; + else + { + comma = split(arg); + if (!atoi_check(arg, &type) && (type = rrtype(arg)) == 0) + ret_err(_("bad RR type")); + } + new = opt_malloc(sizeof(struct rrlist)); new->rr = type; - new->next = daemon->cache_rr; - daemon->cache_rr = new; + if (option == LOPT_CACHE_RR) + { + new->next = daemon->cache_rr; + daemon->cache_rr = new; + } + else + { + new->next = daemon->filter_rr; + daemon->filter_rr = new; + } if (!comma) break; arg = comma; diff --git a/src/dnsmasq/rfc1035.c b/src/dnsmasq/rfc1035.c index 8d704cdd..f497b0c0 100644 --- a/src/dnsmasq/rfc1035.c +++ b/src/dnsmasq/rfc1035.c @@ -929,7 +929,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t returned packet in process_reply() but gets cached here anyway and will be filtered again on the way out of the cache. Here, we just need to alter the logging. */ - if (((flags & F_IPV4) && option_bool(OPT_FILTER_A)) || ((flags & F_IPV6) && option_bool(OPT_FILTER_AAAA))) + if (rr_on_list(daemon->filter_rr, qtype)) negflag = F_NEG | F_CONFIG; log_query(negflag | flags | F_FORWARD | secflag, name, &addr, NULL, aqtype); @@ -1925,7 +1925,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, if (!(crecp->flags & (F_HOSTS | F_DHCP))) auth = 0; - if ((((flag & F_IPV4) && option_bool(OPT_FILTER_A)) || ((flag & F_IPV6) && option_bool(OPT_FILTER_AAAA))) && + if (rr_on_list(daemon->filter_rr, qtype) && !(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG | F_NEG))) { /* We have a cached answer but we're filtering it. */ @@ -1934,7 +1934,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, if (!dryrun) log_query(F_NEG | F_CONFIG | flag, name, NULL, NULL, 0); - + if (filtered) *filtered = 1; } @@ -1996,27 +1996,6 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, anscount++; } } - else if (((flag & F_IPV4) && option_bool(OPT_FILTER_A)) || ((flag & F_IPV6) && option_bool(OPT_FILTER_AAAA))) - { - /* We don't have a cached answer and when we get an answer from upstream we're going to - filter it anyway. If we have a cached answer for the domain for another RRtype then - that may be enough to tell us if the answer should be NODATA and save the round trip. - Cached NXDOMAIN has already been handled, so here we look for any record for the domain, - since its existence allows us to return a NODATA answer. Note that we never set the AD flag, - since we didn't authentucate the record. */ - - if (cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_RR)) - { - ans = 1; - sec_data = auth = 0; - - if (!dryrun) - log_query(F_NEG | F_CONFIG | flag, name, NULL, NULL, 0); - - if (filtered) - *filtered = 1; - } - } } if (qtype == T_MX || qtype == T_ANY) @@ -2131,30 +2110,32 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, log_query(F_CONFIG | F_NEG, name, &addr, NULL, 0); } - if (!ans && qtype != T_ANY) + if (!ans) { if ((crecp = cache_find_by_name(NULL, name, now, F_RR | F_NXDOMAIN | (dryrun ? F_NO_RR : 0))) && rd_bit && (!do_bit || cache_validated(crecp))) do { - int stale_flag = 0; + int flags = crecp->flags; - if (crecp->addr.rr.rrtype == qtype) + if ((flags & F_NXDOMAIN) || crecp->addr.rr.rrtype == qtype) { if (crec_isstale(crecp, now)) { if (stale) *stale = 1; - stale_flag = F_STALE; + flags |= F_STALE; } - if (!(crecp->flags & F_DNSSECOK)) + if (!(flags & F_DNSSECOK)) sec_data = 0; - if (crecp->flags & F_NXDOMAIN) + if (flags & F_NXDOMAIN) nxdomain = 1; - + else if (rr_on_list(daemon->filter_rr, qtype)) + flags |= F_NEG | F_CONFIG; + auth = 0; ans = 1; @@ -2162,7 +2143,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, { char *rrdata = NULL; - if (!(crecp->flags & F_NEG)) + if (!(flags & F_NEG)) { rrdata = blockdata_retrieve(crecp->addr.rr.rrdata, crecp->addr.rr.datalen, NULL); @@ -2176,38 +2157,46 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, if (qtype == T_TXT && !(crecp->flags & F_NEG)) log_txt(name, (unsigned char *)rrdata, crecp->addr.rr.datalen, crecp->flags & F_DNSSECOK); else - log_query(stale_flag | crecp->flags, name, &crecp->addr, NULL, 0); + log_query(flags, name, &crecp->addr, NULL, 0); } } } while ((crecp = cache_find_by_name(crecp, name, now, F_RR))); } - } - - - if (!ans) - { - if (option_bool(OPT_FILTER) && (qtype == T_SRV || (qtype == T_ANY && strchr(name, '_')))) + + if (!ans && option_bool(OPT_FILTER) && (qtype == T_SRV || (qtype == T_ANY && strchr(name, '_')))) { ans = 1; sec_data = 0; if (!dryrun) log_query(F_CONFIG | F_NEG, name, NULL, NULL, 0); } - else if ((crecp = cache_find_by_name(NULL, name, now, F_NXDOMAIN))) + + + if (!ans && rr_on_list(daemon->filter_rr, qtype)) { - /* We may know that the domain doesn't exist for any RRtype. */ - ans = nxdomain = 1; - auth = 0; - - if (!(crecp->flags & F_DNSSECOK)) - sec_data = 0; + /* We don't have a cached answer and when we get an answer from upstream we're going to + filter it anyway. If we have a cached answer for the domain for another RRtype then + that may be enough to tell us if the answer should be NODATA and save the round trip. + Cached NXDOMAIN has already been handled, so here we look for any record for the domain, + since its existence allows us to return a NODATA answer. Note that we never set the AD flag, + since we didn't authenticate the record. */ - if (!dryrun) - log_query(F_NXDOMAIN | F_NEG, name, NULL, NULL, 0); + if (cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_RR | F_CNAME)) + { + ans = 1; + sec_data = auth = 0; + + if (!dryrun) + log_query(F_NEG | F_CONFIG | flag, name, NULL, NULL, 0); + + if (filtered) + *filtered = 1; + } } - else - return 0; /* failed to answer a question */ } + + if (!ans) + return 0; /* failed to answer a question */ } if (dryrun) diff --git a/src/dnsmasq/rrfilter.c b/src/dnsmasq/rrfilter.c index e4c56cb5..d98236e4 100644 --- a/src/dnsmasq/rrfilter.c +++ b/src/dnsmasq/rrfilter.c @@ -219,10 +219,7 @@ size_t rrfilter(struct dns_header *header, size_t *plen, int mode) if (class != C_IN) continue; - if (mode == RRFILTER_A && type != T_A) - continue; - - if (mode == RRFILTER_AAAA && type != T_AAAA) + if (!rr_on_list(daemon->filter_rr, type)) continue; } From 18a77d301824a609c6c3f2db508622ac529c7dcf Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Thu, 30 Mar 2023 16:00:04 +0100 Subject: [PATCH 27/29] Optimise no-action case in rrfilter(). Signed-off-by: DL6ER --- src/dnsmasq/rrfilter.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/dnsmasq/rrfilter.c b/src/dnsmasq/rrfilter.c index d98236e4..d380e148 100644 --- a/src/dnsmasq/rrfilter.c +++ b/src/dnsmasq/rrfilter.c @@ -167,6 +167,9 @@ size_t rrfilter(struct dns_header *header, size_t *plen, int mode) size_t rr_found = 0; int i, rdlen, qtype, qclass, chop_an, chop_ns, chop_ar; + if (mode == RRFILTER_CONF && !daemon->filter_rr) + return 0; + if (ntohs(header->qdcount) != 1 || !(p = skip_name(p, header, *plen, 4))) return 0; From 8b1d06498628b4d7dc98afa3148b5e49b5f1c18c Mon Sep 17 00:00:00 2001 From: DL6ER Date: Fri, 31 Mar 2023 08:57:35 +0200 Subject: [PATCH 28/29] Apply necessasry changes to FTL due to most recent dnsmasq patch Signed-off-by: DL6ER --- src/dnsmasq/cache.c | 2 -- src/dnsmasq/dnsmasq.h | 1 - src/dnsmasq_interface.c | 3 +-- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/dnsmasq/cache.c b/src/dnsmasq/cache.c index 44c5212a..116feaaf 100644 --- a/src/dnsmasq/cache.c +++ b/src/dnsmasq/cache.c @@ -1876,8 +1876,6 @@ void get_dnsmasq_cache_info(struct cache_info *ci) ci->valid.ipv6++; else if (cache->flags & F_CNAME) ci->valid.cname++; - else if (cache->flags & F_SRV) - ci->valid.srv++; #ifdef HAVE_DNSSEC else if (cache->flags & F_DS) ci->valid.ds++; diff --git a/src/dnsmasq/dnsmasq.h b/src/dnsmasq/dnsmasq.h index a1256616..b0178920 100644 --- a/src/dnsmasq/dnsmasq.h +++ b/src/dnsmasq/dnsmasq.h @@ -1325,7 +1325,6 @@ struct cache_info { int ipv4; int ipv6; int cname; - int srv; int ds; int dnskey; int other; diff --git a/src/dnsmasq_interface.c b/src/dnsmasq_interface.c index cab1a960..7285a399 100644 --- a/src/dnsmasq_interface.c +++ b/src/dnsmasq_interface.c @@ -2857,13 +2857,12 @@ void getCacheInformation(const int sock) { struct cache_info ci; get_dnsmasq_cache_info(&ci); - ssend(sock, "cache-size: %i\ncache-live-freed: %i\ncache-inserted: %i\nipv4: %i\nipv6: %i\nsrv: %i\ncname: %i\nds: %i\ndnskey: %i\nother: %i\nexpired: %i\nimmortal: %i\n", + ssend(sock, "cache-size: %i\ncache-live-freed: %i\ncache-inserted: %i\nipv4: %i\nipv6: %i\nsrv: 0\ncname: %i\nds: %i\ndnskey: %i\nother: %i\nexpired: %i\nimmortal: %i\n", daemon->cachesize, daemon->metrics[METRIC_DNS_CACHE_LIVE_FREED], daemon->metrics[METRIC_DNS_CACHE_INSERTED], ci.valid.ipv4, ci.valid.ipv6, - ci.valid.srv, ci.valid.cname, ci.valid.ds, ci.valid.dnskey, From fa2aeccc0e55e7eeb0a508f2ad050b9d7df7ad90 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Fri, 31 Mar 2023 09:55:43 +0200 Subject: [PATCH 29/29] ANY is RRNAME not BLOB after the most recent dnsmasq code changes Signed-off-by: DL6ER --- src/dnsmasq_interface.c | 11 +++++++---- test/test_suite.bats | 6 +++--- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/dnsmasq_interface.c b/src/dnsmasq_interface.c index 7285a399..f492ca0a 100644 --- a/src/dnsmasq_interface.c +++ b/src/dnsmasq_interface.c @@ -95,15 +95,18 @@ static struct { static union mysockaddr last_server = {{ 0 }}; unsigned char* pihole_privacylevel = &config.privacylevel; -const char *flagnames[] = {"F_IMMORTAL ", "F_NAMEP ", "F_REVERSE ", "F_FORWARD ", "F_DHCP ", "F_NEG ", "F_HOSTS ", "F_IPV4 ", "F_IPV6 ", "F_BIGNAME ", "F_NXDOMAIN ", "F_CNAME ", "F_DNSKEY ", "F_CONFIG ", "F_DS ", "F_DNSSECOK ", "F_UPSTREAM ", "F_RRNAME ", "F_SERVER ", "F_QUERY ", "F_NOERR ", "F_AUTH ", "F_RR ", "F_KEYTAG ", "F_SECSTAT ", "F_NO_RR ", "F_IPSET ", "F_NOEXTRA ", "F_SERVFAIL", "F_RCODE", "F_SRV", "F_STALE" }; +const char *flagnames[] = {"F_IMMORTAL ", "F_NAMEP ", "F_REVERSE ", "F_FORWARD ", "F_DHCP ", "F_NEG ", "F_HOSTS ", "F_IPV4 ", "F_IPV6 ", "F_BIGNAME ", "F_NXDOMAIN ", "F_CNAME ", "F_DNSKEY ", "F_CONFIG ", "F_DS ", "F_DNSSECOK ", "F_UPSTREAM ", "F_RRNAME ", "F_SERVER ", "F_QUERY ", "F_NOERR ", "F_AUTH ", "F_DNSSEC ", "F_KEYTAG ", "F_SECSTAT ", "F_NO_RR ", "F_IPSET ", "F_NOEXTRA ", "F_DOMAINSRV", "F_RCODE", "F_RR", "F_STALE" }; -void FTL_hook(unsigned int flags, const char *name, union all_addr *addr, char *arg, int id, unsigned short type, const char* file, const int line) +void FTL_hook(unsigned int flags, +const char *name, +union all_addr *addr, char *arg, int id, unsigned short type, const char* file, const int line) { // Extract filename from path const char *path = short_path(file); if(config.debug & DEBUG_FLAGS) { - logg("Processing FTL hook from %s:%d (name: \"%s\")...", path, line, name); + const char *types = flags & F_RR ? querystr(arg, type) : "?"; + logg("Processing FTL hook from %s:%d (type: %s, name: \"%s\", id: %i)...", path, line, types, name, id); print_flags(flags); } @@ -122,7 +125,7 @@ void FTL_hook(unsigned int flags, const char *name, union all_addr *addr, char * else if(flags & F_RCODE && name && strcasecmp(name, "error") == 0) // upstream sent something different than NOERROR or NXDOMAIN FTL_upstream_error(addr, flags, id, path, line); - else if(flags & F_NOEXTRA && flags & F_NOERR) + else if(flags & F_NOEXTRA && flags & F_DNSSEC) { // This is a new DNSSEC query (dnssec-query[DS]) if(!config.show_dnssec) diff --git a/test/test_suite.bats b/test/test_suite.bats index cc340ffb..3cb61dcc 100644 --- a/test/test_suite.bats +++ b/test/test_suite.bats @@ -459,14 +459,14 @@ [[ ${lines[14]} == "reply_CNAME 7" ]] [[ ${lines[15]} == "reply_IP 25" ]] [[ ${lines[16]} == "reply_DOMAIN 0" ]] - [[ ${lines[17]} == "reply_RRNAME 5" ]] + [[ ${lines[17]} == "reply_RRNAME 6" ]] [[ ${lines[18]} == "reply_SERVFAIL 0" ]] [[ ${lines[19]} == "reply_REFUSED 0" ]] [[ ${lines[20]} == "reply_NOTIMP 0" ]] [[ ${lines[21]} == "reply_OTHER 0" ]] [[ ${lines[22]} == "reply_DNSSEC 6" ]] [[ ${lines[23]} == "reply_NONE 0" ]] - [[ ${lines[24]} == "reply_BLOB 10" ]] + [[ ${lines[24]} == "reply_BLOB 9" ]] [[ ${lines[25]} == "dns_queries_all_replies 54" ]] [[ ${lines[26]} == "privacy_level 0" ]] [[ ${lines[27]} == "status enabled" ]] @@ -617,7 +617,7 @@ [[ ${lines[25]} == *" A use-application-dns.net 127.0.0.1 16 2 2 "*" N/A -1 N/A#0 \"\" \"24\""* ]] [[ ${lines[26]} == *" A a.ftl 127.0.0.1 3 2 4 "*" N/A -1 N/A#0 \"\" \"25\""* ]] [[ ${lines[27]} == *" AAAA aaaa.ftl 127.0.0.1 3 2 4 "*" N/A -1 N/A#0 \"\" \"26\""* ]] - [[ ${lines[28]} == *" ANY any.ftl 127.0.0.1 2 2 13 "*" N/A -1 127.0.0.1#5555 \"\" \"27\""* ]] + [[ ${lines[28]} == *" ANY any.ftl 127.0.0.1 2 2 6 "*" N/A -1 127.0.0.1#5555 \"\" \"27\""* ]] [[ ${lines[29]} == *" [CNAME] cname-ok.ftl 127.0.0.1 2 2 3 "*" N/A -1 127.0.0.1#5555 \"\" \"28\""* ]] [[ ${lines[30]} == *" SRV srv.ftl 127.0.0.1 2 2 13 "*" N/A -1 127.0.0.1#5555 \"\" \"29\""* ]] [[ ${lines[31]} == *" SOA ftl 127.0.0.1 2 2 13 "*" N/A -1 127.0.0.1#5555 \"\" \"30\""* ]]