backport some community patches and fix CVE-2021-3448
This commit is contained in:
parent
a91f6c4d3c
commit
1c27275731
361
backport-0001-Handle-caching-with-EDNS-options-better.patch
Normal file
361
backport-0001-Handle-caching-with-EDNS-options-better.patch
Normal file
@ -0,0 +1,361 @@
|
|||||||
|
From 25e63f1e56f5acdcf91893a1b92ad1e0f2f552d8 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Simon Kelley <simon@thekelleys.org.uk>
|
||||||
|
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
|
||||||
|
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
From 12af2b171de0d678d98583e2190789e544440e02 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Simon Kelley <simon@thekelleys.org.uk>
|
||||||
|
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
|
||||||
|
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
From 3f535da79e7a42104543ef5c7b5fa2bed819a78b Mon Sep 17 00:00:00 2001
|
||||||
|
From: Simon Kelley <simon@thekelleys.org.uk>
|
||||||
|
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
|
||||||
|
|
||||||
126
backport-0004-Fix-problem-with-DNS-retries-in-2.83-2.84.patch
Normal file
126
backport-0004-Fix-problem-with-DNS-retries-in-2.83-2.84.patch
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
From 141a26f979b4bc959d8e866a295e24f8cf456920 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Simon Kelley <simon@thekelleys.org.uk>
|
||||||
|
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
|
||||||
|
|
||||||
114
backport-0005-Simplify-preceding-fix.patch
Normal file
114
backport-0005-Simplify-preceding-fix.patch
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
From 305cb79c5754d5554729b18a2c06fe7ce699687a Mon Sep 17 00:00:00 2001
|
||||||
|
From: Simon Kelley <simon@thekelleys.org.uk>
|
||||||
|
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
|
||||||
|
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
From cc0b4489c782f6b90ca118abb18e716a7a831289 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Simon Kelley <simon@thekelleys.org.uk>
|
||||||
|
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
|
||||||
|
|
||||||
@ -0,0 +1,84 @@
|
|||||||
|
From a2a7e040b128a8ec369ba8f22beca2705435b85b Mon Sep 17 00:00:00 2001
|
||||||
|
From: Simon Kelley <simon@thekelleys.org.uk>
|
||||||
|
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
|
||||||
|
|
||||||
@ -0,0 +1,252 @@
|
|||||||
|
From 4c0aecc68524c5fd74053244a605e905dc644228 Mon Sep 17 00:00:00 2001
|
||||||
|
From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= <pemensik@redhat.com>
|
||||||
|
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
|
||||||
|
|
||||||
@ -0,0 +1,65 @@
|
|||||||
|
From 04490bf622ac84891aad6f2dd2edf83725decdee Mon Sep 17 00:00:00 2001
|
||||||
|
From: Simon Kelley <simon@thekelleys.org.uk>
|
||||||
|
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
|
||||||
|
|
||||||
1098
backport-0010-CVE-2021-3448.patch
Normal file
1098
backport-0010-CVE-2021-3448.patch
Normal file
File diff suppressed because it is too large
Load Diff
27
dnsmasq.spec
27
dnsmasq.spec
@ -1,6 +1,6 @@
|
|||||||
Name: dnsmasq
|
Name: dnsmasq
|
||||||
Version: 2.82
|
Version: 2.82
|
||||||
Release: 6
|
Release: 7
|
||||||
Summary: Dnsmasq provides network infrastructure for small networks
|
Summary: Dnsmasq provides network infrastructure for small networks
|
||||||
License: GPLv2 or GPLv3
|
License: GPLv2 or GPLv3
|
||||||
URL: http://www.thekelleys.org.uk/dnsmasq/
|
URL: http://www.thekelleys.org.uk/dnsmasq/
|
||||||
@ -22,6 +22,16 @@ Patch11: backport-fix-regression-in-s_config_in_context-method.patch
|
|||||||
Patch12: backport-Add-missing-check-for-NULL-return-from-allocate_rfd.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
|
Patch13: backport-Fix-DNS-reply-when-asking-for-DNSSEC-and-a-validated.patch
|
||||||
Patch14: backport-Handle-DHCPREBIND-requests-in-the-DHCPv6-server.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: dbus-devel pkgconfig libidn2-devel nettle-devel systemd
|
BuildRequires: dbus-devel pkgconfig libidn2-devel nettle-devel systemd
|
||||||
Requires: nettle >= 3.4
|
Requires: nettle >= 3.4
|
||||||
@ -114,6 +124,21 @@ install -Dpm644 %{SOURCE2} $RPM_BUILD_ROOT%{_sysusersdir}/dnsmasq.conf
|
|||||||
%{_mandir}/man8/dnsmasq*
|
%{_mandir}/man8/dnsmasq*
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
* Wed Apr 21 2021 gaihuiying <gaihuiying1@huawei.com> - 2.82-7
|
||||||
|
- Type:CVE
|
||||||
|
- Id:NA
|
||||||
|
- SUG:NA
|
||||||
|
- DESC: Handle caching with EDNS options better
|
||||||
|
Fix to 75e2f0aec33e58ef5b8d4d107d821c215a52827c
|
||||||
|
Fix for 12af2b171de0d678d98583e2190789e544440e02
|
||||||
|
Fix problem with DNS retries in 2.83 2.84 version
|
||||||
|
Simplify preceding fix
|
||||||
|
Update to new struct frec fields in conntrack code
|
||||||
|
Use the values of min port and max port in TCP connection
|
||||||
|
Correct occasional bind dynamic synchronization
|
||||||
|
Move fd into frec_src fixes 15b60ddf935a531269bb8c68
|
||||||
|
fix CVE-2021-3448
|
||||||
|
|
||||||
* Tue Apr 12 2021 seuzw <930zhaowei@163.com> - 2.82-6
|
* Tue Apr 12 2021 seuzw <930zhaowei@163.com> - 2.82-6
|
||||||
- Type:requirement
|
- Type:requirement
|
||||||
- Id:NA
|
- Id:NA
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user