Compare commits

...

68 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
Simon Kelley
7f227a83f2 Fix struct hostinfo free code and BSD compile.
The code added in6 c596f1cc1d92b2b90ef5ce043ace314eefa868b
fails to free the returned datastructures from gethostinfo()
because sdetails.hostinfo is used to loop through the addresses
and ends up NULL. In some libc implementations this results
in a SEGV when freeaddrinfo() is called.

Also fix FTBFS under BSD. Thanks to Johnny S. Lee for the bug report.
2022-11-17 13:16:55 +00:00
Simon Kelley
9ed3ee67ec 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
as a validation error.
2022-11-16 16:49:30 +00:00
Simon Kelley
1f9215f5f9 Fix GOST signature algorithms for DNSSEC validation.
Use CryptoPro version of the hash function.
Handle the little-endian wire format of key data.
Get the wire order of S and R correct.

Note that Nettle version 3.6 or later is required for GOST support.
2022-11-16 15:57:31 +00:00
Simon Kelley
f52cfdd8c3 Handle known DNSSEC signature algorithms which are not supported.
This fixes a confusion if certain algorithms are not supported
because the version is the crypto library is too old.  The validation
should be treated the same as for a completely unknown algorithm,
(ie return unverified answer) and not as a validation failure
(ie return SERVFAIL).

The algorithems affected are GOST and ED448.
2022-11-13 15:55:09 +00:00
Simon Kelley
2fc904111d Fix --server=/domain/# 2022-11-07 23:24:31 +00:00
Simon Kelley
262dadf50e Fix --server with multiple domains. 2022-11-07 23:14:30 +00:00
Simon Kelley
6c596f1cc1 Make specifying nameservers by name work for DBus API. 2022-11-07 23:00:34 +00:00
Simon Kelley
dafa16c400 Call freeaddrinfo() in domain_rev[46]() 2022-11-07 22:01:28 +00:00
Simon Kelley
1db9943c68 Extend specifying DNS servers by domain-name to --rev-server
Also Dbus SetDomainServers method.

Revert getaddrinfo hints.ai_socktype to SOCK_DGRAM to eliminate
duplicating every address three times for DGRAM, STREAM and RAW
in the results.
2022-11-06 21:10:19 +00:00
Simon Kelley
5b868c213b Fix breakage of --local=/domain.name/1.2.3.4 in immediately previous commit. 2022-11-06 20:18:27 +00:00
Dominik Derigs
2d8905dafd Allow domain names as well is IP addresses in --server options. 2022-11-05 11:49:52 +00:00
Simon Kelley
9002108551 Bump Debian version and close bug. 2022-11-02 22:18:35 +00:00
Simon Kelley
d3c21c596e Reconcile "names" and "address" counts when reading hostfiles. 2022-10-30 15:40:20 +00:00
Simon Kelley
34fac952b6 Inotify: make "flushed" log message more understandable.
Saying we've "flushed x outdated entries" is confusing, since
the count is the total number of entries in the modified file,
most of which are going	to get added straight back when	the file
is re-read.

The log now looks like

dnsmasq: inotify: /tmp/dir/1 (new or modified)
dnsmasq: inotify: flushed 1 addresses read from /tmp/dir/1
dnsmasq: read /tmp/dir/1 - 2 addresses

which hopefully make it more obvious that /tmp/dir/1 contained one
address before, and now contains two.
2022-10-27 13:24:37 +01:00
Dominik Derigs
92c32e0bac Do not (try to) re-read deleted files inside a --hostsdir. 2022-10-27 12:36:38 +01:00
Simon Kelley
1bcad67806 Fix in dhcpv4 rapid-commit code.
1) Cosmetic: don't log the tags twice.

2) Functional. If a host has an old lease for a different address,
   the rapid-commit will appear to work, but the old lease will
   not be removed and the new lease will not be recorded, so
   the client and server will have conflicting state, leading to
   problems later.
2022-10-27 12:04:58 +01:00
Simon Kelley
fe9a134baf Add --no-round-robin option. 2022-10-18 16:06:48 +01:00
Simon Kelley
930428fb97 Fix loss of DNS servers on config reload.
A bug, introduced in 2.87, which could result in DNS
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)
could mysteriously disappear.
2022-10-17 21:15:43 +01:00
Dominik Derigs
936be022d9 Handle multiple addresses when removing duplicates in host files. 2022-10-16 22:30:08 +01:00
Dominik Derigs
0017dd74d5 Enhance --hostdir so that records are automatically removed when re-reading.
Initial patch from Dominik Derigs, re-written by Simon Kelley.
2022-10-16 22:10:48 +01:00
Dominik Derigs
0ba25a0512 Improve logging of DNS record source from --hostsdir files.
Patch author Dominik Derigs <dl6er@dl6er.de> with subsequent bugfixes
and tweaks from Simon Kelley.
2022-10-16 21:14:16 +01:00
Simon Kelley
a176cf1bc3 Move fast-dns-retry and use-stale-cache writeups in the CHANGELOG.
These are 2.88 changes, but the branch merge put them unde 2.87.
2022-10-14 11:46:13 +01:00
Simon Kelley
fdd9a96a8c Merge branch 'aws' 2022-10-13 15:37:52 +01:00
Simon Kelley
b87d7aa041 Fix bug in --dynamic-host when interface has /16 IPv4 address. 2022-10-13 15:02:54 +01:00
Temuri Doghonadze
f753e7eba6 Add Georgian translation. 2022-10-13 14:33:01 +01:00
Simon Kelley
78a5a21655 Fix Debian changelog date Fubar. 2022-09-25 23:55:09 +01:00
Simon Kelley
a5cbe6d112 Add ClearMetrics Dbus method. 2022-09-16 12:58:41 +01:00
Simon Kelley
9403664616 Optimise cache code when stale caching in use.
Exclude DNSSEC entries from stale caching.
2022-09-16 12:44:04 +01:00
Simon Kelley
f32498465d Don't exclude stale-cache answers from "local answered" metric. 2022-09-16 09:35:44 +01:00
Simon Kelley
fa45e06431 Initialise modified-moving-average latency calc better.
Use the first value, rather than initialising at zero,
which takes many queries to converge.
2022-09-16 00:16:18 +01:00
Simon Kelley
6722ec6c78 Split failed queries in retries in stat counting. 2022-09-16 00:07:36 +01:00
Simon Kelley
d882dfdae9 Tweak server-selection logic in the fast-retry case. 2022-09-15 23:54:53 +01:00
Simon Kelley
a2ee2426bf Keep a per-DNS-server moving average of query latency. 2022-09-15 23:22:02 +01:00
Simon Kelley
84bd46ddd7 Combine server stats from all records for the same server in DBUS method.
The DBUS per-server stats method should combine the stats from
different records (for different domains) in the same way at the
logging code.
2022-09-15 22:43:08 +01:00
Simon Kelley
271790685a Count NXDOMAIN replies from each server. 2022-09-15 22:29:44 +01:00
Simon Kelley
7a74037267 Add metric for queries which never see an answer. 2022-09-15 22:06:39 +01:00
Simon Kelley
9a9f6e147c Make fast-retry more configurable and do exponential backoff. 2022-09-15 19:29:49 +01:00
Simon Kelley
8f2d432799 Remove unused vars. 2022-09-13 09:36:08 +01:00
Simon Kelley
92eab03b12 Return EDE_STALE extended error when returning stale data from cache. 2022-09-12 15:28:46 +01:00
Simon Kelley
1ba4ae2830 Add stale cache replies to metrics. 2022-09-12 14:50:17 +01:00
Simon Kelley
0076481dfd Add GetServerMetrics method to DBus interface. 2022-09-12 14:35:40 +01:00
Simon Kelley
c0e731d545 Further optimisation of --port-limit.
No longer try and fail to open every port when the port range
is in complete use; go straight to re-using an existing socket.

Die at startup if port range is smaller than --port-limit, since
the code behaves badly in this case.
2022-09-09 23:15:50 +01:00
Simon Kelley
3f56bb8ba1 Second try at port-limit option.
1) It's expected to fail to bind a new source port when they
   are scarce, suppress warning in log in this case.

2) Optimse bind_local when max_port - min_port is small. There's no
   randomness in this case, so we try all possible source ports
   rather than poking at random ones for an arbitrary number of tries.

3) In allocate_rfd() handle the case that all available source ports
   are already open. In this case we need to pick an existing
   socket/port to use, such that it has a different port from any we
   already hold. This gives the required property that the set of ports
   utilised by any given query is set by --port-limit and we don't
   re-use any until we have port-limit different ones.
2022-09-09 17:09:32 +01:00
Simon Kelley
e518e87533 Fix namebuff overwrite leading to wrong log after socket bind warning. 2022-09-09 15:57:39 +01:00
Simon Kelley
1d53d958bb Remove fast-retry development logging. 2022-09-06 22:43:33 +01:00
Simon Kelley
d334e7c34f Add --use-stale-cache option. 2022-09-06 22:43:33 +01:00
Simon Kelley
d21438a7df 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 with some cost in memory usage and network bandwidth.
2022-09-06 22:43:33 +01:00
Simon Kelley
24c3b5b3d4 Add --port-limit option.
By default, when sending a query via random ports to multiple upstream servers or
retrying a query dnsmasq will use a single random port for all the tries/retries.
This option allows a larger number of ports to be used, which can increase robustness
in certain network configurations. Note that increasing this to more than
two or three can have security and resource implications and should only
be done with understanding of those.
2022-09-06 22:43:33 +01:00
33 changed files with 11973 additions and 6313 deletions

View File

@@ -1,3 +1,88 @@
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.
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 networks. It comes
with some cost in memory usage and network bandwidth.
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
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.
Fix bug, introduced in 2.87, which could result in DNS
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 (i.e., hard coded or another dynamic source)
could mysteriously disappear. Thanks to all reporting this,
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
(it will mysteriously fail to work if the dnsmasq instance
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,
cases where is can be useful. Thanks to Dominik Derigs for
the patch.
Handle DS records for unsupported crypto algorithms correctly.
Such a DS, as long as it is validated, should allow answers
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
--domain=....,local
@@ -84,7 +169,7 @@ version 2.87
client facing network. Thanks to Luis Thomas for spotting this
and initial patch.
version 2.86
Handle DHCPREBIND requests in the DHCPv6 server code.
Thanks to Aichun Li for spotting this omission, and the initial

View File

@@ -252,6 +252,15 @@ GetMetrics
Returns an array with various metrics for DNS and DHCP.
GetServerMetrics
----------------
Returns per-DNS-server metrics.
ClearMetrics
------------
Clear call metric counters, global and per-server.
2. SIGNALS
----------

16
debian/changelog vendored
View File

@@ -1,3 +1,17 @@
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.
* Fix loss of server configuration (closes: #1020830)
Git commit 930428fb970f4991e5c2933fd5a5d2504c18a551
-- Simon Kelley <simon@thekelleys.org.uk> Wed, 2 Nov 2022 22:15:45 +0000
dnsmasq (2.87-1) unstable; urgency=low
* New upstream. (closes: #1001209, #1003156)
@@ -6,7 +20,7 @@ dnsmasq (2.87-1) unstable; urgency=low
* Fix rare lockup in DNSSEC. (closes: #1001576)
* Close old bug. (closes: #902963)
-- Simon Kelley <simon@thekelleys.org.uk> Wed, 08 Sep 2021 23:11:25 +0000
-- Simon Kelley <simon@thekelleys.org.uk> Wed, 25 Sep 2022 23:11:25 +0000
dnsmasq (2.86-1.1) unstable; urgency=medium

View File

@@ -60,7 +60,8 @@ in alphabetical order.
.TP
.B --hostsdir=<path>
Read all the hosts files contained in the directory. New or changed files
are read automatically. See \fB--dhcp-hostsdir\fP for details.
are read automatically and modified and deleted files have removed records
automatically deleted.
.TP
.B \-E, --expand-hosts
Add the domain to simple names (without a period) in /etc/hosts
@@ -105,6 +106,16 @@ Dnsmasq limits the value of this option to one hour, unless recompiled.
.B --auth-ttl=<time>
Set the TTL value returned in answers from the authoritative server.
.TP
.B --fast-dns-retry=[<initial retry delay in ms>[,<time to continue retries in ms>]]
Under normal circumstances, dnsmasq relies on DNS clients to do retries; it
does not generate timeouts itself. Setting this option
instructs dnsmasq to generate its own retries starting after a delay
which defaults to 1000ms. If the second parameter is given this controls
how long the retries will continue for
otherwise this defaults to 10000ms. Retries are repeated with exponential
backoff. Using this option increases memory usage and
network bandwidth.
.TP
.B \-k, --keep-in-foreground
Do not go into the background at startup but otherwise run as
normal. This is intended for use when dnsmasq is run under daemontools
@@ -180,7 +191,15 @@ specific UDP port <query_port> instead of using random ports. NOTE
that using this option will make dnsmasq less secure against DNS
spoofing attacks but it may be faster and use less resources. Setting this option
to zero makes dnsmasq use a single port allocated to it by the
OS: this was the default behaviour in versions prior to 2.43.
OS: this was the default behaviour in versions prior to 2.43.
.TP
.B --port-limit=<#ports>
By default, when sending a query via random ports to multiple upstream servers or
retrying a query dnsmasq will use a single random port for all the tries/retries.
This option allows a larger number of ports to be used, which can increase robustness
in certain network configurations. Note that increasing this to more than
two or three can have security and resource implications and should only
be done with understanding of those.
.TP
.B --min-port=<port>
Do not use ports less than that given as source for outbound DNS
@@ -443,8 +462,8 @@ Tells dnsmasq to never forward A or AAAA queries for plain names, without dots
or domain parts, to upstream nameservers. If the name is not known
from /etc/hosts or DHCP then a "not found" answer is returned.
.TP
.B \-S, --local, --server=[/[<domain>]/[domain/]][<ipaddr>[#<port>]][@<interface>][@<source-ip>[#<port>]]
Specify IP address of upstream servers directly. Setting this flag does
.B \-S, --local, --server=[/[<domain>]/[domain/]][<server>[#<port>]][@<interface>][@<source-ip>[#<port>]]
Specify upstream servers directly. Setting this flag does
not suppress reading of /etc/resolv.conf, use \fB--no-resolv\fP to do that. If one or more
optional domains are given, that server is used only for those domains
and they are queried only using the specified server. This is
@@ -512,8 +531,14 @@ The query-port flag is ignored for any servers which have a
source address specified but the port may be specified directly as
part of the source address. Forcing queries to an interface is not
implemented on all platforms supported by dnsmasq.
Upstream servers may be specified with a hostname rather than an IP address.
In this case, dnsmasq will try to use the system resolver to get the IP address
of a server during startup. If name resolution fails, starting dnsmasq fails, too.
If the system's configuration is such that the system resolver sends DNS queries
through the dnsmasq instance which is starting up then this will time-out and fail.
.TP
.B --rev-server=<ip-address>[/<prefix-len>][,<ipaddr>][#<port>][@<interface>][@<source-ip>[#<port>]]
.B --rev-server=<ip-address>[/<prefix-len>][,<server>][#<port>][@<interface>][@<source-ip>[#<port>]]
This is functionally the same as
.B --server,
but provides some syntactic sugar to make specifying address-to-name queries easier. For example
@@ -797,6 +822,19 @@ Disable negative caching. Negative caching allows dnsmasq to remember
"no such domain" answers from upstream nameservers and answer
identical queries without forwarding them again.
.TP
.B --no-round-robin
Dnsmasq normally permutes the order of A or AAAA records for the same
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[=<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
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
150, which should be fine for most setups. The only known situation
@@ -1129,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
@@ -2176,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,
@@ -2225,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
)

1241
po/de.po

File diff suppressed because it is too large Load Diff

1233
po/es.po

File diff suppressed because it is too large Load Diff

1525
po/fi.po

File diff suppressed because it is too large Load Diff

1234
po/fr.po

File diff suppressed because it is too large Load Diff

1247
po/id.po

File diff suppressed because it is too large Load Diff

1525
po/it.po

File diff suppressed because it is too large Load Diff

2746
po/ka.po Normal file

File diff suppressed because it is too large Load Diff

1229
po/no.po

File diff suppressed because it is too large Load Diff

1233
po/pl.po

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1229
po/ro.po

File diff suppressed because it is too large Load Diff

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 */
@@ -189,7 +190,7 @@ static void rehash(int size)
else if (new_size <= hash_size || !(new = whine_malloc(new_size * sizeof(struct crec *))))
return;
for(i = 0; i < new_size; i++)
for (i = 0; i < new_size; i++)
new[i] = NULL;
old = hash_table;
@@ -233,17 +234,27 @@ static void cache_hash(struct crec *crecp)
immortal entries are at the end of the hash-chain.
This allows reverse searches and garbage collection to be optimised */
struct crec **up = hash_bucket(cache_get_name(crecp));
if (!(crecp->flags & F_REVERSE))
char *name = cache_get_name(crecp);
struct crec **up = hash_bucket(name);
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.
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;
*up = crecp;
}
@@ -374,6 +385,19 @@ 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 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)
return 0;
@@ -383,6 +407,27 @@ static int is_expired(time_t now, struct crec *crecp)
return 1;
}
/* Remove entries with a given UID from the cache */
unsigned int cache_remove_uid(const unsigned int uid)
{
int i;
unsigned int removed = 0;
struct crec *crecp, **up;
for (i = 0; i < hash_size; i++)
for (crecp = hash_table[i], up = &hash_table[i]; crecp; crecp = crecp->hash_next)
if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) && crecp->uid == uid)
{
*up = crecp->hash_next;
free(crecp);
removed++;
}
else
up = &crecp->hash_next;
return removed;
}
static struct crec *cache_scan_free(char *name, union all_addr *addr, unsigned short class, time_t now,
unsigned int flags, struct crec **target_crec, unsigned int *target_uid)
{
@@ -553,8 +598,8 @@ 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;
int freed_all = (flags & F_REVERSE);
struct crec *free_avail = NULL;
unsigned int target_uid;
/* if previous insertion failed give up now. */
@@ -602,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
@@ -614,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;
}
@@ -627,9 +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. */
cache_scan_free(cache_get_name(new), &new->addr, new->uid, now, new->flags, NULL, NULL);
daemon->metrics[METRIC_DNS_CACHE_LIVE_FREED]++;
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);
}
else
{
@@ -692,7 +738,7 @@ static struct crec *really_insert(char *name, union all_addr *addr, unsigned sho
new->ttd = now + (time_t)ttl;
new->next = new_chain;
new_chain = new;
return new;
}
@@ -870,7 +916,7 @@ int cache_find_non_terminal(char *name, time_t now)
struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsigned int prot)
{
struct crec *ans;
int no_rr = prot & F_NO_RR;
int no_rr = (prot & F_NO_RR) || option_bool(OPT_NORR);
prot &= ~F_NO_RR;
@@ -1018,16 +1064,17 @@ struct crec *cache_find_by_addr(struct crec *crecp, union all_addr *addr,
static void add_hosts_entry(struct crec *cache, union all_addr *addr, int addrlen,
unsigned int index, struct crec **rhash, int hashsz)
{
struct crec *lookup = cache_find_by_name(NULL, cache_get_name(cache), 0, cache->flags & (F_IPV4 | F_IPV6));
int i;
unsigned int j;
struct crec *lookup = NULL;
/* Remove duplicates in hosts files. */
if (lookup && (lookup->flags & F_HOSTS) && memcmp(&lookup->addr, addr, addrlen) == 0)
{
free(cache);
return;
}
while ((lookup = cache_find_by_name(lookup, cache_get_name(cache), 0, cache->flags & (F_IPV4 | F_IPV6))))
if ((lookup->flags & F_HOSTS) && memcmp(&lookup->addr, addr, addrlen) == 0)
{
free(cache);
return;
}
/* Ensure there is only one address -> name mapping (first one trumps)
We do this by steam here, The entries are kept in hash chains, linked
@@ -1035,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
@@ -1132,7 +1178,7 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
{
FILE *f = fopen(filename, "r");
char *token = daemon->namebuff, *domain_suffix = NULL;
int addr_count = 0, name_count = cache_size, lineno = 1;
int names_done = 0, name_count = cache_size, lineno = 1;
unsigned int flags = 0;
union all_addr addr;
int atnl, addrlen = 0;
@@ -1168,8 +1214,6 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
continue;
}
addr_count++;
/* rehash every 1000 names. */
if (rhash && ((name_count - cache_size) > 1000))
{
@@ -1201,6 +1245,7 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
cache->ttd = daemon->local_ttl;
add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz);
name_count++;
names_done++;
}
if ((cache = whine_malloc(SIZEOF_BARE_CREC + strlen(canon) + 1)))
{
@@ -1209,6 +1254,7 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
cache->ttd = daemon->local_ttl;
add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz);
name_count++;
names_done++;
}
free(canon);
@@ -1225,7 +1271,7 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
if (rhash)
rehash(name_count);
my_syslog(LOG_INFO, _("read %s - %d addresses"), filename, addr_count);
my_syslog(LOG_INFO, _("read %s - %d names"), filename, names_done);
return name_count;
}
@@ -1715,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)
{
@@ -1725,6 +1859,8 @@ 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 (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]);
#endif
@@ -1739,111 +1875,45 @@ void dump_cache(time_t now)
if (!(serv->flags & SERV_MARK))
{
int port;
unsigned int queries = 0, failed_queries = 0;
unsigned int queries = 0, failed_queries = 0, nxdomain_replies = 0, retrys = 0;
unsigned int sigma_latency = 0, count_latency = 0;
for (serv1 = serv; serv1; serv1 = serv1->next)
if (!(serv1->flags & SERV_MARK) && sockaddr_isequal(&serv->addr, &serv1->addr))
{
serv1->flags |= SERV_MARK;
queries += serv1->queries;
failed_queries += serv1->failed_queries;
nxdomain_replies += serv1->nxdomain_replies;
retrys += serv1->retrys;
sigma_latency += serv1->query_latency;
count_latency++;
}
port = prettyprint_addr(&serv->addr, daemon->addrbuff);
my_syslog(LOG_INFO, _("server %s#%d: queries sent %u, retried or failed %u"), daemon->addrbuff, port, queries, failed_queries);
my_syslog(LOG_INFO, _("server %s#%d: queries sent %u, retried %u, failed %u, nxdomain replies %u, avg. latency %ums"),
daemon->addrbuff, port, queries, retrys, failed_queries, nxdomain_replies, sigma_latency/count_latency);
}
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)
@@ -1854,9 +1924,11 @@ char *record_source(unsigned int index)
return ah->fname;
#ifdef HAVE_INOTIFY
for (ah = daemon->dynamic_dirs; ah; ah = ah->next)
if (ah->index == index)
return ah->fname;
/* Dynamic directories contain multiple files */
for (dd = daemon->dynamic_dirs; dd; dd = dd->next)
for (ah = dd->files; ah; ah = ah->next)
if (ah->index == index)
return ah->fname;
#endif
return "<unknown>";
@@ -2079,6 +2151,8 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg,
name = arg;
verb = daemon->addrbuff;
}
else if (flags & F_STALE)
source = "cached-stale";
else
source = "cached";

View File

@@ -24,6 +24,7 @@
#define KEYBLOCK_LEN 40 /* choose to minimise fragmentation when storing DNSSEC keys */
#define DNSSEC_WORK 50 /* Max number of queries to validate one question */
#define TIMEOUT 10 /* drop UDP queries after TIMEOUT seconds */
#define SMALL_PORT_RANGE 30 /* If DNS port range is smaller than this, use different allocation. */
#define FORWARD_TEST 50 /* try all servers every 50 queries */
#define FORWARD_TIME 20 /* or 20 seconds */
#define UDP_TEST_TIME 60 /* How often to reset our idea of max packet size. */
@@ -58,6 +59,8 @@
#define SOA_EXPIRY 1209600 /* SOA expiry default */
#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

View File

@@ -309,14 +309,14 @@ static int dnsmasq_gostdsa_verify(struct blockdata *key_data, unsigned int key_l
mpz_init(y);
}
mpz_import(x, 32 , 1, 1, 0, 0, p);
mpz_import(y, 32 , 1, 1, 0, 0, p + 32);
mpz_import(x, 32, -1, 1, 0, 0, p);
mpz_import(y, 32, -1, 1, 0, 0, p + 32);
if (!ecc_point_set(gost_key, x, y))
return 0;
return 0;
mpz_import(sig_struct->r, 32, 1, 1, 0, 0, sig);
mpz_import(sig_struct->s, 32, 1, 1, 0, 0, sig + 32);
mpz_import(sig_struct->s, 32, 1, 1, 0, 0, sig);
mpz_import(sig_struct->r, 32, 1, 1, 0, 0, sig + 32);
return nettle_gostdsa_verify(gost_key, digest_len, digest, sig_struct);
}
@@ -390,7 +390,12 @@ static int (*verify_func(int algo))(struct blockdata *key_data, unsigned int key
return dnsmasq_ecdsa_verify;
#if MIN_VERSION(3, 1)
case 15: case 16:
case 15:
return dnsmasq_eddsa_verify;
#endif
#if MIN_VERSION(3, 6)
case 16:
return dnsmasq_eddsa_verify;
#endif
}
@@ -425,7 +430,9 @@ char *ds_digest_name(int digest)
{
case 1: return "sha1";
case 2: return "sha256";
case 3: return "gosthash94";
#if MIN_VERSION(3, 6)
case 3: return "gosthash94cp";
#endif
case 4: return "sha384";
default: return NULL;
}
@@ -444,11 +451,17 @@ char *algo_digest_name(int algo)
case 7: return "sha1"; /* RSASHA1-NSEC3-SHA1 */
case 8: return "sha256"; /* RSA/SHA-256 */
case 10: return "sha512"; /* RSA/SHA-512 */
case 12: return "gosthash94"; /* ECC-GOST */
#if MIN_VERSION(3, 6)
case 12: return "gosthash94cp"; /* ECC-GOST */
#endif
case 13: return "sha256"; /* ECDSAP256SHA256 */
case 14: return "sha384"; /* ECDSAP384SHA384 */
#if MIN_VERSION(3, 1)
case 15: return "null_hash"; /* ED25519 */
# if MIN_VERSION(3, 6)
case 16: return "null_hash"; /* ED448 */
# endif
#endif
default: return NULL;
}
}

View File

@@ -91,6 +91,11 @@ const char* introspection_xml_template =
" <method name=\"GetMetrics\">\n"
" <arg name=\"metrics\" direction=\"out\" type=\"a{su}\"/>\n"
" </method>\n"
" <method name=\"GetServerMetrics\">\n"
" <arg name=\"metrics\" direction=\"out\" type=\"a{ss}\"/>\n"
" </method>\n"
" <method name=\"ClearMetrics\">\n"
" </method>\n"
" </interface>\n"
"</node>\n";
@@ -287,6 +292,11 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
u16 flags = 0;
char interface[IF_NAMESIZE];
char *str_addr, *str_domain = NULL;
struct server_details sdetails = { 0 };
sdetails.addr = &addr;
sdetails.source_addr = &source_addr;
sdetails.interface = interface;
sdetails.flags = &flags;
if (strings)
{
@@ -369,20 +379,6 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
strcpy(str_addr, str);
}
/* parse the IP address */
if ((addr_err = parse_server(str_addr, &addr, &source_addr, (char *) &interface, &flags)))
{
error = dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
"Invalid IP address '%s': %s",
str, addr_err);
break;
}
/* 0.0.0.0 for server address == NULL, for Dbus */
if (addr.in.sin_family == AF_INET &&
addr.in.sin_addr.s_addr == 0)
flags |= SERV_LITERAL_ADDRESS;
if (strings)
{
char *p;
@@ -396,7 +392,31 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
else
p = NULL;
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str_domain, NULL);
if (strings && strlen(str_addr) == 0)
add_update_server(SERV_LITERAL_ADDRESS | SERV_FROM_DBUS, &addr, &source_addr, interface, str_domain, NULL);
else
{
if ((addr_err = parse_server(str_addr, &sdetails)))
{
error = dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
"Invalid IP address '%s': %s",
str, addr_err);
break;
}
while (parse_server_next(&sdetails))
{
if ((addr_err = parse_server_addr(&sdetails)))
{
error = dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
"Invalid IP address '%s': %s",
str, addr_err);
break;
}
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str_domain, NULL);
}
}
} while ((str_domain = p));
}
else
@@ -410,11 +430,40 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
if (dbus_message_iter_get_arg_type(&string_iter) == DBUS_TYPE_STRING)
dbus_message_iter_get_basic(&string_iter, &str);
dbus_message_iter_next (&string_iter);
if ((addr_err = parse_server(str_addr, &sdetails)))
{
error = dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
"Invalid IP address '%s': %s",
str, addr_err);
break;
}
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str, NULL);
while (parse_server_next(&sdetails))
{
if ((addr_err = parse_server_addr(&sdetails)))
{
error = dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
"Invalid IP address '%s': %s",
str, addr_err);
break;
}
/* 0.0.0.0 for server address == NULL, for Dbus */
if (addr.in.sin_family == AF_INET &&
addr.in.sin_addr.s_addr == 0)
flags |= SERV_LITERAL_ADDRESS;
else
flags &= ~SERV_LITERAL_ADDRESS;
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str, NULL);
}
} while (dbus_message_iter_get_arg_type(&string_iter) == DBUS_TYPE_STRING);
}
if (sdetails.orig_hostinfo)
freeaddrinfo(sdetails.orig_hostinfo);
/* jump to next element in outer array */
dbus_message_iter_next(&array_iter);
}
@@ -644,6 +693,77 @@ static DBusMessage *dbus_get_metrics(DBusMessage* message)
return reply;
}
static void add_dict_entry(DBusMessageIter *container, const char *key, const char *val)
{
DBusMessageIter dict;
dbus_message_iter_open_container(container, DBUS_TYPE_DICT_ENTRY, NULL, &dict);
dbus_message_iter_append_basic(&dict, DBUS_TYPE_STRING, &key);
dbus_message_iter_append_basic(&dict, DBUS_TYPE_STRING, &val);
dbus_message_iter_close_container(container, &dict);
}
static void add_dict_int(DBusMessageIter *container, const char *key, const unsigned int val)
{
snprintf(daemon->namebuff, MAXDNAME, "%u", val);
add_dict_entry(container, key, daemon->namebuff);
}
static DBusMessage *dbus_get_server_metrics(DBusMessage* message)
{
DBusMessage *reply = dbus_message_new_method_return(message);
DBusMessageIter server_array, dict_array, server_iter;
struct server *serv;
dbus_message_iter_init_append(reply, &server_iter);
dbus_message_iter_open_container(&server_iter, DBUS_TYPE_ARRAY, "a{ss}", &server_array);
/* sum counts from different records for same server */
for (serv = daemon->servers; serv; serv = serv->next)
serv->flags &= ~SERV_MARK;
for (serv = daemon->servers; serv; serv = serv->next)
if (!(serv->flags & SERV_MARK))
{
unsigned int port;
unsigned int queries = 0, failed_queries = 0, nxdomain_replies = 0, retrys = 0;
unsigned int sigma_latency = 0, count_latency = 0;
struct server *serv1;
for (serv1 = serv; serv1; serv1 = serv1->next)
if (!(serv1->flags & SERV_MARK) && sockaddr_isequal(&serv->addr, &serv1->addr))
{
serv1->flags |= SERV_MARK;
queries += serv1->queries;
failed_queries += serv1->failed_queries;
nxdomain_replies += serv1->nxdomain_replies;
retrys += serv1->retrys;
sigma_latency += serv1->query_latency;
count_latency++;
}
dbus_message_iter_open_container(&server_array, DBUS_TYPE_ARRAY, "{ss}", &dict_array);
port = prettyprint_addr(&serv->addr, daemon->namebuff);
add_dict_entry(&dict_array, "address", daemon->namebuff);
add_dict_int(&dict_array, "port", port);
add_dict_int(&dict_array, "queries", serv->queries);
add_dict_int(&dict_array, "failed_queries", serv->failed_queries);
add_dict_int(&dict_array, "nxdomain", serv->nxdomain_replies);
add_dict_int(&dict_array, "retries", serv->retrys);
add_dict_int(&dict_array, "latency", sigma_latency/count_latency);
dbus_message_iter_close_container(&server_array, &dict_array);
}
dbus_message_iter_close_container(&server_iter, &server_array);
return reply;
}
DBusHandlerResult message_handler(DBusConnection *connection,
DBusMessage *message,
void *user_data)
@@ -719,6 +839,14 @@ DBusHandlerResult message_handler(DBusConnection *connection,
{
reply = dbus_get_metrics(message);
}
else if (strcmp(method, "GetServerMetrics") == 0)
{
reply = dbus_get_server_metrics(message);
}
else if (strcmp(method, "ClearMetrics") == 0)
{
clear_metrics();
}
else if (strcmp(method, "ClearCache") == 0)
clear_cache = 1;
else

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 },

View File

@@ -265,6 +265,10 @@ int main (int argc, char **argv)
if (daemon->max_port < daemon->min_port)
die(_("max_port cannot be smaller than min_port"), NULL, EC_BADCONF);
if (daemon->max_port != 0 &&
daemon->max_port - daemon->min_port + 1 < daemon->randport_limit)
die(_("port_limit must not be larger than available port range"), NULL, EC_BADCONF);
now = dnsmasq_time();
@@ -1055,19 +1059,20 @@ int main (int argc, char **argv)
while (1)
{
int timeout = -1;
int timeout = fast_retry(now);
poll_reset();
/* Whilst polling for the dbus, or doing a tftp transfer, wake every quarter second */
if (daemon->tftp_trans ||
(option_bool(OPT_DBUS) && !daemon->dbus))
if ((daemon->tftp_trans || (option_bool(OPT_DBUS) && !daemon->dbus)) &&
(timeout == -1 || timeout > 250))
timeout = 250;
/* Wake every second whilst waiting for DAD to complete */
else if (is_dad_listeners())
else if (is_dad_listeners() &&
(timeout == -1 || timeout > 1000))
timeout = 1000;
set_dns_listeners();
#ifdef HAVE_DBUS
@@ -2014,9 +2019,6 @@ static void check_dns_listeners(time_t now)
buff = tcp_request(confd, now, &tcp_addr, netmask, auth_dns);
shutdown(confd, SHUT_RDWR);
close(confd);
if (buff)
free(buff);

View File

@@ -133,6 +133,7 @@ typedef unsigned long long u64;
#include <sys/uio.h>
#include <syslog.h>
#include <dirent.h>
#include <netdb.h>
#ifndef HAVE_LINUX_NETWORK
# include <net/if_dl.h>
#endif
@@ -279,7 +280,9 @@ struct event_desc {
#define OPT_FILTER_AAAA 68
#define OPT_STRIP_ECS 69
#define OPT_STRIP_MAC 70
#define OPT_LAST 71
#define OPT_NORR 71
#define OPT_NO_IDENT 72
#define OPT_LAST 73
#define OPTION_BITS (sizeof(unsigned int)*8)
#define OPTION_SIZE ( (OPT_LAST/OPTION_BITS)+((OPT_LAST%OPTION_BITS)!=0) )
@@ -510,6 +513,7 @@ struct crec {
#define F_DOMAINSRV (1u<<28)
#define F_RCODE (1u<<29)
#define F_SRV (1u<<30)
#define F_STALE (1u<<31)
#define UID_NONE 0
/* Values of uid in crecs with F_CONFIG bit set. */
@@ -584,7 +588,8 @@ struct server {
struct serverfd *sfd;
int tcpfd, edns_pktsz;
time_t pktsz_reduced;
unsigned int queries, failed_queries;
unsigned int queries, failed_queries, nxdomain_replies, retrys;
unsigned int query_latency, mma_latency;
time_t forwardtime;
int forwardcount;
#ifdef HAVE_LOOP
@@ -684,10 +689,17 @@ struct hostsfile {
struct hostsfile *next;
int flags;
char *fname;
unsigned int index; /* matches to cache entries for logging */
};
struct dyndir {
struct dyndir *next;
struct hostsfile *files;
int flags;
char *dname;
#ifdef HAVE_INOTIFY
int wd; /* inotify watch descriptor */
#endif
unsigned int index; /* matches to cache entries for logging */
};
/* packet-dump flags */
@@ -755,11 +767,13 @@ struct frec {
unsigned short new_id;
int forwardall, flags;
time_t time;
u32 forward_timestamp;
int forward_delay;
unsigned char *hash[HASH_SIZE];
#ifdef HAVE_DNSSEC
int class, work_counter;
struct blockdata *stash; /* Saved reply, whilst we validate */
size_t stash_len;
#ifdef HAVE_DNSSEC
int class, work_counter;
struct frec *dependent; /* Query awaiting internally-generated DNSKEY or DS query */
struct frec *next_dependent; /* list of above. */
struct frec *blocking_query; /* Query which is blocking us. */
@@ -1139,6 +1153,7 @@ extern struct daemon {
int log_fac; /* log facility */
char *log_file; /* optional log file */
int max_logs; /* queue limit */
int randport_limit; /* Maximum number of source ports for query. */
int cachesize, ftabsize;
int port, query_port, min_port, max_port;
unsigned long local_ttl, neg_ttl, max_ttl, min_cache_ttl, max_cache_ttl, auth_ttl, dhcp_ttl, use_dhcp_ttl;
@@ -1146,6 +1161,7 @@ extern struct daemon {
u32 umbrella_org;
u32 umbrella_asset;
u8 umbrella_device[8];
int host_index;
struct hostsfile *addn_hosts;
struct dhcp_context *dhcp, *dhcp6;
struct ra_interface *ra_interfaces;
@@ -1166,7 +1182,8 @@ extern struct daemon {
int doing_ra, doing_dhcp6;
struct dhcp_netid_list *dhcp_ignore, *dhcp_ignore_names, *dhcp_gen_names;
struct dhcp_netid_list *force_broadcast, *bootp_dynamic;
struct hostsfile *dhcp_hosts_file, *dhcp_opts_file, *dynamic_dirs;
struct hostsfile *dhcp_hosts_file, *dhcp_opts_file;
struct dyndir *dynamic_dirs;
int dhcp_max, tftp_max, tftp_mtu;
int dhcp_server_port, dhcp_client_port;
int start_tftp_port, end_tftp_port;
@@ -1183,6 +1200,8 @@ extern struct daemon {
int dump_mask;
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;
@@ -1275,6 +1294,14 @@ extern struct daemon {
#endif
} *daemon;
struct server_details {
union mysockaddr *addr, *source_addr;
struct addrinfo *hostinfo, *orig_hostinfo;
char *interface, *source, *scope_id, *interface_opt;
int serv_port, source_port, addr_type, scope_index, valid;
u16 *flags;
};
/* cache.c */
void cache_init(void);
void next_uid(struct crec *crecp);
@@ -1288,6 +1315,7 @@ struct crec *cache_find_by_name(struct crec *crecp,
char *name, time_t now, unsigned int prot);
void cache_end_insert(void);
void cache_start_insert(void);
unsigned int cache_remove_uid(const unsigned int uid);
int cache_recv_insert(time_t now, int fd);
struct crec *cache_insert(char *name, union all_addr *addr, unsigned short class,
time_t now, unsigned long ttl, unsigned int flags);
@@ -1337,7 +1365,8 @@ void report_addresses(struct dns_header *header, size_t len, u32 mark);
#endif
size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
struct in_addr local_addr, struct in_addr local_netmask,
time_t now, int ad_reqd, int do_bit, int have_pseudoheader);
time_t now, int ad_reqd, int do_bit, int have_pseudoheader,
int *stale);
int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
time_t now);
int check_for_ignored_address(struct dns_header *header, size_t qlen);
@@ -1399,10 +1428,12 @@ void *whine_malloc(size_t size);
void *whine_realloc(void *ptr, size_t size);
int sa_len(union mysockaddr *addr);
int sockaddr_isequal(const union mysockaddr *s1, const union mysockaddr *s2);
int sockaddr_isnull(const union mysockaddr *s);
int hostname_order(const char *a, const char *b);
int hostname_isequal(const char *a, const char *b);
int hostname_issubdomain(char *a, char *b);
time_t dnsmasq_time(void);
u32 dnsmasq_milliseconds(void);
int netmask_length(struct in_addr mask);
int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask);
int is_same_net_prefix(struct in_addr a, struct in_addr b, int prefix);
@@ -1446,8 +1477,9 @@ void read_servers_file(void);
void set_option_bool(unsigned int opt);
void reset_option_bool(unsigned int opt);
struct hostsfile *expand_filelist(struct hostsfile *list);
char *parse_server(char *arg, union mysockaddr *addr,
union mysockaddr *source_addr, char *interface, u16 *flags);
char *parse_server(char *arg, struct server_details *sdetails);
char *parse_server_addr(struct server_details *sdetails);
int parse_server_next(struct server_details *sdetails);
int option_read_dynfile(char *file, int flags);
/* forward.c */
@@ -1462,6 +1494,7 @@ int send_from(int fd, int nowild, char *packet, size_t len,
void resend_query(void);
int allocate_rfd(struct randfd_list **fdlp, struct server *serv);
void free_rfds(struct randfd_list **fdlp);
int fast_retry(time_t now);
/* network.c */
int indextoname(int fd, int index, char *name);

View File

@@ -979,10 +979,13 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
}
/* The DNS packet is expected to contain the answer to a DS query
Put all DSs in the answer which are valid into the cache.
Put all DSs in the answer which are valid and have hash and signature algos
we support into the cache.
Also handles replies which prove that there's no DS at this location,
either because the zone is unsigned or this isn't a zone cut. These are
cached too.
If none of the DS's are for supported algos, treat the answer as if
it's a proof of no DS at this location. RFC4035 para 5.2.
return codes:
STAT_OK At least one valid DS found and in cache.
STAT_BOGUS no DS in reply or not signed, fails validation, bad packet.
@@ -993,8 +996,8 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class)
{
unsigned char *p = (unsigned char *)(header+1);
int qtype, qclass, rc, i, neganswer, nons, neg_ttl = 0;
int aclass, atype, rdlen;
int qtype, qclass, rc, i, neganswer, nons, neg_ttl = 0, found_supported = 0;
int aclass, atype, rdlen, flags;
unsigned long ttl;
union all_addr a;
@@ -1065,14 +1068,22 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
algo = *p++;
digest = *p++;
if ((key = blockdata_alloc((char*)p, rdlen - 4)))
if (!ds_digest_name(digest) || !algo_digest_name(algo))
{
a.log.keytag = keytag;
a.log.algo = algo;
a.log.digest = digest;
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %hu, algo %hu, digest %hu (not supported)", 0);
neg_ttl = ttl;
}
else if ((key = blockdata_alloc((char*)p, rdlen - 4)))
{
a.ds.digest = digest;
a.ds.keydata = key;
a.ds.algo = algo;
a.ds.keytag = keytag;
a.ds.keylen = rdlen - 4;
if (!cache_insert(name, &a, class, now, ttl, F_FORWARD | F_DS | F_DNSSECOK))
{
blockdata_free(key);
@@ -1083,26 +1094,29 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
a.log.keytag = keytag;
a.log.algo = algo;
a.log.digest = digest;
if (ds_digest_name(digest) && algo_digest_name(algo))
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %hu, algo %hu, digest %hu", 0);
else
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %hu, algo %hu, digest %hu (not supported)", 0);
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %hu, algo %hu, digest %hu", 0);
found_supported = 1;
}
}
p = psave;
}
if (!ADD_RDLEN(header, p, plen, rdlen))
return STAT_BOGUS; /* bad packet */
}
cache_end_insert();
/* Fall through if no supported algo DS found. */
if (found_supported)
return STAT_OK;
}
else
flags = F_FORWARD | F_DS | F_NEG | F_DNSSECOK;
if (neganswer)
{
int flags = F_FORWARD | F_DS | F_NEG | F_DNSSECOK;
if (RCODE(header) == NXDOMAIN)
flags |= F_NXDOMAIN;
@@ -1110,17 +1124,18 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
to store presence/absence of NS. */
if (nons)
flags &= ~F_DNSSECOK;
cache_start_insert();
/* Use TTL from NSEC for negative cache entries */
if (!cache_insert(name, NULL, class, now, neg_ttl, flags))
return STAT_BOGUS;
cache_end_insert();
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, nons ? "no DS/cut" : "no DS", 0);
}
cache_start_insert();
/* Use TTL from NSEC for negative cache entries */
if (!cache_insert(name, NULL, class, now, neg_ttl, flags))
return STAT_BOGUS;
cache_end_insert();
if (neganswer)
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, nons ? "no DS/cut" : "no DS", 0);
return STAT_OK;
}

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,25 +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;
}
}
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);

View File

@@ -170,7 +170,7 @@ static int domain_no_rebind(char *domain)
static int forward_query(int udpfd, union mysockaddr *udpaddr,
union all_addr *dst_addr, unsigned int dst_iface,
struct dns_header *header, size_t plen, char *limit, time_t now,
struct frec *forward, int ad_reqd, int do_bit)
struct frec *forward, int ad_reqd, int do_bit, int fast_retry)
{
unsigned int flags = 0;
unsigned int fwd_flags = 0;
@@ -313,6 +313,13 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
goto reply;
/* table full - flags == 0, return REFUSED */
/* Keep copy of query if we're doing fast retry. */
if (daemon->fast_retry_time != 0)
{
forward->stash = blockdata_alloc((char *)header, plen);
forward->stash_len = plen;
}
forward->frec_src.log_id = daemon->log_id;
forward->frec_src.source = *udpaddr;
forward->frec_src.orig_id = ntohs(header->id);
@@ -360,14 +367,14 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
#ifdef HAVE_DNSSEC
/* If we've already got an answer to this query, but we're awaiting keys for validation,
there's no point retrying the query, retry the key query instead...... */
if (forward->blocking_query)
while (forward->blocking_query)
forward = forward->blocking_query;
if (forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY))
{
int is_sign;
unsigned char *pheader;
while (forward->blocking_query)
forward = forward->blocking_query;
/* log_id should match previous DNSSEC query. */
daemon->log_display_id = forward->frec_src.log_id;
@@ -390,7 +397,10 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
#endif
{
/* retry on existing query, from original source. Send to all available servers */
forward->sentto->failed_queries++;
if (udpfd == -1 && !fast_retry)
forward->sentto->failed_queries++;
else
forward->sentto->retrys++;
if (!filter_servers(forward->sentto->arrayposn, F_SERVER, &first, &last))
goto reply;
@@ -398,13 +408,13 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
master = daemon->serverarray[first];
/* Forward to all available servers on retry of query from same host. */
if (!option_bool(OPT_ORDER) && old_src)
if (!option_bool(OPT_ORDER) && old_src && !fast_retry)
forward->forwardall = 1;
else
{
start = forward->sentto->arrayposn;
if (option_bool(OPT_ORDER))
if (option_bool(OPT_ORDER) && !fast_retry)
{
/* In strict order mode, there must be a server later in the list
left to send to, otherwise without the forwardall mechanism,
@@ -553,7 +563,10 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
}
if (forwarded || is_dnssec)
return 1;
{
forward->forward_timestamp = dnsmasq_milliseconds();
return 1;
}
/* could not send on, prepare to return */
header->id = htons(forward->frec_src.orig_id);
@@ -592,6 +605,61 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
return 0;
}
/* Check if any frecs need to do a retry, and action that if so.
Return time in milliseconds until he next retry will be required,
or -1 if none. */
int fast_retry(time_t now)
{
struct frec *f;
int ret = -1;
if (daemon->fast_retry_time != 0)
{
u32 millis = dnsmasq_milliseconds();
for (f = daemon->frec_list; f; f = f->next)
if (f->sentto && f->stash && difftime(now, f->time) < daemon->fast_retry_timeout)
{
#ifdef HAVE_DNSSEC
if (f->blocking_query)
continue;
#endif
/* t is milliseconds since last query sent. */
int to_run, t = (int)(millis - f->forward_timestamp);
if (t < f->forward_delay)
to_run = f->forward_delay - t;
else
{
unsigned char *udpsz;
unsigned short udp_size = PACKETSZ; /* default if no EDNS0 */
struct dns_header *header = (struct dns_header *)daemon->packet;
/* packet buffer overwritten */
daemon->srv_save = NULL;
blockdata_retrieve(f->stash, f->stash_len, (void *)header);
/* UDP size already set in saved query. */
if (find_pseudoheader(header, f->stash_len, NULL, &udpsz, NULL, NULL))
GETSHORT(udp_size, udpsz);
daemon->log_display_id = f->frec_src.log_id;
forward_query(-1, NULL, NULL, 0, header, f->stash_len, ((char *) header) + udp_size, now, f,
f->flags & FREC_AD_QUESTION, f->flags & FREC_DO_QUESTION, 1);
to_run = f->forward_delay = 2 * f->forward_delay;
}
if (ret == -1 || ret > to_run)
ret = to_run;
}
}
return ret;
}
static struct ipsets *domain_find_sets(struct ipsets *setlist, const char *domain) {
/* Similar algorithm to search_servers. */
struct ipsets *ipset_pos, *ret = NULL;
@@ -753,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)
@@ -807,6 +885,9 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
n = add_pseudoheader(header, n, limit, daemon->edns_pktsz, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 1);
}
if (RCODE(header) == NXDOMAIN)
server->nxdomain_replies++;
return n;
}
@@ -949,6 +1030,8 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
/* Save query for retransmission and de-dup */
new->stash = blockdata_alloc((char *)header, nn);
new->stash_len = nn;
if (daemon->fast_retry_time != 0)
new->forward_timestamp = dnsmasq_milliseconds();
/* Don't resend this. */
daemon->srv_save = NULL;
@@ -1066,7 +1149,7 @@ void reply_query(int fd, time_t now)
if (daemon->ignore_addr && RCODE(header) == NOERROR &&
check_for_ignored_address(header, n))
return;
/* Note: if we send extra options in the EDNS0 header, we can't recreate
the query from the reply. */
if ((RCODE(header) == REFUSED || RCODE(header) == SERVFAIL) &&
@@ -1097,38 +1180,50 @@ void reply_query(int fd, time_t now)
else
#endif
{
/* recreate query from reply */
if ((pheader = find_pseudoheader(header, (size_t)n, &plen, &udpsz, &is_sign, NULL)))
GETSHORT(udp_size, udpsz);
/* If the client provides an EDNS0 UDP size, use that to limit our reply.
(bounded by the maximum configured). If no EDNS0, then it
defaults to 512 */
if (udp_size > daemon->edns_pktsz)
udp_size = daemon->edns_pktsz;
else if (udp_size < PACKETSZ)
udp_size = PACKETSZ; /* Sanity check - can't reduce below default. RFC 6891 6.2.3 */
if (!is_sign &&
(nn = resize_packet(header, (size_t)n, pheader, plen)) &&
(forward->flags & FREC_DO_QUESTION))
add_do_bit(header, nn, (unsigned char *)pheader + plen);
/* in fast retry mode, we have a copy of the query. */
if (daemon->fast_retry_time != 0 && forward->stash)
{
blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
nn = forward->stash_len;
/* UDP size already set in saved query. */
if (find_pseudoheader(header, (size_t)n, NULL, &udpsz, NULL, NULL))
GETSHORT(udp_size, udpsz);
}
else
{
/* recreate query from reply */
if ((pheader = find_pseudoheader(header, (size_t)n, &plen, &udpsz, &is_sign, NULL)))
GETSHORT(udp_size, udpsz);
/* If the client provides an EDNS0 UDP size, use that to limit our reply.
(bounded by the maximum configured). If no EDNS0, then it
defaults to 512 */
if (udp_size > daemon->edns_pktsz)
udp_size = daemon->edns_pktsz;
else if (udp_size < PACKETSZ)
udp_size = PACKETSZ; /* Sanity check - can't reduce below default. RFC 6891 6.2.3 */
header->ancount = htons(0);
header->nscount = htons(0);
header->arcount = htons(0);
header->hb3 &= ~(HB3_QR | HB3_AA | HB3_TC);
header->hb4 &= ~(HB4_RA | HB4_RCODE | HB4_CD | HB4_AD);
if (forward->flags & FREC_CHECKING_DISABLED)
header->hb4 |= HB4_CD;
if (forward->flags & FREC_AD_QUESTION)
header->hb4 |= HB4_AD;
header->ancount = htons(0);
header->nscount = htons(0);
header->arcount = htons(0);
header->hb3 &= ~(HB3_QR | HB3_AA | HB3_TC);
header->hb4 &= ~(HB4_RA | HB4_RCODE | HB4_CD | HB4_AD);
if (forward->flags & FREC_CHECKING_DISABLED)
header->hb4 |= HB4_CD;
if (forward->flags & FREC_AD_QUESTION)
header->hb4 |= HB4_AD;
if (!is_sign &&
(nn = resize_packet(header, (size_t)n, pheader, plen)) &&
(forward->flags & FREC_DO_QUESTION))
add_do_bit(header, nn, (unsigned char *)pheader + plen);
}
}
if (nn)
{
forward_query(-1, NULL, NULL, 0, header, nn, ((char *) header) + udp_size, now, forward,
forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION);
forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION, 0);
return;
}
}
@@ -1162,6 +1257,15 @@ void reply_query(int fd, time_t now)
answers, to conserve file descriptors, and to save work reading and
discarding answers for other upstreams. */
free_rfds(&forward->rfds);
/* calculate modified moving average of server latency */
if (server->query_latency == 0)
server->mma_latency = (dnsmasq_milliseconds() - forward->forward_timestamp) * 128; /* init */
else
server->mma_latency += dnsmasq_milliseconds() - forward->forward_timestamp - server->query_latency;
/* denominator controls how many queries we average over. */
server->query_latency = server->mma_latency/128;
#ifdef HAVE_DNSSEC
if ((forward->sentto->flags & SERV_DO_DNSSEC) &&
@@ -1268,10 +1372,6 @@ static void return_reply(time_t now, struct frec *forward, struct dns_header *he
{
header->id = htons(src->orig_id);
#ifdef HAVE_DUMPFILE
dump_packet_udp(DUMP_REPLY, daemon->packet, (size_t)nn, NULL, &src->source, src->fd);
#endif
#if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS)
if (option_bool(OPT_CMARK_ALST_EN))
{
@@ -1282,14 +1382,20 @@ static void return_reply(time_t now, struct frec *forward, struct dns_header *he
}
#endif
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)
if (src->fd != -1)
{
daemon->log_display_id = src->log_id;
daemon->log_source_addr = &src->source;
log_query(F_UPSTREAM, "query", NULL, "duplicate", 0);
#ifdef HAVE_DUMPFILE
dump_packet_udp(DUMP_REPLY, daemon->packet, (size_t)nn, NULL, &src->source, src->fd);
#endif
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)
{
daemon->log_display_id = src->log_id;
daemon->log_source_addr = &src->source;
log_query(F_UPSTREAM, "query", NULL, "duplicate", 0);
}
}
}
}
@@ -1379,7 +1485,7 @@ void receive_query(struct listener *listen, time_t now)
int family = listen->addr.sa.sa_family;
/* Can always get recvd interface for IPv6 */
int check_dst = !option_bool(OPT_NOWILD) || family == AF_INET6;
/* packet buffer overwritten */
daemon->srv_save = NULL;
@@ -1702,16 +1808,27 @@ void receive_query(struct listener *listen, time_t now)
#endif
else
{
int stale;
int ad_reqd = do_bit;
u16 hb3 = header->hb3, hb4 = header->hb4;
int fd = listen->fd;
/* RFC 6840 5.7 */
if (header->hb4 & HB4_AD)
ad_reqd = 1;
m = answer_request(header, ((char *) header) + udp_size, (size_t)n,
dst_addr_4, netmask, now, ad_reqd, do_bit, have_pseudoheader);
dst_addr_4, netmask, now, ad_reqd, do_bit, have_pseudoheader, &stale);
if (m >= 1)
{
if (stale && have_pseudoheader)
{
u16 swap = htons(EDE_STALE);
m = add_pseudoheader(header, m, ((unsigned char *) header) + udp_size, daemon->edns_pktsz,
EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0);
}
#ifdef HAVE_DUMPFILE
dump_packet_udp(DUMP_REPLY, daemon->packet, m, NULL, &source_addr, listen->fd);
#endif
@@ -1722,12 +1839,39 @@ void receive_query(struct listener *listen, time_t now)
send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND),
(char *)header, m, &source_addr, &dst_addr, if_index);
daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]++;
if (stale)
daemon->metrics[METRIC_DNS_STALE_ANSWERED]++;
}
if (m == 0 || stale)
{
if (m != 0)
{
size_t plen;
/* We answered with stale cache data, so forward the query anyway to
refresh that. Restore the query from the answer packet. */
pheader = find_pseudoheader(header, (size_t)m, &plen, NULL, NULL, NULL);
header->hb3 = hb3;
header->hb4 = hb4;
header->ancount = htons(0);
header->nscount = htons(0);
header->arcount = htons(0);
m = resize_packet(header, m, pheader, plen);
/* We've already answered the client, so don't send it the answer
when it comes back. */
fd = -1;
}
if (forward_query(fd, &source_addr, &dst_addr, if_index,
header, (size_t)n, ((char *) header) + udp_size, now, NULL, ad_reqd, do_bit, 0))
daemon->metrics[METRIC_DNS_QUERIES_FORWARDED]++;
else
daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]++;
}
else if (forward_query(listen->fd, &source_addr, &dst_addr, if_index,
header, (size_t)n, ((char *) header) + udp_size, now, NULL, ad_reqd, do_bit))
daemon->metrics[METRIC_DNS_QUERIES_FORWARDED]++;
else
daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]++;
}
}
@@ -1954,8 +2098,9 @@ unsigned char *tcp_request(int confd, time_t now,
unsigned char *pheader;
unsigned int mark = 0;
int have_mark = 0;
int first, last;
int first, last, stale, do_stale = 0;
unsigned int flags = 0;
u16 hb3, hb4;
if (!packet || getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) == -1)
return packet;
@@ -2010,13 +2155,37 @@ unsigned char *tcp_request(int confd, time_t now,
{
int ede = EDE_UNSET;
if (query_count == TCP_MAX_QUERIES ||
!packet ||
!read_write(confd, &c1, 1, 1) || !read_write(confd, &c2, 1, 1) ||
!(size = c1 << 8 | c2) ||
!read_write(confd, payload, size, 1))
return packet;
if (query_count == TCP_MAX_QUERIES)
return packet;
if (do_stale)
{
size_t plen;
/* We answered the last query with stale data. Now try and get fresh data.
Restore query from answer. */
pheader = find_pseudoheader(header, m, &plen, NULL, NULL, NULL);
header->hb3 = hb3;
header->hb4 = hb4;
header->ancount = htons(0);
header->nscount = htons(0);
header->arcount = htons(0);
size = resize_packet(header, m, pheader, plen);
}
else
{
if (!read_write(confd, &c1, 1, 1) || !read_write(confd, &c2, 1, 1) ||
!(size = c1 << 8 | c2) ||
!read_write(confd, payload, size, 1))
return packet;
/* for stale-answer processing. */
hb3 = header->hb3;
hb4 = header->hb4;
}
if (size < (int)sizeof(struct dns_header))
continue;
@@ -2041,24 +2210,27 @@ unsigned char *tcp_request(int confd, time_t now,
struct auth_zone *zone;
#endif
log_query_mysockaddr(F_QUERY | F_FORWARD, daemon->namebuff,
&peer_addr, auth_dns ? "auth" : "query", qtype);
#ifdef HAVE_CONNTRACK
is_single_query = 1;
#endif
if (!do_stale)
{
log_query_mysockaddr(F_QUERY | F_FORWARD, daemon->namebuff,
&peer_addr, auth_dns ? "auth" : "query", qtype);
#ifdef HAVE_AUTH
/* find queries for zones we're authoritative for, and answer them directly */
if (!auth_dns && !option_bool(OPT_LOCALISE))
for (zone = daemon->auth_zones; zone; zone = zone->next)
if (in_zone(zone, daemon->namebuff, NULL))
{
auth_dns = 1;
local_auth = 1;
break;
}
/* find queries for zones we're authoritative for, and answer them directly */
if (!auth_dns && !option_bool(OPT_LOCALISE))
for (zone = daemon->auth_zones; zone; zone = zone->next)
if (in_zone(zone, daemon->namebuff, NULL))
{
auth_dns = 1;
local_auth = 1;
break;
}
#endif
}
}
norebind = domain_no_rebind(daemon->namebuff);
@@ -2114,11 +2286,14 @@ unsigned char *tcp_request(int confd, time_t now,
/* RFC 6840 5.7 */
if (header->hb4 & HB4_AD)
ad_reqd = 1;
if (do_stale)
m = 0;
else
/* m > 0 if answered from cache */
m = answer_request(header, ((char *) header) + 65536, (size_t)size,
dst_addr_4, netmask, now, ad_reqd, do_bit, have_pseudoheader, &stale);
/* m > 0 if answered from cache */
m = answer_request(header, ((char *) header) + 65536, (size_t)size,
dst_addr_4, netmask, now, ad_reqd, do_bit, have_pseudoheader);
/* Do this by steam now we're not in the select() loop */
check_log_writer(1);
@@ -2236,6 +2411,9 @@ unsigned char *tcp_request(int confd, time_t now,
}
}
if (do_stale)
break;
/* In case of local answer or no connections made. */
if (m == 0)
{
@@ -2246,13 +2424,19 @@ unsigned char *tcp_request(int confd, time_t now,
if (have_pseudoheader)
{
u16 swap = htons((u16)ede);
if (ede != EDE_UNSET)
m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0);
else
m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0);
if (ede != EDE_UNSET)
m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0);
else
m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0);
}
}
else if (stale)
{
u16 swap = htons((u16)EDE_STALE);
m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0);
}
check_log_writer(1);
@@ -2267,8 +2451,26 @@ unsigned char *tcp_request(int confd, time_t now,
#endif
if (!read_write(confd, packet, m + sizeof(u16), 0))
break;
/* If we answered with stale data, this process will now try and get fresh data into
the cache then and cannot therefore accept new queries. Close the incoming
connection to signal that to the client. Then set do_stale and loop round
once more to try and get fresh data, after which we exit. */
if (stale)
{
shutdown(confd, SHUT_RDWR);
close(confd);
do_stale = 1;
}
}
/* If we ran once to get fresh data, confd is already closed. */
if (!do_stale)
{
shutdown(confd, SHUT_RDWR);
close(confd);
}
return packet;
}
@@ -2280,16 +2482,36 @@ static int random_sock(struct server *s)
if ((fd = socket(s->source_addr.sa.sa_family, SOCK_DGRAM, 0)) != -1)
{
/* We need to set IPV6ONLY so we can use the same ports
for IPv4 and IPV6, otherwise, in restriced port situations,
we can end up with all our available ports in use for
one address family, and the other address family cannot be used. */
if (s->source_addr.sa.sa_family == AF_INET6)
{
int opt = 1;
if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)) == -1)
{
close(fd);
return -1;
}
}
if (local_bind(fd, &s->source_addr, s->interface, s->ifindex, 0))
return fd;
if (s->interface[0] == 0)
(void)prettyprint_addr(&s->source_addr, daemon->namebuff);
else
strcpy(daemon->namebuff, s->interface);
my_syslog(LOG_ERR, _("failed to bind server socket to %s: %s"),
daemon->namebuff, strerror(errno));
/* don't log errors due to running out of available ports, we handle those. */
if (!sockaddr_isnull(&s->source_addr) || errno != EADDRINUSE)
{
if (s->interface[0] == 0)
(void)prettyprint_addr(&s->source_addr, daemon->addrbuff);
else
safe_strncpy(daemon->addrbuff, s->interface, ADDRSTRLEN);
my_syslog(LOG_ERR, _("failed to bind server socket to %s: %s"),
daemon->addrbuff, strerror(errno));
}
close(fd);
}
@@ -2319,39 +2541,93 @@ int allocate_rfd(struct randfd_list **fdlp, struct server *serv)
{
static int finger = 0;
int i, j = 0;
struct randfd_list *rfl;
int ports_full = 0;
struct randfd_list **up, *rfl, *found, **found_link;
struct randfd *rfd = NULL;
int fd = 0;
int ports_avail = 0;
/* We can't have more randomsocks for this AF available than ports in our port range,
so check that here, to avoid trying and failing to bind every port
in local_bind(), called from random_sock(). The actual check is below when
ports_avail != 0 */
if (daemon->max_port != 0)
{
ports_avail = daemon->max_port - daemon->min_port + 1;
if (ports_avail >= SMALL_PORT_RANGE)
ports_avail = 0;
}
/* If server has a pre-allocated fd, use that. */
if (serv->sfd)
return serv->sfd->fd;
/* existing suitable random port socket linked to this transaction? */
for (rfl = *fdlp; rfl; rfl = rfl->next)
/* existing suitable random port socket linked to this transaction?
Find the last one in the list and count how many there are. */
for (found = NULL, found_link = NULL, i = 0, up = fdlp, rfl = *fdlp; rfl; up = &rfl->next, rfl = rfl->next)
if (server_isequal(serv, rfl->rfd->serv))
return rfl->rfd->fd;
{
i++;
found = rfl;
found_link = up;
}
/* No. need new link. */
/* We have the maximum number for this query already. Promote
the last one on the list to the head, to circulate them,
and return it. */
if (found && i >= daemon->randport_limit)
{
*found_link = found->next;
found->next = *fdlp;
*fdlp = found;
return found->rfd->fd;
}
/* check for all available ports in use. */
if (ports_avail != 0)
{
int ports_inuse;
for (ports_inuse = 0, i = 0; i < daemon->numrrand; i++)
if (daemon->randomsocks[i].refcount != 0 &&
daemon->randomsocks[i].serv->source_addr.sa.sa_family == serv->source_addr.sa.sa_family &&
++ports_inuse >= ports_avail)
{
ports_full = 1;
break;
}
}
/* limit the number of sockets we have open to avoid starvation of
(eg) TFTP. Once we have a reasonable number, randomness should be OK */
if (!ports_full)
for (i = 0; i < daemon->numrrand; i++)
if (daemon->randomsocks[i].refcount == 0)
{
if ((fd = random_sock(serv)) != -1)
{
rfd = &daemon->randomsocks[i];
rfd->serv = serv;
rfd->fd = fd;
rfd->refcount = 1;
}
break;
}
/* No good existing. Need new link. */
if ((rfl = daemon->rfl_spare))
daemon->rfl_spare = rfl->next;
else if (!(rfl = whine_malloc(sizeof(struct randfd_list))))
return -1;
/* limit the number of sockets we have open to avoid starvation of
(eg) TFTP. Once we have a reasonable number, randomness should be OK */
for (i = 0; i < daemon->numrrand; i++)
if (daemon->randomsocks[i].refcount == 0)
{
if ((fd = random_sock(serv)) != -1)
{
rfd = &daemon->randomsocks[i];
rfd->serv = serv;
rfd->fd = fd;
rfd->refcount = 1;
}
break;
}
{
/* malloc failed, don't leak allocated sock */
if (rfd)
{
close(rfd->fd);
rfd->refcount = 0;
}
return -1;
}
/* No free ones or cannot get new socket, grab an existing one */
if (!rfd)
@@ -2362,10 +2638,19 @@ int allocate_rfd(struct randfd_list **fdlp, struct server *serv)
server_isequal(serv, daemon->randomsocks[i].serv) &&
daemon->randomsocks[i].refcount != 0xfffe)
{
finger = i + 1;
rfd = &daemon->randomsocks[i];
rfd->refcount++;
break;
struct randfd_list *rl;
/* Don't pick one we already have. */
for (rl = *fdlp; rl; rl = rl->next)
if (rl->rfd == &daemon->randomsocks[i])
break;
if (!rl)
{
finger = i + 1;
rfd = &daemon->randomsocks[i];
rfd->refcount++;
break;
}
}
}
@@ -2477,13 +2762,13 @@ static void free_frec(struct frec *f)
f->sentto = NULL;
f->flags = 0;
#ifdef HAVE_DNSSEC
if (f->stash)
{
blockdata_free(f->stash);
f->stash = NULL;
}
#ifdef HAVE_DNSSEC
/* Anything we're waiting on is pointless now, too */
if (f->blocking_query)
{
@@ -2539,6 +2824,7 @@ static struct frec *get_new_frec(time_t now, struct server *master, int force)
{
if (difftime(now, f->time) >= 4*TIMEOUT)
{
daemon->metrics[METRIC_DNS_UNANSWERED_QUERY]++;
free_frec(f);
target = f;
}
@@ -2560,6 +2846,7 @@ static struct frec *get_new_frec(time_t now, struct server *master, int force)
if (!target && oldest && ((int)difftime(now, oldest->time)) >= TIMEOUT)
{
/* can't find empty one, use oldest if there is one and it's older than timeout */
daemon->metrics[METRIC_DNS_UNANSWERED_QUERY]++;
free_frec(oldest);
target = oldest;
}
@@ -2571,8 +2858,11 @@ static struct frec *get_new_frec(time_t now, struct server *master, int force)
}
if (target)
target->time = now;
{
target->time = now;
target->forward_delay = daemon->fast_retry_time;
}
return target;
}

View File

@@ -133,81 +133,112 @@ void inotify_dnsmasq_init()
}
}
static struct hostsfile *dyndir_addhosts(struct dyndir *dd, char *path)
{
/* Check if this file is already known in dd->files */
struct hostsfile *ah = NULL;
for(ah = dd->files; ah; ah = ah->next)
if(ah && ah->fname && strcmp(path, ah->fname) == 0)
return ah;
/* Not known, create new hostsfile record for this dyndir */
struct hostsfile *newah = NULL;
if(!(newah = whine_malloc(sizeof(struct hostsfile))))
return NULL;
/* Add this file to the tip of the linked list */
newah->next = dd->files;
dd->files = newah;
/* Copy flags, set index and the full file path */
newah->flags = dd->flags;
newah->index = daemon->host_index++;
newah->fname = path;
return newah;
}
/* initialisation for dynamic-dir. Set inotify watch for each directory, and read pre-existing files */
void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revhashsz)
{
struct hostsfile *ah;
for (ah = daemon->dynamic_dirs; ah; ah = ah->next)
struct dyndir *dd;
for (dd = daemon->dynamic_dirs; dd; dd = dd->next)
{
DIR *dir_stream = NULL;
struct dirent *ent;
struct stat buf;
if (!(ah->flags & flag))
if (!(dd->flags & flag))
continue;
if (stat(ah->fname, &buf) == -1)
if (stat(dd->dname, &buf) == -1)
{
my_syslog(LOG_ERR, _("bad dynamic directory %s: %s"),
ah->fname, strerror(errno));
dd->dname, strerror(errno));
continue;
}
if (!(S_ISDIR(buf.st_mode)))
{
my_syslog(LOG_ERR, _("bad dynamic directory %s: %s"),
ah->fname, _("not a directory"));
dd->dname, _("not a directory"));
continue;
}
if (!(ah->flags & AH_WD_DONE))
if (!(dd->flags & AH_WD_DONE))
{
ah->wd = inotify_add_watch(daemon->inotifyfd, ah->fname, IN_CLOSE_WRITE | IN_MOVED_TO);
ah->flags |= AH_WD_DONE;
dd->wd = inotify_add_watch(daemon->inotifyfd, dd->dname, IN_CLOSE_WRITE | IN_MOVED_TO | IN_DELETE);
dd->flags |= AH_WD_DONE;
}
/* Read contents of dir _after_ calling add_watch, in the hope of avoiding
a race which misses files being added as we start */
if (ah->wd == -1 || !(dir_stream = opendir(ah->fname)))
if (dd->wd == -1 || !(dir_stream = opendir(dd->dname)))
{
my_syslog(LOG_ERR, _("failed to create inotify for %s: %s"),
ah->fname, strerror(errno));
dd->dname, strerror(errno));
continue;
}
while ((ent = readdir(dir_stream)))
{
size_t lendir = strlen(ah->fname);
size_t lendir = strlen(dd->dname);
size_t lenfile = strlen(ent->d_name);
char *path;
/* ignore emacs backups and dotfiles */
if (lenfile == 0 ||
ent->d_name[lenfile - 1] == '~' ||
(ent->d_name[0] == '#' && ent->d_name[lenfile - 1] == '#') ||
ent->d_name[0] == '.')
continue;
if ((path = whine_malloc(lendir + lenfile + 2)))
{
strcpy(path, ah->fname);
struct hostsfile *ah;
strcpy(path, dd->dname);
strcat(path, "/");
strcat(path, ent->d_name);
if (!(ah = dyndir_addhosts(dd, path)))
{
free(path);
continue;
}
/* ignore non-regular files */
if (stat(path, &buf) != -1 && S_ISREG(buf.st_mode))
{
if (ah->flags & AH_HOSTS)
if (dd->flags & AH_HOSTS)
total_size = read_hostsfile(path, ah->index, total_size, rhash, revhashsz);
#ifdef HAVE_DHCP
else if (ah->flags & (AH_DHCP_HST | AH_DHCP_OPT))
option_read_dynfile(path, ah->flags);
else if (dd->flags & (AH_DHCP_HST | AH_DHCP_OPT))
option_read_dynfile(path, dd->flags);
#endif
}
free(path);
}
}
@@ -218,7 +249,7 @@ void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revh
int inotify_check(time_t now)
{
int hit = 0;
struct hostsfile *ah;
struct dyndir *dd;
while (1)
{
@@ -249,36 +280,51 @@ int inotify_check(time_t now)
if (res->wd == in->wd && strcmp(res->file, in->name) == 0)
hit = 1;
for (ah = daemon->dynamic_dirs; ah; ah = ah->next)
if (ah->wd == in->wd)
for (dd = daemon->dynamic_dirs; dd; dd = dd->next)
if (dd->wd == in->wd)
{
size_t lendir = strlen(ah->fname);
size_t lendir = strlen(dd->dname);
char *path;
if ((path = whine_malloc(lendir + in->len + 2)))
{
strcpy(path, ah->fname);
struct hostsfile *ah = NULL;
strcpy(path, dd->dname);
strcat(path, "/");
strcat(path, in->name);
my_syslog(LOG_INFO, _("inotify, new or changed file %s"), path);
if (ah->flags & AH_HOSTS)
/* Is this is a deletion event? */
if (in->mask & IN_DELETE)
my_syslog(LOG_INFO, _("inotify: %s removed"), path);
else
my_syslog(LOG_INFO, _("inotify: %s new or modified"), path);
if (dd->flags & AH_HOSTS)
{
read_hostsfile(path, ah->index, 0, NULL, 0);
#ifdef HAVE_DHCP
if (daemon->dhcp || daemon->doing_dhcp6)
if ((ah = dyndir_addhosts(dd, path)))
{
/* Propagate the consequences of loading a new dhcp-host */
dhcp_update_configs(daemon->dhcp_conf);
lease_update_from_configs();
lease_update_file(now);
lease_update_dns(1);
}
const unsigned int removed = cache_remove_uid(ah->index);
if (removed > 0)
my_syslog(LOG_INFO, _("inotify: flushed %u names read from %s"), removed, path);
/* (Re-)load hostsfile only if this event isn't triggered by deletion */
if (!(in->mask & IN_DELETE))
read_hostsfile(path, ah->index, 0, NULL, 0);
#ifdef HAVE_DHCP
if (daemon->dhcp || daemon->doing_dhcp6)
{
/* Propagate the consequences of loading a new dhcp-host */
dhcp_update_configs(daemon->dhcp_conf);
lease_update_from_configs();
lease_update_file(now);
lease_update_dns(1);
}
#endif
}
}
#ifdef HAVE_DHCP
else if (ah->flags & AH_DHCP_HST)
else if (dd->flags & AH_DHCP_HST)
{
if (option_read_dynfile(path, AH_DHCP_HST))
{
@@ -289,11 +335,12 @@ int inotify_check(time_t now)
lease_update_dns(1);
}
}
else if (ah->flags & AH_DHCP_OPT)
else if (dd->flags & AH_DHCP_OPT)
option_read_dynfile(path, AH_DHCP_OPT);
#endif
free(path);
if (!ah)
free(path);
}
}
}

View File

@@ -22,6 +22,8 @@ const char * metric_names[] = {
"dns_queries_forwarded",
"dns_auth_answered",
"dns_local_answered",
"dns_stale_answered",
"dns_unanswered",
"bootp",
"pxe",
"dhcp_ack",
@@ -42,3 +44,23 @@ const char * metric_names[] = {
const char* get_metric_name(int i) {
return metric_names[i];
}
void clear_metrics(void)
{
int i;
struct server *serv;
for (i = 0; i < __METRIC_MAX; i++)
daemon->metrics[i] = 0;
for (serv = daemon->servers; serv; serv = serv->next)
{
serv->queries = 0;
serv->failed_queries = 0;
serv->failed_queries = 0;
serv->retrys = 0;
serv->nxdomain_replies = 0;
serv->query_latency = 0;
}
}

View File

@@ -21,6 +21,8 @@ enum {
METRIC_DNS_QUERIES_FORWARDED,
METRIC_DNS_AUTH_ANSWERED,
METRIC_DNS_LOCAL_ANSWERED,
METRIC_DNS_STALE_ANSWERED,
METRIC_DNS_UNANSWERED_QUERY,
METRIC_BOOTP,
METRIC_PXE,
METRIC_DHCPACK,
@@ -41,3 +43,4 @@ enum {
};
const char* get_metric_name(int);
void clear_metrics(void);

View File

@@ -360,7 +360,7 @@ static int iface_allowed(struct iface_param *param, int if_index, char *label,
if (int_name->flags & INP4)
{
if (netmask.s_addr == 0xffff)
if (netmask.s_addr == 0xffffffff)
continue;
newaddr.s_addr = (addr->in.sin_addr.s_addr & netmask.s_addr) |
@@ -1371,7 +1371,7 @@ int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifind
or both are set. Otherwise use the OS's random ephemeral port allocation by
leaving port == 0 and tries == 1 */
ports_avail = daemon->max_port - daemon->min_port + 1;
tries = ports_avail < 30 ? 3 * ports_avail : 100;
tries = (ports_avail < SMALL_PORT_RANGE) ? ports_avail : 100;
port = htons(daemon->min_port + (rand16() % ports_avail));
}
@@ -1400,7 +1400,16 @@ int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifind
if (--tries == 0)
return 0;
port = htons(daemon->min_port + (rand16() % ports_avail));
/* For small ranges, do a systematic search, not a random one. */
if (ports_avail < SMALL_PORT_RANGE)
{
unsigned short hport = ntohs(port);
if (hport++ == daemon->max_port)
hport = daemon->min_port;
port = htons(hport);
}
else
port = htons(daemon->min_port + (rand16() % ports_avail));
}
if (!is_tcp && ifindex > 0)

View File

@@ -181,6 +181,11 @@ struct myoption {
#define LOPT_STRIP_MAC 372
#define LOPT_CONF_OPT 373
#define LOPT_CONF_SCRIPT 374
#define LOPT_RANDPORT_LIM 375
#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[] =
@@ -233,6 +238,7 @@ static const struct myoption opts[] =
{ "localmx", 0, 0, 'L' },
{ "local-ttl", 1, 0, 'T' },
{ "no-negcache", 0, 0, 'N' },
{ "no-round-robin", 0, 0, LOPT_NORR },
{ "addn-hosts", 1, 0, 'H' },
{ "hostsdir", 1, 0, LOPT_HOST_INOTIFY },
{ "query-port", 1, 0, 'Q' },
@@ -366,6 +372,10 @@ static const struct myoption opts[] =
{ "log-debug", 0, 0, LOPT_LOG_DEBUG },
{ "umbrella", 2, 0, LOPT_UMBRELLA },
{ "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", 2, 0 , LOPT_STALE_CACHE },
{ "no-ident", 0, 0, LOPT_NO_IDENT },
{ NULL, 0, 0, 0 }
};
@@ -423,6 +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, 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},
@@ -430,6 +441,7 @@ static struct {
{ 'P', ARG_ONE, "<integer>", gettext_noop("Maximum supported UDP packet size for EDNS.0 (defaults to %s)."), "*" },
{ 'q', ARG_DUP, NULL, gettext_noop("Log DNS queries."), NULL },
{ 'Q', ARG_ONE, "<integer>", gettext_noop("Force the originating port for upstream DNS queries."), NULL },
{ LOPT_RANDPORT_LIM, ARG_ONE, "#ports", gettext_noop("Set maximum number of random originating ports for a query."), NULL },
{ 'R', OPT_NO_RESOLV, NULL, gettext_noop("Do NOT read resolv.conf."), NULL },
{ 'r', ARG_DUP, "<path>", gettext_noop("Specify path to resolv.conf (defaults to %s)."), RESOLVFILE },
{ LOPT_SERVERS_FILE, ARG_ONE, "<path>", gettext_noop("Specify path to file with server= options"), NULL },
@@ -443,6 +455,7 @@ static struct {
{ LOPT_MAXTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for maximum TTL to send to clients."), NULL },
{ LOPT_MAXCTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live ceiling for cache."), NULL },
{ LOPT_MINCTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live floor for cache."), NULL },
{ LOPT_FAST_RETRY, ARG_ONE, "<milliseconds>", gettext_noop("Retry DNS queries after this many milliseconds."), NULL},
{ 'u', ARG_ONE, "<username>", gettext_noop("Change to this user after startup. (defaults to %s)."), CHUSER },
{ 'U', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP vendor class to tag."), NULL },
{ 'v', 0, NULL, gettext_noop("Display dnsmasq version and copyright information."), NULL },
@@ -558,6 +571,8 @@ static struct {
{ LOPT_SCRIPT_TIME, OPT_LEASE_RENEW, NULL, gettext_noop("Call dhcp-script when lease expiry changes."), 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_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 }
};
@@ -843,117 +858,241 @@ static char *parse_mysockaddr(char *arg, union mysockaddr *addr)
return NULL;
}
char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_addr, char *interface, u16 *flags)
char *parse_server(char *arg, struct server_details *sdetails)
{
int source_port = 0, serv_port = NAMESERVER_PORT;
char *portno, *source;
char *interface_opt = NULL;
int scope_index = 0;
char *scope_id;
*interface = 0;
sdetails->serv_port = NAMESERVER_PORT;
char *portno;
int ecode = 0;
struct addrinfo hints;
memset(&hints, 0, sizeof(struct addrinfo));
*sdetails->interface = 0;
sdetails->addr_type = AF_UNSPEC;
if (strcmp(arg, "#") == 0)
{
if (flags)
*flags |= SERV_USE_RESOLV;
if (sdetails->flags)
*sdetails->flags |= SERV_USE_RESOLV;
sdetails->addr_type = AF_LOCAL;
sdetails->valid = 1;
return NULL;
}
if ((source = split_chr(arg, '@')) && /* is there a source. */
(portno = split_chr(source, '#')) &&
!atoi_check16(portno, &source_port))
if ((sdetails->source = split_chr(arg, '@')) && /* is there a source. */
(portno = split_chr(sdetails->source, '#')) &&
!atoi_check16(portno, &sdetails->source_port))
return _("bad port");
if ((portno = split_chr(arg, '#')) && /* is there a port no. */
!atoi_check16(portno, &serv_port))
!atoi_check16(portno, &sdetails->serv_port))
return _("bad port");
scope_id = split_chr(arg, '%');
sdetails->scope_id = split_chr(arg, '%');
if (source) {
interface_opt = split_chr(source, '@');
if (sdetails->source) {
sdetails->interface_opt = split_chr(sdetails->source, '@');
if (interface_opt)
if (sdetails->interface_opt)
{
#if defined(SO_BINDTODEVICE)
safe_strncpy(interface, source, IF_NAMESIZE);
source = interface_opt;
safe_strncpy(sdetails->interface, sdetails->source, IF_NAMESIZE);
sdetails->source = sdetails->interface_opt;
#else
return _("interface binding not supported");
#endif
}
}
if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0)
if (inet_pton(AF_INET, arg, &sdetails->addr->in.sin_addr) > 0)
sdetails->addr_type = AF_INET;
else if (inet_pton(AF_INET6, arg, &sdetails->addr->in6.sin6_addr) > 0)
sdetails->addr_type = AF_INET6;
else
{
addr->in.sin_port = htons(serv_port);
addr->sa.sa_family = source_addr->sa.sa_family = AF_INET;
#ifdef HAVE_SOCKADDR_SA_LEN
source_addr->in.sin_len = addr->in.sin_len = sizeof(struct sockaddr_in);
/* if the argument is neither an IPv4 not an IPv6 address, it might be a
hostname and we should try to resolve it to a suitable address. */
memset(&hints, 0, sizeof(hints));
/* The AI_ADDRCONFIG flag ensures that then IPv4 addresses are returned in
the result only if the local system has at least one IPv4 address
configured, and IPv6 addresses are returned only if the local system
has at least one IPv6 address configured. The loopback address is not
considered for this case as valid as a configured address. This flag is
useful on, for example, IPv4-only systems, to ensure that getaddrinfo()
does not return IPv6 socket addresses that would always fail in
subsequent connect() or bind() attempts. */
hints.ai_flags = AI_ADDRCONFIG;
#if defined(HAVE_IDN) && defined(AI_IDN)
/* If the AI_IDN flag is specified and we have glibc 2.3.4 or newer, then
the node name given in node is converted to IDN format if necessary.
The source encoding is that of the current locale. */
hints.ai_flags |= AI_IDN;
#endif
source_addr->in.sin_addr.s_addr = INADDR_ANY;
source_addr->in.sin_port = htons(daemon->query_port);
if (source)
{
if (flags)
*flags |= SERV_HAS_SOURCE;
source_addr->in.sin_port = htons(source_port);
if (!(inet_pton(AF_INET, source, &source_addr->in.sin_addr) > 0))
{
#if defined(SO_BINDTODEVICE)
if (interface_opt)
return _("interface can only be specified once");
source_addr->in.sin_addr.s_addr = INADDR_ANY;
safe_strncpy(interface, source, IF_NAMESIZE);
#else
return _("interface binding not supported");
#endif
}
}
}
else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)
{
if (scope_id && (scope_index = if_nametoindex(scope_id)) == 0)
return _("bad interface name");
addr->in6.sin6_port = htons(serv_port);
addr->in6.sin6_scope_id = scope_index;
source_addr->in6.sin6_addr = in6addr_any;
source_addr->in6.sin6_port = htons(daemon->query_port);
source_addr->in6.sin6_scope_id = 0;
addr->sa.sa_family = source_addr->sa.sa_family = AF_INET6;
addr->in6.sin6_flowinfo = source_addr->in6.sin6_flowinfo = 0;
#ifdef HAVE_SOCKADDR_SA_LEN
addr->in6.sin6_len = source_addr->in6.sin6_len = sizeof(addr->in6);
#endif
if (source)
{
if (flags)
*flags |= SERV_HAS_SOURCE;
source_addr->in6.sin6_port = htons(source_port);
if (inet_pton(AF_INET6, source, &source_addr->in6.sin6_addr) == 0)
{
#if defined(SO_BINDTODEVICE)
if (interface_opt)
return _("interface can only be specified once");
source_addr->in6.sin6_addr = in6addr_any;
safe_strncpy(interface, source, IF_NAMESIZE);
#else
return _("interface binding not supported");
#endif
}
}
}
else
return _("bad address");
/* The value AF_UNSPEC indicates that getaddrinfo() should return socket
addresses for any address family (either IPv4 or IPv6, for example)
that can be used with node <arg> and service "domain". */
hints.ai_family = AF_UNSPEC;
/* Get addresses suitable for sending datagrams. We assume that we can use the
same addresses for TCP connections. Settting this to zero gets each address
threes times, for SOCK_STREAM, SOCK_RAW and SOCK_DGRAM, which is not useful. */
hints.ai_socktype = SOCK_DGRAM;
/* Get address associated with this hostname */
ecode = getaddrinfo(arg, NULL, &hints, &sdetails->hostinfo);
if (ecode == 0)
{
/* The getaddrinfo() function allocated and initialized a linked list of
addrinfo structures, one for each network address that matches node
and service, subject to the restrictions imposed by our <hints>
above, and returns a pointer to the start of the list in <hostinfo>.
The items in the linked list are linked by the <ai_next> field. */
sdetails->valid = 1;
sdetails->orig_hostinfo = sdetails->hostinfo;
return NULL;
}
else
{
/* Lookup failed, return human readable error string */
if (ecode == EAI_AGAIN)
return _("Cannot resolve server name");
else
return _((char*)gai_strerror(ecode));
}
}
sdetails->valid = 1;
return NULL;
}
char *parse_server_addr(struct server_details *sdetails)
{
if (sdetails->addr_type == AF_INET)
{
sdetails->addr->in.sin_port = htons(sdetails->serv_port);
sdetails->addr->sa.sa_family = sdetails->source_addr->sa.sa_family = AF_INET;
#ifdef HAVE_SOCKADDR_SA_LEN
sdetails->source_addr->in.sin_len = sdetails->addr->in.sin_len = sizeof(struct sockaddr_in);
#endif
sdetails->source_addr->in.sin_addr.s_addr = INADDR_ANY;
sdetails->source_addr->in.sin_port = htons(daemon->query_port);
if (sdetails->source)
{
if (sdetails->flags)
*sdetails->flags |= SERV_HAS_SOURCE;
sdetails->source_addr->in.sin_port = htons(sdetails->source_port);
if (inet_pton(AF_INET, sdetails->source, &sdetails->source_addr->in.sin_addr) == 0)
{
if (inet_pton(AF_INET6, sdetails->source, &sdetails->source_addr->in6.sin6_addr) == 1)
{
sdetails->source_addr->sa.sa_family = AF_INET6;
/* When resolving a server IP by hostname, we can simply skip mismatching
server / source IP pairs. Otherwise, when an IP address is given directly,
this is a fatal error. */
if (!sdetails->orig_hostinfo)
return _("cannot use IPv4 server address with IPv6 source address");
}
else
{
#if defined(SO_BINDTODEVICE)
if (sdetails->interface_opt)
return _("interface can only be specified once");
sdetails->source_addr->in.sin_addr.s_addr = INADDR_ANY;
safe_strncpy(sdetails->interface, sdetails->source, IF_NAMESIZE);
#else
return _("interface binding not supported");
#endif
}
}
}
}
else if (sdetails->addr_type == AF_INET6)
{
if (sdetails->scope_id && (sdetails->scope_index = if_nametoindex(sdetails->scope_id)) == 0)
return _("bad interface name");
sdetails->addr->in6.sin6_port = htons(sdetails->serv_port);
sdetails->addr->in6.sin6_scope_id = sdetails->scope_index;
sdetails->source_addr->in6.sin6_addr = in6addr_any;
sdetails->source_addr->in6.sin6_port = htons(daemon->query_port);
sdetails->source_addr->in6.sin6_scope_id = 0;
sdetails->addr->sa.sa_family = sdetails->source_addr->sa.sa_family = AF_INET6;
sdetails->addr->in6.sin6_flowinfo = sdetails->source_addr->in6.sin6_flowinfo = 0;
#ifdef HAVE_SOCKADDR_SA_LEN
sdetails->addr->in6.sin6_len = sdetails->source_addr->in6.sin6_len = sizeof(sdetails->addr->in6);
#endif
if (sdetails->source)
{
if (sdetails->flags)
*sdetails->flags |= SERV_HAS_SOURCE;
sdetails->source_addr->in6.sin6_port = htons(sdetails->source_port);
if (inet_pton(AF_INET6, sdetails->source, &sdetails->source_addr->in6.sin6_addr) == 0)
{
if (inet_pton(AF_INET, sdetails->source, &sdetails->source_addr->in.sin_addr) == 1)
{
sdetails->source_addr->sa.sa_family = AF_INET;
/* When resolving a server IP by hostname, we can simply skip mismatching
server / source IP pairs. Otherwise, when an IP address is given directly,
this is a fatal error. */
if(!sdetails->orig_hostinfo)
return _("cannot use IPv6 server address with IPv4 source address");
}
else
{
#if defined(SO_BINDTODEVICE)
if (sdetails->interface_opt)
return _("interface can only be specified once");
sdetails->source_addr->in6.sin6_addr = in6addr_any;
safe_strncpy(sdetails->interface, sdetails->source, IF_NAMESIZE);
#else
return _("interface binding not supported");
#endif
}
}
}
}
else if (sdetails->addr_type != AF_LOCAL)
return _("bad address");
return NULL;
}
int parse_server_next(struct server_details *sdetails)
{
/* Looping over resolved addresses? */
if (sdetails->hostinfo)
{
/* Get address type */
sdetails->addr_type = sdetails->hostinfo->ai_family;
/* Get address */
if (sdetails->addr_type == AF_INET)
memcpy(&sdetails->addr->in.sin_addr,
&((struct sockaddr_in *) sdetails->hostinfo->ai_addr)->sin_addr,
sizeof(sdetails->addr->in.sin_addr));
else if (sdetails->addr_type == AF_INET6)
memcpy(&sdetails->addr->in6.sin6_addr,
&((struct sockaddr_in6 *) sdetails->hostinfo->ai_addr)->sin6_addr,
sizeof(sdetails->addr->in6.sin6_addr));
/* Iterate to the next available address */
sdetails->valid = sdetails->hostinfo->ai_next != NULL;
sdetails->hostinfo = sdetails->hostinfo->ai_next;
return 1;
}
else if (sdetails->valid)
{
/* When using an IP address, we return the address only once */
sdetails->valid = 0;
return 1;
}
/* Stop iterating here, we used all available addresses */
return 0;
}
static char *domain_rev4(int from_file, char *server, struct in_addr *addr4, int size)
{
int i, j;
@@ -964,22 +1103,29 @@ static char *domain_rev4(int from_file, char *server, struct in_addr *addr4, int
union mysockaddr serv_addr, source_addr;
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;
sdetails.flags = &flags;
if (!server)
flags = SERV_LITERAL_ADDRESS;
else if ((string = parse_server(server, &serv_addr, &source_addr, interface, &flags)))
else if ((string = parse_server(server, &sdetails)))
return string;
if (from_file)
flags |= SERV_FROM_FILE;
rem = size & 0x7;
addrbytes = (32 - size) >> 3;
addrbits = (32 - size) & 7;
if (size > 32 || size < 1)
return _("bad IPv4 prefix length");
/* Zero out last address bits according to CIDR mask */
((u8 *)addr4)[3-addrbytes] &= ~((1 << addrbits)-1);
@@ -993,23 +1139,40 @@ static char *domain_rev4(int from_file, char *server, struct in_addr *addr4, int
*domain = 0;
string = domain;
msize = size/8;
for (j = (rem == 0) ? msize-1 : msize; j >= 0; j--)
{
int dig = ((unsigned char *)addr4)[j];
if (j == msize)
dig += i;
string += sprintf(string, "%d.", dig);
}
sprintf(string, "in-addr.arpa");
if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
return _("error");
}
if (flags & SERV_LITERAL_ADDRESS)
{
if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
return _("error");
}
else
{
while (parse_server_next(&sdetails))
{
if ((string = parse_server_addr(&sdetails)))
return string;
if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
return _("error");
}
if (sdetails.orig_hostinfo)
freeaddrinfo(sdetails.orig_hostinfo);
}
}
return NULL;
}
@@ -1023,10 +1186,17 @@ static char *domain_rev6(int from_file, char *server, struct in6_addr *addr6, in
union mysockaddr serv_addr, source_addr;
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;
sdetails.flags = &flags;
if (!server)
flags = SERV_LITERAL_ADDRESS;
else if ((string = parse_server(server, &serv_addr, &source_addr, interface, &flags)))
else if ((string = parse_server(server, &sdetails)))
return string;
if (from_file)
@@ -1067,10 +1237,27 @@ static char *domain_rev6(int from_file, char *server, struct in6_addr *addr6, in
sprintf(string, "ip6.arpa");
if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
return _("error");
}
if (flags & SERV_LITERAL_ADDRESS)
{
if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
return _("error");
}
else
{
while (parse_server_next(&sdetails))
{
if ((string = parse_server_addr(&sdetails)))
return string;
if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
return _("error");
}
if (sdetails.orig_hostinfo)
freeaddrinfo(sdetails.orig_hostinfo);
}
}
return NULL;
}
@@ -2153,15 +2340,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
case LOPT_DHCP_HOST: /* --dhcp-hostsfile */
case LOPT_DHCP_OPTS: /* --dhcp-optsfile */
case LOPT_DHCP_INOTIFY: /* --dhcp-hostsdir */
case LOPT_DHOPT_INOTIFY: /* --dhcp-optsdir */
case LOPT_HOST_INOTIFY: /* --hostsdir */
case 'H': /* --addn-hosts */
{
struct hostsfile *new = opt_malloc(sizeof(struct hostsfile));
static unsigned int hosts_index = SRC_AH;
new->fname = opt_string_alloc(arg);
new->index = hosts_index++;
new->index = daemon->host_index++;
new->flags = 0;
if (option == 'H')
{
@@ -2177,21 +2360,29 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
{
new->next = daemon->dhcp_opts_file;
daemon->dhcp_opts_file = new;
}
else
{
new->next = daemon->dynamic_dirs;
daemon->dynamic_dirs = new;
if (option == LOPT_DHCP_INOTIFY)
new->flags |= AH_DHCP_HST;
else if (option == LOPT_DHOPT_INOTIFY)
new->flags |= AH_DHCP_OPT;
else if (option == LOPT_HOST_INOTIFY)
new->flags |= AH_HOSTS;
}
break;
}
case LOPT_DHCP_INOTIFY: /* --dhcp-hostsdir */
case LOPT_DHOPT_INOTIFY: /* --dhcp-optsdir */
case LOPT_HOST_INOTIFY: /* --hostsdir */
{
struct dyndir *new = opt_malloc(sizeof(struct dyndir));
new->dname = opt_string_alloc(arg);
new->flags = 0;
new->next = daemon->dynamic_dirs;
daemon->dynamic_dirs = new;
if (option == LOPT_DHCP_INOTIFY)
new->flags |= AH_DHCP_HST;
else if (option == LOPT_DHOPT_INOTIFY)
new->flags |= AH_DHCP_OPT;
else if (option == LOPT_HOST_INOTIFY)
new->flags |= AH_HOSTS;
break;
}
case LOPT_AUTHSERV: /* --auth-server */
comma = split(arg);
@@ -2777,13 +2968,20 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
case LOPT_LOCAL: /* --local */
case 'A': /* --address */
{
char *lastdomain = NULL, *domain = "";
char *lastdomain = NULL, *domain = "", *cur_domain;
u16 flags = 0;
char *err;
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. */
@@ -2816,36 +3014,55 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
}
else
{
if ((err = parse_server(arg, &serv_addr, &source_addr, interface, &flags)))
if ((err = parse_server(arg, &sdetails)))
ret_err(err);
}
if (servers_only && option == 'S')
flags |= SERV_FROM_FILE;
while (1)
{
/* server=//1.2.3.4 is special. */
if (lastdomain)
{
if (strlen(domain) == 0)
flags |= SERV_FOR_NODOTS;
else
flags &= ~SERV_FOR_NODOTS;
/* address=/#/ matches the same as without domain */
if (option == 'A' && domain[0] == '#' && domain[1] == 0)
domain[0] = 0;
cur_domain = domain;
while ((flags & SERV_LITERAL_ADDRESS) || parse_server_next(&sdetails))
{
cur_domain = domain;
if (!(flags & SERV_LITERAL_ADDRESS) && (err = parse_server_addr(&sdetails)))
ret_err(err);
/* When source is set only use DNS records of the same type and skip all others */
if (flags & SERV_HAS_SOURCE && sdetails.addr_type != sdetails.source_addr->sa.sa_family)
continue;
while (1)
{
/* server=//1.2.3.4 is special. */
if (lastdomain)
{
if (strlen(cur_domain) == 0)
flags |= SERV_FOR_NODOTS;
else
flags &= ~SERV_FOR_NODOTS;
/* address=/#/ matches the same as without domain */
if (option == 'A' && cur_domain[0] == '#' && cur_domain[1] == 0)
cur_domain[0] = 0;
}
if (!add_update_server(flags, sdetails.addr, sdetails.source_addr, sdetails.interface, cur_domain, &addr))
ret_err(gen_err);
if (!lastdomain || cur_domain == lastdomain)
break;
cur_domain += strlen(cur_domain) + 1;
}
if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, &addr))
ret_err(gen_err);
if (!lastdomain || domain == lastdomain)
if (flags & SERV_LITERAL_ADDRESS)
break;
domain += strlen(domain) + 1;
}
if (sdetails.orig_hostinfo)
freeaddrinfo(sdetails.orig_hostinfo);
break;
}
@@ -3179,6 +3396,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
if (daemon->query_port == 0)
daemon->osport = 1;
break;
case LOPT_RANDPORT_LIM: /* --port-limit */
if (!atoi_check(arg, &daemon->randport_limit) || (daemon->randport_limit < 1))
ret_err(gen_err);
break;
case 'T': /* --local-ttl */
case LOPT_NEGTTL: /* --neg-ttl */
@@ -3214,7 +3436,30 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
daemon->local_ttl = (unsigned long)ttl;
break;
}
case LOPT_FAST_RETRY:
daemon->fast_retry_timeout = TIMEOUT;
if (!arg)
daemon->fast_retry_time = DEFAULT_FAST_RETRY;
else
{
int retry;
comma = split(arg);
if (!atoi_check(arg, &retry) || retry < 50)
ret_err(gen_err);
daemon->fast_retry_time = retry;
if (comma)
{
if (!atoi_check(comma, &retry))
ret_err(gen_err);
daemon->fast_retry_timeout = retry/1000;
}
}
break;
#ifdef HAVE_DHCP
case 'X': /* --dhcp-lease-max */
if (!atoi_check(arg, &daemon->dhcp_max))
@@ -4909,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);
@@ -5494,22 +5757,12 @@ void read_opts(int argc, char **argv, char *compile_opts)
daemon->soa_refresh = SOA_REFRESH;
daemon->soa_retry = SOA_RETRY;
daemon->soa_expiry = SOA_EXPIRY;
daemon->randport_limit = 1;
daemon->host_index = SRC_AH;
/* See comment above make_servers(). Optimises server-read code. */
mark_servers(0);
#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
while (1)
{
#ifdef HAVE_GETOPT_LONG
@@ -5604,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)
{

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)))
@@ -1360,8 +1362,15 @@ int add_resource_record(struct dns_header *header, char *limit, int *truncp, int
#undef CHECK_LIMIT
}
static int crec_isstale(struct crec *crecp, time_t now)
{
return (!(crecp->flags & F_IMMORTAL)) && difftime(crecp->ttd, now) < 0;
}
static unsigned long crec_ttl(struct crec *crecp, time_t now)
{
signed long ttl = difftime(crecp->ttd, now);
/* Return 0 ttl for DHCP entries, which might change
before the lease expires, unless configured otherwise. */
@@ -1370,8 +1379,8 @@ static unsigned long crec_ttl(struct crec *crecp, time_t now)
int conf_ttl = daemon->use_dhcp_ttl ? daemon->dhcp_ttl : daemon->local_ttl;
/* Apply ceiling of actual lease length to configured TTL. */
if (!(crecp->flags & F_IMMORTAL) && (crecp->ttd - now) < conf_ttl)
return crecp->ttd - now;
if (!(crecp->flags & F_IMMORTAL) && ttl < conf_ttl)
return ttl;
return conf_ttl;
}
@@ -1380,9 +1389,13 @@ static unsigned long crec_ttl(struct crec *crecp, time_t now)
if (crecp->flags & F_IMMORTAL)
return crecp->ttd;
/* Stale cache entries. */
if (ttl < 0)
return 0;
/* Return the Max TTL value if it is lower than the actual TTL */
if (daemon->max_ttl == 0 || ((unsigned)(crecp->ttd - now) < daemon->max_ttl))
return crecp->ttd - now;
if (daemon->max_ttl == 0 || ((unsigned)ttl < daemon->max_ttl))
return ttl;
else
return daemon->max_ttl;
}
@@ -1395,7 +1408,8 @@ static int cache_validated(const struct crec *crecp)
/* return zero if we can't answer from cache, or packet size if we can */
size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
struct in_addr local_addr, struct in_addr local_netmask,
time_t now, int ad_reqd, int do_bit, int have_pseudoheader)
time_t now, int ad_reqd, int do_bit, int have_pseudoheader,
int *stale)
{
char *name = daemon->namebuff;
unsigned char *p, *ansp;
@@ -1411,6 +1425,9 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
size_t len;
int rd_bit = (header->hb3 & HB3_RD);
if (stale)
*stale = 0;
/* never answer queries with RD unset, to avoid cache snooping. */
if (ntohs(header->ancount) != 0 ||
ntohs(header->nscount) != 0 ||
@@ -1459,13 +1476,22 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
while (--count != 0 && (crecp = cache_find_by_name(NULL, name, now, F_CNAME | F_NXDOMAIN)))
{
char *cname_target;
int stale_flag = 0;
if (crec_isstale(crecp, now))
{
if (stale)
*stale = 1;
stale_flag = F_STALE;
}
if (crecp->flags & F_NXDOMAIN)
{
if (qtype == T_CNAME)
{
if (!dryrun)
log_query(crecp->flags, name, NULL, record_source(crecp->uid), 0);
log_query(stale_flag | crecp->flags, name, NULL, record_source(crecp->uid), 0);
auth = 0;
nxdomain = 1;
ans = 1;
@@ -1487,7 +1513,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (!dryrun)
{
log_query(crecp->flags, name, NULL, record_source(crecp->uid), 0);
log_query(stale_flag | crecp->flags, name, NULL, record_source(crecp->uid), 0);
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
crec_ttl(crecp, now), &nameoffset,
T_CNAME, C_IN, "d", cname_target))
@@ -1656,22 +1682,33 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
{
do
{
int stale_flag = 0;
if (crec_isstale(crecp, now))
{
if (stale)
*stale = 1;
stale_flag = F_STALE;
}
/* don't answer wildcard queries with data not from /etc/hosts or dhcp leases */
if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP)))
continue;
if (!(crecp->flags & F_DNSSECOK))
sec_data = 0;
ans = 1;
if (crecp->flags & F_NEG)
{
auth = 0;
if (crecp->flags & F_NXDOMAIN)
nxdomain = 1;
if (!dryrun)
log_query(crecp->flags & ~F_FORWARD, name, &addr, NULL, 0);
log_query(stale_flag | (crecp->flags & ~F_FORWARD), name, &addr, NULL, 0);
}
else
{
@@ -1679,7 +1716,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
auth = 0;
if (!dryrun)
{
log_query(crecp->flags & ~F_FORWARD, cache_get_name(crecp), &addr,
log_query(stale_flag | (crecp->flags & ~F_FORWARD), cache_get_name(crecp), &addr,
record_source(crecp->uid), 0);
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
@@ -1788,7 +1825,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if ((crecp = cache_find_by_name(NULL, name, now, flag | F_NXDOMAIN | (dryrun ? F_NO_RR : 0))))
{
int localise = 0;
/* See if a putative address is on the network from which we received
the query, is so we'll filter other answers. */
if (local_addr.s_addr != 0 && option_bool(OPT_LOCALISE) && flag == F_IPV4)
@@ -1810,6 +1847,16 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
(rd_bit && (!do_bit || cache_validated(crecp)) ))
do
{
int stale_flag = 0;
if (crec_isstale(crecp, now))
{
if (stale)
*stale = 1;
stale_flag = F_STALE;
}
/* don't answer wildcard queries with data not from /etc/hosts
or DHCP leases */
if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
@@ -1825,7 +1872,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (crecp->flags & F_NXDOMAIN)
nxdomain = 1;
if (!dryrun)
log_query(crecp->flags, name, NULL, NULL, 0);
log_query(stale_flag | crecp->flags, name, NULL, NULL, 0);
}
else
{
@@ -1842,7 +1889,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
ans = 1;
if (!dryrun)
{
log_query(crecp->flags & ~F_REVERSE, name, &crecp->addr,
log_query(stale_flag | (crecp->flags & ~F_REVERSE), name, &crecp->addr,
record_source(crecp->uid), 0);
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
@@ -1953,6 +2000,15 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
rd_bit && (!do_bit || (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK))))
do
{
int stale_flag = 0;
if (crec_isstale(crecp, now))
{
if (stale)
*stale = 1;
stale_flag = F_STALE;
}
/* don't answer wildcard queries with data not from /etc/hosts or dhcp leases, except for NXDOMAIN */
if (qtype == T_ANY && !(crecp->flags & (F_NXDOMAIN)))
break;
@@ -1968,12 +2024,12 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (crecp->flags & F_NXDOMAIN)
nxdomain = 1;
if (!dryrun)
log_query(crecp->flags, name, NULL, NULL, 0);
log_query(stale_flag | crecp->flags, name, NULL, NULL, 0);
}
else if (!dryrun)
{
char *target = blockdata_retrieve(crecp->addr.srv.target, crecp->addr.srv.targetlen, NULL);
log_query(crecp->flags, name, NULL, NULL, 0);
log_query(stale_flag | crecp->flags, name, NULL, NULL, 0);
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
crec_ttl(crecp, now), NULL, T_SRV, C_IN, "sssd",

View File

@@ -1153,15 +1153,22 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
tagif_netid = run_tag_if(&context->netid);
}
log_tags(tagif_netid, ntohl(mess->xid));
apply_delay(mess->xid, recvtime, tagif_netid);
if (option_bool(OPT_RAPID_COMMIT) && option_find(mess, sz, OPTION_RAPID_COMMIT, 0))
{
rapid_commit = 1;
/* If a lease exists for this host and another address, squash it. */
if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
{
lease_prune(lease, now);
lease = NULL;
}
goto rapid_commit;
}
log_tags(tagif_netid, ntohl(mess->xid));
daemon->metrics[METRIC_DHCPOFFER]++;
log_packet("DHCPOFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL, NULL, mess->xid);

View File

@@ -364,6 +364,19 @@ int sockaddr_isequal(const union mysockaddr *s1, const union mysockaddr *s2)
return 0;
}
int sockaddr_isnull(const union mysockaddr *s)
{
if (s->sa.sa_family == AF_INET &&
s->in.sin_addr.s_addr == 0)
return 1;
if (s->sa.sa_family == AF_INET6 &&
IN6_IS_ADDR_UNSPECIFIED(&s->in6.sin6_addr))
return 1;
return 0;
}
int sa_len(union mysockaddr *addr)
{
#ifdef HAVE_SOCKADDR_SA_LEN
@@ -457,6 +470,15 @@ time_t dnsmasq_time(void)
#endif
}
u32 dnsmasq_milliseconds(void)
{
struct timeval tv;
gettimeofday(&tv, NULL);
return (tv.tv_sec) * 1000 + (tv.tv_usec / 1000);
}
int netmask_length(struct in_addr mask)
{
int zero_count = 0;