diff --git a/backport-0001-Handle-caching-with-EDNS-options-better.patch b/backport-0001-Handle-caching-with-EDNS-options-better.patch deleted file mode 100644 index 5064580..0000000 --- a/backport-0001-Handle-caching-with-EDNS-options-better.patch +++ /dev/null @@ -1,361 +0,0 @@ -From 25e63f1e56f5acdcf91893a1b92ad1e0f2f552d8 Mon Sep 17 00:00:00 2001 -From: Simon Kelley -Date: Wed, 25 Nov 2020 21:17:52 +0000 -Subject: [PATCH] Handle caching with EDNS options better. - -If we add the EDNS client subnet option, or the client's -MAC address, then the reply we get back may very depending on -that. Since the cache is ignorant of such things, it's not safe to -cache such replies. This patch determines when a dangerous EDNS -option is being added and disables caching. - -Note that for much the same reason, we can't combine multiple -queries for the same question when dangerous EDNS options are -being added, and the code now handles that in the same way. This -query combining is required for security against cache poisoning, -so disabling the cache has a security function as well as a -correctness one. ---- - man/dnsmasq.8 | 4 ++-- - src/dnsmasq.h | 3 ++- - src/edns0.c | 75 +++++++++++++++++++++++++++++++++++++---------------------- - src/forward.c | 41 +++++++++++++++++++++----------- - 4 files changed, 78 insertions(+), 45 deletions(-) - -diff --git a/man/dnsmasq.8 b/man/dnsmasq.8 -index 7c6b405..ac7c9fa 100644 ---- a/man/dnsmasq.8 -+++ b/man/dnsmasq.8 -@@ -692,8 +692,8 @@ still marks the request so that no upstream nameserver will add client - address information either. The default is zero for both IPv4 and - IPv6. Note that upstream nameservers may be configured to return - different results based on this information, but the dnsmasq cache --does not take account. If a dnsmasq instance is configured such that --different results may be encountered, caching should be disabled. -+does not take account. Caching is therefore disabled for such replies, -+unless the subnet address being added is constant. - - For example, - .B --add-subnet=24,96 -diff --git a/src/dnsmasq.h b/src/dnsmasq.h -index fa8c5ea..9f74c7a 100644 ---- a/src/dnsmasq.h -+++ b/src/dnsmasq.h -@@ -655,6 +655,7 @@ struct hostsfile { - #define FREC_TEST_PKTSZ 256 - #define FREC_HAS_EXTRADATA 512 - #define FREC_HAS_PHEADER 1024 -+#define FREC_NO_CACHE 2048 - - #define HASH_SIZE 32 /* SHA-256 digest size */ - -@@ -1658,7 +1659,7 @@ size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *l - unsigned short udp_sz, int optno, unsigned char *opt, size_t optlen, int set_do, int replace); - size_t add_do_bit(struct dns_header *header, size_t plen, unsigned char *limit); - size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *limit, -- union mysockaddr *source, time_t now, int *check_subnet); -+ union mysockaddr *source, time_t now, int *check_subnet, int *cacheable); - int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer); - - /* arp.c */ -diff --git a/src/edns0.c b/src/edns0.c -index d75d3cc..53cfe24 100644 ---- a/src/edns0.c -+++ b/src/edns0.c -@@ -264,7 +264,8 @@ static void encoder(unsigned char *in, char *out) - out[3] = char64(in[2]); - } - --static size_t add_dns_client(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *l3, time_t now) -+static size_t add_dns_client(struct dns_header *header, size_t plen, unsigned char *limit, -+ union mysockaddr *l3, time_t now, int *cacheablep) - { - int maclen, replace = 2; /* can't get mac address, just delete any incoming. */ - unsigned char mac[DHCP_CHADDR_MAX]; -@@ -273,6 +274,7 @@ static size_t add_dns_client(struct dns_header *header, size_t plen, unsigned ch - if ((maclen = find_mac(l3, mac, 1, now)) == 6) - { - replace = 1; -+ *cacheablep = 0; - - if (option_bool(OPT_MAC_HEX)) - print_mac(encode, mac, maclen); -@@ -288,14 +290,18 @@ static size_t add_dns_client(struct dns_header *header, size_t plen, unsigned ch - } - - --static size_t add_mac(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *l3, time_t now) -+static size_t add_mac(struct dns_header *header, size_t plen, unsigned char *limit, -+ union mysockaddr *l3, time_t now, int *cacheablep) - { - int maclen; - unsigned char mac[DHCP_CHADDR_MAX]; - - if ((maclen = find_mac(l3, mac, 1, now)) != 0) -- plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_MAC, mac, maclen, 0, 0); -- -+ { -+ *cacheablep = 0; -+ plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_MAC, mac, maclen, 0, 0); -+ } -+ - return plen; - } - -@@ -313,17 +319,18 @@ static void *get_addrp(union mysockaddr *addr, const short family) - return &addr->in.sin_addr; - } - --static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source) -+static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source, int *cacheablep) - { - /* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */ - - int len; - void *addrp = NULL; - int sa_family = source->sa.sa_family; -- -+ int cacheable = 0; -+ - opt->source_netmask = 0; - opt->scope_netmask = 0; -- -+ - if (source->sa.sa_family == AF_INET6 && daemon->add_subnet6) - { - opt->source_netmask = daemon->add_subnet6->mask; -@@ -331,6 +338,7 @@ static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source) - { - sa_family = daemon->add_subnet6->addr.sa.sa_family; - addrp = get_addrp(&daemon->add_subnet6->addr, sa_family); -+ cacheable = 1; - } - else - addrp = &source->in6.sin6_addr; -@@ -343,6 +351,7 @@ static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source) - { - sa_family = daemon->add_subnet4->addr.sa.sa_family; - addrp = get_addrp(&daemon->add_subnet4->addr, sa_family); -+ cacheable = 1; /* Address is constant */ - } - else - addrp = &source->in.sin_addr; -@@ -350,8 +359,6 @@ static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source) - - opt->family = htons(sa_family == AF_INET6 ? 2 : 1); - -- len = 0; -- - if (addrp && opt->source_netmask != 0) - { - len = ((opt->source_netmask - 1) >> 3) + 1; -@@ -359,18 +366,26 @@ static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source) - if (opt->source_netmask & 7) - opt->addr[len-1] &= 0xff << (8 - (opt->source_netmask & 7)); - } -+ else -+ { -+ cacheable = 1; /* No address ever supplied. */ -+ len = 0; -+ } -+ -+ if (cacheablep) -+ *cacheablep = cacheable; - - return len + 4; - } - --static size_t add_source_addr(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *source) -+static size_t add_source_addr(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *source, int *cacheable) - { - /* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */ - - int len; - struct subnet_opt opt; - -- len = calc_subnet_opt(&opt, source); -+ len = calc_subnet_opt(&opt, source, cacheable); - return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0, 0); - } - -@@ -383,18 +398,18 @@ int check_source(struct dns_header *header, size_t plen, unsigned char *pseudohe - unsigned char *p; - int code, i, rdlen; - -- calc_len = calc_subnet_opt(&opt, peer); -- -- if (!(p = skip_name(pseudoheader, header, plen, 10))) -- return 1; -- -- p += 8; /* skip UDP length and RCODE */ -+ calc_len = calc_subnet_opt(&opt, peer, NULL); - -- GETSHORT(rdlen, p); -- if (!CHECK_LEN(header, p, plen, rdlen)) -- return 1; /* bad packet */ -- -- /* check if option there */ -+ if (!(p = skip_name(pseudoheader, header, plen, 10))) -+ return 1; -+ -+ p += 8; /* skip UDP length and RCODE */ -+ -+ GETSHORT(rdlen, p); -+ if (!CHECK_LEN(header, p, plen, rdlen)) -+ return 1; /* bad packet */ -+ -+ /* check if option there */ - for (i = 0; i + 4 < rdlen; i += len + 4) - { - GETSHORT(code, p); -@@ -412,24 +427,28 @@ int check_source(struct dns_header *header, size_t plen, unsigned char *pseudohe - return 1; - } - -+/* Set *check_subnet if we add a client subnet option, which needs to checked -+ in the reply. Set *cacheable to zero if we add an option which the answer -+ may depend on. */ - size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *limit, -- union mysockaddr *source, time_t now, int *check_subnet) -+ union mysockaddr *source, time_t now, int *check_subnet, int *cacheable) - { - *check_subnet = 0; -- -+ *cacheable = 1; -+ - if (option_bool(OPT_ADD_MAC)) -- plen = add_mac(header, plen, limit, source, now); -+ plen = add_mac(header, plen, limit, source, now, cacheable); - - if (option_bool(OPT_MAC_B64) || option_bool(OPT_MAC_HEX)) -- plen = add_dns_client(header, plen, limit, source, now); -- -+ plen = add_dns_client(header, plen, limit, source, now, cacheable); -+ - if (daemon->dns_client_id) - plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMCPEID, - (unsigned char *)daemon->dns_client_id, strlen(daemon->dns_client_id), 0, 1); - - if (option_bool(OPT_CLIENT_SUBNET)) - { -- plen = add_source_addr(header, plen, limit, source); -+ plen = add_source_addr(header, plen, limit, source, cacheable); - *check_subnet = 1; - } - -diff --git a/src/forward.c b/src/forward.c -index d9b32b3..70b84d7 100644 ---- a/src/forward.c -+++ b/src/forward.c -@@ -352,13 +352,10 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, - { - /* Query from new source, but the same query may be in progress - from another source. If so, just add this client to the -- list that will get the reply. -+ list that will get the reply.*/ - -- Note that is the EDNS client subnet option is in use, we can't do this, -- as the clients (and therefore query EDNS options) will be different -- for each query. The EDNS subnet code has checks to avoid -- attacks in this case. */ -- if (!option_bool(OPT_CLIENT_SUBNET) && (forward = lookup_frec_by_query(hash, fwd_flags))) -+ if (!option_bool(OPT_ADD_MAC) && !option_bool(OPT_MAC_B64) && -+ (forward = lookup_frec_by_query(hash, fwd_flags))) - { - /* Note whine_malloc() zeros memory. */ - if (!daemon->free_frec_src && -@@ -455,18 +452,21 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, - if (!flags && forward) - { - struct server *firstsentto = start; -- int subnet, forwarded = 0; -+ int subnet, cacheable, forwarded = 0; - size_t edns0_len; - unsigned char *pheader; - - /* If a query is retried, use the log_id for the retry when logging the answer. */ - forward->frec_src.log_id = daemon->log_id; - -- plen = add_edns0_config(header, plen, ((unsigned char *)header) + PACKETSZ, &forward->frec_src.source, now, &subnet); -+ plen = add_edns0_config(header, plen, ((unsigned char *)header) + PACKETSZ, &forward->frec_src.source, now, &subnet, &cacheable); - - if (subnet) - forward->flags |= FREC_HAS_SUBNET; -- -+ -+ if (!cacheable) -+ forward->flags |= FREC_NO_CACHE; -+ - #ifdef HAVE_DNSSEC - if (option_bool(OPT_DNSSEC_VALID) && do_dnssec) - { -@@ -650,7 +650,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server - } - } - #endif -- -+ - if ((pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign, NULL))) - { - /* Get extended RCODE. */ -@@ -1260,6 +1260,11 @@ void reply_query(int fd, int family, time_t now) - header->hb4 |= HB4_CD; - else - header->hb4 &= ~HB4_CD; -+ -+ /* Never cache answers which are contingent on the source or MAC address EDSN0 option, -+ since the cache is ignorant of such things. */ -+ if (forward->flags & FREC_NO_CACHE) -+ no_cache_dnssec = 1; - - if ((nn = process_reply(header, now, forward->sentto, (size_t)n, check_rebind, no_cache_dnssec, cache_secure, bogusanswer, - forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION, -@@ -1821,7 +1826,7 @@ unsigned char *tcp_request(int confd, time_t now, - int local_auth = 0; - #endif - int checking_disabled, do_bit, added_pheader = 0, have_pseudoheader = 0; -- int check_subnet, no_cache_dnssec = 0, cache_secure = 0, bogusanswer = 0; -+ int check_subnet, cacheable, no_cache_dnssec = 0, cache_secure = 0, bogusanswer = 0; - size_t m; - unsigned short qtype; - unsigned int gotname; -@@ -1992,7 +1997,7 @@ unsigned char *tcp_request(int confd, time_t now, - char *domain = NULL; - unsigned char *oph = find_pseudoheader(header, size, NULL, NULL, NULL, NULL); - -- size = add_edns0_config(header, size, ((unsigned char *) header) + 65536, &peer_addr, now, &check_subnet); -+ size = add_edns0_config(header, size, ((unsigned char *) header) + 65536, &peer_addr, now, &check_subnet, &cacheable); - - if (gotname) - flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind); -@@ -2171,6 +2176,11 @@ unsigned char *tcp_request(int confd, time_t now, - break; - } - -+ /* Never cache answers which are contingent on the source or MAC address EDSN0 option, -+ since the cache is ignorant of such things. */ -+ if (!cacheable) -+ no_cache_dnssec = 1; -+ - m = process_reply(header, now, last_server, (unsigned int)m, - option_bool(OPT_NO_REBIND) && !norebind, no_cache_dnssec, cache_secure, bogusanswer, - ad_reqd, do_bit, added_pheader, check_subnet, &peer_addr); -@@ -2435,10 +2445,13 @@ static struct frec *lookup_frec_by_query(void *hash, unsigned int flags) - struct frec *f; - - /* FREC_DNSKEY and FREC_DS_QUERY are never set in flags, so the test below -- ensures that no frec created for internal DNSSEC query can be returned here. */ -+ ensures that no frec created for internal DNSSEC query can be returned here. -+ -+ Similarly FREC_NO_CACHE is never set in flags, so a query which is -+ contigent on a particular source address EDNS0 option will never be matched. */ - - #define FLAGMASK (FREC_CHECKING_DISABLED | FREC_AD_QUESTION | FREC_DO_QUESTION \ -- | FREC_HAS_PHEADER | FREC_DNSKEY_QUERY | FREC_DS_QUERY) -+ | FREC_HAS_PHEADER | FREC_DNSKEY_QUERY | FREC_DS_QUERY | FREC_NO_CACHE) - - for(f = daemon->frec_list; f; f = f->next) - if (f->sentto && --- -1.8.3.1 - diff --git a/backport-0002-Fix-to-75e2f0aec33e58ef5b8d4d107d821c215a52827c.patch b/backport-0002-Fix-to-75e2f0aec33e58ef5b8d4d107d821c215a52827c.patch deleted file mode 100644 index 3a233ff..0000000 --- a/backport-0002-Fix-to-75e2f0aec33e58ef5b8d4d107d821c215a52827c.patch +++ /dev/null @@ -1,24 +0,0 @@ -From 12af2b171de0d678d98583e2190789e544440e02 Mon Sep 17 00:00:00 2001 -From: Simon Kelley -Date: Fri, 22 Jan 2021 18:24:03 +0000 -Subject: [PATCH] Fix to 75e2f0aec33e58ef5b8d4d107d821c215a52827c - ---- - src/forward.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/src/forward.c b/src/forward.c -index 43d0ae7..1def931 100644 ---- a/src/forward.c -+++ b/src/forward.c -@@ -378,6 +378,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, - new->dest = *dst_addr; - new->log_id = daemon->log_id; - new->iface = dst_iface; -+ forward->frec_src.fd = udpfd; - } - - return 1; --- -1.8.3.1 - diff --git a/backport-0003-Fix-for-12af2b171de0d678d98583e2190789e544440e02.patch b/backport-0003-Fix-for-12af2b171de0d678d98583e2190789e544440e02.patch deleted file mode 100644 index f5fb4cd..0000000 --- a/backport-0003-Fix-for-12af2b171de0d678d98583e2190789e544440e02.patch +++ /dev/null @@ -1,25 +0,0 @@ -From 3f535da79e7a42104543ef5c7b5fa2bed819a78b Mon Sep 17 00:00:00 2001 -From: Simon Kelley -Date: Fri, 22 Jan 2021 22:26:25 +0000 -Subject: [PATCH] Fix for 12af2b171de0d678d98583e2190789e544440e02 - ---- - src/forward.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/forward.c b/src/forward.c -index 1def931..5c9cbbb 100644 ---- a/src/forward.c -+++ b/src/forward.c -@@ -378,7 +378,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, - new->dest = *dst_addr; - new->log_id = daemon->log_id; - new->iface = dst_iface; -- forward->frec_src.fd = udpfd; -+ new->fd = udpfd; - } - - return 1; --- -1.8.3.1 - diff --git a/backport-0004-Fix-problem-with-DNS-retries-in-2.83-2.84.patch b/backport-0004-Fix-problem-with-DNS-retries-in-2.83-2.84.patch deleted file mode 100644 index d2b29df..0000000 --- a/backport-0004-Fix-problem-with-DNS-retries-in-2.83-2.84.patch +++ /dev/null @@ -1,126 +0,0 @@ -From 141a26f979b4bc959d8e866a295e24f8cf456920 Mon Sep 17 00:00:00 2001 -From: Simon Kelley -Date: Wed, 17 Feb 2021 23:56:32 +0000 -Subject: [PATCH] Fix problem with DNS retries in 2.83/2.84. - -The new logic in 2.83/2.84 which merges distinct requests for the -same domain causes problems with clients which do retries as distinct -requests (differing IDs and/or source ports.) The retries just get -piggy-backed on the first, failed, request. - -The logic is now changed so that distinct requests for repeated -queries still get merged into a single ID/source port, but they now -always trigger a re-try upstream. - -Thanks to Nicholas Mu for his analysis. ---- - src/forward.c | 79 ++++++++++++++++++++++++++++++++--------------------------- - 1 file changed, 43 insertions(+), 36 deletions(-) - -diff --git a/src/forward.c b/src/forward.c -index 8fb0327..e82e14a 100644 ---- a/src/forward.c -+++ b/src/forward.c -@@ -278,8 +278,46 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, - fwd_flags |= FREC_DO_QUESTION; - #endif - -- /* may be no servers available. */ -- if (forward || (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, hash))) -+ /* Check for retry on existing query from same source */ -+ if (!forward && (!(forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, hash)))) -+ { -+ /* Maybe query from new source, but the same query may be in progress -+ from another source. If so, just add this client to the -+ list that will get the reply.*/ -+ -+ if (!option_bool(OPT_ADD_MAC) && !option_bool(OPT_MAC_B64) && -+ (forward = lookup_frec_by_query(hash, fwd_flags))) -+ { -+ struct frec_src *new; -+ -+ /* Note whine_malloc() zeros memory. */ -+ if (!daemon->free_frec_src && -+ daemon->frec_src_count < daemon->ftabsize && -+ (daemon->free_frec_src = whine_malloc(sizeof(struct frec_src)))) -+ { -+ daemon->frec_src_count++; -+ daemon->free_frec_src->next = NULL; -+ } -+ -+ /* If we've been spammed with many duplicates, just drop the query. */ -+ if (!daemon->free_frec_src) -+ return 0; -+ -+ new = daemon->free_frec_src; -+ daemon->free_frec_src = new->next; -+ new->next = forward->frec_src.next; -+ forward->frec_src.next = new; -+ new->orig_id = ntohs(header->id); -+ new->source = *udpaddr; -+ new->dest = *dst_addr; -+ new->log_id = daemon->log_id; -+ new->iface = dst_iface; -+ new->fd = udpfd; -+ } -+ } -+ -+ /* retry existing query */ -+ if (forward) - { - /* If we didn't get an answer advertising a maximal packet in EDNS, - fall back to 1280, which should work everywhere on IPv6. -@@ -350,40 +388,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, - } - else - { -- /* Query from new source, but the same query may be in progress -- from another source. If so, just add this client to the -- list that will get the reply.*/ -- -- if (!option_bool(OPT_ADD_MAC) && !option_bool(OPT_MAC_B64) && -- (forward = lookup_frec_by_query(hash, fwd_flags))) -- { -- /* Note whine_malloc() zeros memory. */ -- if (!daemon->free_frec_src && -- daemon->frec_src_count < daemon->ftabsize && -- (daemon->free_frec_src = whine_malloc(sizeof(struct frec_src)))) -- { -- daemon->frec_src_count++; -- daemon->free_frec_src->next = NULL; -- } -- -- /* If we've been spammed with many duplicates, just drop the query. */ -- if (daemon->free_frec_src) -- { -- struct frec_src *new = daemon->free_frec_src; -- daemon->free_frec_src = new->next; -- new->next = forward->frec_src.next; -- forward->frec_src.next = new; -- new->orig_id = ntohs(header->id); -- new->source = *udpaddr; -- new->dest = *dst_addr; -- new->log_id = daemon->log_id; -- new->iface = dst_iface; -- new->fd = udpfd; -- } -- -- return 1; -- } -- -+ /* new query */ -+ - if (gotname) - flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind); - -@@ -392,6 +398,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, - #endif - type &= ~SERV_DO_DNSSEC; - -+ /* may be no servers available. */ - if (daemon->servers && !flags) - forward = get_new_frec(now, NULL, NULL); - /* table full - flags == 0, return REFUSED */ --- -1.8.3.1 - diff --git a/backport-0005-Simplify-preceding-fix.patch b/backport-0005-Simplify-preceding-fix.patch deleted file mode 100644 index 5f65aa0..0000000 --- a/backport-0005-Simplify-preceding-fix.patch +++ /dev/null @@ -1,114 +0,0 @@ -From 305cb79c5754d5554729b18a2c06fe7ce699687a Mon Sep 17 00:00:00 2001 -From: Simon Kelley -Date: Thu, 18 Feb 2021 21:35:09 +0000 -Subject: [PATCH] Simplify preceding fix. - -Remove distinction between retry with same QID/SP and -retry for same query with different QID/SP. If the -QID/SP are the same as an existing one, simply retry, -if a new QID/SP is seen, add to the list to be replied to. ---- - src/forward.c | 64 ++++++++++++++++++++--------------------------------------- - 1 file changed, 22 insertions(+), 42 deletions(-) - -diff --git a/src/forward.c b/src/forward.c -index e82e14a..6bbf8a4 100644 ---- a/src/forward.c -+++ b/src/forward.c -@@ -17,9 +17,6 @@ - #include "dnsmasq.h" - - static struct frec *lookup_frec(unsigned short id, int fd, int family, void *hash); --static struct frec *lookup_frec_by_sender(unsigned short id, -- union mysockaddr *addr, -- void *hash); - static struct frec *lookup_frec_by_query(void *hash, unsigned int flags); - - static unsigned short get_id(void); -@@ -278,18 +275,20 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, - fwd_flags |= FREC_DO_QUESTION; - #endif - -- /* Check for retry on existing query from same source */ -- if (!forward && (!(forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, hash)))) -+ /* Check for retry on existing query */ -+ if (!forward && (forward = lookup_frec_by_query(hash, fwd_flags))) - { -- /* Maybe query from new source, but the same query may be in progress -- from another source. If so, just add this client to the -- list that will get the reply.*/ -- -- if (!option_bool(OPT_ADD_MAC) && !option_bool(OPT_MAC_B64) && -- (forward = lookup_frec_by_query(hash, fwd_flags))) -- { -- struct frec_src *new; -+ struct frec_src *src; -+ -+ for (src = &forward->frec_src; src; src = src->next) -+ if (src->orig_id == ntohs(header->id) && -+ sockaddr_isequal(&src->source, udpaddr)) -+ break; - -+ /* Existing query, but from new source, just add this -+ client to the list that will get the reply.*/ -+ if (!src) -+ { - /* Note whine_malloc() zeros memory. */ - if (!daemon->free_frec_src && - daemon->frec_src_count < daemon->ftabsize && -@@ -303,16 +302,16 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, - if (!daemon->free_frec_src) - return 0; - -- new = daemon->free_frec_src; -- daemon->free_frec_src = new->next; -- new->next = forward->frec_src.next; -- forward->frec_src.next = new; -- new->orig_id = ntohs(header->id); -- new->source = *udpaddr; -- new->dest = *dst_addr; -- new->log_id = daemon->log_id; -- new->iface = dst_iface; -- new->fd = udpfd; -+ src = daemon->free_frec_src; -+ daemon->free_frec_src = src->next; -+ src->next = forward->frec_src.next; -+ forward->frec_src.next = src; -+ src->orig_id = ntohs(header->id); -+ src->source = *udpaddr; -+ src->dest = *dst_addr; -+ src->log_id = daemon->log_id; -+ src->iface = dst_iface; -+ src->fd = udpfd; - } - } - -@@ -2433,25 +2432,6 @@ static struct frec *lookup_frec(unsigned short id, int fd, int family, void *has - return NULL; - } - --static struct frec *lookup_frec_by_sender(unsigned short id, -- union mysockaddr *addr, -- void *hash) --{ -- struct frec *f; -- struct frec_src *src; -- -- for (f = daemon->frec_list; f; f = f->next) -- if (f->sentto && -- !(f->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY)) && -- memcmp(hash, f->hash, HASH_SIZE) == 0) -- for (src = &f->frec_src; src; src = src->next) -- if (src->orig_id == id && -- sockaddr_isequal(&src->source, addr)) -- return f; -- -- return NULL; --} -- - static struct frec *lookup_frec_by_query(void *hash, unsigned int flags) - { - struct frec *f; --- -1.8.3.1 - diff --git a/backport-0006-Update-to-new-struct-frec-fields-in-conntrack-code.patch b/backport-0006-Update-to-new-struct-frec-fields-in-conntrack-code.patch deleted file mode 100644 index fe02f16..0000000 --- a/backport-0006-Update-to-new-struct-frec-fields-in-conntrack-code.patch +++ /dev/null @@ -1,34 +0,0 @@ -From cc0b4489c782f6b90ca118abb18e716a7a831289 Mon Sep 17 00:00:00 2001 -From: Simon Kelley -Date: Fri, 15 Jan 2021 22:21:52 +0000 -Subject: [PATCH] Update to new struct frec fields in conntrack code. - ---- - src/forward.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/src/forward.c b/src/forward.c -index f94c4cf..7a95ddf 100644 ---- a/src/forward.c -+++ b/src/forward.c -@@ -538,7 +538,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, - if (option_bool(OPT_CONNTRACK)) - { - unsigned int mark; -- if (get_incoming_mark(&forward->source, &forward->dest, 0, &mark)) -+ if (get_incoming_mark(&forward->frec_src.source, &forward->frec_src.dest, 0, &mark)) - setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int)); - } - #endif -@@ -1193,7 +1193,7 @@ void reply_query(int fd, int family, time_t now) - if (option_bool(OPT_CONNTRACK)) - { - unsigned int mark; -- if (get_incoming_mark(&orig->source, &orig->dest, 0, &mark)) -+ if (get_incoming_mark(&orig->frec_src.source, &orig->frec_src.dest, 0, &mark)) - setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int)); - } - #endif --- -1.8.3.1 - diff --git a/backport-0007-Use-the-values-of-min-port-and-max-port-in-TCP-conne.patch b/backport-0007-Use-the-values-of-min-port-and-max-port-in-TCP-conne.patch deleted file mode 100644 index a24a23c..0000000 --- a/backport-0007-Use-the-values-of-min-port-and-max-port-in-TCP-conne.patch +++ /dev/null @@ -1,84 +0,0 @@ -From a2a7e040b128a8ec369ba8f22beca2705435b85b Mon Sep 17 00:00:00 2001 -From: Simon Kelley -Date: Sat, 12 Dec 2020 23:26:45 +0000 -Subject: [PATCH] Use the values of --min-port and --max-port in TCP - connections. - -Rather that letting the kernel pick source ports, do it ourselves -so that the --min-port and --max-port parameters are be obeyed. ---- - CHANGELOG | 5 +++++ - src/network.c | 37 +++++++++++++++++++++++++++++++++---- - 2 files changed, 38 insertions(+), 4 deletions(-) - -diff --git a/CHANGELOG b/CHANGELOG -index e6a2231..34ad22e 100644 ---- a/CHANGELOG -+++ b/CHANGELOG -@@ -1,3 +1,8 @@ -+version 2.83 -+ Use the values of --min-port and --max-port in outgoing -+ TCP connections to upstream DNS servers. -+ -+ - version 2.82 - Improve behaviour in the face of network interfaces which come - and go and change index. Thanks to Petr Mensik for the patch. -diff --git a/src/network.c b/src/network.c -index c7d002b..7cf2546 100644 ---- a/src/network.c -+++ b/src/network.c -@@ -1262,17 +1262,46 @@ int random_sock(int family) - int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifindex, int is_tcp) - { - union mysockaddr addr_copy = *addr; -+ unsigned short port; -+ int tries = 1, done = 0; -+ unsigned int ports_avail = ((unsigned short)daemon->max_port - (unsigned short)daemon->min_port) + 1; -+ -+ if (addr_copy.sa.sa_family == AF_INET) -+ port = addr_copy.in.sin_port; -+ else -+ port = addr_copy.in6.sin6_port; - - /* cannot set source _port_ for TCP connections. */ - if (is_tcp) -+ port = 0; -+ -+ /* Bind a random port within the range given by min-port and max-port */ -+ if (port == 0) -+ { -+ tries = ports_avail < 30 ? 3 * ports_avail : 100; -+ port = htons(daemon->min_port + (rand16() % ((unsigned short)ports_avail))); -+ } -+ -+ while (tries--) - { - if (addr_copy.sa.sa_family == AF_INET) -- addr_copy.in.sin_port = 0; -+ addr_copy.in.sin_port = port; - else -- addr_copy.in6.sin6_port = 0; -+ addr_copy.in6.sin6_port = port; -+ -+ if (bind(fd, (struct sockaddr *)&addr_copy, sa_len(&addr_copy)) != -1) -+ { -+ done = 1; -+ break; -+ } -+ -+ if (errno != EADDRINUSE && errno != EACCES) -+ return 0; -+ -+ port = htons(daemon->min_port + (rand16() % ((unsigned short)ports_avail))); - } -- -- if (bind(fd, (struct sockaddr *)&addr_copy, sa_len(&addr_copy)) == -1) -+ -+ if (!done) - return 0; - - if (!is_tcp && ifindex > 0) --- -1.8.3.1 - diff --git a/backport-0008-Correct-occasional-bind-dynamic-synchronization-brea.patch b/backport-0008-Correct-occasional-bind-dynamic-synchronization-brea.patch deleted file mode 100644 index 4aca129..0000000 --- a/backport-0008-Correct-occasional-bind-dynamic-synchronization-brea.patch +++ /dev/null @@ -1,252 +0,0 @@ -From 4c0aecc68524c5fd74053244a605e905dc644228 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= -Date: Tue, 2 Mar 2021 18:21:32 +0000 -Subject: [PATCH] Correct occasional --bind-dynamic synchronization break - -Request only one re-read of addresses and/or routes - -Previous implementation re-reads systemd addresses exactly the same -number of time equal number of notifications received. -This is not necessary, we need just notification of change, then re-read -the current state and adapt listeners. Repeated re-reading slows netlink -processing and highers CPU usage on mass interface changes. - -Continue reading multicast events from netlink, even when ENOBUFS -arrive. Broadcasts are not trusted anyway and refresh would be done in -iface_enumerate. Save queued events sent again. - -Remove sleeping on netlink ENOBUFS - -With reduced number of written events netlink should receive ENOBUFS -rarely. It does not make sense to wait if it is received. It is just a -signal some packets got missing. Fast reading all pending packets is required, -seq checking ensures it already. Finishes changes by -commit 1d07667ac77c55b9de56b1b2c385167e0e0ec27a. - -Move restart from iface_enumerate to enumerate_interfaces - -When ENOBUFS is received, restart of reading addresses is done. But -previously found addresses might not have been found this time. In order -to catch this, restart both IPv4 and IPv6 enumeration with clearing -found interfaces first. It should deliver up-to-date state also after -ENOBUFS. - -Read all netlink messages before netlink restart - -Before writing again into netlink socket, try fetching all pending -messages. They would be ignored, only might trigger new address -synchronization. Should ensure new try has better chance to succeed. - -ENOBUFS error handling was improved. Netlink is correctly drained before -sending a new request again. It seems ENOBUFS supression is no longer -necessary or wanted. Let kernel tell us when it failed and handle it a -good way. ---- - src/netlink.c | 72 +++++++++++++++++++++++++++++++++++++++-------------------- - src/network.c | 14 ++++++++---- - 2 files changed, 58 insertions(+), 28 deletions(-) - -diff --git a/src/netlink.c b/src/netlink.c -index 0494070..3ad18a6 100644 ---- a/src/netlink.c -+++ b/src/netlink.c -@@ -41,19 +41,26 @@ - - #ifndef NDA_RTA - # define NDA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg)))) --#endif -+#endif -+ -+/* Used to request refresh of addresses or routes just once, -+ * when multiple changes might be announced. */ -+enum async_states { -+ STATE_NEWADDR = (1 << 0), -+ STATE_NEWROUTE = (1 << 1), -+}; - - - static struct iovec iov; - static u32 netlink_pid; - --static void nl_async(struct nlmsghdr *h); -+static unsigned nl_async(struct nlmsghdr *h, unsigned state); -+static void nl_multicast_state(unsigned state); - - char *netlink_init(void) - { - struct sockaddr_nl addr; - socklen_t slen = sizeof(addr); -- int opt = 1; - - addr.nl_family = AF_NETLINK; - addr.nl_pad = 0; -@@ -92,10 +99,6 @@ char *netlink_init(void) - iov.iov_len = 100; - iov.iov_base = safe_malloc(iov.iov_len); - -- if (daemon->kernel_version >= KERNEL_VERSION(2,6,30) && -- setsockopt(daemon->netlinkfd, SOL_NETLINK, NETLINK_NO_ENOBUFS, &opt, sizeof(opt)) == -1) -- return _("warning: failed to set NETLINK_NO_ENOBUFS on netlink socket"); -- - return NULL; - } - -@@ -151,7 +154,9 @@ static ssize_t netlink_recv(void) - - - /* family = AF_UNSPEC finds ARP table entries. -- family = AF_LOCAL finds MAC addresses. */ -+ family = AF_LOCAL finds MAC addresses. -+ returns 0 on failure, 1 on success, -1 when restart is required -+*/ - int iface_enumerate(int family, void *parm, int (*callback)()) - { - struct sockaddr_nl addr; -@@ -159,6 +164,7 @@ int iface_enumerate(int family, void *parm, int (*callback)()) - ssize_t len; - static unsigned int seq = 0; - int callback_ok = 1; -+ unsigned state = 0; - - struct { - struct nlmsghdr nlh; -@@ -170,7 +176,6 @@ int iface_enumerate(int family, void *parm, int (*callback)()) - - addr.nl_family = AF_NETLINK; - -- again: - if (family == AF_UNSPEC) - req.nlh.nlmsg_type = RTM_GETNEIGH; - else if (family == AF_LOCAL) -@@ -197,8 +202,8 @@ int iface_enumerate(int family, void *parm, int (*callback)()) - { - if (errno == ENOBUFS) - { -- sleep(1); -- goto again; -+ nl_multicast_state(state); -+ return -1; - } - return 0; - } -@@ -207,7 +212,7 @@ int iface_enumerate(int family, void *parm, int (*callback)()) - if (h->nlmsg_pid != netlink_pid || h->nlmsg_type == NLMSG_ERROR) - { - /* May be multicast arriving async */ -- nl_async(h); -+ state = nl_async(h, state); - } - else if (h->nlmsg_seq != seq) - { -@@ -341,26 +346,36 @@ int iface_enumerate(int family, void *parm, int (*callback)()) - } - } - --void netlink_multicast(void) -+static void nl_multicast_state(unsigned state) - { - ssize_t len; - struct nlmsghdr *h; - int flags; -- -- /* don't risk blocking reading netlink messages here. */ -+ - if ((flags = fcntl(daemon->netlinkfd, F_GETFL)) == -1 || - fcntl(daemon->netlinkfd, F_SETFL, flags | O_NONBLOCK) == -1) - return; -+ -+ do { -+ /* don't risk blocking reading netlink messages here. */ -+ while ((len = netlink_recv()) != -1) - -- if ((len = netlink_recv()) != -1) -- for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len)) -- nl_async(h); -- -+ for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len)) -+ state = nl_async(h, state); -+ } while (errno == ENOBUFS); -+ - /* restore non-blocking status */ - fcntl(daemon->netlinkfd, F_SETFL, flags); - } - --static void nl_async(struct nlmsghdr *h) -+void netlink_multicast(void) -+{ -+ unsigned state = 0; -+ nl_multicast_state(state); -+} -+ -+ -+static unsigned nl_async(struct nlmsghdr *h, unsigned state) - { - if (h->nlmsg_type == NLMSG_ERROR) - { -@@ -368,7 +383,8 @@ static void nl_async(struct nlmsghdr *h) - if (err->error != 0) - my_syslog(LOG_ERR, _("netlink returns error: %s"), strerror(-(err->error))); - } -- else if (h->nlmsg_pid == 0 && h->nlmsg_type == RTM_NEWROUTE) -+ else if (h->nlmsg_pid == 0 && h->nlmsg_type == RTM_NEWROUTE && -+ (state & STATE_NEWROUTE)==0) - { - /* We arrange to receive netlink multicast messages whenever the network route is added. - If this happens and we still have a DNS packet in the buffer, we re-send it. -@@ -380,10 +396,18 @@ static void nl_async(struct nlmsghdr *h) - if (rtm->rtm_type == RTN_UNICAST && rtm->rtm_scope == RT_SCOPE_LINK && - (rtm->rtm_table == RT_TABLE_MAIN || - rtm->rtm_table == RT_TABLE_LOCAL)) -- queue_event(EVENT_NEWROUTE); -+ { -+ queue_event(EVENT_NEWROUTE); -+ state |= STATE_NEWROUTE; -+ } -+ } -+ else if ((h->nlmsg_type == RTM_NEWADDR || h->nlmsg_type == RTM_DELADDR) && -+ (state & STATE_NEWADDR)==0) -+ { -+ queue_event(EVENT_NEWADDR); -+ state |= STATE_NEWADDR; - } -- else if (h->nlmsg_type == RTM_NEWADDR || h->nlmsg_type == RTM_DELADDR) -- queue_event(EVENT_NEWADDR); -+ return state; - } - #endif - -diff --git a/src/network.c b/src/network.c -index 6cf15a9..77eb6ad 100644 ---- a/src/network.c -+++ b/src/network.c -@@ -638,7 +638,8 @@ int enumerate_interfaces(int reset) - - if ((param.fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) - return 0; -- -+ -+again: - /* Mark interfaces for garbage collection */ - for (iface = daemon->interfaces; iface; iface = iface->next) - iface->found = 0; -@@ -690,9 +691,14 @@ int enumerate_interfaces(int reset) - param.spare = spare; - - ret = iface_enumerate(AF_INET6, ¶m, iface_allowed_v6); -- -- if (ret) -- ret = iface_enumerate(AF_INET, ¶m, iface_allowed_v4); -+ if (ret < 0) -+ goto again; -+ else if (ret) -+ { -+ ret = iface_enumerate(AF_INET, ¶m, iface_allowed_v4); -+ if (ret < 0) -+ goto again; -+ } - - errsave = errno; - close(param.fd); --- -1.8.3.1 - diff --git a/backport-0009-Move-fd-into-frec_src-fixes-15b60ddf935a531269bb8c68.patch b/backport-0009-Move-fd-into-frec_src-fixes-15b60ddf935a531269bb8c68.patch deleted file mode 100644 index fac271d..0000000 --- a/backport-0009-Move-fd-into-frec_src-fixes-15b60ddf935a531269bb8c68.patch +++ /dev/null @@ -1,65 +0,0 @@ -From 04490bf622ac84891aad6f2dd2edf83725decdee Mon Sep 17 00:00:00 2001 -From: Simon Kelley -Date: Fri, 22 Jan 2021 16:49:12 +0000 -Subject: [PATCH] Move fd into frec_src, fixes - 15b60ddf935a531269bb8c68198de012a4967156 - -If identical queries from IPv4 and IPv6 sources are combined by the -new code added in 15b60ddf935a531269bb8c68198de012a4967156 then replies -can end up being sent via the wrong family of socket. The ->fd -should be per query, not per-question. - -In bind-interfaces mode, this could also result in replies being sent -via the wrong socket even when IPv4/IPV6 issues are not in play. ---- - src/dnsmasq.h | 3 ++- - src/forward.c | 4 ++-- - 2 files changed, 4 insertions(+), 3 deletions(-) - -diff --git a/src/dnsmasq.h b/src/dnsmasq.h -index 914f469..360c226 100644 ---- a/src/dnsmasq.h -+++ b/src/dnsmasq.h -@@ -664,6 +664,7 @@ struct frec { - union mysockaddr source; - union all_addr dest; - unsigned int iface, log_id; -+ int fd; - unsigned short orig_id; - struct frec_src *next; - } frec_src; -@@ -671,7 +672,7 @@ struct frec { - struct randfd *rfd4; - struct randfd *rfd6; - unsigned short new_id; -- int fd, forwardall, flags; -+ int forwardall, flags; - time_t time; - unsigned char *hash[HASH_SIZE]; - #ifdef HAVE_DNSSEC -diff --git a/src/forward.c b/src/forward.c -index 7a95ddf..43d0ae7 100644 ---- a/src/forward.c -+++ b/src/forward.c -@@ -402,8 +402,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, - forward->frec_src.dest = *dst_addr; - forward->frec_src.iface = dst_iface; - forward->frec_src.next = NULL; -+ forward->frec_src.fd = udpfd; - forward->new_id = get_id(); -- forward->fd = udpfd; - memcpy(forward->hash, hash, HASH_SIZE); - forward->forwardall = 0; - forward->flags = fwd_flags; -@@ -1300,7 +1300,7 @@ void reply_query(int fd, int family, time_t now) - dump_packet(DUMP_REPLY, daemon->packet, (size_t)nn, NULL, &src->source); - #endif - -- send_from(forward->fd, option_bool(OPT_NOWILD) || option_bool (OPT_CLEVERBIND), daemon->packet, nn, -+ send_from(src->fd, option_bool(OPT_NOWILD) || option_bool (OPT_CLEVERBIND), daemon->packet, nn, - &src->source, &src->dest, src->iface); - - if (option_bool(OPT_EXTRALOG) && src != &forward->frec_src) --- -1.8.3.1 - diff --git a/backport-0010-CVE-2021-3448.patch b/backport-0010-CVE-2021-3448.patch deleted file mode 100644 index 00b1d6e..0000000 --- a/backport-0010-CVE-2021-3448.patch +++ /dev/null @@ -1,1098 +0,0 @@ -From 74d4fcd756a85bc1823232ea74334f7ccfb9d5d2 Mon Sep 17 00:00:00 2001 -From: Simon Kelley -Date: Mon, 15 Mar 2021 21:59:51 +0000 -Subject: [PATCH] Use random source ports where possible if source - addresses/interfaces in use. - -CVE-2021-3448 applies. - -It's possible to specify the source address or interface to be -used when contacting upstream nameservers: server=8.8.8.8@1.2.3.4 -or server=8.8.8.8@1.2.3.4#66 or server=8.8.8.8@eth0, and all of -these have, until now, used a single socket, bound to a fixed -port. This was originally done to allow an error (non-existent -interface, or non-local address) to be detected at start-up. This -means that any upstream servers specified in such a way don't use -random source ports, and are more susceptible to cache-poisoning -attacks. - -We now use random ports where possible, even when the -source is specified, so server=8.8.8.8@1.2.3.4 or -server=8.8.8.8@eth0 will use random source -ports. server=8.8.8.8@1.2.3.4#66 or any use of --query-port will -use the explicitly configured port, and should only be done with -understanding of the security implications. -Note that this change changes non-existing interface, or non-local -source address errors from fatal to run-time. The error will be -logged and communiction with the server not possible. ---- - man/dnsmasq.8 | 4 +- - src/dnsmasq.c | 31 +++-- - src/dnsmasq.h | 26 ++-- - src/forward.c | 392 +++++++++++++++++++++++++++++++++++----------------------- - src/loop.c | 20 ++- - src/network.c | 110 +++++----------- - src/option.c | 3 +- - src/tftp.c | 6 +- - src/util.c | 2 +- - 10 files changed, 344 insertions(+), 272 deletions(-) - -diff --git a/man/dnsmasq.8 b/man/dnsmasq.8 -index 6dee5a4..4910bd2 100644 ---- a/man/dnsmasq.8 -+++ b/man/dnsmasq.8 -@@ -431,7 +431,7 @@ Tells dnsmasq to never forward A or AAAA queries for plain names, without dots - or domain parts, to upstream nameservers. If the name is not known - from /etc/hosts or DHCP then a "not found" answer is returned. - .TP --.B \-S, --local, --server=[/[]/[domain/]][[#]][@|[#]] -+.B \-S, --local, --server=[/[]/[domain/]][[#]][@][@[#]] - Specify IP address of upstream servers directly. Setting this flag does - not suppress reading of /etc/resolv.conf, use \fB--no-resolv\fP to do that. If one or more - optional domains are given, that server is used only for those domains -@@ -492,7 +492,7 @@ source address specified but the port may be specified directly as - part of the source address. Forcing queries to an interface is not - implemented on all platforms supported by dnsmasq. - .TP --.B --rev-server=/[,][#][@|[#]] -+.B --rev-server=/[,][#][@][@[#]] - This is functionally the same as - .B --server, - but provides some syntactic sugar to make specifying address-to-name queries easier. For example -diff --git a/src/dnsmasq.c b/src/dnsmasq.c -index a2e1796..d1806fc 100644 ---- a/src/dnsmasq.c -+++ b/src/dnsmasq.c -@@ -1672,6 +1672,7 @@ static int set_dns_listeners(time_t now) - { - struct serverfd *serverfdp; - struct listener *listener; -+ struct randfd_list *rfl; - int wait = 0, i; - - #ifdef HAVE_TFTP -@@ -1692,11 +1693,14 @@ static int set_dns_listeners(time_t now) - for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next) - poll_listen(serverfdp->fd, POLLIN); - -- if (daemon->port != 0 && !daemon->osport) -- for (i = 0; i < RANDOM_SOCKS; i++) -- if (daemon->randomsocks[i].refcount != 0) -- poll_listen(daemon->randomsocks[i].fd, POLLIN); -- -+ for (i = 0; i < RANDOM_SOCKS; i++) -+ if (daemon->randomsocks[i].refcount != 0) -+ poll_listen(daemon->randomsocks[i].fd, POLLIN); -+ -+ /* Check overflow random sockets too. */ -+ for (rfl = daemon->rfl_poll; rfl; rfl = rfl->next) -+ poll_listen(rfl->rfd->fd, POLLIN); -+ - for (listener = daemon->listeners; listener; listener = listener->next) - { - /* only listen for queries if we have resources */ -@@ -1733,18 +1737,23 @@ static void check_dns_listeners(time_t now) - { - struct serverfd *serverfdp; - struct listener *listener; -+ struct randfd_list *rfl; - int i; - int pipefd[2]; - - for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next) - if (poll_check(serverfdp->fd, POLLIN)) -- reply_query(serverfdp->fd, serverfdp->source_addr.sa.sa_family, now); -+ reply_query(serverfdp->fd, now); - -- if (daemon->port != 0 && !daemon->osport) -- for (i = 0; i < RANDOM_SOCKS; i++) -- if (daemon->randomsocks[i].refcount != 0 && -- poll_check(daemon->randomsocks[i].fd, POLLIN)) -- reply_query(daemon->randomsocks[i].fd, daemon->randomsocks[i].family, now); -+ for (i = 0; i < RANDOM_SOCKS; i++) -+ if (daemon->randomsocks[i].refcount != 0 && -+ poll_check(daemon->randomsocks[i].fd, POLLIN)) -+ reply_query(daemon->randomsocks[i].fd, now); -+ -+ /* Check overflow random sockets too. */ -+ for (rfl = daemon->rfl_poll; rfl; rfl = rfl->next) -+ if (poll_check(rfl->rfd->fd, POLLIN)) -+ reply_query(rfl->rfd->fd, now); - - /* Races. The child process can die before we read all of the data from the - pipe, or vice versa. Therefore send tcp_pids to zero when we wait() the -diff --git a/src/dnsmasq.h b/src/dnsmasq.h -index e201b7a..b0080d0 100644 ---- a/src/dnsmasq.h -+++ b/src/dnsmasq.h -@@ -549,13 +549,20 @@ struct serverfd { - }; - - struct randfd { -+ struct server *serv; - int fd; -- unsigned short refcount, family; -+ unsigned short refcount; /* refcount == 0xffff means overflow record. */ - }; -- -+ -+struct randfd_list { -+ struct randfd *rfd; -+ struct randfd_list *next; -+}; -+ - struct server { - union mysockaddr addr, source_addr; - char interface[IF_NAMESIZE+1]; -+ unsigned int ifindex; /* corresponding to interface, above */ - struct serverfd *sfd; - char *domain; /* set if this server only handles a domain. */ - int flags, tcpfd, edns_pktsz; -@@ -679,8 +686,7 @@ struct frec { - struct frec_src *next; - } frec_src; - struct server *sentto; /* NULL means free */ -- struct randfd *rfd4; -- struct randfd *rfd6; -+ struct randfd_list *rfds; - unsigned short new_id; - int forwardall, flags; - time_t time; -@@ -1120,11 +1126,12 @@ extern struct daemon { - int forwardcount; - struct server *srv_save; /* Used for resend on DoD */ - size_t packet_len; /* " " */ -- struct randfd *rfd_save; /* " " */ -+ int fd_save; /* " " */ - pid_t tcp_pids[MAX_PROCS]; - int tcp_pipes[MAX_PROCS]; - int pipe_to_parent; - struct randfd randomsocks[RANDOM_SOCKS]; -+ struct randfd_list *rfl_spare, *rfl_poll; - int v6pktinfo; - struct addrlist *interface_addrs; /* list of all addresses/prefix lengths associated with all local interfaces */ - int log_id, log_display_id; /* ids of transactions for logging */ -@@ -1296,7 +1303,7 @@ void safe_strncpy(char *dest, const char *src, size_t size); - void safe_pipe(int *fd, int read_noblock); - void *whine_malloc(size_t size); - int sa_len(union mysockaddr *addr); --int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2); -+int sockaddr_isequal(const union mysockaddr *s1, const union mysockaddr *s2); - int hostname_isequal(const char *a, const char *b); - int hostname_issubdomain(char *a, char *b); - time_t dnsmasq_time(void); -@@ -1347,7 +1354,7 @@ char *parse_server(char *arg, union mysockaddr *addr, - int option_read_dynfile(char *file, int flags); - - /* forward.c */ --void reply_query(int fd, int family, time_t now); -+void reply_query(int fd, time_t now); - void receive_query(struct listener *listen, time_t now); - unsigned char *tcp_request(int confd, time_t now, - union mysockaddr *local_addr, struct in_addr netmask, int auth_dns); -@@ -1357,13 +1364,12 @@ int send_from(int fd, int nowild, char *packet, size_t len, - union mysockaddr *to, union all_addr *source, - unsigned int iface); - void resend_query(void); --struct randfd *allocate_rfd(int family); --void free_rfd(struct randfd *rfd); -+int allocate_rfd(struct randfd_list **fdlp, struct server *serv); -+void free_rfds(struct randfd_list **fdlp); - - /* network.c */ - int indextoname(int fd, int index, char *name); - int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifindex, int is_tcp); --int random_sock(int family); - void pre_allocate_sfds(void); - int reload_servers(char *fname); - void mark_servers(int flag); -diff --git a/src/forward.c b/src/forward.c -index 6bbf8a4..a15ac10 100644 ---- a/src/forward.c -+++ b/src/forward.c -@@ -16,7 +16,7 @@ - - #include "dnsmasq.h" - --static struct frec *lookup_frec(unsigned short id, int fd, int family, void *hash); -+static struct frec *lookup_frec(unsigned short id, int fd, void *hash); - static struct frec *lookup_frec_by_query(void *hash, unsigned int flags); - - static unsigned short get_id(void); -@@ -344,26 +344,18 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, - if (find_pseudoheader(header, plen, NULL, &pheader, &is_sign, NULL) && !is_sign) - PUTSHORT(SAFE_PKTSZ, pheader); - -- if (forward->sentto->addr.sa.sa_family == AF_INET) -- log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, "retry", (union all_addr *)&forward->sentto->addr.in.sin_addr, "dnssec"); -- else -- log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, "retry", (union all_addr *)&forward->sentto->addr.in6.sin6_addr, "dnssec"); -- -- -- if (forward->sentto->sfd) -- fd = forward->sentto->sfd->fd; -- else -+ if ((fd = allocate_rfd(&forward->rfds, forward->sentto)) != -1) - { -- if (forward->sentto->addr.sa.sa_family == AF_INET6) -- fd = forward->rfd6->fd; -+ if (forward->sentto->addr.sa.sa_family == AF_INET) -+ log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, "retry", (union all_addr *)&forward->sentto->addr.in.sin_addr, "dnssec"); - else -- fd = forward->rfd4->fd; -+ log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, "retry", (union all_addr *)&forward->sentto->addr.in6.sin6_addr, "dnssec"); -+ -+ while (retry_send(sendto(fd, (char *)header, plen, 0, -+ &forward->sentto->addr.sa, -+ sa_len(&forward->sentto->addr)))); - } - -- while (retry_send(sendto(fd, (char *)header, plen, 0, -- &forward->sentto->addr.sa, -- sa_len(&forward->sentto->addr)))); -- - return 1; - } - #endif -@@ -508,48 +500,27 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, - - while (1) - { -+ int fd; -+ - /* only send to servers dealing with our domain. - domain may be NULL, in which case server->domain - must be NULL also. */ - - if (type == (start->flags & SERV_TYPE) && - (type != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)) && -- !(start->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP))) -+ !(start->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP)) && -+ ((fd = allocate_rfd(&forward->rfds, start)) != -1)) - { -- int fd; -- -- /* find server socket to use, may need to get random one. */ -- if (start->sfd) -- fd = start->sfd->fd; -- else -- { -- if (start->addr.sa.sa_family == AF_INET6) -- { -- if (!forward->rfd6 && -- !(forward->rfd6 = allocate_rfd(AF_INET6))) -- break; -- daemon->rfd_save = forward->rfd6; -- fd = forward->rfd6->fd; -- } -- else -- { -- if (!forward->rfd4 && -- !(forward->rfd4 = allocate_rfd(AF_INET))) -- break; -- daemon->rfd_save = forward->rfd4; -- fd = forward->rfd4->fd; -- } -- -+ - #ifdef HAVE_CONNTRACK -- /* Copy connection mark of incoming query to outgoing connection. */ -- if (option_bool(OPT_CONNTRACK)) -- { -- unsigned int mark; -- if (get_incoming_mark(&forward->frec_src.source, &forward->frec_src.dest, 0, &mark)) -- setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int)); -- } --#endif -+ /* Copy connection mark of incoming query to outgoing connection. */ -+ if (option_bool(OPT_CONNTRACK)) -+ { -+ unsigned int mark; -+ if (get_incoming_mark(&forward->frec_src.source, &forward->frec_src.dest, 0, &mark)) -+ setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int)); - } -+#endif - - #ifdef HAVE_DNSSEC - if (option_bool(OPT_DNSSEC_VALID) && (forward->flags & FREC_ADDED_PHEADER)) -@@ -581,6 +552,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, - /* Keep info in case we want to re-send this packet */ - daemon->srv_save = start; - daemon->packet_len = plen; -+ daemon->fd_save = fd; - - if (!gotname) - strcpy(daemon->namebuff, "query"); -@@ -597,7 +569,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, - break; - forward->forwardall++; - } -- } -+ } - - if (!(start = start->next)) - start = daemon->servers; -@@ -812,7 +784,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server - } - - /* sets new last_server */ --void reply_query(int fd, int family, time_t now) -+void reply_query(int fd, time_t now) - { - /* packet from peer server, extract data for cache, and send to - original requester */ -@@ -827,9 +799,9 @@ void reply_query(int fd, int family, time_t now) - - /* packet buffer overwritten */ - daemon->srv_save = NULL; -- -+ - /* Determine the address of the server replying so that we can mark that as good */ -- if ((serveraddr.sa.sa_family = family) == AF_INET6) -+ if (serveraddr.sa.sa_family == AF_INET6) - serveraddr.in6.sin6_flowinfo = 0; - - header = (struct dns_header *)daemon->packet; -@@ -852,7 +824,7 @@ void reply_query(int fd, int family, time_t now) - - hash = hash_questions(header, n, daemon->namebuff); - -- if (!(forward = lookup_frec(ntohs(header->id), fd, family, hash))) -+ if (!(forward = lookup_frec(ntohs(header->id), fd, hash))) - return; - - #ifdef HAVE_DUMPFILE -@@ -906,28 +878,7 @@ void reply_query(int fd, int family, time_t now) - } - - -- fd = -1; -- -- if (start->sfd) -- fd = start->sfd->fd; -- else -- { -- if (start->addr.sa.sa_family == AF_INET6) -- { -- /* may have changed family */ -- if (forward->rfd6 || (forward->rfd6 = allocate_rfd(AF_INET6))) -- fd = forward->rfd6->fd; -- } -- else -- { -- /* may have changed family */ -- if (forward->rfd4 || (forward->rfd4 = allocate_rfd(AF_INET))) -- fd = forward->rfd4->fd; -- } -- } -- -- /* Can't get socket. */ -- if (fd == -1) -+ if ((fd = allocate_rfd(&forward->rfds, start)) == -1) - return; - - #ifdef HAVE_DUMPFILE -@@ -1136,8 +1087,7 @@ void reply_query(int fd, int family, time_t now) - } - - new->sentto = server; -- new->rfd4 = NULL; -- new->rfd6 = NULL; -+ new->rfds = NULL; - new->frec_src.next = NULL; - new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY | FREC_HAS_EXTRADATA); - new->forwardall = 0; -@@ -1176,24 +1126,7 @@ void reply_query(int fd, int family, time_t now) - /* Don't resend this. */ - daemon->srv_save = NULL; - -- if (server->sfd) -- fd = server->sfd->fd; -- else -- { -- fd = -1; -- if (server->addr.sa.sa_family == AF_INET6) -- { -- if (new->rfd6 || (new->rfd6 = allocate_rfd(AF_INET6))) -- fd = new->rfd6->fd; -- } -- else -- { -- if (new->rfd4 || (new->rfd4 = allocate_rfd(AF_INET))) -- fd = new->rfd4->fd; -- } -- } -- -- if (fd != -1) -+ if ((fd = allocate_rfd(&new->rfds, server)) != -1) - { - #ifdef HAVE_CONNTRACK - /* Copy connection mark of incoming query to outgoing connection. */ -@@ -1360,7 +1293,7 @@ void receive_query(struct listener *listen, time_t now) - - /* packet buffer overwritten */ - daemon->srv_save = NULL; -- -+ - dst_addr_4.s_addr = dst_addr.addr4.s_addr = 0; - netmask.s_addr = 0; - -@@ -2228,9 +2161,8 @@ static struct frec *allocate_frec(time_t now) - f->next = daemon->frec_list; - f->time = now; - f->sentto = NULL; -- f->rfd4 = NULL; -+ f->rfds = NULL; - f->flags = 0; -- f->rfd6 = NULL; - #ifdef HAVE_DNSSEC - f->dependent = NULL; - f->blocking_query = NULL; -@@ -2242,46 +2174,192 @@ static struct frec *allocate_frec(time_t now) - return f; - } - --struct randfd *allocate_rfd(int family) -+/* return a UDP socket bound to a random port, have to cope with straying into -+ occupied port nos and reserved ones. */ -+static int random_sock(struct server *s) - { -- static int finger = 0; -- int i; -+ int fd; -+ -+ if ((fd = socket(s->source_addr.sa.sa_family, SOCK_DGRAM, 0)) != -1) -+ { -+ if (local_bind(fd, &s->source_addr, s->interface, s->ifindex, 0)) -+ return fd; - -+ if (s->interface[0] == 0) -+ (void)prettyprint_addr(&s->source_addr, daemon->namebuff); -+ else -+ strcpy(daemon->namebuff, s->interface); -+ -+ my_syslog(LOG_ERR, _("failed to bind server socket to %s: %s"), -+ daemon->namebuff, strerror(errno)); -+ close(fd); -+ } -+ -+ return -1; -+} -+ -+/* compare source addresses and interface, serv2 can be null. */ -+static int server_isequal(const struct server *serv1, -+ const struct server *serv2) -+{ -+ return (serv2 && -+ serv2->ifindex == serv1->ifindex && -+ sockaddr_isequal(&serv2->source_addr, &serv1->source_addr) && -+ strncmp(serv2->interface, serv1->interface, IF_NAMESIZE) == 0); -+} -+ -+/* fdlp points to chain of randomfds already in use by transaction. -+ If there's already a suitable one, return it, else allocate a -+ new one and add it to the list. -+ -+ Not leaking any resources in the face of allocation failures -+ is rather convoluted here. -+ -+ Note that rfd->serv may be NULL, when a server goes away. -+*/ -+int allocate_rfd(struct randfd_list **fdlp, struct server *serv) -+{ -+ static int finger = 0; -+ int i, j = 0; -+ struct randfd_list *rfl; -+ struct randfd *rfd = NULL; -+ int fd = 0; -+ -+ /* If server has a pre-allocated fd, use that. */ -+ if (serv->sfd) -+ return serv->sfd->fd; -+ -+ /* existing suitable random port socket linked to this transaction? */ -+ for (rfl = *fdlp; rfl; rfl = rfl->next) -+ if (server_isequal(serv, rfl->rfd->serv)) -+ return rfl->rfd->fd; -+ -+ /* No. need new link. */ -+ if ((rfl = daemon->rfl_spare)) -+ daemon->rfl_spare = rfl->next; -+ else if (!(rfl = whine_malloc(sizeof(struct randfd_list)))) -+ return -1; -+ - /* limit the number of sockets we have open to avoid starvation of - (eg) TFTP. Once we have a reasonable number, randomness should be OK */ -- - for (i = 0; i < RANDOM_SOCKS; i++) - if (daemon->randomsocks[i].refcount == 0) - { -- if ((daemon->randomsocks[i].fd = random_sock(family)) == -1) -- break; -- -- daemon->randomsocks[i].refcount = 1; -- daemon->randomsocks[i].family = family; -- return &daemon->randomsocks[i]; -+ if ((fd = random_sock(serv)) != -1) -+ { -+ rfd = &daemon->randomsocks[i]; -+ rfd->serv = serv; -+ rfd->fd = fd; -+ rfd->refcount = 1; -+ } -+ break; - } -- -+ - /* No free ones or cannot get new socket, grab an existing one */ -- for (i = 0; i < RANDOM_SOCKS; i++) -+ if (!rfd) -+ for (j = 0; j < RANDOM_SOCKS; j++) -+ { -+ i = (j + finger) % RANDOM_SOCKS; -+ if (daemon->randomsocks[i].refcount != 0 && -+ server_isequal(serv, daemon->randomsocks[i].serv) && -+ daemon->randomsocks[i].refcount != 0xfffe) -+ { -+ finger = i + 1; -+ rfd = &daemon->randomsocks[i]; -+ rfd->refcount++; -+ break; -+ } -+ } -+ -+ if (j == RANDOM_SOCKS) - { -- int j = (i+finger) % RANDOM_SOCKS; -- if (daemon->randomsocks[j].refcount != 0 && -- daemon->randomsocks[j].family == family && -- daemon->randomsocks[j].refcount != 0xffff) -+ struct randfd_list *rfl_poll; -+ -+ /* there are no free slots, and non with the same parameters we can piggy-back on. -+ We're going to have to allocate a new temporary record, distinguished by -+ refcount == 0xffff. This will exist in the frec randfd list, never be shared, -+ and be freed when no longer in use. It will also be held on -+ the daemon->rfl_poll list so the poll system can find it. */ -+ -+ if ((rfl_poll = daemon->rfl_spare)) -+ daemon->rfl_spare = rfl_poll->next; -+ else -+ rfl_poll = whine_malloc(sizeof(struct randfd_list)); -+ -+ if (!rfl_poll || -+ !(rfd = whine_malloc(sizeof(struct randfd))) || -+ (fd = random_sock(serv)) == -1) - { -- finger = j; -- daemon->randomsocks[j].refcount++; -- return &daemon->randomsocks[j]; -+ -+ /* Don't leak anything we may already have */ -+ rfl->next = daemon->rfl_spare; -+ daemon->rfl_spare = rfl; -+ -+ if (rfl_poll) -+ { -+ rfl_poll->next = daemon->rfl_spare; -+ daemon->rfl_spare = rfl_poll; -+ } -+ -+ if (rfd) -+ free(rfd); -+ -+ return -1; /* doom */ - } -- } - -- return NULL; /* doom */ -+ /* Note rfd->serv not set here, since it's not reused */ -+ rfd->fd = fd; -+ rfd->refcount = 0xffff; /* marker for temp record */ -+ -+ rfl_poll->rfd = rfd; -+ rfl_poll->next = daemon->rfl_poll; -+ daemon->rfl_poll = rfl_poll; -+ } -+ -+ rfl->rfd = rfd; -+ rfl->next = *fdlp; -+ *fdlp = rfl; -+ -+ return rfl->rfd->fd; - } - --void free_rfd(struct randfd *rfd) -+void free_rfds(struct randfd_list **fdlp) - { -- if (rfd && --(rfd->refcount) == 0) -- close(rfd->fd); -+ struct randfd_list *tmp, *rfl, *poll, *next, **up; -+ -+ for (rfl = *fdlp; rfl; rfl = tmp) -+ { -+ if (rfl->rfd->refcount == 0xffff || --(rfl->rfd->refcount) == 0) -+ close(rfl->rfd->fd); -+ -+ /* temporary overflow record */ -+ if (rfl->rfd->refcount == 0xffff) -+ { -+ free(rfl->rfd); -+ -+ /* go through the link of all these by steam to delete. -+ This list is expected to be almost always empty. */ -+ for (poll = daemon->rfl_poll, up = &daemon->rfl_poll; poll; poll = next) -+ { -+ next = poll->next; -+ -+ if (poll->rfd == rfl->rfd) -+ { -+ *up = poll->next; -+ poll->next = daemon->rfl_spare; -+ daemon->rfl_spare = poll; -+ } -+ else -+ up = &poll->next; -+ } -+ } -+ -+ tmp = rfl->next; -+ rfl->next = daemon->rfl_spare; -+ daemon->rfl_spare = rfl; -+ } -+ -+ *fdlp = NULL; - } - - static void free_frec(struct frec *f) -@@ -2297,12 +2375,9 @@ static void free_frec(struct frec *f) - } - - f->frec_src.next = NULL; -- free_rfd(f->rfd4); -- f->rfd4 = NULL; -+ free_rfds(&f->rfds); - f->sentto = NULL; - f->flags = 0; -- free_rfd(f->rfd6); -- f->rfd6 = NULL; - - #ifdef HAVE_DNSSEC - if (f->stash) -@@ -2409,26 +2484,39 @@ struct frec *get_new_frec(time_t now, int *wait, struct frec *force) - return f; /* OK if malloc fails and this is NULL */ - } - --static struct frec *lookup_frec(unsigned short id, int fd, int family, void *hash) -+static struct frec *lookup_frec(unsigned short id, int fd, void *hash) - { - struct frec *f; -- -+ struct server *s; -+ int type; -+ struct randfd_list *fdl; -+ - for(f = daemon->frec_list; f; f = f->next) - if (f->sentto && f->new_id == id && - (memcmp(hash, f->hash, HASH_SIZE) == 0)) - { - /* sent from random port */ -- if (family == AF_INET && f->rfd4 && f->rfd4->fd == fd) -- return f; -- -- if (family == AF_INET6 && f->rfd6 && f->rfd6->fd == fd) -- return f; -- -- /* sent to upstream from bound socket. */ -- if (f->sentto->sfd && f->sentto->sfd->fd == fd) -+ for (fdl = f->rfds; fdl; fdl = fdl->next) -+ if (fdl->rfd->fd == fd) - return f; -+ -+ /* Sent to upstream from socket associated with a server. -+ Note we have to iterate over all the possible servers, since they may -+ have different bound sockets. */ -+ type = f->sentto->flags & SERV_TYPE; -+ s = f->sentto; -+ do { -+ if ((type == (s->flags & SERV_TYPE)) && -+ (type != SERV_HAS_DOMAIN || -+ (s->domain && hostname_isequal(f->sentto->domain, s->domain))) && -+ !(s->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP)) && -+ s->sfd && s->sfd->fd == fd) -+ return f; -+ -+ s = s->next ? s->next : daemon->servers; -+ } while (s != f->sentto); - } -- -+ - return NULL; - } - -@@ -2458,34 +2546,30 @@ static struct frec *lookup_frec_by_query(void *hash, unsigned int flags) - void resend_query() - { - if (daemon->srv_save) -- { -- int fd; -- -- if (daemon->srv_save->sfd) -- fd = daemon->srv_save->sfd->fd; -- else if (daemon->rfd_save && daemon->rfd_save->refcount != 0) -- fd = daemon->rfd_save->fd; -- else -- return; -- -- while(retry_send(sendto(fd, daemon->packet, daemon->packet_len, 0, -- &daemon->srv_save->addr.sa, -- sa_len(&daemon->srv_save->addr)))); -- } -+ while(retry_send(sendto(daemon->fd_save, daemon->packet, daemon->packet_len, 0, -+ &daemon->srv_save->addr.sa, -+ sa_len(&daemon->srv_save->addr)))); - } - - /* A server record is going away, remove references to it */ - void server_gone(struct server *server) - { - struct frec *f; -+ int i; - - for (f = daemon->frec_list; f; f = f->next) - if (f->sentto && f->sentto == server) - free_frec(f); -+ -+ /* If any random socket refers to this server, NULL the reference. -+ No more references to the socket will be created in the future. */ -+ for (i = 0; i < RANDOM_SOCKS; i++) -+ if (daemon->randomsocks[i].refcount != 0 && daemon->randomsocks[i].serv == server) -+ daemon->randomsocks[i].serv = NULL; - - if (daemon->last_server == server) - daemon->last_server = NULL; -- -+ - if (daemon->srv_save == server) - daemon->srv_save = NULL; - } -diff --git a/src/loop.c b/src/loop.c -index 15e34c7..758674b 100644 ---- a/src/loop.c -+++ b/src/loop.c -@@ -22,6 +22,7 @@ static ssize_t loop_make_probe(u32 uid); - void loop_send_probes() - { - struct server *serv; -+ struct randfd_list *rfds = NULL; - - if (!option_bool(OPT_LOOP_DETECT)) - return; -@@ -34,29 +35,22 @@ void loop_send_probes() - { - ssize_t len = loop_make_probe(serv->uid); - int fd; -- struct randfd *rfd = NULL; - -- if (serv->sfd) -- fd = serv->sfd->fd; -- else -- { -- if (!(rfd = allocate_rfd(serv->addr.sa.sa_family))) -- continue; -- fd = rfd->fd; -- } -- -+ if ((fd = allocate_rfd(&rfds, serv)) == -1) -+ continue; -+ - while (retry_send(sendto(fd, daemon->packet, len, 0, - &serv->addr.sa, sa_len(&serv->addr)))); -- -- free_rfd(rfd); - } -+ -+ free_rfds(&rfds); - } - - static ssize_t loop_make_probe(u32 uid) - { - struct dns_header *header = (struct dns_header *)daemon->packet; - unsigned char *p = (unsigned char *)(header+1); -- -+ - /* packet buffer overwritten */ - daemon->srv_save = NULL; - -diff --git a/src/network.c b/src/network.c -index 5286c61..cca6ff2 100644 ---- a/src/network.c -+++ b/src/network.c -@@ -696,7 +696,8 @@ int enumerate_interfaces(int reset) - #ifdef HAVE_AUTH - struct auth_zone *zone; - #endif -- -+ struct server *serv; -+ - /* Do this max once per select cycle - also inhibits netlink socket use - in TCP child processes. */ - -@@ -714,6 +715,20 @@ int enumerate_interfaces(int reset) - if ((param.fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) - return 0; - -+ /* iface indexes can change when interfaces are created/destroyed. -+ We use them in the main forwarding control path, when the path -+ to a server is specified by an interface, so cache them. -+ Update the cache here. */ -+ for (serv = daemon->servers; serv; serv = serv->next) -+ if (strlen(serv->interface) != 0) -+ { -+ struct ifreq ifr; -+ -+ safe_strncpy(ifr.ifr_name, serv->interface, IF_NAMESIZE); -+ if (ioctl(param.fd, SIOCGIFINDEX, &ifr) != -1) -+ serv->ifindex = ifr.ifr_ifindex; -+ } -+ - again: - /* Mark interfaces for garbage collection */ - for (iface = daemon->interfaces; iface; iface = iface->next) -@@ -808,7 +823,7 @@ again: - - errno = errsave; - spare = param.spare; -- -+ - return ret; - } - -@@ -948,10 +963,10 @@ int tcp_interface(int fd, int af) - /* use mshdr so that the CMSDG_* macros are available */ - msg.msg_control = daemon->packet; - msg.msg_controllen = len = daemon->packet_buff_sz; -- -+ - /* we overwrote the buffer... */ -- daemon->srv_save = NULL; -- -+ daemon->srv_save = NULL; -+ - if (af == AF_INET) - { - if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt)) != -1 && -@@ -1287,59 +1302,6 @@ void join_multicast(int dienow) - } - #endif - --/* return a UDP socket bound to a random port, have to cope with straying into -- occupied port nos and reserved ones. */ --int random_sock(int family) --{ -- int fd; -- -- if ((fd = socket(family, SOCK_DGRAM, 0)) != -1) -- { -- union mysockaddr addr; -- unsigned int ports_avail = ((unsigned short)daemon->max_port - (unsigned short)daemon->min_port) + 1; -- int tries = ports_avail < 30 ? 3 * ports_avail : 100; -- -- memset(&addr, 0, sizeof(addr)); -- addr.sa.sa_family = family; -- -- /* don't loop forever if all ports in use. */ -- -- if (fix_fd(fd)) -- while(tries--) -- { -- unsigned short port = htons(daemon->min_port + (rand16() % ((unsigned short)ports_avail))); -- -- if (family == AF_INET) -- { -- addr.in.sin_addr.s_addr = INADDR_ANY; -- addr.in.sin_port = port; --#ifdef HAVE_SOCKADDR_SA_LEN -- addr.in.sin_len = sizeof(struct sockaddr_in); --#endif -- } -- else -- { -- addr.in6.sin6_addr = in6addr_any; -- addr.in6.sin6_port = port; --#ifdef HAVE_SOCKADDR_SA_LEN -- addr.in6.sin6_len = sizeof(struct sockaddr_in6); --#endif -- } -- -- if (bind(fd, (struct sockaddr *)&addr, sa_len(&addr)) == 0) -- return fd; -- -- if (errno != EADDRINUSE && errno != EACCES) -- break; -- } -- -- close(fd); -- } -- -- return -1; --} -- -- - int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifindex, int is_tcp) - { - union mysockaddr addr_copy = *addr; -@@ -1355,10 +1317,9 @@ int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifind - /* cannot set source _port_ for TCP connections. */ - if (is_tcp) - port = 0; -- -- /* Bind a random port within the range given by min-port and max-port */ -- if (port == 0) -+ else if (port == 0) - { -+ /* Bind a random port within the range given by min-port and max-port */ - tries = ports_avail < 30 ? 3 * ports_avail : 100; - port = htons(daemon->min_port + (rand16() % ((unsigned short)ports_avail))); - } -@@ -1413,38 +1374,33 @@ int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifind - return 1; - } - --static struct serverfd *allocate_sfd(union mysockaddr *addr, char *intname) -+static struct serverfd *allocate_sfd(union mysockaddr *addr, char *intname, unsigned int ifindex) - { - struct serverfd *sfd; -- unsigned int ifindex = 0; - int errsave; - int opt = 1; - - /* when using random ports, servers which would otherwise use -- the INADDR_ANY/port0 socket have sfd set to NULL */ -- if (!daemon->osport && intname[0] == 0) -+ the INADDR_ANY/port0 socket have sfd set to NULL, this is -+ anything without an explictly set source port. */ -+ if (!daemon->osport) - { - errno = 0; - - if (addr->sa.sa_family == AF_INET && -- addr->in.sin_addr.s_addr == INADDR_ANY && - addr->in.sin_port == htons(0)) - return NULL; - - if (addr->sa.sa_family == AF_INET6 && -- memcmp(&addr->in6.sin6_addr, &in6addr_any, sizeof(in6addr_any)) == 0 && - addr->in6.sin6_port == htons(0)) - return NULL; - } - -- if (intname && strlen(intname) != 0) -- ifindex = if_nametoindex(intname); /* index == 0 when not binding to an interface */ -- - /* may have a suitable one already */ - for (sfd = daemon->sfds; sfd; sfd = sfd->next ) -- if (sockaddr_isequal(&sfd->source_addr, addr) && -- strcmp(intname, sfd->interface) == 0 && -- ifindex == sfd->ifindex) -+ if (ifindex == sfd->ifindex && -+ sockaddr_isequal(&sfd->source_addr, addr) && -+ strcmp(intname, sfd->interface) == 0) - return sfd; - - /* need to make a new one. */ -@@ -1495,7 +1451,7 @@ void pre_allocate_sfds(void) - #ifdef HAVE_SOCKADDR_SA_LEN - addr.in.sin_len = sizeof(struct sockaddr_in); - #endif -- if ((sfd = allocate_sfd(&addr, ""))) -+ if ((sfd = allocate_sfd(&addr, "", 0))) - sfd->preallocated = 1; - - memset(&addr, 0, sizeof(addr)); -@@ -1505,13 +1461,13 @@ void pre_allocate_sfds(void) - #ifdef HAVE_SOCKADDR_SA_LEN - addr.in6.sin6_len = sizeof(struct sockaddr_in6); - #endif -- if ((sfd = allocate_sfd(&addr, ""))) -+ if ((sfd = allocate_sfd(&addr, "", 0))) - sfd->preallocated = 1; - } - - for (srv = daemon->servers; srv; srv = srv->next) - if (!(srv->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND)) && -- !allocate_sfd(&srv->source_addr, srv->interface) && -+ !allocate_sfd(&srv->source_addr, srv->interface, srv->ifindex) && - errno != 0 && - option_bool(OPT_NOWILD)) - { -@@ -1720,7 +1676,7 @@ void check_servers(void) - - /* Do we need a socket set? */ - if (!serv->sfd && -- !(serv->sfd = allocate_sfd(&serv->source_addr, serv->interface)) && -+ !(serv->sfd = allocate_sfd(&serv->source_addr, serv->interface, serv->ifindex)) && - errno != 0) - { - my_syslog(LOG_WARNING, -diff --git a/src/option.c b/src/option.c -index bfda212..159d1d4 100644 ---- a/src/option.c -+++ b/src/option.c -@@ -819,7 +819,8 @@ char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_a - if (interface_opt) - { - #if defined(SO_BINDTODEVICE) -- safe_strncpy(interface, interface_opt, IF_NAMESIZE); -+ safe_strncpy(interface, source, IF_NAMESIZE); -+ source = interface_opt; - #else - return _("interface binding not supported"); - #endif -diff --git a/src/tftp.c b/src/tftp.c -index 846b32e..ea2aa84 100644 ---- a/src/tftp.c -+++ b/src/tftp.c -@@ -94,7 +94,7 @@ void tftp_request(struct listener *listen, time_t now) - - if ((len = recvmsg(listen->tftpfd, &msg, 0)) < 2) - return; -- -+ - /* Can always get recvd interface for IPv6 */ - if (!check_dest) - { -@@ -587,7 +587,7 @@ void check_tftp_listeners(time_t now) - daemon->srv_save = NULL; - handle_tftp(now, transfer, recv(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0)); - } -- -+ - for (transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; transfer = tmp) - { - tmp = transfer->next; -@@ -602,7 +602,7 @@ void check_tftp_listeners(time_t now) - - /* we overwrote the buffer... */ - daemon->srv_save = NULL; -- -+ - if ((len = get_block(daemon->packet, transfer)) == -1) - { - len = tftp_err_oops(daemon->packet, transfer->file->filename); -diff --git a/src/util.c b/src/util.c -index 972c60b..6176dc9 100644 ---- a/src/util.c -+++ b/src/util.c -@@ -316,7 +316,7 @@ void *whine_malloc(size_t size) - return ret; - } - --int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2) -+int sockaddr_isequal(const union mysockaddr *s1, const union mysockaddr *s2) - { - if (s1->sa.sa_family == s2->sa.sa_family) - { --- -1.8.3.1 - diff --git a/backport-Add-missing-check-for-NULL-return-from-allocate_rfd.patch b/backport-Add-missing-check-for-NULL-return-from-allocate_rfd.patch deleted file mode 100644 index 9de07a4..0000000 --- a/backport-Add-missing-check-for-NULL-return-from-allocate_rfd.patch +++ /dev/null @@ -1,71 +0,0 @@ -From 824461192ca5098043f9ca4ddeba7df1f65b30ba Mon Sep 17 00:00:00 2001 -From: Simon Kelley -Date: Sun, 15 Nov 2020 22:13:25 +0000 -Subject: [PATCH] Add missing check for NULL return from allocate_rfd(). - -Conflict:NA -Reference:https://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=commit;h=824461192ca5098043f9ca4ddeba7df1f65b30ba ---- - src/forward.c | 18 ++++++++++-------- - 1 file changed, 10 insertions(+), 8 deletions(-) - -diff --git a/src/forward.c b/src/forward.c -index 4f9a963..50da095 100644 ---- a/src/forward.c -+++ b/src/forward.c -@@ -823,7 +823,6 @@ void reply_query(int fd, int family, time_t now) - int is_sign; - - #ifdef HAVE_DNSSEC -- /* For DNSSEC originated queries, just retry the query to the same server. */ - if (forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY)) - { - struct server *start; -@@ -849,6 +848,8 @@ void reply_query(int fd, int family, time_t now) - } - - -+ fd = -1; -+ - if (start->sfd) - fd = start->sfd->fd; - else -@@ -856,19 +857,21 @@ void reply_query(int fd, int family, time_t now) - if (start->addr.sa.sa_family == AF_INET6) - { - /* may have changed family */ -- if (!forward->rfd6) -- forward->rfd6 = allocate_rfd(AF_INET6); -- fd = forward->rfd6->fd; -+ if (forward->rfd6 || (forward->rfd6 = allocate_rfd(AF_INET6))) -+ fd = forward->rfd6->fd; - } - else - { - /* may have changed family */ -- if (!forward->rfd4) -- forward->rfd4 = allocate_rfd(AF_INET); -- fd = forward->rfd4->fd; -+ if (forward->rfd4 || (forward->rfd4 = allocate_rfd(AF_INET))) -+ fd = forward->rfd4->fd; - } - } - -+ /* Can't get socket. */ -+ if (fd == -1) -+ return; -+ - #ifdef HAVE_DUMPFILE - dump_packet(DUMP_SEC_QUERY, (void *)header, (size_t)plen, NULL, &start->addr); - #endif -@@ -2311,7 +2314,6 @@ struct frec *get_new_frec(time_t now, int *wait, struct frec *force) - return f; /* OK if malloc fails and this is NULL */ - } - --/* crc is all-ones if not known. */ - static struct frec *lookup_frec(unsigned short id, int fd, int family, void *hash) - { - struct frec *f; --- -2.23.0 - diff --git a/backport-CVE-2020-25681_CVE-2020-25682_CVE-2020-25683_CVE-2020-25687.patch b/backport-CVE-2020-25681_CVE-2020-25682_CVE-2020-25683_CVE-2020-25687.patch deleted file mode 100644 index dbd96a8..0000000 --- a/backport-CVE-2020-25681_CVE-2020-25682_CVE-2020-25683_CVE-2020-25687.patch +++ /dev/null @@ -1,364 +0,0 @@ -From 4e96a4be685c9e4445f6ee79ad0b36b9119b502a Mon Sep 17 00:00:00 2001 -From: Simon Kelley -Date: Wed, 11 Nov 2020 23:25:04 +0000 -Subject: [PATCH] Fix remote buffer overflow CERT VU#434904 - -The problem is in the sort_rrset() function and allows a remote -attacker to overwrite memory. Any dnsmasq instance with DNSSEC -enabled is vulnerable. ---- - src/dnssec.c | 273 +++++++++++++++++++++++++++++++++-------------------------- - 1 files changed, 151 insertions(+), 122 deletions(-) - -diff --git a/src/dnssec.c b/src/dnssec.c -index db5c2d1..e95aa34 100644 ---- a/src/dnssec.c -+++ b/src/dnssec.c -@@ -223,138 +223,147 @@ static int check_date_range(unsigned long curtime, u32 date_start, u32 date_end) - && serial_compare_32(curtime, date_end) == SERIAL_LT; - } - --/* Return bytes of canonicalised rdata, when the return value is zero, the remaining -- data, pointed to by *p, should be used raw. */ --static int get_rdata(struct dns_header *header, size_t plen, unsigned char *end, char *buff, int bufflen, -- unsigned char **p, u16 **desc) -+/* Return bytes of canonicalised rrdata one by one. -+ Init state->ip with the RR, and state->end with the end of same. -+ Init state->op to NULL. -+ Init state->desc to RR descriptor. -+ Init state->buff with a MAXDNAME * 2 buffer. -+ -+ After each call which returns 1, state->op points to the next byte of data. -+ On returning 0, the end has been reached. -+*/ -+struct rdata_state { -+ u16 *desc; -+ size_t c; -+ unsigned char *end, *ip, *op; -+ char *buff; -+}; -+ -+static int get_rdata(struct dns_header *header, size_t plen, struct rdata_state *state) - { -- int d = **desc; -+ int d; - -- /* No more data needs mangling */ -- if (d == (u16)-1) -+ if (state->op && state->c != 1) - { -- /* If there's more data than we have space for, just return what fits, -- we'll get called again for more chunks */ -- if (end - *p > bufflen) -- { -- memcpy(buff, *p, bufflen); -- *p += bufflen; -- return bufflen; -- } -- -- return 0; -+ state->op++; -+ state->c--; -+ return 1; - } -- -- (*desc)++; -- -- if (d == 0 && extract_name(header, plen, p, buff, 1, 0)) -- /* domain-name, canonicalise */ -- return to_wire(buff); -- else -- { -- /* plain data preceding a domain-name, don't run off the end of the data */ -- if ((end - *p) < d) -- d = end - *p; -+ -+ while (1) -+ { -+ d = *(state->desc); - -- if (d != 0) -+ if (d == (u16)-1) - { -- memcpy(buff, *p, d); -- *p += d; -+ /* all the bytes to the end. */ -+ if ((state->c = state->end - state->ip) != 0) -+ { -+ state->op = state->ip; -+ state->ip = state->end;; -+ } -+ else -+ return 0; -+ } -+ else -+ { -+ state->desc++; -+ -+ if (d == (u16)0) -+ { -+ /* domain-name, canonicalise */ -+ int len; -+ -+ if (!extract_name(header, plen, &state->ip, state->buff, 1, 0) || -+ (len = to_wire(state->buff)) == 0) -+ continue; -+ -+ state->c = len; -+ state->op = (unsigned char *)state->buff; -+ } -+ else -+ { -+ /* plain data preceding a domain-name, don't run off the end of the data */ -+ if ((state->end - state->ip) < d) -+ d = state->end - state->ip; -+ -+ if (d == 0) -+ continue; -+ -+ state->op = state->ip; -+ state->c = d; -+ state->ip += d; -+ } - } - -- return d; -+ return 1; - } - } - --/* Bubble sort the RRset into the canonical order. -- Note that the byte-streams from two RRs may get unsynced: consider -- RRs which have two domain-names at the start and then other data. -- The domain-names may have different lengths in each RR, but sort equal -- -- ------------ -- |abcde|fghi| -- ------------ -- |abcd|efghi| -- ------------ -- -- leaving the following bytes as deciding the order. Hence the nasty left1 and left2 variables. --*/ -+/* Bubble sort the RRset into the canonical order. */ - - static int sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int rrsetidx, - unsigned char **rrset, char *buff1, char *buff2) - { -- int swap, quit, i, j; -+ int swap, i, j; - - do - { - for (swap = 0, i = 0; i < rrsetidx-1; i++) - { -- int rdlen1, rdlen2, left1, left2, len1, len2, len, rc; -- u16 *dp1, *dp2; -- unsigned char *end1, *end2; -+ int rdlen1, rdlen2; -+ struct rdata_state state1, state2; -+ - /* Note that these have been determined to be OK previously, - so we don't need to check for NULL return here. */ -- unsigned char *p1 = skip_name(rrset[i], header, plen, 10); -- unsigned char *p2 = skip_name(rrset[i+1], header, plen, 10); -- -- p1 += 8; /* skip class, type, ttl */ -- GETSHORT(rdlen1, p1); -- end1 = p1 + rdlen1; -- -- p2 += 8; /* skip class, type, ttl */ -- GETSHORT(rdlen2, p2); -- end2 = p2 + rdlen2; -+ state1.ip = skip_name(rrset[i], header, plen, 10); -+ state2.ip = skip_name(rrset[i+1], header, plen, 10); -+ state1.op = state2.op = NULL; -+ state1.buff = buff1; -+ state2.buff = buff2; -+ state1.desc = state2.desc = rr_desc; - -- dp1 = dp2 = rr_desc; -+ state1.ip += 8; /* skip class, type, ttl */ -+ GETSHORT(rdlen1, state1.ip); -+ if (!CHECK_LEN(header, state1.ip, plen, rdlen1)) -+ return rrsetidx; /* short packet */ -+ state1.end = state1.ip + rdlen1; - -- for (quit = 0, left1 = 0, left2 = 0, len1 = 0, len2 = 0; !quit;) -+ state2.ip += 8; /* skip class, type, ttl */ -+ GETSHORT(rdlen2, state2.ip); -+ if (!CHECK_LEN(header, state2.ip, plen, rdlen2)) -+ return rrsetidx; /* short packet */ -+ state2.end = state2.ip + rdlen2; -+ -+ while (1) - { -- if (left1 != 0) -- memmove(buff1, buff1 + len1 - left1, left1); -+ int ok1, ok2; - -- if ((len1 = get_rdata(header, plen, end1, buff1 + left1, (MAXDNAME * 2) - left1, &p1, &dp1)) == 0) -- { -- quit = 1; -- len1 = end1 - p1; -- memcpy(buff1 + left1, p1, len1); -- } -- len1 += left1; -- -- if (left2 != 0) -- memmove(buff2, buff2 + len2 - left2, left2); -- -- if ((len2 = get_rdata(header, plen, end2, buff2 + left2, (MAXDNAME *2) - left2, &p2, &dp2)) == 0) -- { -- quit = 1; -- len2 = end2 - p2; -- memcpy(buff2 + left2, p2, len2); -- } -- len2 += left2; -- -- if (len1 > len2) -- left1 = len1 - len2, left2 = 0, len = len2; -- else -- left2 = len2 - len1, left1 = 0, len = len1; -- -- rc = (len == 0) ? 0 : memcmp(buff1, buff2, len); -- -- if (rc > 0 || (rc == 0 && quit && len1 > len2)) -- { -- unsigned char *tmp = rrset[i+1]; -- rrset[i+1] = rrset[i]; -- rrset[i] = tmp; -- swap = quit = 1; -- } -- else if (rc == 0 && quit && len1 == len2) -+ ok1 = get_rdata(header, plen, &state1); -+ ok2 = get_rdata(header, plen, &state2); -+ -+ if (!ok1 && !ok2) - { - /* Two RRs are equal, remove one copy. RFC 4034, para 6.3 */ - for (j = i+1; j < rrsetidx-1; j++) - rrset[j] = rrset[j+1]; - rrsetidx--; - i--; -+ break; -+ } -+ else if (ok1 && (!ok2 || *state1.op > *state2.op)) -+ { -+ unsigned char *tmp = rrset[i+1]; -+ rrset[i+1] = rrset[i]; -+ rrset[i] = tmp; -+ swap = 1; -+ break; - } -- else if (rc < 0) -- quit = 1; -+ else if (ok2 && (!ok1 || *state2.op > *state1.op)) -+ break; -+ -+ /* arrive here when bytes are equal, go round the loop again -+ and compare the next ones. */ - } - } - } while (swap); -@@ -569,15 +578,18 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in - wire_len = to_wire(keyname); - hash->update(ctx, (unsigned int)wire_len, (unsigned char*)keyname); - from_wire(keyname); -+ -+#define RRBUFLEN 300 /* Most RRs are smaller than this. */ - - for (i = 0; i < rrsetidx; ++i) - { -- int seg; -- unsigned char *end, *cp; -- u16 len, *dp; -+ int j; -+ struct rdata_state state; -+ u16 len; -+ unsigned char rrbuf[RRBUFLEN]; - - p = rrset[i]; -- -+ - if (!extract_name(header, plen, &p, name, 1, 10)) - return STAT_BOGUS; - -@@ -586,12 +598,11 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in - /* if more labels than in RRsig name, hash *. 4035 5.3.2 */ - if (labels < name_labels) - { -- int k; -- for (k = name_labels - labels; k != 0; k--) -+ for (j = name_labels - labels; j != 0; j--) - { - while (*name_start != '.' && *name_start != 0) - name_start++; -- if (k != 1 && *name_start == '.') -+ if (j != 1 && *name_start == '.') - name_start++; - } - -@@ -612,24 +623,44 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in - if (!CHECK_LEN(header, p, plen, rdlen)) - return STAT_BOGUS; - -- end = p + rdlen; -+ /* canonicalise rdata and calculate length of same, use -+ name buffer as workspace for get_rdata. */ -+ state.ip = p; -+ state.op = NULL; -+ state.desc = rr_desc; -+ state.buff = name; -+ state.end = p + rdlen; - -- /* canonicalise rdata and calculate length of same, use name buffer as workspace. -- Note that name buffer is twice MAXDNAME long in DNSSEC mode. */ -- cp = p; -- dp = rr_desc; -- for (len = 0; (seg = get_rdata(header, plen, end, name, MAXDNAME * 2, &cp, &dp)) != 0; len += seg); -- len += end - cp; -- len = htons(len); -+ for (j = 0; get_rdata(header, plen, &state); j++) -+ if (j < RRBUFLEN) -+ rrbuf[j] = *state.op; -+ -+ len = htons((u16)j); - hash->update(ctx, 2, (unsigned char *)&len); -+ -+ /* If the RR is shorter than RRBUFLEN (most of them, in practice) -+ then we can just digest it now. If it exceeds RRBUFLEN we have to -+ go back to the start and do it in chunks. */ -+ if (j >= RRBUFLEN) -+ { -+ state.ip = p; -+ state.op = NULL; -+ state.desc = rr_desc; -+ -+ for (j = 0; get_rdata(header, plen, &state); j++) -+ { -+ rrbuf[j] = *state.op; -+ -+ if (j == RRBUFLEN - 1) -+ { -+ hash->update(ctx, RRBUFLEN, rrbuf); -+ j = -1; -+ } -+ } -+ } - -- /* Now canonicalise again and digest. */ -- cp = p; -- dp = rr_desc; -- while ((seg = get_rdata(header, plen, end, name, MAXDNAME * 2, &cp, &dp))) -- hash->update(ctx, seg, (unsigned char *)name); -- if (cp != end) -- hash->update(ctx, end - cp, cp); -+ if (j != 0) -+ hash->update(ctx, j, rrbuf); - } - - hash->digest(ctx, hash->digest_size, digest); --- -1.8.3.1 - diff --git a/backport-CVE-2020-25684.patch b/backport-CVE-2020-25684.patch deleted file mode 100644 index 1feed1f..0000000 --- a/backport-CVE-2020-25684.patch +++ /dev/null @@ -1,94 +0,0 @@ -From 257ac0c5f7732cbc6aa96fdd3b06602234593aca Mon Sep 17 00:00:00 2001 -From: Simon Kelley -Date: Thu, 12 Nov 2020 18:49:23 +0000 -Subject: [PATCH] Check destination of DNS UDP query replies. - -At any time, dnsmasq will have a set of sockets open, bound to -random ports, on which it sends queries to upstream nameservers. -This patch fixes the existing problem that a reply for ANY in-flight -query would be accepted via ANY open port, which increases the -chances of an attacker flooding answers "in the blind" in an -attempt to poison the DNS cache. CERT VU#434904 refers. ---- - src/forward.c | 37 ++++++++++++++++++++++++++++--------- - 1 files changed, 27 insertions(+), 10 deletions(-) - -diff --git a/src/forward.c b/src/forward.c -index 9c2b2c6..134e0fc 100644 ---- a/src/forward.c -+++ b/src/forward.c -@@ -16,7 +16,7 @@ - - #include "dnsmasq.h" - --static struct frec *lookup_frec(unsigned short id, void *hash); -+static struct frec *lookup_frec(unsigned short id, int fd, int family, void *hash); - static struct frec *lookup_frec_by_sender(unsigned short id, - union mysockaddr *addr, - void *hash); -@@ -805,7 +805,7 @@ void reply_query(int fd, int family, time_t now) - crc = questions_crc(header, n, daemon->namebuff); - #endif - -- if (!(forward = lookup_frec(ntohs(header->id), hash))) -+ if (!(forward = lookup_frec(ntohs(header->id), fd, family, hash))) - return; - - #ifdef HAVE_DUMPFILE -@@ -2339,14 +2339,25 @@ struct frec *get_new_frec(time_t now, int *wait, struct frec *force) - } - - /* crc is all-ones if not known. */ --static struct frec *lookup_frec(unsigned short id, void *hash) -+static struct frec *lookup_frec(unsigned short id, int fd, int family, void *hash) - { - struct frec *f; - - for(f = daemon->frec_list; f; f = f->next) - if (f->sentto && f->new_id == id && - (!hash || memcmp(hash, f->hash, HASH_SIZE) == 0)) -- return f; -+ { -+ /* sent from random port */ -+ if (family == AF_INET && f->rfd4 && f->rfd4->fd == fd) -+ return f; -+ -+ if (family == AF_INET6 && f->rfd6 && f->rfd6->fd == fd) -+ return f; -+ -+ /* sent to upstream from bound socket. */ -+ if (f->sentto->sfd && f->sentto->sfd->fd == fd) -+ return f; -+ } - - return NULL; - } -@@ -2407,12 +2418,20 @@ void server_gone(struct server *server) - static unsigned short get_id(void) - { - unsigned short ret = 0; -+ struct frec *f; - -- do -- ret = rand16(); -- while (lookup_frec(ret, NULL)); -- -- return ret; -+ while (1) -+ { -+ ret = rand16(); -+ -+ /* ensure id is unique. */ -+ for (f = daemon->frec_list; f; f = f->next) -+ if (f->sentto && f->new_id == ret) -+ break; -+ -+ if (!f) -+ return ret; -+ } - } - - --- -1.8.3.1 - diff --git a/backport-CVE-2020-25685_1.patch b/backport-CVE-2020-25685_1.patch deleted file mode 100644 index c43a216..0000000 --- a/backport-CVE-2020-25685_1.patch +++ /dev/null @@ -1,584 +0,0 @@ -From 2d765867c597db18be9d876c9c17e2c0fe1953cd Mon Sep 17 00:00:00 2001 -From: Simon Kelley -Date: Thu, 12 Nov 2020 22:06:07 +0000 -Subject: [PATCH] Use SHA-256 to provide security against DNS cache poisoning. - -Use the SHA-256 hash function to verify that DNS answers -received are for the questions originally asked. This replaces -the slightly insecure SHA-1 (when compiled with DNSSEC) or -the very insecure CRC32 (otherwise). Refer: CERT VU#434904. ---- - Makefile | 3 +- - bld/Android.mk | 2 +- - src/dnsmasq.h | 11 +- - src/dnssec.c | 31 ------ - src/forward.c | 43 ++------ - src/hash_questions.c | 281 +++++++++++++++++++++++++++++++++++++++++++++++++++ - src/rfc1035.c | 49 --------- - 7 files changed, 296 insertions(+), 124 deletions(-) - create mode 100644 src/hash_questions.c - -diff --git a/Makefile b/Makefile -index 78e25f0..0354e0f 100644 ---- a/Makefile -+++ b/Makefile -@@ -77,7 +77,8 @@ objs = cache.o rfc1035.o util.o option.o forward.o network.o \ - helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o \ - dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o \ - domain.o dnssec.o blockdata.o tables.o loop.o inotify.o \ -- poll.o rrfilter.o edns0.o arp.o crypto.o dump.o ubus.o metrics.o -+ poll.o rrfilter.o edns0.o arp.o crypto.o dump.o ubus.o \ -+ metrics.o hash_questions.o - - hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \ - dns-protocol.h radv-protocol.h ip6addr.h metrics.h -diff --git a/bld/Android.mk b/bld/Android.mk -index 080a615..f924be9 100644 ---- a/bld/Android.mk -+++ b/bld/Android.mk -@@ -11,7 +11,7 @@ LOCAL_SRC_FILES := bpf.c cache.c dbus.c dhcp.c dnsmasq.c \ - radv.c slaac.c auth.c ipset.c domain.c \ - dnssec.c dnssec-openssl.c blockdata.c tables.c \ - loop.c inotify.c poll.c rrfilter.c edns0.c arp.c \ -- crypto.c dump.c ubus.c -+ crypto.c dump.c ubus.c metrics.c hash_questions.c - - LOCAL_MODULE := dnsmasq - -diff --git a/src/dnsmasq.h b/src/dnsmasq.h -index 4d78c37..0a7639f 100644 ---- a/src/dnsmasq.h -+++ b/src/dnsmasq.h -@@ -655,11 +655,7 @@ struct hostsfile { - #define FREC_TEST_PKTSZ 256 - #define FREC_HAS_EXTRADATA 512 - --#ifdef HAVE_DNSSEC --#define HASH_SIZE 20 /* SHA-1 digest size */ --#else --#define HASH_SIZE sizeof(int) --#endif -+#define HASH_SIZE 32 /* SHA-256 digest size */ - - struct frec { - union mysockaddr source; -@@ -1229,7 +1225,6 @@ int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name, - struct bogus_addr *baddr, time_t now); - int check_for_ignored_address(struct dns_header *header, size_t qlen, struct bogus_addr *baddr); - int check_for_local_domain(char *name, time_t now); --unsigned int questions_crc(struct dns_header *header, size_t plen, char *name); - size_t resize_packet(struct dns_header *header, size_t plen, - unsigned char *pheader, size_t hlen); - int add_resource_record(struct dns_header *header, char *limit, int *truncp, -@@ -1254,9 +1249,11 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch - int check_unsigned, int *neganswer, int *nons, int *nsec_ttl); - int dnskey_keytag(int alg, int flags, unsigned char *key, int keylen); - size_t filter_rrsigs(struct dns_header *header, size_t plen); --unsigned char* hash_questions(struct dns_header *header, size_t plen, char *name); - int setup_timestamp(void); - -+/* hash_questions.c */ -+unsigned char *hash_questions(struct dns_header *header, size_t plen, char *name); -+ - /* crypto.c */ - const struct nettle_hash *hash_find(char *name); - int hash_init(const struct nettle_hash *hash, void **ctxp, unsigned char **digestp); -diff --git a/src/dnssec.c b/src/dnssec.c -index e95aa34..9410a7e 100644 ---- a/src/dnssec.c -+++ b/src/dnssec.c -@@ -2087,35 +2087,4 @@ size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char - return ret; - } - --unsigned char* hash_questions(struct dns_header *header, size_t plen, char *name) --{ -- int q; -- unsigned int len; -- unsigned char *p = (unsigned char *)(header+1); -- const struct nettle_hash *hash; -- void *ctx; -- unsigned char *digest; -- -- if (!(hash = hash_find("sha1")) || !hash_init(hash, &ctx, &digest)) -- return NULL; -- -- for (q = ntohs(header->qdcount); q != 0; q--) -- { -- if (!extract_name(header, plen, &p, name, 1, 4)) -- break; /* bad packet */ -- -- len = to_wire(name); -- hash->update(ctx, len, (unsigned char *)name); -- /* CRC the class and type as well */ -- hash->update(ctx, 4, p); -- -- p += 4; -- if (!CHECK_LEN(header, p, plen, 0)) -- break; /* bad packet */ -- } -- -- hash->digest(ctx, hash->digest_size, digest); -- return digest; --} -- - #endif /* HAVE_DNSSEC */ -diff --git a/src/forward.c b/src/forward.c -index 134e0fc..4f9a963 100644 ---- a/src/forward.c -+++ b/src/forward.c -@@ -256,19 +256,16 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, - union all_addr *addrp = NULL; - unsigned int flags = 0; - struct server *start = NULL; --#ifdef HAVE_DNSSEC - void *hash = hash_questions(header, plen, daemon->namebuff); -+#ifdef HAVE_DNSSEC - int do_dnssec = 0; --#else -- unsigned int crc = questions_crc(header, plen, daemon->namebuff); -- void *hash = &crc; - #endif - unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL); - unsigned char *oph = find_pseudoheader(header, plen, NULL, NULL, NULL, NULL); - (void)do_bit; - - /* may be no servers available. */ -- if (forward || (hash && (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, hash)))) -+ if (forward || (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, hash))) - { - /* If we didn't get an answer advertising a maximal packet in EDNS, - fall back to 1280, which should work everywhere on IPv6. -@@ -769,9 +766,6 @@ void reply_query(int fd, int family, time_t now) - size_t nn; - struct server *server; - void *hash; --#ifndef HAVE_DNSSEC -- unsigned int crc; --#endif - - /* packet buffer overwritten */ - daemon->srv_save = NULL; -@@ -798,12 +792,7 @@ void reply_query(int fd, int family, time_t now) - if (difftime(now, server->pktsz_reduced) > UDP_TEST_TIME) - server->edns_pktsz = daemon->edns_pktsz; - --#ifdef HAVE_DNSSEC - hash = hash_questions(header, n, daemon->namebuff); --#else -- hash = &crc; -- crc = questions_crc(header, n, daemon->namebuff); --#endif - - if (!(forward = lookup_frec(ntohs(header->id), fd, family, hash))) - return; -@@ -1115,8 +1104,7 @@ void reply_query(int fd, int family, time_t now) - log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, daemon->keyname, (union all_addr *)&(server->addr.in6.sin6_addr), - querystr("dnssec-query", querytype)); - -- if ((hash = hash_questions(header, nn, daemon->namebuff))) -- memcpy(new->hash, hash, HASH_SIZE); -+ memcpy(new->hash, hash_questions(header, nn, daemon->namebuff), HASH_SIZE); - new->new_id = get_id(); - header->id = htons(new->new_id); - /* Save query for retransmission */ -@@ -1970,15 +1958,9 @@ unsigned char *tcp_request(int confd, time_t now, - if (!flags && last_server) - { - struct server *firstsendto = NULL; --#ifdef HAVE_DNSSEC -- unsigned char *newhash, hash[HASH_SIZE]; -- if ((newhash = hash_questions(header, (unsigned int)size, daemon->namebuff))) -- memcpy(hash, newhash, HASH_SIZE); -- else -- memset(hash, 0, HASH_SIZE); --#else -- unsigned int crc = questions_crc(header, (unsigned int)size, daemon->namebuff); --#endif -+ unsigned char hash[HASH_SIZE]; -+ memcpy(hash, hash_questions(header, (unsigned int)size, daemon->namebuff), HASH_SIZE); -+ - /* Loop round available servers until we succeed in connecting to one. - Note that this code subtly ensures that consecutive queries on this connection - which can go to the same server, do so. */ -@@ -2117,20 +2099,11 @@ unsigned char *tcp_request(int confd, time_t now, - /* If the crc of the question section doesn't match the crc we sent, then - someone might be attempting to insert bogus values into the cache by - sending replies containing questions and bogus answers. */ --#ifdef HAVE_DNSSEC -- newhash = hash_questions(header, (unsigned int)m, daemon->namebuff); -- if (!newhash || memcmp(hash, newhash, HASH_SIZE) != 0) -+ if (memcmp(hash, hash_questions(header, (unsigned int)m, daemon->namebuff), HASH_SIZE) != 0) - { - m = 0; - break; - } --#else -- if (crc != questions_crc(header, (unsigned int)m, daemon->namebuff)) -- { -- m = 0; -- break; -- } --#endif - - m = process_reply(header, now, last_server, (unsigned int)m, - option_bool(OPT_NO_REBIND) && !norebind, no_cache_dnssec, cache_secure, bogusanswer, -@@ -2345,7 +2318,7 @@ static struct frec *lookup_frec(unsigned short id, int fd, int family, void *has - - for(f = daemon->frec_list; f; f = f->next) - if (f->sentto && f->new_id == id && -- (!hash || memcmp(hash, f->hash, HASH_SIZE) == 0)) -+ (memcmp(hash, f->hash, HASH_SIZE) == 0)) - { - /* sent from random port */ - if (family == AF_INET && f->rfd4 && f->rfd4->fd == fd) -diff --git a/src/hash_questions.c b/src/hash_questions.c -new file mode 100644 -index 0000000..ae112ac ---- /dev/null -+++ b/src/hash_questions.c -@@ -0,0 +1,281 @@ -+/* Copyright (c) 2012-2020 Simon Kelley -+ -+ This program is free software; you can redistribute it and/or modify -+ it under the terms of the GNU General Public License as published by -+ the Free Software Foundation; version 2 dated June, 1991, or -+ (at your option) version 3 dated 29 June, 2007. -+ -+ This program is distributed in the hope that it will be useful, -+ but WITHOUT ANY WARRANTY; without even the implied warranty of -+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ GNU General Public License for more details. -+ -+ You should have received a copy of the GNU General Public License -+ along with this program. If not, see . -+*/ -+ -+ -+/* Hash the question section. This is used to safely detect query -+ retransmission and to detect answers to questions we didn't ask, which -+ might be poisoning attacks. Note that we decode the name rather -+ than CRC the raw bytes, since replies might be compressed differently. -+ We ignore case in the names for the same reason. -+ -+ The hash used is SHA-256. If we're building with DNSSEC support, -+ we use the Nettle cypto library. If not, we prefer not to -+ add a dependency on Nettle, and use a stand-alone implementaion. -+*/ -+ -+#include "dnsmasq.h" -+ -+#ifdef HAVE_DNSSEC -+unsigned char *hash_questions(struct dns_header *header, size_t plen, char *name) -+{ -+ int q; -+ unsigned char *p = (unsigned char *)(header+1); -+ const struct nettle_hash *hash; -+ void *ctx; -+ unsigned char *digest; -+ -+ if (!(hash = hash_find("sha256")) || !hash_init(hash, &ctx, &digest)) -+ { -+ /* don't think this can ever happen. */ -+ static unsigned char dummy[HASH_SIZE]; -+ static int warned = 0; -+ -+ if (warned) -+ my_syslog(LOG_ERR, _("Failed to create SHA-256 hash object")); -+ warned = 1; -+ -+ return dummy; -+ } -+ -+ for (q = ntohs(header->qdcount); q != 0; q--) -+ { -+ char *cp, c; -+ -+ if (!extract_name(header, plen, &p, name, 1, 4)) -+ break; /* bad packet */ -+ -+ for (cp = name; (c = *cp); cp++) -+ if (c >= 'A' && c <= 'Z') -+ *cp += 'a' - 'A'; -+ -+ hash->update(ctx, cp - name, (unsigned char *)name); -+ /* CRC the class and type as well */ -+ hash->update(ctx, 4, p); -+ -+ p += 4; -+ if (!CHECK_LEN(header, p, plen, 0)) -+ break; /* bad packet */ -+ } -+ -+ hash->digest(ctx, hash->digest_size, digest); -+ return digest; -+} -+ -+#else /* HAVE_DNSSEC */ -+ -+#define SHA256_BLOCK_SIZE 32 // SHA256 outputs a 32 byte digest -+typedef unsigned char BYTE; // 8-bit byte -+typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines -+ -+typedef struct { -+ BYTE data[64]; -+ WORD datalen; -+ unsigned long long bitlen; -+ WORD state[8]; -+} SHA256_CTX; -+ -+static void sha256_init(SHA256_CTX *ctx); -+static void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len); -+static void sha256_final(SHA256_CTX *ctx, BYTE hash[]); -+ -+ -+unsigned char *hash_questions(struct dns_header *header, size_t plen, char *name) -+{ -+ int q; -+ unsigned char *p = (unsigned char *)(header+1); -+ SHA256_CTX ctx; -+ static BYTE digest[SHA256_BLOCK_SIZE]; -+ -+ sha256_init(&ctx); -+ -+ for (q = ntohs(header->qdcount); q != 0; q--) -+ { -+ char *cp, c; -+ -+ if (!extract_name(header, plen, &p, name, 1, 4)) -+ break; /* bad packet */ -+ -+ for (cp = name; (c = *cp); cp++) -+ if (c >= 'A' && c <= 'Z') -+ *cp += 'a' - 'A'; -+ -+ sha256_update(&ctx, (BYTE *)name, cp - name); -+ /* CRC the class and type as well */ -+ sha256_update(&ctx, (BYTE *)p, 4); -+ -+ p += 4; -+ if (!CHECK_LEN(header, p, plen, 0)) -+ break; /* bad packet */ -+ } -+ -+ sha256_final(&ctx, digest); -+ return (unsigned char *)digest; -+} -+ -+/* Code from here onwards comes from https://github.com/B-Con/crypto-algorithms -+ and was written by Brad Conte (brad@bradconte.com), to whom all credit is given. -+ -+ This code is in the public domain, and the copyright notice at the head of this -+ file does not apply to it. -+*/ -+ -+ -+/****************************** MACROS ******************************/ -+#define ROTLEFT(a,b) (((a) << (b)) | ((a) >> (32-(b)))) -+#define ROTRIGHT(a,b) (((a) >> (b)) | ((a) << (32-(b)))) -+ -+#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z))) -+#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) -+#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22)) -+#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25)) -+#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3)) -+#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10)) -+ -+/**************************** VARIABLES *****************************/ -+static const WORD k[64] = { -+ 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5, -+ 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174, -+ 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da, -+ 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967, -+ 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85, -+ 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070, -+ 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3, -+ 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 -+}; -+ -+/*********************** FUNCTION DEFINITIONS ***********************/ -+static void sha256_transform(SHA256_CTX *ctx, const BYTE data[]) -+{ -+ WORD a, b, c, d, e, f, g, h, i, j, t1, t2, m[64]; -+ -+ for (i = 0, j = 0; i < 16; ++i, j += 4) -+ m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]); -+ for ( ; i < 64; ++i) -+ m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16]; -+ -+ a = ctx->state[0]; -+ b = ctx->state[1]; -+ c = ctx->state[2]; -+ d = ctx->state[3]; -+ e = ctx->state[4]; -+ f = ctx->state[5]; -+ g = ctx->state[6]; -+ h = ctx->state[7]; -+ -+ for (i = 0; i < 64; ++i) -+ { -+ t1 = h + EP1(e) + CH(e,f,g) + k[i] + m[i]; -+ t2 = EP0(a) + MAJ(a,b,c); -+ h = g; -+ g = f; -+ f = e; -+ e = d + t1; -+ d = c; -+ c = b; -+ b = a; -+ a = t1 + t2; -+ } -+ -+ ctx->state[0] += a; -+ ctx->state[1] += b; -+ ctx->state[2] += c; -+ ctx->state[3] += d; -+ ctx->state[4] += e; -+ ctx->state[5] += f; -+ ctx->state[6] += g; -+ ctx->state[7] += h; -+} -+ -+static void sha256_init(SHA256_CTX *ctx) -+{ -+ ctx->datalen = 0; -+ ctx->bitlen = 0; -+ ctx->state[0] = 0x6a09e667; -+ ctx->state[1] = 0xbb67ae85; -+ ctx->state[2] = 0x3c6ef372; -+ ctx->state[3] = 0xa54ff53a; -+ ctx->state[4] = 0x510e527f; -+ ctx->state[5] = 0x9b05688c; -+ ctx->state[6] = 0x1f83d9ab; -+ ctx->state[7] = 0x5be0cd19; -+} -+ -+static void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len) -+{ -+ WORD i; -+ -+ for (i = 0; i < len; ++i) -+ { -+ ctx->data[ctx->datalen] = data[i]; -+ ctx->datalen++; -+ if (ctx->datalen == 64) { -+ sha256_transform(ctx, ctx->data); -+ ctx->bitlen += 512; -+ ctx->datalen = 0; -+ } -+ } -+} -+ -+static void sha256_final(SHA256_CTX *ctx, BYTE hash[]) -+{ -+ WORD i; -+ -+ i = ctx->datalen; -+ -+ // Pad whatever data is left in the buffer. -+ if (ctx->datalen < 56) -+ { -+ ctx->data[i++] = 0x80; -+ while (i < 56) -+ ctx->data[i++] = 0x00; -+ } -+ else -+ { -+ ctx->data[i++] = 0x80; -+ while (i < 64) -+ ctx->data[i++] = 0x00; -+ sha256_transform(ctx, ctx->data); -+ memset(ctx->data, 0, 56); -+ } -+ -+ // Append to the padding the total message's length in bits and transform. -+ ctx->bitlen += ctx->datalen * 8; -+ ctx->data[63] = ctx->bitlen; -+ ctx->data[62] = ctx->bitlen >> 8; -+ ctx->data[61] = ctx->bitlen >> 16; -+ ctx->data[60] = ctx->bitlen >> 24; -+ ctx->data[59] = ctx->bitlen >> 32; -+ ctx->data[58] = ctx->bitlen >> 40; -+ ctx->data[57] = ctx->bitlen >> 48; -+ ctx->data[56] = ctx->bitlen >> 56; -+ sha256_transform(ctx, ctx->data); -+ -+ // Since this implementation uses little endian byte ordering and SHA uses big endian, -+ // reverse all the bytes when copying the final state to the output hash. -+ for (i = 0; i < 4; ++i) -+ { -+ hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff; -+ hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff; -+ hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff; -+ hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff; -+ hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff; -+ hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff; -+ hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff; -+ hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff; -+ } -+} -+ -+#endif -diff --git a/src/rfc1035.c b/src/rfc1035.c -index fefe63d..a8cdc6e 100644 ---- a/src/rfc1035.c -+++ b/src/rfc1035.c -@@ -333,55 +333,6 @@ unsigned char *skip_section(unsigned char *ansp, int count, struct dns_header *h - return ansp; - } - --/* CRC the question section. This is used to safely detect query -- retransmission and to detect answers to questions we didn't ask, which -- might be poisoning attacks. Note that we decode the name rather -- than CRC the raw bytes, since replies might be compressed differently. -- We ignore case in the names for the same reason. Return all-ones -- if there is not question section. */ --#ifndef HAVE_DNSSEC --unsigned int questions_crc(struct dns_header *header, size_t plen, char *name) --{ -- int q; -- unsigned int crc = 0xffffffff; -- unsigned char *p1, *p = (unsigned char *)(header+1); -- -- for (q = ntohs(header->qdcount); q != 0; q--) -- { -- if (!extract_name(header, plen, &p, name, 1, 4)) -- return crc; /* bad packet */ -- -- for (p1 = (unsigned char *)name; *p1; p1++) -- { -- int i = 8; -- char c = *p1; -- -- if (c >= 'A' && c <= 'Z') -- c += 'a' - 'A'; -- -- crc ^= c << 24; -- while (i--) -- crc = crc & 0x80000000 ? (crc << 1) ^ 0x04c11db7 : crc << 1; -- } -- -- /* CRC the class and type as well */ -- for (p1 = p; p1 < p+4; p1++) -- { -- int i = 8; -- crc ^= *p1 << 24; -- while (i--) -- crc = crc & 0x80000000 ? (crc << 1) ^ 0x04c11db7 : crc << 1; -- } -- -- p += 4; -- if (!CHECK_LEN(header, p, plen, 0)) -- return crc; /* bad packet */ -- } -- -- return crc; --} --#endif -- - size_t resize_packet(struct dns_header *header, size_t plen, unsigned char *pheader, size_t hlen) - { - unsigned char *ansp = skip_questions(header, plen); --- -1.8.3.1 - diff --git a/backport-CVE-2020-25685_2.patch b/backport-CVE-2020-25685_2.patch deleted file mode 100644 index 326b8a8..0000000 --- a/backport-CVE-2020-25685_2.patch +++ /dev/null @@ -1,196 +0,0 @@ -From 2024f9729713fd657d65e64c2e4e471baa0a3e5b Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= -Date: Wed, 25 Nov 2020 17:18:55 +0100 -Subject: [PATCH] Support hash function from nettle (only) - -Unlike COPTS=-DHAVE_DNSSEC, allow usage of just sha256 function from -nettle, but keep DNSSEC disabled at build time. Skips use of internal -hash implementation without support for validation built-in. ---- - Makefile | 8 +++++--- - bld/pkg-wrapper | 41 ++++++++++++++++++++++------------------- - src/config.h | 8 ++++++++ - src/crypto.c | 7 +++++++ - src/dnsmasq.h | 2 +- - src/hash_questions.c | 2 +- - 6 files changed, 44 insertions(+), 24 deletions(-) - -diff --git a/Makefile b/Makefile -index 0354e0f..7d2afd1 100644 ---- a/Makefile -+++ b/Makefile -@@ -53,7 +53,7 @@ top?=$(CURDIR) - - dbus_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DBUS $(PKG_CONFIG) --cflags dbus-1` - dbus_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DBUS $(PKG_CONFIG) --libs dbus-1` --ubus_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_UBUS "" --copy -lubox -lubus` -+ubus_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_UBUS "" --copy '-lubox -lubus'` - idn_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_IDN $(PKG_CONFIG) --cflags libidn` - idn_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_IDN $(PKG_CONFIG) --libs libidn` - idn2_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LIBIDN2 $(PKG_CONFIG) --cflags libidn2` -@@ -62,8 +62,10 @@ ct_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_CONNTRACK $(PKG_CON - ct_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_CONNTRACK $(PKG_CONFIG) --libs libnetfilter_conntrack` - lua_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LUASCRIPT $(PKG_CONFIG) --cflags lua5.2` - lua_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LUASCRIPT $(PKG_CONFIG) --libs lua5.2` --nettle_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC $(PKG_CONFIG) --cflags nettle hogweed` --nettle_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC $(PKG_CONFIG) --libs nettle hogweed` -+nettle_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC $(PKG_CONFIG) --cflags 'nettle hogweed' \ -+ HAVE_NETTLEHASH $(PKG_CONFIG) --cflags nettle` -+nettle_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC $(PKG_CONFIG) --libs 'nettle hogweed' \ -+ HAVE_NETTLEHASH $(PKG_CONFIG) --libs nettle` - gmp_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC NO_GMP --copy -lgmp` - sunos_libs = `if uname | grep SunOS >/dev/null 2>&1; then echo -lsocket -lnsl -lposix4; fi` - version = -DVERSION='\"`$(top)/bld/get-version $(top)`\"' -diff --git a/bld/pkg-wrapper b/bld/pkg-wrapper -index 704bdd3..89713b4 100755 ---- a/bld/pkg-wrapper -+++ b/bld/pkg-wrapper -@@ -1,35 +1,37 @@ - #!/bin/sh - --search=$1 --shift --pkg=$1 --shift --op=$1 --shift -- - in=`cat` - --if grep "^\#[[:space:]]*define[[:space:]]*$search" config.h >/dev/null 2>&1 || \ -- echo $in | grep $search >/dev/null 2>&1; then -+search() -+{ -+ grep "^\#[[:space:]]*define[[:space:]]*$1" config.h >/dev/null 2>&1 || \ -+ echo $in | grep $1 >/dev/null 2>&1 -+} -+ -+while [ "$#" -gt 0 ]; do -+ search=$1 -+ pkg=$2 -+ op=$3 -+ lib=$4 -+ shift 4 -+if search "$search"; then -+ - # Nasty, nasty, in --copy, arg 2 (if non-empty) is another config to search for, used with NO_GMP - if [ $op = "--copy" ]; then - if [ -z "$pkg" ]; then -- pkg="$*" -- elif grep "^\#[[:space:]]*define[[:space:]]*$pkg" config.h >/dev/null 2>&1 || \ -- echo $in | grep $pkg >/dev/null 2>&1; then -+ pkg="$lib" -+ elif search "$pkg"; then - pkg="" - else -- pkg="$*" -+ pkg="$lib" - fi -- elif grep "^\#[[:space:]]*define[[:space:]]*${search}_STATIC" config.h >/dev/null 2>&1 || \ -- echo $in | grep ${search}_STATIC >/dev/null 2>&1; then -- pkg=`$pkg --static $op $*` -+ elif search "${search}_STATIC"; then -+ pkg=`$pkg --static $op $lib` - else -- pkg=`$pkg $op $*` -+ pkg=`$pkg $op $lib` - fi - -- if grep "^\#[[:space:]]*define[[:space:]]*${search}_STATIC" config.h >/dev/null 2>&1 || \ -- echo $in | grep ${search}_STATIC >/dev/null 2>&1; then -+ if search "${search}_STATIC"; then - if [ $op = "--libs" ] || [ $op = "--copy" ]; then - echo "-Wl,-Bstatic $pkg -Wl,-Bdynamic" - else -@@ -40,3 +42,4 @@ if grep "^\#[[:space:]]*define[[:space:]]*$search" config.h >/dev/null 2>&1 || \ - fi - fi - -+done -diff --git a/src/config.h b/src/config.h -index 7187ffa..e71a117 100644 ---- a/src/config.h -+++ b/src/config.h -@@ -120,6 +120,9 @@ HAVE_AUTH - define this to include the facility to act as an authoritative DNS - server for one or more zones. - -+HAVE_NETTLEHASH -+ include just hash function from nettle, but no DNSSEC. -+ - HAVE_DNSSEC - include DNSSEC validator. - -@@ -187,6 +190,7 @@ RESOLVFILE - /* #define HAVE_IDN */ - /* #define HAVE_LIBIDN2 */ - /* #define HAVE_CONNTRACK */ -+/* #define HAVE_NETTLEHASH */ - /* #define HAVE_DNSSEC */ - - -@@ -420,6 +424,10 @@ static char *compile_opts = - "no-" - #endif - "auth " -+#if !defined(HAVE_NETTLEHASH) && !defined(HAVE_DNSSEC) -+"no-" -+#endif -+"nettlehash " - #ifndef HAVE_DNSSEC - "no-" - #endif -diff --git a/src/crypto.c b/src/crypto.c -index ca63111..09525d2 100644 ---- a/src/crypto.c -+++ b/src/crypto.c -@@ -25,6 +25,9 @@ - #if NETTLE_VERSION_MAJOR == 3 && NETTLE_VERSION_MINOR >= 6 - # include - #endif -+#endif -+ -+#if defined(HAVE_DNSSEC) || defined(HAVE_NETTLEHASH) - #include - #include - -@@ -167,6 +170,10 @@ int hash_init(const struct nettle_hash *hash, void **ctxp, unsigned char **diges - - return 1; - } -+ -+#endif -+ -+#ifdef HAVE_DNSSEC - - static int dnsmasq_rsa_verify(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len, - unsigned char *digest, size_t digest_len, int algo) -diff --git a/src/dnsmasq.h b/src/dnsmasq.h -index 9f74c7a..914f469 100644 ---- a/src/dnsmasq.h -+++ b/src/dnsmasq.h -@@ -157,7 +157,7 @@ extern int capget(cap_user_header_t header, cap_user_data_t data); - #include - #endif - --#ifdef HAVE_DNSSEC -+#if defined(HAVE_DNSSEC) || defined(HAVE_NETTLEHASH) - # include - #endif - -diff --git a/src/hash_questions.c b/src/hash_questions.c -index ae112ac..917c18e 100644 ---- a/src/hash_questions.c -+++ b/src/hash_questions.c -@@ -28,7 +28,7 @@ - - #include "dnsmasq.h" - --#ifdef HAVE_DNSSEC -+#if defined(HAVE_DNSSEC) || defined(HAVE_NETTLEHASH) - unsigned char *hash_questions(struct dns_header *header, size_t plen, char *name) - { - int q; --- -1.8.3.1 - diff --git a/backport-CVE-2020-25686_1.patch b/backport-CVE-2020-25686_1.patch deleted file mode 100644 index eeb2bfe..0000000 --- a/backport-CVE-2020-25686_1.patch +++ /dev/null @@ -1,328 +0,0 @@ -From 15b60ddf935a531269bb8c68198de012a4967156 Mon Sep 17 00:00:00 2001 -From: Simon Kelley -Date: Wed, 18 Nov 2020 18:34:55 +0000 -Subject: [PATCH] Handle multiple identical near simultaneous DNS queries - better. - -Previously, such queries would all be forwarded -independently. This is, in theory, inefficent but in practise -not a problem, _except_ that is means that an answer for any -of the forwarded queries will be accepted and cached. -An attacker can send a query multiple times, and for each repeat, -another {port, ID} becomes capable of accepting the answer he is -sending in the blind, to random IDs and ports. The chance of a -succesful attack is therefore multiplied by the number of repeats -of the query. The new behaviour detects repeated queries and -merely stores the clients sending repeats so that when the -first query completes, the answer can be sent to all the -clients who asked. Refer: CERT VU#434904. ---- - src/dnsmasq.h | 19 +++++--- - src/forward.c | 142 ++++++++++++++++++++++++++++++++++++++++++++++++---------- - 2 files changed, 132 insertions(+), 29 deletions(-) - -diff --git a/src/dnsmasq.h b/src/dnsmasq.h -index 0a7639f..fa8c5ea 100644 ---- a/src/dnsmasq.h -+++ b/src/dnsmasq.h -@@ -653,19 +653,24 @@ struct hostsfile { - #define FREC_DO_QUESTION 64 - #define FREC_ADDED_PHEADER 128 - #define FREC_TEST_PKTSZ 256 --#define FREC_HAS_EXTRADATA 512 -+#define FREC_HAS_EXTRADATA 512 -+#define FREC_HAS_PHEADER 1024 - - #define HASH_SIZE 32 /* SHA-256 digest size */ - - struct frec { -- union mysockaddr source; -- union all_addr dest; -+ struct frec_src { -+ union mysockaddr source; -+ union all_addr dest; -+ unsigned int iface, log_id; -+ unsigned short orig_id; -+ struct frec_src *next; -+ } frec_src; - struct server *sentto; /* NULL means free */ - struct randfd *rfd4; - struct randfd *rfd6; -- unsigned int iface; -- unsigned short orig_id, new_id; -- int log_id, fd, forwardall, flags; -+ unsigned short new_id; -+ int fd, forwardall, flags; - time_t time; - unsigned char *hash[HASH_SIZE]; - #ifdef HAVE_DNSSEC -@@ -1093,6 +1098,8 @@ extern struct daemon { - int back_to_the_future; - #endif - struct frec *frec_list; -+ struct frec_src *free_frec_src; -+ int frec_src_count; - struct serverfd *sfds; - struct irec *interfaces; - struct listener *listeners; -diff --git a/src/forward.c b/src/forward.c -index 50da095..d9b32b3 100644 ---- a/src/forward.c -+++ b/src/forward.c -@@ -20,6 +20,8 @@ static struct frec *lookup_frec(unsigned short id, int fd, int family, void *has - static struct frec *lookup_frec_by_sender(unsigned short id, - union mysockaddr *addr, - void *hash); -+static struct frec *lookup_frec_by_query(void *hash, unsigned int flags); -+ - static unsigned short get_id(void); - static void free_frec(struct frec *f); - -@@ -255,6 +257,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, - int type = SERV_DO_DNSSEC, norebind = 0; - union all_addr *addrp = NULL; - unsigned int flags = 0; -+ unsigned int fwd_flags = 0; - struct server *start = NULL; - void *hash = hash_questions(header, plen, daemon->namebuff); - #ifdef HAVE_DNSSEC -@@ -263,7 +266,18 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, - unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL); - unsigned char *oph = find_pseudoheader(header, plen, NULL, NULL, NULL, NULL); - (void)do_bit; -- -+ -+ if (header->hb4 & HB4_CD) -+ fwd_flags |= FREC_CHECKING_DISABLED; -+ if (ad_reqd) -+ fwd_flags |= FREC_AD_QUESTION; -+ if (oph) -+ fwd_flags |= FREC_HAS_PHEADER; -+#ifdef HAVE_DNSSEC -+ if (do_bit) -+ fwd_flags |= FREC_DO_QUESTION; -+#endif -+ - /* may be no servers available. */ - if (forward || (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, hash))) - { -@@ -336,6 +350,39 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, - } - else - { -+ /* Query from new source, but the same query may be in progress -+ from another source. If so, just add this client to the -+ list that will get the reply. -+ -+ Note that is the EDNS client subnet option is in use, we can't do this, -+ as the clients (and therefore query EDNS options) will be different -+ for each query. The EDNS subnet code has checks to avoid -+ attacks in this case. */ -+ if (!option_bool(OPT_CLIENT_SUBNET) && (forward = lookup_frec_by_query(hash, fwd_flags))) -+ { -+ /* Note whine_malloc() zeros memory. */ -+ if (!daemon->free_frec_src && -+ daemon->frec_src_count < daemon->ftabsize && -+ (daemon->free_frec_src = whine_malloc(sizeof(struct frec_src)))) -+ daemon->frec_src_count++; -+ -+ /* If we've been spammed with many duplicates, just drop the query. */ -+ if (daemon->free_frec_src) -+ { -+ struct frec_src *new = daemon->free_frec_src; -+ daemon->free_frec_src = new->next; -+ new->next = forward->frec_src.next; -+ forward->frec_src.next = new; -+ new->orig_id = ntohs(header->id); -+ new->source = *udpaddr; -+ new->dest = *dst_addr; -+ new->log_id = daemon->log_id; -+ new->iface = dst_iface; -+ } -+ -+ return 1; -+ } -+ - if (gotname) - flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind); - -@@ -343,22 +390,22 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, - do_dnssec = type & SERV_DO_DNSSEC; - #endif - type &= ~SERV_DO_DNSSEC; -- -+ - if (daemon->servers && !flags) - forward = get_new_frec(now, NULL, NULL); - /* table full - flags == 0, return REFUSED */ - - if (forward) - { -- forward->source = *udpaddr; -- forward->dest = *dst_addr; -- forward->iface = dst_iface; -- forward->orig_id = ntohs(header->id); -+ forward->frec_src.source = *udpaddr; -+ forward->frec_src.orig_id = ntohs(header->id); -+ forward->frec_src.dest = *dst_addr; -+ forward->frec_src.iface = dst_iface; - forward->new_id = get_id(); - forward->fd = udpfd; - memcpy(forward->hash, hash, HASH_SIZE); - forward->forwardall = 0; -- forward->flags = 0; -+ forward->flags = fwd_flags; - if (norebind) - forward->flags |= FREC_NOREBIND; - if (header->hb4 & HB4_CD) -@@ -413,9 +460,9 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, - unsigned char *pheader; - - /* If a query is retried, use the log_id for the retry when logging the answer. */ -- forward->log_id = daemon->log_id; -+ forward->frec_src.log_id = daemon->log_id; - -- plen = add_edns0_config(header, plen, ((unsigned char *)header) + PACKETSZ, &forward->source, now, &subnet); -+ plen = add_edns0_config(header, plen, ((unsigned char *)header) + PACKETSZ, &forward->frec_src.source, now, &subnet); - - if (subnet) - forward->flags |= FREC_HAS_SUBNET; -@@ -552,7 +599,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, - return 1; - - /* could not send on, prepare to return */ -- header->id = htons(forward->orig_id); -+ header->id = htons(forward->frec_src.orig_id); - free_frec(forward); /* cancel */ - } - -@@ -804,8 +851,8 @@ void reply_query(int fd, int family, time_t now) - - /* log_query gets called indirectly all over the place, so - pass these in global variables - sorry. */ -- daemon->log_display_id = forward->log_id; -- daemon->log_source_addr = &forward->source; -+ daemon->log_display_id = forward->frec_src.log_id; -+ daemon->log_source_addr = &forward->frec_src.source; - - if (daemon->ignore_addr && RCODE(header) == NOERROR && - check_for_ignored_address(header, n, daemon->ignore_addr)) -@@ -1080,6 +1127,7 @@ void reply_query(int fd, int family, time_t now) - new->sentto = server; - new->rfd4 = NULL; - new->rfd6 = NULL; -+ new->frec_src.next = NULL; - new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY | FREC_HAS_EXTRADATA); - new->forwardall = 0; - -@@ -1215,9 +1263,11 @@ void reply_query(int fd, int family, time_t now) - - if ((nn = process_reply(header, now, forward->sentto, (size_t)n, check_rebind, no_cache_dnssec, cache_secure, bogusanswer, - forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION, -- forward->flags & FREC_ADDED_PHEADER, forward->flags & FREC_HAS_SUBNET, &forward->source))) -+ forward->flags & FREC_ADDED_PHEADER, forward->flags & FREC_HAS_SUBNET, &forward->frec_src.source))) - { -- header->id = htons(forward->orig_id); -+ struct frec_src *src; -+ -+ header->id = htons(forward->frec_src.orig_id); - header->hb4 |= HB4_RA; /* recursion if available */ - #ifdef HAVE_DNSSEC - /* We added an EDNSO header for the purpose of getting DNSSEC RRs, and set the value of the UDP payload size -@@ -1233,13 +1283,26 @@ void reply_query(int fd, int family, time_t now) - } - #endif - -+ for (src = &forward->frec_src; src; src = src->next) -+ { -+ header->id = htons(src->orig_id); -+ - #ifdef HAVE_DUMPFILE -- dump_packet(DUMP_REPLY, daemon->packet, (size_t)nn, NULL, &forward->source); -+ dump_packet(DUMP_REPLY, daemon->packet, (size_t)nn, NULL, &src->source); - #endif -- -- send_from(forward->fd, option_bool(OPT_NOWILD) || option_bool (OPT_CLEVERBIND), daemon->packet, nn, -- &forward->source, &forward->dest, forward->iface); -+ -+ send_from(forward->fd, option_bool(OPT_NOWILD) || option_bool (OPT_CLEVERBIND), daemon->packet, nn, -+ &src->source, &src->dest, src->iface); -+ -+ if (option_bool(OPT_EXTRALOG) && src != &forward->frec_src) -+ { -+ daemon->log_display_id = src->log_id; -+ daemon->log_source_addr = &src->source; -+ log_query(F_UPSTREAM, "query", NULL, "duplicate"); -+ } -+ } - } -+ - free_frec(forward); /* cancel */ - } - } -@@ -2202,6 +2265,17 @@ void free_rfd(struct randfd *rfd) - - static void free_frec(struct frec *f) - { -+ struct frec_src *src, *tmp; -+ -+ /* add back to freelist of not the record builtin to every frec. */ -+ for (src = f->frec_src.next; src; src = tmp) -+ { -+ tmp = src->next; -+ src->next = daemon->free_frec_src; -+ daemon->free_frec_src = src; -+ } -+ -+ f->frec_src.next = NULL; - free_rfd(f->rfd4); - f->rfd4 = NULL; - f->sentto = NULL; -@@ -2342,17 +2416,39 @@ static struct frec *lookup_frec_by_sender(unsigned short id, - void *hash) - { - struct frec *f; -+ struct frec_src *src; -+ -+ for (f = daemon->frec_list; f; f = f->next) -+ if (f->sentto && -+ !(f->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY)) && -+ memcmp(hash, f->hash, HASH_SIZE) == 0) -+ for (src = &f->frec_src; src; src = src->next) -+ if (src->orig_id == id && -+ sockaddr_isequal(&src->source, addr)) -+ return f; -+ -+ return NULL; -+} -+ -+static struct frec *lookup_frec_by_query(void *hash, unsigned int flags) -+{ -+ struct frec *f; -+ -+ /* FREC_DNSKEY and FREC_DS_QUERY are never set in flags, so the test below -+ ensures that no frec created for internal DNSSEC query can be returned here. */ -+ -+#define FLAGMASK (FREC_CHECKING_DISABLED | FREC_AD_QUESTION | FREC_DO_QUESTION \ -+ | FREC_HAS_PHEADER | FREC_DNSKEY_QUERY | FREC_DS_QUERY) - - for(f = daemon->frec_list; f; f = f->next) - if (f->sentto && -- f->orig_id == id && -- memcmp(hash, f->hash, HASH_SIZE) == 0 && -- sockaddr_isequal(&f->source, addr)) -+ (f->flags & FLAGMASK) == flags && -+ memcmp(hash, f->hash, HASH_SIZE) == 0) - return f; -- -+ - return NULL; - } -- -+ - /* Send query packet again, if we can. */ - void resend_query() - { --- -1.8.3.1 - diff --git a/backport-CVE-2020-25686_2.patch b/backport-CVE-2020-25686_2.patch deleted file mode 100644 index f4b17f1..0000000 --- a/backport-CVE-2020-25686_2.patch +++ /dev/null @@ -1,61 +0,0 @@ -From 6a6e06fbb0d4690507ceaf2bb6f0d8910f3d4914 Mon Sep 17 00:00:00 2001 -From: Simon Kelley -Date: Fri, 4 Dec 2020 18:35:11 +0000 -Subject: [PATCH] Small cleanups in frec_src datastucture handling. - ---- - src/forward.c | 22 +++++++++++++--------- - 1 file changed, 13 insertions(+), 9 deletions(-) - -diff --git a/src/forward.c b/src/forward.c -index 70b84d7..f94c4cf 100644 ---- a/src/forward.c -+++ b/src/forward.c -@@ -361,7 +361,10 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, - if (!daemon->free_frec_src && - daemon->frec_src_count < daemon->ftabsize && - (daemon->free_frec_src = whine_malloc(sizeof(struct frec_src)))) -- daemon->frec_src_count++; -+ { -+ daemon->frec_src_count++; -+ daemon->free_frec_src->next = NULL; -+ } - - /* If we've been spammed with many duplicates, just drop the query. */ - if (daemon->free_frec_src) -@@ -398,6 +401,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, - forward->frec_src.orig_id = ntohs(header->id); - forward->frec_src.dest = *dst_addr; - forward->frec_src.iface = dst_iface; -+ forward->frec_src.next = NULL; - forward->new_id = get_id(); - forward->fd = udpfd; - memcpy(forward->hash, hash, HASH_SIZE); -@@ -2275,16 +2279,16 @@ void free_rfd(struct randfd *rfd) - - static void free_frec(struct frec *f) - { -- struct frec_src *src, *tmp; -- -- /* add back to freelist of not the record builtin to every frec. */ -- for (src = f->frec_src.next; src; src = tmp) -+ struct frec_src *last; -+ -+ /* add back to freelist if not the record builtin to every frec. */ -+ for (last = f->frec_src.next; last && last->next; last = last->next) ; -+ if (last) - { -- tmp = src->next; -- src->next = daemon->free_frec_src; -- daemon->free_frec_src = src; -+ last->next = daemon->free_frec_src; -+ daemon->free_frec_src = f->frec_src.next; - } -- -+ - f->frec_src.next = NULL; - free_rfd(f->rfd4); - f->rfd4 = NULL; --- -1.8.3.1 - diff --git a/backport-Fix-DNS-reply-when-asking-for-DNSSEC-and-a-validated.patch b/backport-Fix-DNS-reply-when-asking-for-DNSSEC-and-a-validated.patch deleted file mode 100644 index 95738b6..0000000 --- a/backport-Fix-DNS-reply-when-asking-for-DNSSEC-and-a-validated.patch +++ /dev/null @@ -1,28 +0,0 @@ -From 1eb6cedb03cb335071fda22ee7c623b2298d3729 Mon Sep 17 00:00:00 2001 -From: Simon Kelley -Date: Sat, 14 Nov 2020 15:29:34 +0000 -Subject: [PATCH] Fix DNS reply when asking for DNSSEC and a validated CNAME is - already cached. - -Conflict:NA -Reference:https://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=commit;h=1eb6cedb03cb335071fda22ee7c623b2298d3729 ---- - src/rfc1035.c | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/src/rfc1035.c b/src/rfc1035.c -index a8cdc6e..79af53f 100644 ---- a/src/rfc1035.c -+++ b/src/rfc1035.c -@@ -1359,6 +1359,8 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, - } - - } -+ else -+ return 0; /* give up if any cached CNAME in chain can't be used for DNSSEC reasons. */ - - strcpy(name, cname_target); - } --- -2.23.0 - diff --git a/backport-fix-regression-in-s_config_in_context-method.patch b/backport-fix-regression-in-s_config_in_context-method.patch deleted file mode 100644 index 8112509..0000000 --- a/backport-fix-regression-in-s_config_in_context-method.patch +++ /dev/null @@ -1,62 +0,0 @@ -From 3d113137fd64cd0723cbecab6a36a75d3ecfb0a6 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Harald=20Jens=C3=A5s?= -Date: Thu, 7 May 2020 00:33:54 +0200 -Subject: [PATCH 1/1] Fix regression in s_config_in_context() method - -Prior to commit 137286e9baecf6a3ba97722ef1b49c851b531810 -a config would not be considered in context if: -a) it has no address family flags set -b) it has the address family flag of current context set - -Since above commit config is considered in context if the -address family is the opposite of current context. - -The result is that a config with two dhcp-host records, -one for IPv6 and another for IPv4 no longer works, for -example with the below config the config with the IPv6 -address would be considered in context for a DHCP(v4) -request. - dhcp-host=52:54:00:bc:c3:fd,172.20.0.11,host2 - dhcp-host=52:54:00:bc:c3:fd,[fd12:3456:789a:1::aadd],host2 - -This commit restores the previous behavior. - -https://src.fedoraproject.org/rpms/dnsmasq/blob/master/f/dnsmasq-2.81-rh1834454.patch ---- - src/dhcp-common.c | 10 +++++++--- - 1 file changed, 7 insertions(+), 3 deletions(-) - -diff --git a/src/dhcp-common.c b/src/dhcp-common.c -index eae9886..ffc78ca 100644 ---- a/src/dhcp-common.c -+++ b/src/dhcp-common.c -@@ -280,14 +280,18 @@ static int is_config_in_context(struct dhcp_context *context, struct dhcp_config - { - if (!context) /* called via find_config() from lease_update_from_configs() */ - return 1; -- -+ -+ /* No address present in config == in context */ -+ if (!(config->flags & (CONFIG_ADDR | CONFIG_ADDR6))) -+ return 1; -+ - #ifdef HAVE_DHCP6 - if (context->flags & CONTEXT_V6) - { - struct addrlist *addr_list; - - if (!(config->flags & CONFIG_ADDR6)) -- return 1; -+ return 0; - - for (; context; context = context->current) - for (addr_list = config->addr6; addr_list; addr_list = addr_list->next) -@@ -303,7 +307,7 @@ static int is_config_in_context(struct dhcp_context *context, struct dhcp_config - #endif - { - if (!(config->flags & CONFIG_ADDR)) -- return 1; -+ return 0; - - for (; context; context = context->current) - if ((config->flags & CONFIG_ADDR) && is_same_net(config->addr, context->start, context->netmask)) diff --git a/bugfix-allow-binding-mac-with-ipv6.patch b/bugfix-allow-binding-mac-with-ipv6.patch index 9c71c6e..547a99c 100644 --- a/bugfix-allow-binding-mac-with-ipv6.patch +++ b/bugfix-allow-binding-mac-with-ipv6.patch @@ -31,12 +31,12 @@ index 68e6287..e7f1a0d 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -269,7 +269,8 @@ struct event_desc { - #define OPT_IGNORE_CLID 59 #define OPT_SINGLE_PORT 60 #define OPT_LEASE_RENEW 61 --#define OPT_LAST 62 -+#define OPT_BIND_MAC_IP6 62 -+#define OPT_LAST 63 + #define OPT_LOG_DEBUG 62 +-#define OPT_LAST 63 ++#define OPT_BIND_MAC_IP6 63 ++#define OPT_LAST 64 #define OPTION_BITS (sizeof(unsigned int)*8) #define OPTION_SIZE ( (OPT_LAST/OPTION_BITS)+((OPT_LAST%OPTION_BITS)!=0) ) @@ -53,19 +53,19 @@ index 1f698da..f02d389 100644 --- a/src/option.c +++ b/src/option.c @@ -167,7 +167,8 @@ struct myoption { - #define LOPT_IGNORE_CLID 358 - #define LOPT_SINGLE_PORT 359 - #define LOPT_SCRIPT_TIME 360 + #define LOPT_PXE_VENDOR 361 + #define LOPT_DYNHOST 362 + #define LOPT_LOG_DEBUG 363 - -+#define LOPT_BIND_MAC_IP6 361 ++#define LOPT_BIND_MAC_IP6 364 + #ifdef HAVE_GETOPT_LONG static const struct option opts[] = #else @@ -339,6 +340,7 @@ static const struct myoption opts[] = - { "dumpfile", 1, 0, LOPT_DUMPFILE }, - { "dumpmask", 1, 0, LOPT_DUMPMASK }, { "dhcp-ignore-clid", 0, 0, LOPT_IGNORE_CLID }, + { "dynamic-host", 1, 0, LOPT_DYNHOST }, + { "log-debug", 0, 0, LOPT_LOG_DEBUG }, + { "bind-mac-with-ip6", 0, 0 , LOPT_BIND_MAC_IP6 }, { NULL, 0, 0, 0 } }; diff --git a/dnsmasq-2.82.tar.xz b/dnsmasq-2.82.tar.xz deleted file mode 100644 index 7b6a4ca..0000000 Binary files a/dnsmasq-2.82.tar.xz and /dev/null differ diff --git a/dnsmasq-2.85.tar.xz b/dnsmasq-2.85.tar.xz new file mode 100644 index 0000000..c9ad70a Binary files /dev/null and b/dnsmasq-2.85.tar.xz differ diff --git a/dnsmasq.spec b/dnsmasq.spec index 82d0817..c62b96f 100644 --- a/dnsmasq.spec +++ b/dnsmasq.spec @@ -1,6 +1,6 @@ Name: dnsmasq -Version: 2.82 -Release: 9 +Version: 2.85 +Release: 1 Summary: Dnsmasq provides network infrastructure for small networks License: GPLv2 or GPLv3 URL: http://www.thekelleys.org.uk/dnsmasq/ @@ -12,26 +12,7 @@ Patch1: dnsmasq-2.77-underflow.patch Patch2: dnsmasq-2.78-fips.patch Patch3: bugfix-allow-binding-mac-with-ipv6.patch Patch4: bugfix-deal-with-CONFRIM-when-binding-mac-with-ipv6.patch -Patch5: backport-CVE-2020-25681_CVE-2020-25682_CVE-2020-25683_CVE-2020-25687.patch -Patch6: backport-CVE-2020-25684.patch -Patch7: backport-CVE-2020-25685_1.patch -Patch8: backport-CVE-2020-25685_2.patch -Patch9: backport-CVE-2020-25686_1.patch -Patch10: backport-CVE-2020-25686_2.patch -Patch11: backport-fix-regression-in-s_config_in_context-method.patch -Patch12: backport-Add-missing-check-for-NULL-return-from-allocate_rfd.patch -Patch13: backport-Fix-DNS-reply-when-asking-for-DNSSEC-and-a-validated.patch Patch14: backport-Handle-DHCPREBIND-requests-in-the-DHCPv6-server.patch -Patch15: backport-0001-Handle-caching-with-EDNS-options-better.patch -Patch16: backport-0002-Fix-to-75e2f0aec33e58ef5b8d4d107d821c215a52827c.patch -Patch17: backport-0003-Fix-for-12af2b171de0d678d98583e2190789e544440e02.patch -Patch18: backport-0004-Fix-problem-with-DNS-retries-in-2.83-2.84.patch -Patch19: backport-0005-Simplify-preceding-fix.patch -Patch20: backport-0006-Update-to-new-struct-frec-fields-in-conntrack-code.patch -Patch21: backport-0007-Use-the-values-of-min-port-and-max-port-in-TCP-conne.patch -Patch22: backport-0008-Correct-occasional-bind-dynamic-synchronization-brea.patch -Patch23: backport-0009-Move-fd-into-frec_src-fixes-15b60ddf935a531269bb8c68.patch -Patch24: backport-0010-CVE-2021-3448.patch BuildRequires: gcc BuildRequires: dbus-devel pkgconfig libidn2-devel nettle-devel systemd @@ -125,6 +106,9 @@ install -Dpm644 %{SOURCE2} $RPM_BUILD_ROOT%{_sysusersdir}/dnsmasq.conf %{_mandir}/man8/dnsmasq* %changelog +* Wed Jul 14 2021 gaihuiying - 2.85-1 +- update dnsmasq to 2.85 + * Wed May 26 2021 liuyumeng - 2.82-9 - Add a BuildRequires for gcc