Compare commits

...
20 Commits
Author SHA1 Message Date
Dominik Derigs e5e8cae1ca Add --no-ident option. 2023-01-23 22:48:01 +00:00
Simon Kelley 7f42ca8af8 Add acknowledgements to CHANGELOG for the 2.88 AWS efforts. 2023-01-14 11:12:17 +00:00
Simon Kelley e4251eb13b Fix Changelog typos. 2023-01-14 11:01:10 +00:00
Simon Kelley 5083876910 Bump version in Debian changelog. 2023-01-13 22:03:33 +00:00
Simon Kelley f172fdbb77 Fix bug which can break the invariants on the order of a hash chain.
If there are multiple cache records with the same name but different
F_REVERSE and/or F_IMMORTAL flags, the code added in fe9a134b could
concievable break the REVERSE-FORWARD-IMMORTAL order invariant.

Reproducing this is damn near impossible, but it is responsible
for rare and otherwise inexplicable reversion between 2.87 and 2.88
which manifests itself as a cache internal error. All observed
cases have depended on DNSSEC being enabled, but the bug could in
theory manifest itself without DNSSEC

Thanks to Timo van Roermund for reporting the bug and huge
efforts to isolate it.
2023-01-13 21:12:53 +00:00
Simon Kelley 3822825e54 Fix cosmetic big in dump_cache_entry() 2023-01-04 23:10:07 +00:00
Simon Kelley 1da54210fc Log all cache internal errors. 2023-01-02 22:17:57 +00:00
Simon Kelley 43a2a66531 If we hit a cache internal error, log the entry we failed to remove.
This is code which should never run, but if it does,
we now log information useful for debugging.
2022-12-22 23:19:05 +00:00
Simon Kelley e6841ea2e0 Add posix-timezone and tzdb-timezone DHCPv6 options.
They are already in place for DHCPv4.
2022-12-04 22:00:54 +00:00
Simon Kelley e939b45c9f Handle malformed DNS replies better.
If we detect that that reply from usptream is malformed,
transform it into a SERVFAIL reply before sending to the
original requestor.
2022-11-26 22:19:29 +00:00
Brad Smith e3068ed111 Fix warning in cache.c 2022-11-26 21:48:17 +00:00
Dominik Derigs efbf80be58 Make max staleness of stale cache entries configurable and default to one day. 2022-11-26 21:18:34 +00:00
Petr Menšík 022ad63f0c Fix use-after-free in mark_servers() 2022-11-26 18:49:21 +00:00
Petr Menšík 02f8754339 fixup! Handle DS records for unsupported crypto algorithms. 2022-11-22 22:51:11 +00:00
Simon Kelley 142456cfd0 Merge i18n strings. 2022-11-21 16:56:51 +00:00
Simon Kelley 207ce40db2 Add /etc/hosts gotcha to man page section for --dhcp-hosts. 2022-11-21 16:53:56 +00:00
Simon Kelley 881eaa4dbc Optimise readng large number --server options at start up.
When re-reading upstream servers from /etc/resolv.conf or other
sources that can change dnsmasq tries to avoid memory fragmentation by
re-using existing records that are being re-read unchanged. This
involves seaching all the server records for each new one installed.
During startup this search is pointless, and can cause long start
times with thousands of --server options because the work needed is
O(n^2). Handle this case more intelligently.  Thanks to Ye Zhou for
spotting the problem and an initial patch.
2022-11-21 16:40:53 +00:00
Matthias Andree d6d7527c95 Fix CHANGELOG typos. 2022-11-18 09:10:32 +00:00
Conrad Kostecki 11b4be2036 Update german translation for release 2.88. 2022-11-17 20:00:42 +00:00
Simon Kelley 3e306c1202 Fix SEGV on --local= added by immediately previous commit. 2022-11-17 19:51:15 +00:00
23 changed files with 8357 additions and 6536 deletions

No files matched your search

+44 -11
View File
@@ -1,23 +1,43 @@
version 2.89
Fix bug introduced in 2.88 (commit fe91134b) which can result
in corruption of the DNS cache internal data structures and
logging of "cache internal error". This has only been seen
in one place in the wild, and it took considerable effort
to even generate a test case to reproduce it, but there's
no way to be sure it won't strike, and the effect is to break
the cache badly. Installations with DNSSEC enabled are more
likely to see the problem, but not running DNSSEC does not
guarantee that it won't happen. Thanks to Timo van Roermund
for reporting the bug and for his great efforts in chasing
it down.
version 2.88
Fix bug in --dynamic-host when an interface has /16 IPv4
address. Thanks to Mark Dietzer for spotting this.
address. Thanks to Mark Dietzer for spotting this.
Add --fast-dns-retry option. This gives dnsmasq the ability
to originate retries for upstream DNS queries itself, rather
than relying on the downstream client. This is most useful
when doing DNSSEC over unreliable upstream network. It comes
when doing DNSSEC over unreliable upstream networks. It comes
with some cost in memory usage and network bandwidth.
Add -use-stale-cache option. When set, if a DNS name exists
Add --use-stale-cache option. 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
This can improve speed and reliability. It comes
at the expense of sometimes returning out-of-date data and
less efficient cache utilisation, since old data cannot be
flushed when its TTL expires, so the cache becomes
strictly least-recently-used.
Add --port-limit option which allows tuning for robustness in
the face of some upstream network errors. Thanks to
Prashant Kumar Singh, Ravi Nagayach and Mike Danilov,
all of Amazon Web Services, for their efforts in developing this
and the stale-cache and fast-retry options.
Make --hostsdir (but NOT --dhcp-hostsdir and --dhcp-optsdir)
handle removal of whole files or entries within files.
Thanks to Dominik Derigs for the initial patches for this.
@@ -26,18 +46,18 @@ version 2.88
servers being removed from the configuration when reloading
server configuration from DBus, or re-reading /etc/resolv.conf
Only servers from the same source should be replaced, but some
servers from other sources (ie hard coded or another dynamic source)
servers from other sources (i.e., hard coded or another dynamic source)
could mysteriously disappear. Thanks to all reporting this,
but especially Christopher J. Masden who reduced the problem
to an easily reproducible case which saved mucg labour in
but especially Christopher J. Madsen who reduced the problem
to an easily reproducible case which saved much labour in
finding it.
Add --no-round-robin option.
Allow domain names as well as IP addresses when specifying
upstream DNS servers. There are some gotchas associated with this,
upstream DNS servers. There are some gotchas associated with this
(it will mysteriously fail to work if the dnsmasq instance
being started is in the path from the system resolver to the DNS)
being started is in the path from the system resolver to the DNS),
and a seemingly sensible configuration like
--server=domain.name@1.2.3.4 is unactionable if domain.name
only resolves to an IPv6 address). There are, however,
@@ -46,9 +66,22 @@ version 2.88
Handle DS records for unsupported crypto algorithms correctly.
Such a DS, as long as it is validated, should allow answers
in the domain is attests to be returned as unvalidated, and not
in the domain it attests to be returned as unvalidated, and not
as a validation error.
Optimise reading large numbers of --server options. When re-reading
upstream servers from /etc/resolv.conf or other sources that
can change dnsmasq tries to avoid memory fragmentation by re-using
existing records that are being re-read unchanged. This involves
seaching all the server records for each new one installed.
During startup this search is pointless, and can cause long
start times with thousands of --server options because the work
needed is O(n^2). Handle this case more intelligently.
Thanks to Ye Zhou for spotting the problem and an initial patch.
If we detect that a DNS reply from upstream is malformed don't
return it to the requestor; send a SEVFAIL rcode instead.
version 2.87
Allow arbitrary prefix lengths in --rev-server and
+6
View File
@@ -1,3 +1,9 @@
dnsmasq (2.89-1) unstable; urgency=low
* New upstream.
-- Simon Kelley <simon@thekelleys.org.uk> Fri, 13 Jan 2023 21:57:01 +0000
dnsmasq (2.88-1) unstable; urgency=low
* New upstream.
+19 -12
View File
@@ -828,11 +828,12 @@ name on successive queries, for load-balancing. This turns off that
behaviour, so that the records are always returned in the order
that they are received from upstream.
.TP
.B --use-stale-cache
.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
data with an upstream query after returning the stale data.) This can improve speed and reliability. It comes at the expense
of sometimes returning out-of-date data and less efficient cache utilisation, since old data cannot be flushed when its TTL expires, so the cache becomes
strictly least-recently-used.
mostly least-recently-used. To mitigate issues caused by massively outdated DNS replies, the maximum overaging of cached records can be specified in seconds
(defaulting to not serve anything older than one day). Setting the TTL excess time to zero will serve stale cache data regardless how long it has expired.
.TP
.B \-0, --dns-forward-max=<queries>
Set the maximum number of concurrent DNS queries. The default value is
@@ -1166,7 +1167,8 @@ given in a
.B --dhcp-host
option, but aliases are possible by using CNAMEs. (See
.B --cname
).
). Note that /etc/hosts is NOT used when the DNS server side of dnsmasq
is disabled by setting the DNS server port to zero.
More than one
.B --dhcp-host
@@ -2213,6 +2215,20 @@ exit 0
and /share/ads-domains.gz containing a compressed
list of ad server domains will save disk space with large ad-server blocklists.
.TP
.B --no-ident
Do not respond to class CHAOS and type TXT in domain bind queries.
Without this option being set, the cache statistics are also available in the
DNS as answers to queries of class CHAOS and type TXT in domain bind. The domain
names are cachesize.bind, insertions.bind, evictions.bind, misses.bind,
hits.bind, auth.bind and servers.bind unless disabled at compile-time. An
example command to query this, using the
.B dig
utility would be
dig +short chaos txt cachesize.bind
.SH CONFIG FILE
At startup, dnsmasq reads
.I /etc/dnsmasq.conf,
@@ -2262,15 +2278,6 @@ resulted in an error. In
mode or when full logging is enabled (\fB--log-queries\fP), a complete dump of the
contents of the cache is made.
The cache statistics are also available in the DNS as answers to
queries of class CHAOS and type TXT in domain bind. The domain names are cachesize.bind, insertions.bind, evictions.bind,
misses.bind, hits.bind, auth.bind and servers.bind. An example command to query this, using the
.B dig
utility would be
dig +short chaos txt cachesize.bind
.PP
When it receives SIGUSR2 and it is logging direct to a file (see
.B --log-facility
)
+709 -532
View File
File diff suppressed because it is too large. Load diff
+705 -528
View File
File diff suppressed because it is too large. Load diff
+846 -679
View File
File diff suppressed because it is too large. Load diff
+706 -528
View File
File diff suppressed because it is too large. Load diff
+716 -531
View File
File diff suppressed because it is too large. Load diff
+846 -679
View File
File diff suppressed because it is too large. Load diff
+554 -610
View File
File diff suppressed because it is too large. Load diff
+702 -527
View File
File diff suppressed because it is too large. Load diff
+705 -528
View File
File diff suppressed because it is too large. Load diff
+846 -679
View File
File diff suppressed because it is too large. Load diff
+702 -527
View File
File diff suppressed because it is too large. Load diff
+122 -99
View File
@@ -28,6 +28,7 @@ static int bignames_left, hash_size;
static void make_non_terminals(struct crec *source);
static struct crec *really_insert(char *name, union all_addr *addr, unsigned short class,
time_t now, unsigned long ttl, unsigned int flags);
static void dump_cache_entry(struct crec *cache, time_t now);
/* type->string mapping: this is also used by the name-hash function as a mixing table. */
/* taken from https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml */
@@ -235,19 +236,23 @@ static void cache_hash(struct crec *crecp)
char *name = cache_get_name(crecp);
struct crec **up = hash_bucket(name);
if (!(crecp->flags & F_REVERSE))
unsigned int flags = crecp->flags & (F_IMMORTAL | F_REVERSE);
if (!(flags & F_REVERSE))
{
while (*up && ((*up)->flags & F_REVERSE))
up = &((*up)->hash_next);
if (crecp->flags & F_IMMORTAL)
if (flags & F_IMMORTAL)
while (*up && !((*up)->flags & F_IMMORTAL))
up = &((*up)->hash_next);
}
/* Preserve order when inserting the same name multiple times. */
while (*up && hostname_isequal(cache_get_name(*up), name))
/* Preserve order when inserting the same name multiple times.
Do not mess up the flag invariants. */
while (*up &&
hostname_isequal(cache_get_name(*up), name) &&
flags == ((*up)->flags & (F_IMMORTAL | F_REVERSE)))
up = &((*up)->hash_next);
crecp->hash_next = *up;
@@ -380,9 +385,17 @@ static int is_outdated_cname_pointer(struct crec *crecp)
static int is_expired(time_t now, struct crec *crecp)
{
/* Don't dump expired entries if we're using them, cache becomes strictly LRU in that case.
Never use expired DS or DNSKEY entries. */
if (option_bool(OPT_STALE_CACHE) && !(crecp->flags & (F_DS | F_DNSKEY)))
/* Don't dump expired entries if they are within the accepted timeout range.
The cache becomes approx. LRU. Never use expired DS or DNSKEY entries.
Possible values for daemon->cache_max_expiry:
-1 == serve cached content regardless how long ago it expired
0 == the option is disabled, expired content isn't served
<n> == serve cached content only if it expire less than <n> seconds
ago (where n is a positive integer) */
if (daemon->cache_max_expiry != 0 &&
(daemon->cache_max_expiry == -1 ||
difftime(now, crecp->ttd) < daemon->cache_max_expiry) &&
!(crecp->flags & (F_DS | F_DNSKEY)))
return 0;
if (crecp->flags & F_IMMORTAL)
@@ -586,7 +599,7 @@ static struct crec *really_insert(char *name, union all_addr *addr, unsigned sho
struct crec *new, *target_crec = NULL;
union bigname *big_name = NULL;
int freed_all = (flags & F_REVERSE);
int free_avail = 0;
struct crec *free_avail = NULL;
unsigned int target_uid;
/* if previous insertion failed give up now. */
@@ -634,7 +647,7 @@ static struct crec *really_insert(char *name, union all_addr *addr, unsigned sho
/* Free entry at end of LRU list, use it. */
if (!(new->flags & (F_FORWARD | F_REVERSE)))
break;
break;
/* End of LRU list is still in use: if we didn't scan all the hash
chains for expired entries do that now. If we already tried that
@@ -646,12 +659,9 @@ static struct crec *really_insert(char *name, union all_addr *addr, unsigned sho
insert. Once in this state, all inserts will probably fail. */
if (free_avail)
{
static int warned = 0;
if (!warned)
{
my_syslog(LOG_ERR, _("Internal error in cache."));
warned = 1;
}
my_syslog(LOG_ERR, _("Internal error in cache."));
/* Log the entry we tried to delete. */
dump_cache_entry(free_avail, now);
insert_error = 1;
return NULL;
}
@@ -659,13 +669,13 @@ static struct crec *really_insert(char *name, union all_addr *addr, unsigned sho
if (freed_all)
{
/* For DNSSEC records, uid holds class. */
free_avail = 1; /* Must be free space now. */
free_avail = new; /* Must be free space now. */
/* condition valid when stale-caching */
if (difftime(now, new->ttd) < 0)
daemon->metrics[METRIC_DNS_CACHE_LIVE_FREED]++;
cache_scan_free(cache_get_name(new), &new->addr, new->uid, now, new->flags, NULL, NULL);
cache_scan_free(cache_get_name(new), &new->addr, new->uid, now, new->flags, NULL, NULL);
}
else
{
@@ -1072,7 +1082,6 @@ static void add_hosts_entry(struct crec *cache, union all_addr *addr, int addrle
the array rhash, hashed on address. Note that rhash and the values
in ->next are only valid whilst reading hosts files: the buckets are
then freed, and the ->next pointer used for other things.
Only insert each unique address once into this hashing structure.
This complexity avoids O(n^2) divergent CPU use whilst reading
@@ -1752,6 +1761,94 @@ static char *sanitise(char *name)
return name;
}
static void dump_cache_entry(struct crec *cache, time_t now)
{
(void)now;
static char *buff = NULL;
char *p, *t = " ";
char *a = daemon->addrbuff, *n = cache_get_name(cache);
/* String length is limited below */
if (!buff && !(buff = whine_malloc(150)))
return;
p = buff;
*a = 0;
if (strlen(n) == 0 && !(cache->flags & F_REVERSE))
n = "<Root>";
p += sprintf(p, "%-30.30s ", sanitise(n));
if ((cache->flags & F_CNAME) && !is_outdated_cname_pointer(cache))
a = sanitise(cache_get_cname_target(cache));
else if ((cache->flags & F_SRV) && !(cache->flags & F_NEG))
{
int targetlen = cache->addr.srv.targetlen;
ssize_t len = sprintf(a, "%u %u %u ", cache->addr.srv.priority,
cache->addr.srv.weight, cache->addr.srv.srvport);
if (targetlen > (40 - len))
targetlen = 40 - len;
blockdata_retrieve(cache->addr.srv.target, targetlen, a + len);
a[len + targetlen] = 0;
}
#ifdef HAVE_DNSSEC
else if (cache->flags & F_DS)
{
if (!(cache->flags & F_NEG))
sprintf(a, "%5u %3u %3u", cache->addr.ds.keytag,
cache->addr.ds.algo, cache->addr.ds.digest);
}
else if (cache->flags & F_DNSKEY)
sprintf(a, "%5u %3u %3u", cache->addr.key.keytag,
cache->addr.key.algo, cache->addr.key.flags);
#endif
else if (!(cache->flags & F_NEG) || !(cache->flags & F_FORWARD))
{
a = daemon->addrbuff;
if (cache->flags & F_IPV4)
inet_ntop(AF_INET, &cache->addr, a, ADDRSTRLEN);
else if (cache->flags & F_IPV6)
inet_ntop(AF_INET6, &cache->addr, a, ADDRSTRLEN);
}
if (cache->flags & F_IPV4)
t = "4";
else if (cache->flags & F_IPV6)
t = "6";
else if (cache->flags & F_CNAME)
t = "C";
else if (cache->flags & F_SRV)
t = "V";
#ifdef HAVE_DNSSEC
else if (cache->flags & F_DS)
t = "S";
else if (cache->flags & F_DNSKEY)
t = "K";
#endif
else if (!(cache->flags & F_NXDOMAIN)) /* non-terminal */
t = "!";
p += sprintf(p, "%-40.40s %s%s%s%s%s%s%s%s%s%s ", a, t,
cache->flags & F_FORWARD ? "F" : " ",
cache->flags & F_REVERSE ? "R" : " ",
cache->flags & F_IMMORTAL ? "I" : " ",
cache->flags & F_DHCP ? "D" : " ",
cache->flags & F_NEG ? "N" : " ",
cache->flags & F_NXDOMAIN ? "X" : " ",
cache->flags & F_HOSTS ? "H" : " ",
cache->flags & F_CONFIG ? "C" : " ",
cache->flags & F_DNSSECOK ? "V" : " ");
#ifdef HAVE_BROKEN_RTC
p += sprintf(p, "%-24lu", cache->flags & F_IMMORTAL ? 0: (unsigned long)(cache->ttd - now));
#else
p += sprintf(p, "%-24.24s", cache->flags & F_IMMORTAL ? "" : ctime(&(cache->ttd)));
#endif
if(cache->flags & (F_HOSTS | F_CONFIG) && cache->uid > 0)
p += sprintf(p, " %-40.40s", record_source(cache->uid));
my_syslog(LOG_INFO, "%s", buff);
}
void dump_cache(time_t now)
{
@@ -1762,7 +1859,7 @@ void dump_cache(time_t now)
daemon->cachesize, daemon->metrics[METRIC_DNS_CACHE_LIVE_FREED], daemon->metrics[METRIC_DNS_CACHE_INSERTED]);
my_syslog(LOG_INFO, _("queries forwarded %u, queries answered locally %u"),
daemon->metrics[METRIC_DNS_QUERIES_FORWARDED], daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]);
if (option_bool(OPT_STALE_CACHE))
if (daemon->cache_max_expiry != 0)
my_syslog(LOG_INFO, _("queries answered from stale cache %u"), daemon->metrics[METRIC_DNS_STALE_ANSWERED]);
#ifdef HAVE_AUTH
my_syslog(LOG_INFO, _("queries for authoritative zones %u"), daemon->metrics[METRIC_DNS_AUTH_ANSWERED]);
@@ -1799,98 +1896,24 @@ void dump_cache(time_t now)
if (option_bool(OPT_DEBUG) || option_bool(OPT_LOG))
{
struct crec *cache ;
struct crec *cache;
int i;
my_syslog(LOG_INFO, "Host Address Flags Expires Source");
my_syslog(LOG_INFO, "------------------------------ ---------------------------------------- ---------- ------------------------ ------------");
for (i=0; i<hash_size; i++)
for (cache = hash_table[i]; cache; cache = cache->hash_next)
{
char *t = " ";
char *a = daemon->addrbuff, *p = daemon->namebuff, *n = cache_get_name(cache);
*a = 0;
if (strlen(n) == 0 && !(cache->flags & F_REVERSE))
n = "<Root>";
p += sprintf(p, "%-30.30s ", sanitise(n));
if ((cache->flags & F_CNAME) && !is_outdated_cname_pointer(cache))
a = sanitise(cache_get_cname_target(cache));
else if ((cache->flags & F_SRV) && !(cache->flags & F_NEG))
{
int targetlen = cache->addr.srv.targetlen;
ssize_t len = sprintf(a, "%u %u %u ", cache->addr.srv.priority,
cache->addr.srv.weight, cache->addr.srv.srvport);
if (targetlen > (40 - len))
targetlen = 40 - len;
blockdata_retrieve(cache->addr.srv.target, targetlen, a + len);
a[len + targetlen] = 0;
}
#ifdef HAVE_DNSSEC
else if (cache->flags & F_DS)
{
if (!(cache->flags & F_NEG))
sprintf(a, "%5u %3u %3u", cache->addr.ds.keytag,
cache->addr.ds.algo, cache->addr.ds.digest);
}
else if (cache->flags & F_DNSKEY)
sprintf(a, "%5u %3u %3u", cache->addr.key.keytag,
cache->addr.key.algo, cache->addr.key.flags);
#endif
else if (!(cache->flags & F_NEG) || !(cache->flags & F_FORWARD))
{
a = daemon->addrbuff;
if (cache->flags & F_IPV4)
inet_ntop(AF_INET, &cache->addr, a, ADDRSTRLEN);
else if (cache->flags & F_IPV6)
inet_ntop(AF_INET6, &cache->addr, a, ADDRSTRLEN);
}
if (cache->flags & F_IPV4)
t = "4";
else if (cache->flags & F_IPV6)
t = "6";
else if (cache->flags & F_CNAME)
t = "C";
else if (cache->flags & F_SRV)
t = "V";
#ifdef HAVE_DNSSEC
else if (cache->flags & F_DS)
t = "S";
else if (cache->flags & F_DNSKEY)
t = "K";
#endif
else /* non-terminal */
t = "!";
p += sprintf(p, "%-40.40s %s%s%s%s%s%s%s%s%s%s ", a, t,
cache->flags & F_FORWARD ? "F" : " ",
cache->flags & F_REVERSE ? "R" : " ",
cache->flags & F_IMMORTAL ? "I" : " ",
cache->flags & F_DHCP ? "D" : " ",
cache->flags & F_NEG ? "N" : " ",
cache->flags & F_NXDOMAIN ? "X" : " ",
cache->flags & F_HOSTS ? "H" : " ",
cache->flags & F_CONFIG ? "C" : " ",
cache->flags & F_DNSSECOK ? "V" : " ");
#ifdef HAVE_BROKEN_RTC
p += sprintf(p, "%-24lu", cache->flags & F_IMMORTAL ? 0: (unsigned long)(cache->ttd - now));
#else
p += sprintf(p, "%-24.24s", cache->flags & F_IMMORTAL ? "" : ctime(&(cache->ttd)));
#endif
if(cache->flags & (F_HOSTS | F_CONFIG) && cache->uid > 0)
p += sprintf(p, " %s", record_source(cache->uid));
my_syslog(LOG_INFO, "%s", daemon->namebuff);
}
dump_cache_entry(cache, now);
}
}
char *record_source(unsigned int index)
{
struct hostsfile *ah;
#ifdef HAVE_INOTIFY
struct dyndir *dd;
#endif
if (index == SRC_CONFIG)
return "config";
else if (index == SRC_HOSTS)
+1
View File
@@ -60,6 +60,7 @@
#define LOOP_TEST_DOMAIN "test" /* domain for loop testing, "test" is reserved by RFC 2606 and won't therefore clash */
#define LOOP_TEST_TYPE T_TXT
#define DEFAULT_FAST_RETRY 1000 /* ms, default delay before fast retry */
#define STALE_CACHE_EXPIRY 86400 /* 1 day in secs, default maximum expiry time for stale cache data */
/* compile-time options: uncomment below to enable or do eg.
make COPTS=-DHAVE_BROKEN_RTC
+2
View File
@@ -722,6 +722,8 @@ static const struct opttab_t opttab6[] = {
{ "sntp-server", 31, OT_ADDR_LIST },
{ "information-refresh-time", 32, OT_TIME },
{ "FQDN", 39, OT_INTERNAL | OT_RFC1035_NAME },
{ "posix-timezone", 41, OT_NAME }, /* RFC 4833, Sec. 3 */
{ "tzdb-timezone", 42, OT_NAME }, /* RFC 4833, Sec. 3 */
{ "ntp-server", 56, 0 /* OT_ADDR_LIST | OT_RFC1035_NAME */ },
{ "bootfile-url", 59, OT_NAME },
{ "bootfile-param", 60, OT_CSTRING },
+3 -2
View File
@@ -280,8 +280,8 @@ struct event_desc {
#define OPT_FILTER_AAAA 68
#define OPT_STRIP_ECS 69
#define OPT_STRIP_MAC 70
#define OPT_STALE_CACHE 71
#define OPT_NORR 72
#define OPT_NORR 71
#define OPT_NO_IDENT 72
#define OPT_LAST 73
#define OPTION_BITS (sizeof(unsigned int)*8)
@@ -1201,6 +1201,7 @@ extern struct daemon {
unsigned long soa_sn, soa_refresh, soa_retry, soa_expiry;
u32 metrics[__METRIC_MAX];
int fast_retry_time, fast_retry_timeout;
int cache_max_expiry;
#ifdef HAVE_DNSSEC
struct ds_config *ds;
char *timestamp_file;
+1 -1
View File
@@ -1068,7 +1068,7 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
algo = *p++;
digest = *p++;
if (!ds_digest_name(digest) || !ds_digest_name(digest))
if (!ds_digest_name(digest) || !algo_digest_name(algo))
{
a.log.keytag = keytag;
a.log.algo = algo;
+40 -23
View File
@@ -546,11 +546,23 @@ static int order_qsort(const void *a, const void *b)
return rc;
}
/* When loading large numbers of server=.... lines during startup,
there's no possibility that there will be server records that can be reused, but
searching a long list for each server added grows as O(n^2) and slows things down.
This flag is set only if is known there may be free server records that can be reused.
There's a call to mark_servers(0) in read_opts() to reset the flag before
main config read. */
static int maybe_free_servers = 0;
/* Must be called before add_update_server() to set daemon->servers_tail */
void mark_servers(int flag)
{
struct server *serv, **up;
struct server *serv, *next, **up;
maybe_free_servers = !!flag;
daemon->servers_tail = NULL;
/* mark everything with argument flag */
@@ -568,11 +580,13 @@ void mark_servers(int flag)
1) numerous and 2) not reloaded often. We just delete
and recreate. */
if (flag)
for (serv = daemon->local_domains, up = &daemon->local_domains; serv; serv = serv->next)
for (serv = daemon->local_domains, up = &daemon->local_domains; serv; serv = next)
{
next = serv->next;
if (serv->flags & flag)
{
*up = serv->next;
*up = next;
free(serv->domain);
free(serv);
}
@@ -667,27 +681,30 @@ int add_update_server(int flags,
and move to the end of the list, for order. The entry found may already
be at the end. */
struct server **up, *tmp;
for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp)
{
tmp = serv->next;
if ((serv->flags & SERV_MARK) &&
hostname_isequal(alloc_domain, serv->domain))
{
/* Need to move down? */
if (serv->next)
{
*up = serv->next;
daemon->servers_tail->next = serv;
daemon->servers_tail = serv;
serv->next = NULL;
}
break;
}
else
up = &serv->next;
}
serv = NULL;
if (maybe_free_servers)
for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp)
{
tmp = serv->next;
if ((serv->flags & SERV_MARK) &&
hostname_isequal(alloc_domain, serv->domain))
{
/* Need to move down? */
if (serv->next)
{
*up = serv->next;
daemon->servers_tail->next = serv;
daemon->servers_tail = serv;
serv->next = NULL;
}
break;
}
else
up = &serv->next;
}
if (serv)
{
free(alloc_domain);
+11 -1
View File
@@ -821,12 +821,22 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
n = rrfilter(header, n, RRFILTER_AAAA);
}
if (extract_addresses(header, n, daemon->namebuff, now, ipsets, nftsets, is_sign, check_rebind, no_cache, cache_secure, &doctored))
switch (extract_addresses(header, n, daemon->namebuff, now, ipsets, nftsets, is_sign, check_rebind, no_cache, cache_secure, &doctored))
{
case 1:
my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff);
munged = 1;
cache_secure = 0;
ede = EDE_BLOCKED;
break;
/* extract_addresses() found a malformed answer. */
case 2:
munged = 1;
SET_RCODE(header, SERVFAIL);
cache_secure = 0;
ede = EDE_OTHER;
break;
}
if (doctored)
+53 -23
View File
@@ -185,6 +185,7 @@ struct myoption {
#define LOPT_FAST_RETRY 376
#define LOPT_STALE_CACHE 377
#define LOPT_NORR 378
#define LOPT_NO_IDENT 379
#ifdef HAVE_GETOPT_LONG
static const struct option opts[] =
@@ -373,7 +374,8 @@ static const struct myoption opts[] =
{ "quiet-tftp", 0, 0, LOPT_QUIET_TFTP },
{ "port-limit", 1, 0, LOPT_RANDPORT_LIM },
{ "fast-dns-retry", 2, 0, LOPT_FAST_RETRY },
{ "use-stale-cache", 0, 0 , LOPT_STALE_CACHE },
{ "use-stale-cache", 2, 0 , LOPT_STALE_CACHE },
{ "no-ident", 0, 0, LOPT_NO_IDENT },
{ NULL, 0, 0, 0 }
};
@@ -431,7 +433,7 @@ static struct {
{ 'M', ARG_DUP, "<bootp opts>", gettext_noop("Specify BOOTP options to DHCP server."), NULL },
{ 'n', OPT_NO_POLL, NULL, gettext_noop("Do NOT poll %s file, reload only on SIGHUP."), RESOLVFILE },
{ 'N', OPT_NO_NEG, NULL, gettext_noop("Do NOT cache failed search results."), NULL },
{ LOPT_STALE_CACHE, OPT_STALE_CACHE, NULL, gettext_noop("Use expired cache data for faster reply."), NULL },
{ LOPT_STALE_CACHE, ARG_ONE, "[=<max_expired>]", gettext_noop("Use expired cache data for faster reply."), NULL },
{ 'o', OPT_ORDER, NULL, gettext_noop("Use nameservers strictly in the order given in %s."), RESOLVFILE },
{ 'O', ARG_DUP, "<optspec>", gettext_noop("Specify options to be sent to DHCP clients."), NULL },
{ LOPT_FORCE, ARG_DUP, "<optspec>", gettext_noop("DHCP option sent even if the client does not request it."), NULL},
@@ -570,6 +572,7 @@ static struct {
{ 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_NORR, OPT_NORR, NULL, gettext_noop("Suppress round-robin ordering of DNS records."), NULL },
{ LOPT_NO_IDENT, OPT_NO_IDENT, NULL, gettext_noop("Do not add CHAOS TXT records."), NULL },
{ 0, 0, NULL, NULL, NULL }
};
@@ -866,10 +869,7 @@ char *parse_server(char *arg, struct server_details *sdetails)
*sdetails->interface = 0;
sdetails->addr_type = AF_UNSPEC;
sdetails->valid = 0;
sdetails->hostinfo = sdetails->orig_hostinfo = NULL;
if (strcmp(arg, "#") == 0)
{
if (sdetails->flags)
@@ -1104,6 +1104,8 @@ static char *domain_rev4(int from_file, char *server, struct in_addr *addr4, int
char interface[IF_NAMESIZE+1];
int count = 1, rem, addrbytes, addrbits;
struct server_details sdetails;
memset(&sdetails, 0, sizeof(struct server_details));
sdetails.addr = &serv_addr;
sdetails.source_addr = &source_addr;
sdetails.interface = interface;
@@ -1185,6 +1187,8 @@ static char *domain_rev6(int from_file, char *server, struct in6_addr *addr6, in
char interface[IF_NAMESIZE+1];
int count = 1, rem, addrbytes, addrbits;
struct server_details sdetails;
memset(&sdetails, 0, sizeof(struct server_details));
sdetails.addr = &serv_addr;
sdetails.source_addr = &source_addr;
sdetails.interface = interface;
@@ -2970,13 +2974,14 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
union all_addr addr;
union mysockaddr serv_addr, source_addr;
char interface[IF_NAMESIZE+1];
struct server_details sdetails;
memset(&sdetails, 0, sizeof(struct server_details));
sdetails.addr = &serv_addr;
sdetails.source_addr = &source_addr;
sdetails.interface = interface;
sdetails.flags = &flags;
unhide_metas(arg);
/* split the domain args, if any and skip to the end of them. */
@@ -5149,6 +5154,24 @@ err:
break;
}
case LOPT_STALE_CACHE:
{
int max_expiry = STALE_CACHE_EXPIRY;
if (arg)
{
/* Don't accept negative TTLs here, they'd have the counter-intuitive
side-effect of evicting cache records before they expire */
if (!atoi_check(arg, &max_expiry) || max_expiry < 0)
ret_err(gen_err);
/* Store "serve expired forever" as -1 internally, the option isn't
active for daemon->cache_max_expiry == 0 */
if (max_expiry == 0)
max_expiry = -1;
}
daemon->cache_max_expiry = max_expiry;
break;
}
#ifdef HAVE_DNSSEC
case LOPT_DNSSEC_STAMP: /* --dnssec-timestamp */
daemon->timestamp_file = opt_string_alloc(arg);
@@ -5737,21 +5760,9 @@ void read_opts(int argc, char **argv, char *compile_opts)
daemon->randport_limit = 1;
daemon->host_index = SRC_AH;
#ifndef NO_ID
add_txt("version.bind", "dnsmasq-" VERSION, 0 );
add_txt("authors.bind", "Simon Kelley", 0);
add_txt("copyright.bind", COPYRIGHT, 0);
add_txt("cachesize.bind", NULL, TXT_STAT_CACHESIZE);
add_txt("insertions.bind", NULL, TXT_STAT_INSERTS);
add_txt("evictions.bind", NULL, TXT_STAT_EVICTIONS);
add_txt("misses.bind", NULL, TXT_STAT_MISSES);
add_txt("hits.bind", NULL, TXT_STAT_HITS);
#ifdef HAVE_AUTH
add_txt("auth.bind", NULL, TXT_STAT_AUTH);
#endif
add_txt("servers.bind", NULL, TXT_STAT_SERVERS);
#endif
/* See comment above make_servers(). Optimises server-read code. */
mark_servers(0);
while (1)
{
#ifdef HAVE_GETOPT_LONG
@@ -5846,6 +5857,25 @@ void read_opts(int argc, char **argv, char *compile_opts)
else
one_file(CONFFILE, LOPT_CONF_OPT);
/* Add TXT records if wanted */
#ifndef NO_ID
if (!option_bool(OPT_NO_IDENT))
{
add_txt("version.bind", "dnsmasq-" VERSION, 0 );
add_txt("authors.bind", "Simon Kelley", 0);
add_txt("copyright.bind", COPYRIGHT, 0);
add_txt("cachesize.bind", NULL, TXT_STAT_CACHESIZE);
add_txt("insertions.bind", NULL, TXT_STAT_INSERTS);
add_txt("evictions.bind", NULL, TXT_STAT_EVICTIONS);
add_txt("misses.bind", NULL, TXT_STAT_MISSES);
add_txt("hits.bind", NULL, TXT_STAT_HITS);
#ifdef HAVE_AUTH
add_txt("auth.bind", NULL, TXT_STAT_AUTH);
#endif
add_txt("servers.bind", NULL, TXT_STAT_SERVERS);
}
#endif
/* port might not be known when the address is parsed - fill in here */
if (daemon->servers)
{
+18 -16
View File
@@ -538,7 +538,9 @@ static int print_txt(struct dns_header *header, const size_t qlen, char *name,
/* Note that the following code can create CNAME chains that don't point to a real record,
either because of lack of memory, or lack of SOA records. These are treated by the cache code as
expired and cleaned out that way.
Return 1 if we reject an address because it look like part of dns-rebinding attack. */
Return 1 if we reject an address because it look like part of dns-rebinding attack.
Return 2 if the packet is malformed.
*/
int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t now,
struct ipsets *ipsets, struct ipsets *nftsets, int is_sign, int check_rebind,
int no_cache_dnssec, int secure, int *doctored)
@@ -589,7 +591,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
namep = p = (unsigned char *)(header+1);
if (ntohs(header->qdcount) != 1 || !extract_name(header, qlen, &p, name, 1, 4))
return 0; /* bad packet */
return 2; /* bad packet */
GETSHORT(qtype, p);
GETSHORT(qclass, p);
@@ -607,13 +609,13 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
{
cname_loop:
if (!(p1 = skip_questions(header, qlen)))
return 0;
return 2;
for (j = 0; j < ntohs(header->ancount); j++)
{
int secflag = 0;
if (!(res = extract_name(header, qlen, &p1, name, 0, 10)))
return 0; /* bad packet */
return 2; /* bad packet */
GETSHORT(aqtype, p1);
GETSHORT(aqclass, p1);
@@ -651,7 +653,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
log_query(secflag | F_CNAME | F_FORWARD | F_UPSTREAM, name, NULL, NULL, 0);
if (!extract_name(header, qlen, &p1, name, 1, 0))
return 0;
return 2;
if (aqtype == T_CNAME)
{
@@ -677,7 +679,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
p1 = endrr;
if (!CHECK_LEN(header, p1, qlen, 0))
return 0; /* bad packet */
return 2; /* bad packet */
}
}
@@ -722,14 +724,14 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
cname_loop1:
if (!(p1 = skip_questions(header, qlen)))
return 0;
return 2;
for (j = 0; j < ntohs(header->ancount); j++)
{
int secflag = 0;
if (!(res = extract_name(header, qlen, &p1, name, 0, 10)))
return 0; /* bad packet */
return 2; /* bad packet */
GETSHORT(aqtype, p1);
GETSHORT(aqclass, p1);
@@ -747,7 +749,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
{
p1 = endrr;
if (!CHECK_LEN(header, p1, qlen, 0))
return 0; /* bad packet */
return 2; /* bad packet */
continue;
}
@@ -790,7 +792,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
namep = p1;
if (!extract_name(header, qlen, &p1, name, 1, 0))
return 0;
return 2;
if (qtype != T_CNAME)
goto cname_loop1;
@@ -813,25 +815,25 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
unsigned char *tmp = namep;
if (!CHECK_LEN(header, p1, qlen, 6))
return 0; /* bad packet */
return 2; /* bad packet */
GETSHORT(addr.srv.priority, p1);
GETSHORT(addr.srv.weight, p1);
GETSHORT(addr.srv.srvport, p1);
if (!extract_name(header, qlen, &p1, name, 1, 0))
return 0;
return 2;
addr.srv.targetlen = strlen(name) + 1; /* include terminating zero */
if (!(addr.srv.target = blockdata_alloc(name, addr.srv.targetlen)))
return 0;
/* we overwrote the original name, so get it back here. */
if (!extract_name(header, qlen, &tmp, name, 1, 0))
return 0;
return 2;
}
else if (flags & (F_IPV4 | F_IPV6))
{
/* copy address into aligned storage */
if (!CHECK_LEN(header, p1, qlen, addrlen))
return 0; /* bad packet */
return 2; /* bad packet */
memcpy(&addr, p1, addrlen);
/* check for returned address in private space */
@@ -875,7 +877,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
if (aqtype == T_TXT)
{
if (!print_txt(header, qlen, name, p1, ardlen, secflag))
return 0;
return 2;
}
else
log_query(flags | F_FORWARD | secflag | F_UPSTREAM, name, &addr, NULL, aqtype);
@@ -883,7 +885,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
p1 = endrr;
if (!CHECK_LEN(header, p1, qlen, 0))
return 0; /* bad packet */
return 2; /* bad packet */
}
if (!found && (qtype != T_ANY || (flags & F_NXDOMAIN)))