Add --no-0x20-encode config option.

The "bit 0x20 encoding" implemented in 995a16ca0c
can interact badly with (hopefully) rare broken upstream servers. Provide
an option to turn it off and a log message to give a clue as to why DNS service
is non-functional.
This commit is contained in:
Simon Kelley
2025-02-03 21:02:12 +00:00
parent 1f84cde024
commit 5226b712a3
6 changed files with 62 additions and 19 deletions

View File

@@ -109,7 +109,12 @@ version 2.91
on the number of a-z and A-Z characters in the query, and this on the number of a-z and A-Z characters in the query, and this
implementation puts a hard limit of 32 bits to make rescource implementation puts a hard limit of 32 bits to make rescource
allocation easy. This about doubles entropy over the standard allocation easy. This about doubles entropy over the standard
random ID and random port combination. random ID and random port combination. This technique can interact
badly with rare broken DNS servers which don't preserve the case
of the query in their reply. The first time a reply is returned
which matches the query in all respects except case, a warning
will be logged. If this coincides with DNS not functioning, it
is necessary to disable bit 0x20 encoding with --no-0x20-encode.
version 2.90 version 2.90

View File

@@ -852,6 +852,14 @@ name on successive queries, for load-balancing. This turns off that
behaviour, so that the records are always returned in the order behaviour, so that the records are always returned in the order
that they are received from upstream. that they are received from upstream.
.TP .TP
.B --no-0x20-encode
By default, dnsmasq scambles the case of letters in DNS queries it sends upstream as a security feature.
This technique can interact badly with rare broken DNS servers which don't preserve the case
of the query in their reply. The first time a reply is returned
which matches the query in all respects except case, a warning
will be logged. If this coincides with DNS not functioning, it
is necessary to disable this scrambling with --no-0x20-encode.
.TP
.B --use-stale-cache[=<max TTL excess in s>] .B --use-stale-cache[=<max TTL excess in s>]
When set, if a DNS name exists in the cache, but its time-to-live has expired, dnsmasq will return the data anyway. (It attempts to refresh the When set, if a DNS name exists in the cache, but its time-to-live has expired, dnsmasq will return the data anyway. (It attempts to refresh the
data with an upstream query after returning the stale data.) This can improve speed and reliability. It comes at the expense data with an upstream query after returning the stale data.) This can improve speed and reliability. It comes at the expense

View File

@@ -279,7 +279,8 @@ struct event_desc {
#define OPT_CACHE_RR 71 #define OPT_CACHE_RR 71
#define OPT_LOCALHOST_SERVICE 72 #define OPT_LOCALHOST_SERVICE 72
#define OPT_LOG_PROTO 73 #define OPT_LOG_PROTO 73
#define OPT_LAST 74 #define OPT_NO_0x20 74
#define OPT_LAST 75
#define OPTION_BITS (sizeof(unsigned int)*8) #define OPTION_BITS (sizeof(unsigned int)*8)
#define OPTION_SIZE ( (OPT_LAST/OPTION_BITS)+((OPT_LAST%OPTION_BITS)!=0) ) #define OPTION_SIZE ( (OPT_LAST/OPTION_BITS)+((OPT_LAST%OPTION_BITS)!=0) )

View File

@@ -326,7 +326,7 @@ static void forward_query(int udpfd, union mysockaddr *udpaddr,
forward->new_id = get_id(); forward->new_id = get_id();
header->id = ntohs(forward->new_id); header->id = ntohs(forward->new_id);
forward->encode_bitmap = rand32(); forward->encode_bitmap = option_bool(OPT_NO_0x20) ? 0 : rand32();
p = (unsigned char *)(header+1); p = (unsigned char *)(header+1);
if (!extract_name(header, plen, &p, NULL, EXTR_NAME_FLIP, forward->encode_bitmap)) if (!extract_name(header, plen, &p, NULL, EXTR_NAME_FLIP, forward->encode_bitmap))
goto reply; goto reply;
@@ -2016,7 +2016,7 @@ static ssize_t tcp_talk(int first, int last, int start, unsigned char *packet,
sending replies containing questions and bogus answers. sending replies containing questions and bogus answers.
Try another server, or give up */ Try another server, or give up */
p = (unsigned char *)(header+1); p = (unsigned char *)(header+1);
if (extract_name(header, rsize, &p, daemon->namebuff, EXTR_NAME_NOCASE, 4) != 1) if (extract_name(header, rsize, &p, daemon->namebuff, EXTR_NAME_COMPARE, 4) != 1)
continue; continue;
GETSHORT(rtype, p); GETSHORT(rtype, p);
GETSHORT(rclass, p); GETSHORT(rclass, p);
@@ -3057,21 +3057,34 @@ static struct frec *lookup_frec(char *target, int class, int rrtype, int id, int
(header = blockdata_retrieve(f->stash, f->stash_len, NULL))) (header = blockdata_retrieve(f->stash, f->stash_len, NULL)))
{ {
unsigned char *p = (unsigned char *)(header+1); unsigned char *p = (unsigned char *)(header+1);
int hclass, hrrtype; int hclass, hrrtype, rc;
/* Case sensitive compare for DNS-0x20 encoding. */ /* Case sensitive compare for DNS-0x20 encoding. */
if (extract_name(header, f->stash_len, &p, target, EXTR_NAME_NOCASE, 4) != 1) if ((rc = extract_name(header, f->stash_len, &p, target, option_bool(OPT_NO_0x20) ? EXTR_NAME_COMPARE : EXTR_NAME_NOCASE, 4)))
continue; {
GETSHORT(hrrtype, p);
GETSHORT(hclass, p);
GETSHORT(hrrtype, p); /* type checked by flags for DNSSEC queries. */
GETSHORT(hclass, p); if (rrtype != -1 && rrtype != hrrtype)
continue;
/* type checked by flags for DNSSEC queries. */ if (class != hclass)
if (rrtype != -1 && rrtype != hrrtype) continue;
continue; }
if (class != hclass) if (rc != 1)
continue; {
static int warned = 0;
if (rc == 3 && !warned)
{
my_syslog(LOG_WARNING, _("Case mismatch in DNS reply - check bit 0x20 encoding."));
warned = 1;
}
continue;
}
return f; return f;
} }

View File

@@ -193,6 +193,7 @@ struct myoption {
#define LOPT_MAX_PROCS 384 #define LOPT_MAX_PROCS 384
#define LOPT_DNSSEC_LIMITS 385 #define LOPT_DNSSEC_LIMITS 385
#define LOPT_PXE_OPT 386 #define LOPT_PXE_OPT 386
#define LOPT_NO_ENCODE 387
#ifdef HAVE_GETOPT_LONG #ifdef HAVE_GETOPT_LONG
static const struct option opts[] = static const struct option opts[] =
@@ -247,6 +248,7 @@ static const struct myoption opts[] =
{ "local-ttl", 1, 0, 'T' }, { "local-ttl", 1, 0, 'T' },
{ "no-negcache", 0, 0, 'N' }, { "no-negcache", 0, 0, 'N' },
{ "no-round-robin", 0, 0, LOPT_NORR }, { "no-round-robin", 0, 0, LOPT_NORR },
{ "no-0x20-encode", 0, 0, LOPT_NO_ENCODE },
{ "cache-rr", 1, 0, LOPT_CACHE_RR }, { "cache-rr", 1, 0, LOPT_CACHE_RR },
{ "addn-hosts", 1, 0, 'H' }, { "addn-hosts", 1, 0, 'H' },
{ "hostsdir", 1, 0, LOPT_HOST_INOTIFY }, { "hostsdir", 1, 0, LOPT_HOST_INOTIFY },
@@ -591,6 +593,7 @@ static struct {
{ LOPT_UMBRELLA, ARG_ONE, "[=<optspec>]", gettext_noop("Send Cisco Umbrella identifiers including remote IP."), NULL }, { LOPT_UMBRELLA, ARG_ONE, "[=<optspec>]", gettext_noop("Send Cisco Umbrella identifiers including remote IP."), NULL },
{ LOPT_QUIET_TFTP, OPT_QUIET_TFTP, NULL, gettext_noop("Do not log routine TFTP."), NULL }, { LOPT_QUIET_TFTP, OPT_QUIET_TFTP, NULL, gettext_noop("Do not log routine TFTP."), NULL },
{ LOPT_NORR, OPT_NORR, NULL, gettext_noop("Suppress round-robin ordering of DNS records."), NULL }, { LOPT_NORR, OPT_NORR, NULL, gettext_noop("Suppress round-robin ordering of DNS records."), NULL },
{ LOPT_NO_ENCODE, OPT_NO_0x20, NULL, gettext_noop("Suppress DNS bit 0x20 encoding."), NULL },
{ LOPT_NO_IDENT, OPT_NO_IDENT, NULL, gettext_noop("Do not add CHAOS TXT records."), NULL }, { LOPT_NO_IDENT, OPT_NO_IDENT, NULL, gettext_noop("Do not add CHAOS TXT records."), NULL },
{ LOPT_CACHE_RR, ARG_DUP, "<RR-type>", gettext_noop("Cache this DNS resource record type."), NULL }, { LOPT_CACHE_RR, ARG_DUP, "<RR-type>", gettext_noop("Cache this DNS resource record type."), NULL },
{ LOPT_MAX_PROCS, ARG_ONE, "<integer>", gettext_noop("Maximum number of concurrent tcp connections."), NULL }, { LOPT_MAX_PROCS, ARG_ONE, "<integer>", gettext_noop("Maximum number of concurrent tcp connections."), NULL },

View File

@@ -24,6 +24,7 @@
return = 0 -> error return = 0 -> error
return = 1 -> extract OK, compare OK, flip OK return = 1 -> extract OK, compare OK, flip OK
return = 2 -> extract OK, compare failed. return = 2 -> extract OK, compare failed.
return = 3 -> extract OK, compare failed but only on case.
*/ */
int extract_name(struct dns_header *header, size_t plen, unsigned char **pp, int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
char *name, int func, unsigned int parm) char *name, int func, unsigned int parm)
@@ -141,8 +142,20 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
if (case_insens && c2 >= 'A' && c2 <= 'Z') if (case_insens && c2 >= 'A' && c2 <= 'Z')
c2 += 'a' - 'A'; c2 += 'a' - 'A';
if (!case_insens && retvalue != 2 && c1 != c2)
{
if (c1 >= 'A' && c1 <= 'Z')
c1 += 'a' - 'A';
if (c2 >= 'A' && c2 <= 'Z')
c2 += 'a' - 'A';
if (c1 == c2)
retvalue = 3;
}
if (c1 != c2) if (c1 != c2)
retvalue = 2; retvalue = 2;
} }
} }