Compare commits

...

139 Commits

Author SHA1 Message Date
Simon Kelley
7e22cf28f8 Update doc.html - was positively antediluvian. 2014-04-24 12:05:33 +01:00
Simon Kelley
3b1b3e9d50 CHANGELOG update for 2.70 release. 2014-04-23 15:46:05 +01:00
Simon Kelley
ab72091de2 Bump Debian version. 2014-04-23 15:14:48 +01:00
Matt Comben
66f57867d8 Typo. 2014-04-23 12:28:04 +01:00
Simon Kelley
6375838445 Fix crash on TCP DNS request when DNSSEC not enabled. 2014-04-16 22:20:55 +01:00
Simon Kelley
82a14af5e7 Ensure request name in buffer for ipset lookup. 2014-04-13 20:48:57 +01:00
Simon Kelley
97dce08ed7 Add donate button to doc.html. 2014-04-11 19:05:54 +01:00
Simon Kelley
198d940af6 Update CHANGELOG/release-notes. 2014-04-09 20:36:53 +01:00
Lutz Preßler
1d7e0a36e3 ipv6.arpa -> ip6.arpa 2014-04-07 22:06:23 +01:00
Simon Kelley
10068600f8 Fix NXDOMAIN RCODE in auth PTR replies. 2014-04-03 21:16:40 +01:00
Simon Kelley
b7639d5815 Fix ipsets logging patch. 2014-03-29 09:20:07 +00:00
Wang Jian
49752b90d5 Log IPSET actions. 2014-03-28 20:52:47 +00:00
Simon Kelley
e98bd52e25 Add --dnssec-no-timecheck 2014-03-28 20:41:23 +00:00
Simon Kelley
8a8bbad0cf Ensure ->sentto is valid for DNSSEC forwards. Otherwise retries SEGV. 2014-03-27 22:02:17 +00:00
Simon Kelley
fec216df32 Cache stats availble in CHAOS .bind domain. 2014-03-27 20:54:34 +00:00
Simon Kelley
4e1fe44428 Terminate DS-search when reaching the root via cache entries. 2014-03-26 12:24:19 +00:00
Simon Kelley
51967f9807 SERVFAIL is an expected error return, don't try all servers. 2014-03-25 21:07:00 +00:00
Tomas Hozza
b37f8b99ae Handle failure of hash_questions() 2014-03-25 20:52:28 +00:00
Tomas Hozza
fc2833f172 Memory leak in error path. 2014-03-25 20:43:21 +00:00
Simon Kelley
490f90758d Reorder sanity checks on UDP packet reception, to cope with failed recvfrom() 2014-03-24 22:04:42 +00:00
Simon Kelley
56618c31f6 Add dnssec-check-unsigned to example config file. 2014-03-24 21:13:49 +00:00
Simon Kelley
604f7598c2 CHANGELOG update. 2014-03-22 19:33:43 +00:00
Simon Kelley
2a7a2b84ec Ignore DNS queries from port 0: http://www.ietf.org/mail-archive/web/dnsop/current/msg11441.html 2014-03-22 19:18:06 +00:00
Andy
3e21a1a6fa Tidy uid defines. 2014-03-22 19:10:07 +00:00
Simon Kelley
2b29191e7c Fix DNSSEC crash retrying to IPv6 server. 2014-03-21 11:13:55 +00:00
Simon Kelley
03431d6373 Initialise uid when creating CNAME cache record. 2014-03-20 16:25:43 +00:00
Simon Kelley
cc1a29e250 Make --quiet-dhcp apply to DHCPDISCOVER when client ignored. 2014-03-20 15:47:18 +00:00
Moritz Warning
e62e9b6187 Manpage typos. 2014-03-20 15:32:22 +00:00
Simon Kelley
19c51cfa49 Tidy and fix cache->uid handling.
Some CNAMES left the value of ->uid undefined.

Since there are now special values if this, for CNAMES
to interface names, that could cause a crash
if the undefined value hit the special value.

Also ensure that the special value can't arise
when the uid is encoding the source of an F_CONFIG
record, in case there's a CNAME to it.
2014-03-18 22:38:30 +00:00
Andy
d5082158ee Ensure next_uid() can never return 0. 2014-03-17 19:50:29 +00:00
Simon Kelley
3f7483e816 Handle integer overflow in uid counter. Fixes rare crashes in cache code. 2014-03-16 22:56:58 +00:00
Simon Kelley
0c8584eabc Warn about non-local queries once only for UDP. 2014-03-12 20:12:56 +00:00
Simon Kelley
f00690f93e Typo 2014-03-12 20:07:12 +00:00
Simon Kelley
89b12ed35b OPT_LOCAL_SERVICE needs up-to-date interface list too. 2014-03-06 13:27:57 +00:00
Simon Kelley
1a9a3489ec Set --local-service in Debian package startup. 2014-03-05 15:01:08 +00:00
Simon Kelley
c8a80487cd --local-service. Default protection from DNS amplification attacks. 2014-03-05 14:29:54 +00:00
Simon Kelley
4ea8e80dd9 Add --static to pkg-config command when appropriate. 2014-03-05 11:01:23 +00:00
Simon Kelley
c07d30dcb1 Compiler warning. 2014-03-03 14:19:19 +00:00
Simon Kelley
d588ab54d4 Man page updates for DNSSEC. 2014-03-02 14:30:05 +00:00
Simon Kelley
f8b422a7b6 KEYBLOCK LEN better as a multiple of 8. 2014-03-02 12:46:51 +00:00
Simon Kelley
29fe922b14 Can have local DS records (trust anchors). 2014-03-01 22:53:57 +00:00
Simon Kelley
8707019237 Mass edit of INSECURE->BOGUS returns for server failure/bad input. 2014-03-01 20:48:24 +00:00
Simon Kelley
d1fbb77e0f Don't cache secure replies which we've messsed with. 2014-03-01 20:08:58 +00:00
Simon Kelley
1fbe4d2f5f Tweak tuning params. 2014-03-01 20:03:47 +00:00
Simon Kelley
0575610fa1 Handle replies with no answers and no NS in validate_reply. 2014-03-01 18:07:57 +00:00
Simon Kelley
e3f1455850 Don't free blockdata for negative DS cache entries. 2014-03-01 17:58:28 +00:00
Simon Kelley
bd9b3cf55b Fix off-by-one overwrite. 2014-03-01 16:12:28 +00:00
Simon Kelley
14db4212ab Tidy. 2014-03-01 15:35:50 +00:00
Simon Kelley
00a5b5d477 Check that unsigned replies come from unsigned zones if --dnssec-check-unsigned set. 2014-02-28 18:10:55 +00:00
Simon Kelley
b8eac19177 Negative caching for DS records. 2014-02-27 14:30:03 +00:00
Simon Kelley
b47b04c846 Return INSECURE when validation fails with proved non-existent DS. 2014-02-25 23:13:28 +00:00
Simon Kelley
613ad15d02 Strip DNSSEC RRs when query doesn't have DO bit set. 2014-02-25 23:02:28 +00:00
Simon Kelley
24187530fb Speeling. 2014-02-24 21:46:44 +00:00
Simon Kelley
a857daa351 Code cleanup. 2014-02-24 21:01:09 +00:00
Simon Kelley
f01d7be6c6 An NSEC record cannot attest to its own non-existance! 2014-02-24 20:20:00 +00:00
Simon Kelley
d387380a25 Check signer name in RRSIGs. 2014-02-23 16:20:46 +00:00
Simon Kelley
f2e4c277c4 Bugfix for last commit. 2014-02-23 15:24:26 +00:00
Simon Kelley
5107ace14a NSEC3 validation. First pass. 2014-02-23 10:48:32 +00:00
Simon Kelley
7b1eae4f50 Add --servers-file option. 2014-02-20 13:43:28 +00:00
Simon Kelley
c152dc8492 Omit ECC from DNSSEC if nettle library is old. 2014-02-19 18:14:33 +00:00
Simon Kelley
7bcca0060f More server cleanup. 2014-02-19 17:45:17 +00:00
Simon Kelley
d68c2ca2b7 Cleanup of server reading code, preparation, for dynamic reading from files. 2014-02-18 22:30:30 +00:00
Simon Kelley
de73a497ca --rev-server option. Syntactic sugar for PTR queries. 2014-02-17 21:43:27 +00:00
Simon Kelley
e3ec15af10 Log BOGUS validation result when upstream sends SERVFAIL. 2014-02-13 16:56:30 +00:00
Simon Kelley
dac74312da TYpo. 2014-02-13 16:43:49 +00:00
Simon Kelley
2ecd9bd5c0 No CD in forwarded queries unless dnssec-debug for TCP too. 2014-02-13 16:42:02 +00:00
Simon Kelley
a0ab18f6eb Don't mess with the TTL of DNSSEC RRs. 2014-02-13 16:38:23 +00:00
Simon Kelley
ebe95a831f Add RFC-6605 ECDSA DNSSEC verification. 2014-02-13 14:56:10 +00:00
Simon Kelley
ee4158678a Use DS records as trust anchors, not DNSKEYs.
This allows us to query for the root zone DNSKEY RRset and validate
it, thus automatically handling KSK rollover.
2014-02-11 11:07:22 +00:00
Simon Kelley
83349b8aa4 Further tidying of AD and DO bit handling. 2014-02-10 21:02:01 +00:00
Simon Kelley
7fa836e105 Handle validation when more one key is needed. 2014-02-10 20:11:24 +00:00
Simon Kelley
1633e30834 Fix Byte-order botch: broke DNSSEC on big-endian platforms. 2014-02-10 16:42:46 +00:00
Simon Kelley
c8ca33f810 Fix DNSSEC caching problems: incomplete RRSIG RRsets. 2014-02-10 10:35:42 +00:00
Simon Kelley
e243c072b5 AD bit in queries handled as RFC6840 p5.7 2014-02-06 18:14:09 +00:00
Simon Kelley
da4f372271 Add trust-anchors file to Debian package. 2014-02-06 15:21:37 +00:00
Simon Kelley
610e782a29 Fix stack-smashing crash in DNSSEC. Thanks to Henk Jan Agteresch. 2014-02-06 14:45:17 +00:00
Simon Kelley
854cf26907 DNSSEC config in example file. 2014-02-06 12:07:10 +00:00
Simon Kelley
bb201c211a Protect against malicious DNS replies with very large RRsets. 2014-02-06 12:01:05 +00:00
Simon Kelley
12fae49fff Make RR work when returning A/AAAA records and an RRSIG. 2014-02-04 22:03:06 +00:00
Jesse Glick
fd372273bd Updated version of contrib/try-all-ns 2014-02-04 20:20:35 +00:00
Simon Kelley
b98d22c191 Linking stuff. Latest Debian/Ubuntu don't automatically link gmp. 2014-02-04 18:09:30 +00:00
Simon Kelley
160f6507c3 Make DNSEC default, add build-depends for same, bump version. 2014-02-04 16:49:41 +00:00
Simon Kelley
613d6c5249 CHANGLEOG for DNSSEC. 2014-02-04 11:50:11 +00:00
Simon Kelley
81a883fda3 Format tweak. 2014-02-03 21:17:04 +00:00
Simon Kelley
40b695c1f1 Log NXDOMAIN correctly. 2014-02-03 17:07:51 +00:00
Simon Kelley
5f938534a9 Return configured DNSKEYs even though we don't have RRSIGS for them. 2014-02-03 16:44:32 +00:00
Simon Kelley
8d718cbb3e Nasty cache failure and memory leak with DNSSEC. 2014-02-03 16:27:37 +00:00
Simon Kelley
f6a2b79310 Validate Ooops. 2014-02-01 14:54:26 +00:00
Simon Kelley
82e3f45a9f Blockdata fixes and tuning. 2014-01-31 21:05:48 +00:00
Simon Kelley
072e81b3c5 Blockdata leak. 2014-01-31 12:42:54 +00:00
Simon Kelley
1d97ac4fd2 copy-n-paste error. 2014-01-31 11:12:27 +00:00
Simon Kelley
db73746620 Anounce DNSSEC at startup. 2014-01-31 10:32:45 +00:00
Simon Kelley
97bc798b05 Init ->dependent field in frec allocation. 2014-01-31 10:19:52 +00:00
Simon Kelley
edc231bc58 Compiler warning. 2014-01-31 09:52:50 +00:00
Simon Kelley
b85e092e23 Add a file containing current root trust anchors, for convenience. 2014-01-30 09:49:28 +00:00
Simon Kelley
583043f527 Crash in cache code when compiled with HAVE_DNSSEC. 2014-01-28 14:54:46 +00:00
Simon Kelley
8f6213cce9 Allow use of COPTS in Debian rules invokation for nefarious purposes. 2014-01-28 11:16:49 +00:00
Simon Kelley
00ec693db8 Debian package with DNSSEC now possible.
DNSSEC will eventually become opt-out and when that happens
I'll add libnettle build-depends. For now, build with

fakeroot debian/rules DEB_BUILD_OPTIONS=usednssec

to get DNSSEC support.
2014-01-28 11:08:57 +00:00
Simon Kelley
70b4a818ef Man page entries for DNSSEC flags. 2014-01-27 22:38:48 +00:00
Simon Kelley
7c28612a59 Trivial format fix. 2014-01-27 21:38:11 +00:00
Simon Kelley
6f4681034e Code tidy. 2014-01-26 23:39:17 +00:00
Simon Kelley
6938f3476e Don't mark answers as DNSEC validated if DNS-doctored. 2014-01-26 22:47:39 +00:00
Simon Kelley
17fb9ea763 Exclude CRC code in DNSSEC build - replaced with SHA1. 2014-01-26 09:36:54 +00:00
Simon Kelley
7d23a66ff0 Remove --dnssec-permissive, pointless if we don't set CD upstream. 2014-01-26 09:33:21 +00:00
Simon Kelley
703c7ff429 Fix to last commit. 2014-01-25 23:46:23 +00:00
Simon Kelley
8a9be9e493 Replace CRC32 with SHA1 for spoof detection in DNSSEC builds. 2014-01-25 23:17:21 +00:00
Simon Kelley
c92f0083a2 Get AA flag right in DNSSEC answers from cache. 2014-01-25 18:43:59 +00:00
Simon Kelley
b5dbfd142a RRSIG answer logging. 2014-01-25 18:19:51 +00:00
Simon Kelley
cbf13a2a6d Class specifier in --dnskey, instead of hardwiring C_IN. 2014-01-25 17:59:14 +00:00
Simon Kelley
5b3bf92101 --dnssec-debug 2014-01-25 17:03:07 +00:00
Simon Kelley
0744ca66ad More DNSSEC caching logic, and avoid repeated validation of DS/DNSKEY 2014-01-25 16:40:15 +00:00
Simon Kelley
2d33bda2e6 RRSIGS for PTR records from cache. 2014-01-24 22:37:25 +00:00
Simon Kelley
32f90c0fad Tweak. 2014-01-24 10:37:36 +00:00
Simon Kelley
bce6e1bc6d RRSIGs in DS and DNSKEY cached answers. 2014-01-23 22:02:19 +00:00
Simon Kelley
824202ef54 More DNSSEC cache readout. 2014-01-23 20:59:46 +00:00
Simon Kelley
9ebfca1e84 Compiler warning. 2014-01-23 12:11:43 +00:00
Simon Kelley
6429e421b3 Compiler warning. 2014-01-23 12:09:36 +00:00
Simon Kelley
c9bfa948c3 remove redundant headerage 2014-01-22 22:32:33 +00:00
Simon Kelley
e7829aefd8 Cache RRSIGS. 2014-01-22 22:21:51 +00:00
Simon Kelley
51ea3ca254 Caching of DNSSEC records. 2014-01-22 19:31:38 +00:00
Jonas Gorski
57ab36e77d Tweak definition of a permanent IPv6 address on Linux.
The linux kernel treats all addresses with a limited lifetime as being
non permanent, but when taking over the prefix livetimes from
upstream assigned prefixes through DHCP, addresses will always have a limited
lifetime.

Still reject temporary addresses, as they indicate autoconfigured
interfaces.

Contributed by T-Labs, Deutsche Telekom Innovation Laboratories

Signed-off-by: Jonas Gorski<jogo@openwrt.org>
2014-01-22 11:34:16 +00:00
Simon Kelley
dd0e0a3995 Handle time_t wraparound more sanely. 2014-01-22 11:16:59 +00:00
Simon Kelley
6fd6dacb39 Fix loop in RR sort. 2014-01-21 20:17:40 +00:00
Simon Kelley
39048ad10b bug fix, avoids infinite loop in forwarding code. 2014-01-21 17:33:58 +00:00
Simon Kelley
979cdf9b64 Fix to hostname_cmp, and update to canonicalisation table. RFC 4034 LIES. 2014-01-21 16:26:41 +00:00
Simon Kelley
dbf721235b Rationalise hostname_cmp() 2014-01-21 14:28:02 +00:00
Simon Kelley
c979fa04a4 Provide for static library linking. 2014-01-21 13:45:17 +00:00
Simon Kelley
c5f4ec7d23 NSEC proof-of-non-existence. 2014-01-20 22:37:55 +00:00
Simon Kelley
5d3b87a484 Better handling of truncated DNSSEC replies. 2014-01-20 11:57:23 +00:00
Simon Kelley
72ae2f3d56 Don't validate error returns. 2014-01-19 09:54:16 +00:00
Simon Kelley
6c0cb858c1 Trivial format fix 2014-01-17 14:40:46 +00:00
Simon Kelley
e0c0ad3b5e UDP retries for DNSSEC 2014-01-16 22:42:07 +00:00
Simon Kelley
4619d94622 Fix SEGV and failure to validate on x86_64. 2014-01-16 19:53:06 +00:00
Simon Kelley
0975a58e9b Merge branch 'master' of ssh://central/var/cache/git/dnsmasq 2014-01-15 17:12:08 +00:00
Simon Kelley
a25720a34a protocol handling for DNSSEC 2014-01-14 23:13:55 +00:00
Simon Kelley
cc111e0bab Add ip6addr.h to Makefile list. 2014-01-13 21:38:19 +00:00
Simon Kelley
86bec2d399 Swap crypto library from openSSL to nettle. 2014-01-13 21:31:20 +00:00
Simon Kelley
a59ff5f3df Merge branch 'master' of ssh://central/var/cache/git/dnsmasq 2014-01-12 22:36:12 +00:00
Simon Kelley
6ea1f23b3f Send correct O and M bits when advertising only deprecated prefixes. 2013-12-19 15:45:12 +00:00
34 changed files with 4343 additions and 1816 deletions

109
CHANGELOG
View File

@@ -1,3 +1,12 @@
version 2.70
Fix crash, introduced in 2.69, on TCP request when dnsmasq
compiled with DNSSEC support, but running without DNSSEC
enabled. Thanks to Manish Sing for spotting that one.
Fix regression which broke ipset functionality. Thanks to
Wang Jian for the bug report.
version 2.69
Implement dynamic interface discovery on *BSD. This allows
the contructor: syntax to be used in dhcp-range for DHCPv6
@@ -17,6 +26,106 @@ version 2.69
dnsmasq, [fe80::] with the link-local address.
Thanks to Tsachi Kimeldorfer for championing this.
DNSSEC validation and caching. Dnsmasq needs to be
compiled with this enabled, with
make dnsmasq COPTS=-DHAVE_DNSSEC
this add dependencies on the nettle crypto library and the
gmp maths library. It's possible to have these linked
statically with
make dnsmasq COPTS='-DHAVE_DNSSEC -DHAVE_DNSSEC_STATIC'
which bloats the dnsmasq binary, but saves the size of
the shared libraries which are much bigger.
To enable, DNSSEC, you will need a set of
trust-anchors. Now that the TLDs are signed, this can be
the keys for the root zone, and for convenience they are
included in trust-anchors.conf in the dnsmasq
distribution. You should of course check that these are
legitimate and up-to-date. So, adding
conf-file=/path/to/trust-anchors.conf
dnssec
to your config is all thats needed to get things
working. The upstream nameservers have to be DNSSEC-capable
too, of course. Many ISP nameservers aren't, but the
Google public nameservers (8.8.8.8 and 8.8.4.4) are.
When DNSSEC is configured, dnsmasq validates any queries
for domains which are signed. Query results which are
bogus are replaced with SERVFAIL replies, and results
which are correctly signed have the AD bit set. In
addition, and just as importantly, dnsmasq supplies
correct DNSSEC information to clients which are doing
their own validation, and caches DNSKEY, DS and RRSIG
records, which significantly improve the performance of
downstream validators. Setting --log-queries will show
DNSSEC in action.
If a domain is returned from an upstream nameserver without
DNSSEC signature, dnsmasq by default trusts this. This
means that for unsigned zone (still the majority) there
is effectively no cost for having DNSSEC enabled. Of course
this allows an attacker to replace a signed record with a
false unsigned record. This is addressed by the
--dnssec-check-unsigned flag, which instructs dnsmasq
to prove that an unsigned record is legitimate, by finding
a secure proof that the zone containing the record is not
signed. Doing this has costs (typically one or two extra
upstream queries). It also has a nasty failure mode if
dnsmasq's upstream nameservers are not DNSSEC capable.
Without --dnssec-check-unsigned using such an upstream
server will simply result in not queries being validated;
with --dnssec-check-unsigned enabled and a
DNSSEC-ignorant upstream server, _all_ queries will fail.
Note that DNSSEC requires that the local time is valid and
accurate, if not then DNSSEC validation will fail. NTP
should be running. This presents a problem for routers
without a battery-backed clock. To set the time needs NTP
to do DNS lookups, but lookups will fail until NTP has run.
To address this, there's a flag, --dnssec-no-timecheck
which disables the time checks (only) in DNSSEC. When dnsmasq
is started and the clock is not synced, this flag should
be used. As soon as the clock is synced, SIGHUP dnsmasq.
The SIGHUP clears the cache of partially-validated data and
resets the no-timecheck flag, so that all DNSSEC checks
henceforward will be complete.
The development of DNSSEC in dnsmasq was started by
Giovanni Bajo, to whom huge thanks are owed. It has been
supported by Comcast, whose techfund grant has allowed for
an invaluable period of full-time work to get it to
a workable state.
Add --rev-server. Thanks to Dave Taht for suggesting this.
Add --servers-file. Allows dynamic update of upstream servers
full access to configuration.
Add --local-service. Accept DNS queries only from hosts
whose address is on a local subnet, ie a subnet for which
an interface exists on the server. This option
only has effect if there are no --interface --except-interface,
--listen-address or --auth-server options. It is intended
to be set as a default on installation, to allow
unconfigured installations to be useful but also safe from
being used for DNS amplification attacks.
Fix crashes in cache_get_cname_target() when dangling CNAMEs
encountered. Thanks to Andy and the rt-n56u project for
find this and helping to chase it down.
Fix wrong RCODE in authoritative DNS replies to PTR queries. The
correct answer was included, but the RCODE was set to NXDOMAIN.
Thanks to Craig McQueen for spotting this.
Make statistics available as DNS queries in the .bind TLD as
well as logging them.
version 2.68
Use random addresses for DHCPv6 temporary address

View File

@@ -51,33 +51,34 @@ top!=pwd
# GNU make way.
top?=$(CURDIR)
dbus_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DBUS $(PKG_CONFIG) --cflags dbus-1`
dbus_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DBUS $(PKG_CONFIG) --libs dbus-1`
idn_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_IDN $(PKG_CONFIG) --cflags libidn`
idn_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_IDN $(PKG_CONFIG) --libs libidn`
ct_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_CONNTRACK $(PKG_CONFIG) --cflags libnetfilter_conntrack`
ct_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_CONNTRACK $(PKG_CONFIG) --libs libnetfilter_conntrack`
lua_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LUASCRIPT $(PKG_CONFIG) --cflags lua5.1`
lua_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LUASCRIPT $(PKG_CONFIG) --libs lua5.1`
sec_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC $(PKG_CONFIG) --cflags libcrypto`
sec_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC $(PKG_CONFIG) --libs libcrypto`
sunos_libs = `if uname | grep SunOS >/dev/null 2>&1; then echo -lsocket -lnsl -lposix4; fi`
dbus_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DBUS $(PKG_CONFIG) --cflags dbus-1`
dbus_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DBUS $(PKG_CONFIG) --libs dbus-1`
idn_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_IDN $(PKG_CONFIG) --cflags libidn`
idn_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_IDN $(PKG_CONFIG) --libs libidn`
ct_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_CONNTRACK $(PKG_CONFIG) --cflags libnetfilter_conntrack`
ct_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_CONNTRACK $(PKG_CONFIG) --libs libnetfilter_conntrack`
lua_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LUASCRIPT $(PKG_CONFIG) --cflags lua5.1`
lua_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LUASCRIPT $(PKG_CONFIG) --libs lua5.1`
nettle_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC $(PKG_CONFIG) --cflags nettle hogweed`
nettle_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC $(PKG_CONFIG) --libs nettle hogweed`
gmp_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC $(PKG_CONFIG) --copy -lgmp`
sunos_libs = `if uname | grep SunOS >/dev/null 2>&1; then echo -lsocket -lnsl -lposix4; fi`
version = -DVERSION='\"`$(top)/bld/get-version $(top)`\"'
objs = cache.o rfc1035.o util.o option.o forward.o network.o \
dnsmasq.o dhcp.o lease.o rfc2131.o netlink.o dbus.o bpf.o \
helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o \
dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o \
domain.o dnssec.o dnssec-openssl.o blockdata.o
domain.o dnssec.o blockdata.o
hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \
dns-protocol.h radv-protocol.h
dns-protocol.h radv-protocol.h ip6addr.h
all : $(BUILDDIR)
@cd $(BUILDDIR) && $(MAKE) \
top="$(top)" \
build_cflags="$(version) $(dbus_cflags) $(idn_cflags) $(ct_cflags) $(lua_cflags) $(sec_cflags)" \
build_libs="$(dbus_libs) $(idn_libs) $(ct_libs) $(lua_libs) $(sunos_libs) $(sec_libs)" \
build_cflags="$(version) $(dbus_cflags) $(idn_cflags) $(ct_cflags) $(lua_cflags) $(nettle_cflags)" \
build_libs="$(dbus_libs) $(idn_libs) $(ct_libs) $(lua_libs) $(sunos_libs) $(nettle_libs) $(gmp_libs)" \
-f $(top)/Makefile dnsmasq
mostly_clean :
@@ -100,8 +101,8 @@ all-i18n : $(BUILDDIR)
@cd $(BUILDDIR) && $(MAKE) \
top="$(top)" \
i18n=-DLOCALEDIR=\'\"$(LOCALEDIR)\"\' \
build_cflags="$(version) $(dbus_cflags) $(ct_cflags) $(lua_cflags) `$(PKG_CONFIG) --cflags libidn`" \
build_libs="$(dbus_libs) $(ct_libs) $(lua_libs) $(sunos_libs) `$(PKG_CONFIG) --libs libidn`" \
build_cflags="$(version) $(dbus_cflags) $(ct_cflags) $(lua_cflags) $(nettle_cflags) `$(PKG_CONFIG) --cflags libidn`" \
build_libs="$(dbus_libs) $(ct_libs) $(lua_libs) $(sunos_libs) $(nettle_libs) $(gmp_libs) `$(PKG_CONFIG) --libs libidn`" \
-f $(top)/Makefile dnsmasq
for f in `cd $(PO); echo *.po`; do \
cd $(top) && cd $(BUILDDIR) && $(MAKE) top="$(top)" -f $(top)/Makefile $${f%.po}.mo; \

View File

@@ -2,10 +2,34 @@
search=$1
shift
pkg=$1
shift
op=$1
shift
in=`cat`
if grep "^\#[[:space:]]*define[[:space:]]*$search" config.h >/dev/null 2>&1 || \
grep $search >/dev/null 2>&1; then
exec $*
echo $in | grep $search >/dev/null 2>&1; then
if [ $op = "--copy" ]; then
pkg="$*"
elif grep "^\#[[:space:]]*define[[:space:]]*${search}_STATIC" config.h >/dev/null 2>&1 || \
echo $in | grep ${search}_STATIC >/dev/null 2>&1; then
pkg=`$pkg --static $op $*`
else
pkg=`$pkg $op $*`
fi
if grep "^\#[[:space:]]*define[[:space:]]*${search}_STATIC" config.h >/dev/null 2>&1 || \
echo $in | grep ${search}_STATIC >/dev/null 2>&1; then
if [ $op = "--libs" ] || [ $op = "--copy" ]; then
echo "-Wl,-Bstatic $pkg -Wl,-Bdynamic"
else
echo "$pkg"
fi
else
echo "$pkg"
fi
fi

View File

@@ -0,0 +1,29 @@
From: Jesse Glick <jglick@cloudbees.com>
To: dnsmasq-discuss@lists.thekelleys.org.uk
Subject: Re: [Dnsmasq-discuss] Ability to delegate to one server but fall
back to another after NXDOMAIN?
On Wed, Jan 15, 2014 at 12:30 PM, Simon Kelley <simon@thekelleys.org.uk> wrote:
> > There's a (very old) patch in contrib/try-all-ns that would make a starting point
This does not apply against trunk, so I tried to rework it. The
following appears to do what I expect:
diff --git a/src/forward.c b/src/forward.c
index 8167229..76070b5 100644
--- a/src/forward.c
+++ b/src/forward.c
@@ -610,7 +610,11 @@ void reply_query(int fd, int family, time_t now)
if ((RCODE(header) == SERVFAIL || RCODE(header) == REFUSED) &&
!option_bool(OPT_ORDER) &&
- forward->forwardall == 0)
+ forward->forwardall == 0 ||
+ /* try each in turn */
+ RCODE(header) == NXDOMAIN &&
+ option_bool(OPT_ORDER) &&
+ server->next != NULL)
/* for broken servers, attempt to send to another one. */
{
unsigned char *pheader;

22
debian/changelog vendored
View File

@@ -1,7 +1,27 @@
dnsmasq (2.70-1) unstable; urgency=low
* New upstream.
-- Simon Kelley <simon@thekelleys.org.uk> Wed, 23 Apr 2014 15:14:42 +0000
dnsmasq (2.69-1) unstable; urgency=low
* New upstream.
* Set --local-service. (closes: #732610)
This tells dnsmasq to ignore DNS requests that don't come
from a local network. It's automatically ignored if
--interface --except-interface, --listen-address or
--auth-server exist in the configuration, so for most
installations, it will have no effect, but for
otherwise-unconfigured installations, it stops dnsmasq
from being vulnerable to DNS-reflection attacks.
-- Simon Kelley <simon@thekelleys.org.uk> Tue, 4 Feb 2014 16:28:12 +0000
dnsmasq (2.68-1) unstable; urgency=low
* New upstream. (closes: #730553)
-- Simon Kelley <simon@thekelleys.org.uk> Sun, 8 Dec 2013 15:57:32 +0000
dnsmasq (2.67-1) unstable; urgency=low

3
debian/control vendored
View File

@@ -1,7 +1,8 @@
Source: dnsmasq
Section: net
Priority: optional
Build-depends: gettext, libnetfilter-conntrack-dev [linux-any], libidn11-dev, libdbus-1-dev (>=0.61)
Build-depends: gettext, libnetfilter-conntrack-dev [linux-any],
libidn11-dev, libdbus-1-dev (>=0.61), libgmp-dev, nettle-dev (>=2.4-3)
Maintainer: Simon Kelley <simon@thekelleys.org.uk>
Standards-Version: 3.9.3

2
debian/default vendored
View File

@@ -27,7 +27,7 @@ CONFIG_DIR=/etc/dnsmasq.d,.dpkg-dist,.dpkg-old,.dpkg-new
# If the resolvconf package is installed, dnsmasq will use its output
# rather than the contents of /etc/resolv.conf to find upstream
# nameservers. Uncommenting this line inhibits this behaviour.
# Not that including a "resolv-file=<filename>" line in
# Note that including a "resolv-file=<filename>" line in
# /etc/dnsmasq.conf is not enough to override resolvconf if it is
# installed: the line below must be uncommented.
#IGNORE_RESOLVCONF=yes

8
debian/init vendored
View File

@@ -90,6 +90,14 @@ if [ ! "$DNSMASQ_USER" ]; then
DNSMASQ_USER="dnsmasq"
fi
# This tells dnsmasq to ignore DNS requests that don't come from a local network.
# It's automatically ignored if --interface --except-interface, --listen-address
# or --auth-server exist in the configuration, so for most installations, it will
# have no effect, but for otherwise-unconfigured installations, it stops dnsmasq
# from being vulnerable to DNS-reflection attacks.
DNSMASQ_OPTS="$DNSMASQ_OPTS --local-service"
start()
{
# Return

38
debian/rules vendored
View File

@@ -17,7 +17,11 @@ CFLAGS += -Wall -W
LDFLAGS = $(shell dpkg-buildflags --get LDFLAGS)
COPTS =
DEB_COPTS = $(COPTS)
# The nettle library in Debian is too old to include
# ECC support.
DEB_COPTS += -DNO_NETTLE_ECC
TARGET = install-i18n
@@ -29,52 +33,56 @@ ifneq (,$(filter gitversion,$(DEB_BUILD_OPTIONS)))
endif
ifeq (,$(filter nodbus,$(DEB_BUILD_OPTIONS)))
COPTS += -DHAVE_DBUS
DEB_COPTS += -DHAVE_DBUS
endif
ifeq (,$(filter noconntrack,$(DEB_BUILD_OPTIONS)))
ifeq ($(DEB_BUILD_ARCH_OS),linux)
COPTS += -DHAVE_CONNTRACK
DEB_COPTS += -DHAVE_CONNTRACK
endif
endif
ifneq (,$(filter noipset,$(DEB_BUILD_OPTIONS)))
COPTS += -DNO_IPSET
DEB_COPTS += -DNO_IPSET
endif
ifneq (,$(filter nodhcp6,$(DEB_BUILD_OPTIONS)))
COPTS += -DNO_DHCP6
DEB_COPTS += -DNO_DHCP6
endif
ifneq (,$(filter noipv6,$(DEB_BUILD_OPTIONS)))
COPTS += -DNO_IPV6
DEB_COPTS += -DNO_IPV6
endif
ifneq (,$(filter notftp,$(DEB_BUILD_OPTIONS)))
COPTS += -DNO_TFTP
DEB_COPTS += -DNO_TFTP
endif
ifneq (,$(filter nodhcp,$(DEB_BUILD_OPTIONS)))
COPTS += -DNO_DHCP
DEB_COPTS += -DNO_DHCP
endif
ifneq (,$(filter noscript,$(DEB_BUILD_OPTIONS)))
COPTS += -DNO_SCRIPT
DEB_COPTS += -DNO_SCRIPT
endif
ifneq (,$(filter nortc,$(DEB_BUILD_OPTIONS)))
COPTS += -DHAVE_BROKEN_RTC
DEB_COPTS += -DHAVE_BROKEN_RTC
endif
ifneq (,$(filter noi18n,$(DEB_BUILD_OPTIONS)))
TARGET = install
ifeq (,$(filter noidn, $(DEB_BUILD_OPTIONS)))
COPTS += -DHAVE_IDN
DEB_COPTS += -DHAVE_IDN
endif
endif
ifneq (,$(filter uselua,$(DEB_BUILD_OPTIONS)))
COPTS += -DHAVE_LUASCRIPT
DEB_COPTS += -DHAVE_LUASCRIPT
endif
ifeq (,$(filter nodnssec,$(DEB_BUILD_OPTIONS)))
DEB_COPTS += -DHAVE_DNSSEC
endif
clean:
@@ -122,12 +130,14 @@ binary-arch: checkroot
-d debian/base/usr/share/doc/$(package) \
-d debian/base/usr/share/doc/$(package)/examples \
-d debian/base/var/run \
-d debian/base/usr/share/$(package) \
-d debian/base/var/lib/misc
make $(TARGET) PREFIX=/usr DESTDIR=`pwd`/debian/base CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" COPTS="$(COPTS)" CC=gcc
make $(TARGET) PREFIX=/usr DESTDIR=`pwd`/debian/base CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" COPTS="$(DEB_COPTS)" CC=gcc
ifeq (,$(findstring nodocs,$(DEB_BUILD_OPTIONS)))
install -m 644 doc.html debian/base/usr/share/doc/$(package)/.
install -m 644 setup.html debian/base/usr/share/doc/$(package)/.
install -m 644 dnsmasq.conf.example debian/base/usr/share/doc/$(package)/examples/.
install -m 644 trust-anchors.conf debian/base/usr/share/$(package)/.
install -m 644 FAQ debian/base/usr/share/doc/$(package)/.
gzip -9 debian/base/usr/share/doc/$(package)/FAQ
install -m 644 CHANGELOG debian/base/usr/share/doc/$(package)/changelog
@@ -167,7 +177,7 @@ ifeq ($(DEB_BUILD_ARCH_OS),linux)
-d debian/utils/usr/share/man/man1 \
-d debian/utils/usr/bin \
-d debian/utils/usr/share/doc/dnsmasq-utils
make -C contrib/wrt PREFIX=/usr DESTDIR=`pwd`/debian/utils CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" COPTS="$(COPTS)" CC=gcc
make -C contrib/wrt PREFIX=/usr DESTDIR=`pwd`/debian/utils CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" COPTS="$(DEB_COPTS)" CC=gcc
install -m 755 contrib/wrt/dhcp_release debian/utils/usr/bin/dhcp_release
install -m 644 contrib/wrt/dhcp_release.1 debian/utils/usr/share/man/man1/dhcp_release.1
gzip -9 debian/utils/usr/share/man/man1/dhcp_release.1

View File

@@ -20,6 +20,18 @@
# Never forward addresses in the non-routed address spaces.
#bogus-priv
# Uncomment these to enable DNSSEC validation and caching:
# (Requires dnsmasq to be built with DNSSEC option.)
#conf-file=%%PREFIX%%/share/dnsmasq/trust-anchors.conf
#dnssec
# Replies which are not DNSSEC signed may be legitimate, because the domain
# is unsigned, or may be forgeries. Setting this option tells dnsmasq to
# check that an unsigned reply is OK, by finding a secure proof that a DS
# record somewhere between the root and the domain does not exist.
# The cost of setting this is that even queries in unsigned domains will need
# one or more extra DNS queries to verify.
#dnssec-check-unsigned
# Uncomment this to filter useless windows-originated DNS requests
# which can trigger dial-on-demand links needlessly.

130
doc.html
View File

@@ -1,6 +1,6 @@
<HTML>
<HEAD>
<TITLE> Dnsmasq - a DNS forwarder for NAT firewalls.</TITLE>
<TITLE> Dnsmasq - network services for small networks.</TITLE>
<link rel="icon"
href="http://www.thekelleys.org.uk/dnsmasq/images/favicon.ico">
</HEAD>
@@ -11,82 +11,48 @@
<td align="middle" valign="middle"><h1>Dnsmasq</h1></td>
<td align="right" valign="middle"><img border="0" src="http://www.thekelleys.org.uk/dnsmasq/images/icon.png" /></td></tr>
</table>
Dnsmasq provides network infrastructure for small networks: DNS, DHCP, router advertisement and network boot. It is designed to be
lightweight and have a small footprint, suitable for resource constrained routers and firewalls. It has also been widely used
for tethering on smartphones and portable hotspots, and to support virtual networking in virtualisation frameworks.
Supported platforms include Linux (with glibc and uclibc), Android, *BSD, and Mac OS X. Dnsmasq is included in most
Linux distributions and the ports systems of FreeBSD, OpenBSD and NetBSD. Dnsmasq provides full IPv6 support.
Dnsmasq is a lightweight, easy to configure DNS forwarder and DHCP
server. It is designed to provide DNS and, optionally, DHCP, to a
small network. It can serve the names of local machines which are
not in the global DNS. The DHCP server integrates with the DNS
server and allows machines with DHCP-allocated addresses
to appear in the DNS with names configured either in each host or
in a central configuration file. Dnsmasq supports static and dynamic
DHCP leases and BOOTP/TFTP/PXE for network booting of diskless machines.
<P>
Dnsmasq is targeted at home networks using NAT and
connected to the internet via a modem, cable-modem or ADSL
connection but would be a good choice for any smallish network (up to
1000 clients is known to work) where low
resource use and ease of configuration are important.
<P>
Supported platforms include Linux (with glibc and uclibc), Android, *BSD,
Solaris and Mac OS X.
Dnsmasq is included in at least the following Linux distributions:
Gentoo, Debian, Slackware, Suse, Fedora,
Smoothwall, IP-Cop, floppyfw, Firebox, LEAF, Freesco, fli4l,
CoyoteLinux, Endian Firewall and
Clarkconnect. It is also available as FreeBSD, OpenBSD and NetBSD ports and is used in
Linksys wireless routers (dd-wrt, openwrt and the stock firmware) and the m0n0wall project.
<P>
Dnsmasq provides the following features:
The DNS subsystem provides a local DNS server for the network, with forwarding of all query types to upstream recursive DNS servers and
cacheing of common record types (A, AAAA, CNAME and PTR, also DNSKEY and DS when DNSSEC is enabled).
<DIR>
<LI>
The DNS configuration of machines behind the firewall is simple and
doesn't depend on the details of the ISP's dns servers
<LI>
Clients which try to do DNS lookups while a modem link to the
internet is down will time out immediately.
</LI>
<LI>
Dnsmasq will serve names from the /etc/hosts file on the firewall
machine: If the names of local machines are there, then they can all
be addressed without having to maintain /etc/hosts on each machine.
</LI>
<LI>
The integrated DHCP server supports static and dynamic DHCP leases and
multiple networks and IP ranges. It works across BOOTP relays and
supports DHCP options including RFC3397 DNS search lists.
Machines which are configured by DHCP have their names automatically
included in the DNS and the names can specified by each machine or
centrally by associating a name with a MAC address in the dnsmasq
config file.
</LI>
<LI>
Dnsmasq caches internet addresses (A records and AAAA records) and address-to-name
mappings (PTR records), reducing the load on upstream servers and
improving performance (especially on modem connections).
</LI>
<LI>
Dnsmasq can be configured to automatically pick up the addresses of
its upstream nameservers from ppp or dhcp configuration. It will
automatically reload this information if it changes. This facility
will be of particular interest to maintainers of Linux firewall
distributions since it allows dns configuration to be made automatic.
</LI>
<LI>
On IPv6-enabled boxes, dnsmasq can both talk to upstream servers via IPv6
and offer DNS service via IPv6. On dual-stack (IPv4 and IPv6) boxes it talks
both protocols and can even act as IPv6-to-IPv4 or IPv4-to-IPv6 forwarder.
</LI>
<LI>
Dnsmasq can be configured to send queries for certain domains to
upstream servers handling only those domains. This makes integration
with private DNS systems easy.
</LI>
<LI>
Dnsmasq supports MX and SRV records and can be configured to return MX records
for any or all local machines.
</LI>
<LI>Local DNS names can be defined by reading /etc/hosts, by importing names from the DHCP subsystem, or by configuration of a wide range of useful record types.</LI>
<LI>Upstream servers can be configured in a variety of convenient ways, including dynamic configuration as these change on moving upstream network.
<LI>Authoritative DNS mode allows local DNS names may be exported to zone in the global DNS. Dnsmasq acts as authoritative server for this zone, and also provides
zone transfer to secondaries for the zone, if required.</LI>
<LI>DNSSEC validation may be performed on DNS replies from upstream nameservers, providing security against spoofing and cache poisoning.</LI>
<LI>Specified sub-domains can be directed to their own upstream DNS servers, making VPN configuration easy.</LI>
<LI>Internationalised domain names are supported.
</DIR>
<P>
The DHCP subsystem supports DHCPv4, DHCPv6, BOOTP and PXE.
<DIR>
<LI> Both static and dynamic DHCP leases are supported, along with stateless mode in DHCPv6.</LI>
<LI> The PXE system is a full PXE server, supporting netboot menus and multiple architecture support. It
includes proxy-mode, where the PXE system co-operates with another DHCP server.</LI>
<LI> There is a built in read-only TFTP server to support netboot.</LI>
<LI> Machines which are configured by DHCP have their names automatically
included in the DNS and the names can specified by each machine or
centrally by associating a name with a MAC address or UID in the dnsmasq
configuration file.</LI>
</DIR>
<P>
The Router Advertisement subsystem provides basic autoconfiguration for IPv6 hosts. It can be used stand-alone or in conjunction with DHCPv6.
<DIR>
<LI> The M and O bits are configurable, to control hosts' use of DHCPv6.</LI>
<LI> Router advertisements can include the RDNSS option.</LI>
<LI> There is a mode which uses name information from DHCPv4 configuration to provide DNS entries
for autoconfigured IPv6 addresses which would otherwise be anonymous.</LI>
</DIR>
<P>
For extra compactness, unused features may be omitted at compile time.
<H2>Get code.</H2>
@@ -102,7 +68,7 @@ the repo, or get a copy using git protocol with the command
<PRE><TT>git clone git://thekelleys.org.uk/dnsmasq.git </TT></PRE>
<H2>License.</H2>
Dnsmasq is distributed under the GPL. See the file COPYING in the distribution
Dnsmasq is distributed under the GPL, version 2 or version 3 at your discretion. See the files COPYING and COPYING-v3 in the distribution
for details.
<H2>Contact.</H2>
@@ -110,7 +76,21 @@ There is a dnsmasq mailing list at <A
HREF="http://lists.thekelleys.org.uk/mailman/listinfo/dnsmasq-discuss">
http://lists.thekelleys.org.uk/mailman/listinfo/dnsmasq-discuss</A> which should be the
first location for queries, bugreports, suggestions etc.
Dnsmasq was written by Simon Kelley. You can contact me at <A
You can contact me at <A
HREF="mailto:simon@thekelleys.org.uk">simon@thekelleys.org.uk</A>.
<H2>Donations.</H2>
Dnsmasq is mainly written and maintained by Simon Kelley. For most of its life, dnsmasq has been a spare-time project.
These days I'm working on it as my main activity.
I don't have an employer or anyone who pays me regularly to work on dnsmasq. If you'd like to make
a contribution towards my expenses, please use the donation button below.
<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">
<input type="hidden" name="cmd" value="_s-xclick">
<input type="hidden" name="hosted_button_id" value="V3X9GVW5GX6DA">
<input type="image" src="https://www.paypalobjects.com/en_US/GB/i/btn/btn_donateCC_LG.gif" border="0" name="submit" alt="PayPal The safer, easier way to pay online.">
<img alt="" border="0" src="https://www.paypalobjects.com/en_GB/i/scr/pixel.gif" width="1" height="1">
</form>
</BODY>

View File

@@ -13,7 +13,10 @@ Dnsmasq accepts DNS queries and either answers them from a small, local,
cache or forwards them to a real, recursive, DNS server. It loads the
contents of /etc/hosts so that local hostnames
which do not appear in the global DNS can be resolved and also answers
DNS queries for DHCP configured hosts. It can also act as the authoritative DNS server for one or more domains, allowing local names to appear in the global DNS.
DNS queries for DHCP configured hosts. It can also act as the
authoritative DNS server for one or more domains, allowing local names
to appear in the global DNS. It can be configured to do DNSSEC
validation.
.PP
The dnsmasq DHCP server supports static address assignments and multiple
networks. It automatically
@@ -205,6 +208,14 @@ resolve in the global DNS to a A and/or AAAA record which points to
the address dnsmasq is listening on. When an interface is specified,
it may be qualified with "/4" or "/6" to specify only the IPv4 or IPv6
addresses associated with the interface.
.TP
.B --local-service
Accept DNS queries only from hosts whose address is on a local subnet,
ie a subnet for which an interface exists on the server. This option
only has effect is there are no --interface --except-interface,
--listen-address or --auth-server options. It is intended to be set as
a default on installation, to allow unconfigured installations to be
useful but also safe from being used for DNS amplification attacks.
.TP
.B \-2, --no-dhcp-interface=<interface name>
Do not provide DHCP or TFTP on the specified interface, but do provide DNS service.
@@ -412,6 +423,14 @@ source address specified but the port may be specified directly as
part of the source address. Forcing queries to an interface is not
implemented on all platforms supported by dnsmasq.
.TP
.B --rev-server=<ip-address>/<prefix-len>,<ipaddr>[#<port>][@<source-ip>|<interface>[#<port>]]
This is functionally the same as
.B --server,
but provides some syntactic sugar to make specifying address-to-name queries easier. For example
.B --rev-server=1.2.3.0/24,192.168.0.1
is exactly equivalent to
.B --server=/3.2.1.in-addr.arpa/192.168.0.1
.TP
.B \-A, --address=/<domain>/[domain/]<ipaddr>
Specify an IP address to return for any host in the given domains.
Queries in the domains are never forwarded and always replied to
@@ -579,19 +598,64 @@ Set the maximum number of concurrent DNS queries. The default value is
where this needs to be increased is when using web-server log file
resolvers, which can generate large numbers of concurrent queries.
.TP
.B --dnssec
Validate DNS replies and cache DNSSEC data. When forwarding DNS queries, dnsmasq requests the
DNSSEC records needed to validate the replies. The replies are validated and the result returned as
the Authenticated Data bit in the DNS packet. In addition the DNSSEC records are stored in the cache, making
validation by clients more efficient. Note that validation by clients is the most secure DNSSEC mode, but for
clients unable to do validation, use of the AD bit set by dnsmasq is useful, provided that the network between
the dnsmasq server and the client is trusted. Dnsmasq must be compiled with HAVE_DNSSEC enabled, and DNSSEC
trust anchors provided, see
.B --trust-anchor.
Because the DNSSEC validation process uses the cache, it is not
permitted to reduce the cache size below the default when DNSSEC is
enabled. The nameservers upstream of dnsmasq must be DNSSEC-capable,
ie capable of returning DNSSEC records with data. If they are not,
then dnsmasq will not be able to determine the trusted status of
answers. In the default mode, this menas that all replies will be
marked as untrusted. If
.B --dnssec-check-unsigned
is set and the upstream servers don't support DNSSEC, then DNS service will be entirely broken.
.TP
.B --trust-anchor=[<class>],<domain>,<key-tag>,<algorithm>,<digest-type>,<digest>
Provide DS records to act a trust anchors for DNSSEC
validation. Typically these will be the DS record(s) for Zone Signing
key(s) of the root zone,
but trust anchors for limited domains are also possible. The current
root-zone trust anchors may be donwloaded from https://data.iana.org/root-anchors/root-anchors.xml
.TP
.B --dnssec-check-unsigned
As a default, dnsmasq does not check that unsigned DNS replies are
legitimate: they are assumed to be valid and passed on (without the
"authentic data" bit set, of course). This does not protect against an
attacker forging unsigned replies for signed DNS zones, but it is
fast. If this flag is set, dnsmasq will check the zones of unsigned
replies, to ensure that unsigned replies are allowed in those
zones. The cost of this is more upstream queries and slower
performance. See also the warning about upstream servers in the
section on
.B --dnssec
.TP
.B --dnssec-no-timecheck
DNSSEC signatures are only valid for specified time windows, and should be rejected outside those windows. This generates an
interesting chicken-and-egg problem for machines which don't have a hardware real time clock. For these machines to determine the correct
time typically requires use of NTP and therefore DNS, but validating DNS requires that the correct time is already known. Setting this flag
removes the time-window checks (but not other DNSSEC validation.) only until the dnsmasq process receives SIGHUP. The intention is
that dnsmasq should be started with this flag when the platform determines that reliable time is not currently available. As soon as
reliable time is established, a SIGHUP should be sent to dnsmasq, which enables time checking, and purges the cache of DNS records
which have not been throughly checked.
.TP
.B --proxy-dnssec
A resolver on a client machine can do DNSSEC validation in two ways: it
can perform the cryptograhic operations on the reply it receives, or
it can rely on the upstream recursive nameserver to do the validation
and set a bit in the reply if it succeeds. Dnsmasq is not a DNSSEC
validator, so it cannot perform the validation role of the recursive nameserver,
but it can pass through the validation results from its own upstream
nameservers. This option enables this behaviour. You should only do
this if you trust all the configured upstream nameservers
.I and the network between you and them.
If you use the first DNSSEC mode, validating resolvers in clients,
this option is not required. Dnsmasq always returns all the data
needed for a client to do validation itself.
Copy the DNSSEC Authenticated Data bit from upstream servers to downstream clients and cache it. This is an
alternative to having dnsmasq validate DNSSEC, but it depends on the security of the network between
dnsmasq and the upstream servers, and the trustworthiness of the upstream servers.
.TP
.B --dnssec-debug
Set debugging mode for the DNSSEC validation, set the Checking Disabled bit on upstream queries,
and don't convert replies which do not validate to responses with
a return code of SERVFAIL. Note that
setting this may affect DNS behaviour in bad ways, it is not an
extra-logging flag and should not be set in production.
.TP
.B --auth-zone=<domain>[,<subnet>[/<prefix length>][,<subnet>[/<prefix length>].....]]
Define a DNS zone for which dnsmasq acts as authoritative server. Locally defined DNS records which are in the domain
@@ -611,7 +675,7 @@ Interface-name and address-literal subnet specifications may be used
freely in the same --auth-zone declaration.
The subnet(s) are also used to define in-addr.arpa and
ipv6.arpa domains which are served for reverse-DNS queries. If not
ip6.arpa domains which are served for reverse-DNS queries. If not
specified, the prefix length defaults to 24 for IPv4 and 64 for IPv6.
For IPv4 subnets, the prefix length should be have the value 8, 16 or 24
unless you are familiar with RFC 2317 and have arranged the
@@ -718,7 +782,7 @@ or from /etc/ethers will be served. A static-only subnet with address
all zeros may be used as a "catch-all" address to enable replies to all
Information-request packets on a subnet which is provided with
stateless DHCPv6, ie
.B --dhcp=range=::,static
.B --dhcp-range=::,static
For IPv4, the <mode> may be
.B proxy
@@ -1651,6 +1715,13 @@ files. If extension(s) are given, any files which end in those
extensions are skipped. Any files whose names end in ~ or start with . or start and end
with # are always skipped. This flag may be given on the command
line or in a configuration file.
.TP
.B --servers-file=<file>
A special case of
.B --conf-file
which differs in two respects. Firstly, only --server and --rev-server are allowed
in the configuration file included. Secondly, the file is re-read and the configuration
therein is updated when dnsmasq recieves SIGHUP.
.SH CONFIG FILE
At startup, dnsmasq reads
.I /etc/dnsmasq.conf,
@@ -1691,12 +1762,22 @@ When it receives a SIGUSR1,
writes statistics to the system log. It writes the cache size,
the number of names which have had to removed from the cache before
they expired in order to make room for new names and the total number
of names that have been inserted into the cache. For each upstream
of names that have been inserted into the cache. The number of cache hits and
misses and the number of authoritative queries answered are also given. For each upstream
server it gives the number of queries sent, and the number which
resulted in an error. In
.B --no-daemon
mode or when full logging is enabled (-q), a complete dump of the
contents of the cache is made.
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
@@ -1800,7 +1881,7 @@ which has tags will be used in preference to an untagged
.B dhcp-option,
provided that _all_ the tags match somewhere in the
set collected as described above. The prefix '!' on a tag means 'not'
so --dhcp=option=tag:!purple,3,1.2.3.4 sends the option when the
so --dhcp-option=tag:!purple,3,1.2.3.4 sends the option when the
tag purple is not in the set of valid tags. (If using this in a
command line rather than a configuration file, be sure to escape !,
which is a shell metacharacter)
@@ -1952,7 +2033,7 @@ to particular hosts then
will do so.
Dnsmasq acts as an authoritative server for in-addr.arpa and
ipv6.arpa domains associated with the subnets given in auth-zone
ip6.arpa domains associated with the subnets given in auth-zone
declarations, so reverse (address to name) lookups can be simply
configured with a suitable NS record, for instance in this example,
where we allow 1.2.3.0/24 addresses.

View File

@@ -706,7 +706,7 @@ l'un des sous-réseaux définis, ou dans un réseau correspondant à une plage D
(ce comportement peut-être désactivé par
.B constructor-noauth:
). Le ou les sous-réseaux sont également utilisé(s) pour définir les domaines
in-addr.arpa et ipv6.arpa servant à l'interrogation DNS inverse. Si la longueur
in-addr.arpa et ip6.arpa servant à l'interrogation DNS inverse. Si la longueur
de préfixe n'est pas spécifiée, elle sera par défaut de 24 pour IPv4 et 64 pour
IPv6. Dans le cas d'IPv4, la longueur du masque de réseau devrait-être de 8, 16
ou 24, sauf si en cas de mise en place d'une délégation de la zone in-addr.arpa
@@ -2186,7 +2186,7 @@ spécifiques, vous pouvez le faire via :
.fi
Dnsmasq joue le rôle de serveur faisant autorité pour les domaines in-addr.arpa
et ipv6.arpa associés aux sous-réseaux définis dans la déclaration de zone
et ip6.arpa associés aux sous-réseaux définis dans la déclaration de zone
auth-zone, ce qui fait que les requêtes DNS inversées (de l'adresse vers
le nom) peuvent-simplement être configurées avec un enregistrement NS
adéquat. Par exemple, comme nous définissons plus haut les adresses

View File

@@ -231,8 +231,10 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
} while ((crecp = cache_find_by_addr(crecp, &addr, now, flag)));
if (!found)
log_query(flag | F_NEG | F_NXDOMAIN | F_REVERSE | F_AUTH, NULL, &addr, NULL);
if (found)
nxdomain = 0;
else
log_query(flag | F_NEG | F_NXDOMAIN | F_REVERSE | (auth ? F_AUTH : 0), NULL, &addr, NULL);
continue;
}

View File

@@ -18,13 +18,42 @@
#ifdef HAVE_DNSSEC
static struct blockdata *keyblock_free = NULL;
static unsigned int blockdata_count = 0, blockdata_hwm = 0;
static struct blockdata *keyblock_free;
static unsigned int blockdata_count, blockdata_hwm, blockdata_alloced;
static void blockdata_expand(int n)
{
struct blockdata *new = whine_malloc(n * sizeof(struct blockdata));
if (new)
{
int i;
new[n-1].next = keyblock_free;
keyblock_free = new;
for (i = 0; i < n - 1; i++)
new[i].next = &new[i+1];
blockdata_alloced += n;
}
}
/* Preallocate some blocks, proportional to cachesize, to reduce heap fragmentation. */
void blockdata_init(void)
{
keyblock_free = NULL;
blockdata_alloced = 0;
blockdata_count = 0;
blockdata_hwm = 0;
blockdata_expand((daemon->cachesize * 100) / sizeof(struct blockdata));
}
void blockdata_report(void)
{
my_syslog(LOG_INFO, _("DNSSEC memory in use %u, max %u"),
blockdata_count * sizeof(struct blockdata), blockdata_hwm * sizeof(struct blockdata));
my_syslog(LOG_INFO, _("DNSSEC memory in use %u, max %u, allocated %u"),
blockdata_count * sizeof(struct blockdata), blockdata_hwm * sizeof(struct blockdata), blockdata_alloced * sizeof(struct blockdata));
}
struct blockdata *blockdata_alloc(char *data, size_t len)
@@ -35,25 +64,24 @@ struct blockdata *blockdata_alloc(char *data, size_t len)
while (len > 0)
{
if (!keyblock_free)
blockdata_expand(50);
if (keyblock_free)
{
block = keyblock_free;
keyblock_free = block->next;
blockdata_count++;
}
else if ((block = whine_malloc(sizeof(struct blockdata))))
{
blockdata_count++;
if (blockdata_hwm < blockdata_count)
blockdata_hwm = blockdata_count;
}
if (!block)
else
{
/* failed to alloc, free partial chain */
blockdata_free(ret);
return NULL;
}
if (blockdata_hwm < blockdata_count)
blockdata_hwm = blockdata_count;
blen = len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len;
memcpy(block->key, data, blen);
@@ -67,25 +95,10 @@ struct blockdata *blockdata_alloc(char *data, size_t len)
return ret;
}
size_t blockdata_walk(struct blockdata **key, unsigned char **p, size_t cnt)
{
if (*p == NULL)
*p = (*key)->key;
else if (*p == (*key)->key + KEYBLOCK_LEN)
{
*key = (*key)->next;
if (*key == NULL)
return 0;
*p = (*key)->key;
}
return MIN(cnt, (size_t)((*key)->key + KEYBLOCK_LEN - (*p)));
}
void blockdata_free(struct blockdata *blocks)
{
struct blockdata *tmp;
if (blocks)
{
for (tmp = blocks; tmp->next; tmp = tmp->next)
@@ -96,24 +109,38 @@ void blockdata_free(struct blockdata *blocks)
}
}
/* copy blocks into data[], return 1 if data[] unchanged by so doing */
int blockdata_retrieve(struct blockdata *block, size_t len, void *data)
/* if data == NULL, return pointer to static block of sufficient size */
void *blockdata_retrieve(struct blockdata *block, size_t len, void *data)
{
size_t blen;
struct blockdata *b;
int match = 1;
void *new, *d;
for (b = block; len > 0 && b; b = b->next)
static unsigned int buff_len = 0;
static unsigned char *buff = NULL;
if (!data)
{
if (len > buff_len)
{
if (!(new = whine_malloc(len)))
return NULL;
if (buff)
free(buff);
buff = new;
}
data = buff;
}
for (d = data, b = block; len > 0 && b; b = b->next)
{
blen = len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len;
if (memcmp(data, b->key, blen) != 0)
match = 0;
memcpy(data, b->key, blen);
data += blen;
memcpy(d, b->key, blen);
d += blen;
len -= blen;
}
return match;
return data;
}
#endif

View File

@@ -24,7 +24,6 @@ static struct crec *new_chain = NULL;
static int cache_inserted = 0, cache_live_freed = 0, insert_error;
static union bigname *big_free = NULL;
static int bignames_left, hash_size;
static int uid = 1;
/* type->string mapping: this is also used by the name-hash function as a mixing table. */
static const struct {
@@ -55,7 +54,9 @@ static const struct {
{ 41, "OPT" },
{ 43, "DS" },
{ 46, "RRSIG" },
{ 47, "NSEC" },
{ 48, "DNSKEY" },
{ 50, "NSEC3" },
{ 249, "TKEY" },
{ 250, "TSIG" },
{ 251, "IXFR" },
@@ -71,6 +72,19 @@ static void cache_link(struct crec *crecp);
static void rehash(int size);
static void cache_hash(struct crec *crecp);
static unsigned int next_uid(void)
{
static unsigned int uid = 0;
uid++;
/* uid == 0 used to indicate CNAME to interface name. */
if (uid == SRC_INTERFACE)
uid++;
return uid;
}
void cache_init(void)
{
struct crec *crecp;
@@ -86,7 +100,7 @@ void cache_init(void)
{
cache_link(crecp);
crecp->flags = 0;
crecp->uid = uid++;
crecp->uid = next_uid();
}
}
@@ -170,15 +184,27 @@ static void cache_hash(struct crec *crecp)
crecp->hash_next = *up;
*up = crecp;
}
#ifdef HAVE_DNSSEC
static void cache_blockdata_free(struct crec *crecp)
{
if (crecp->flags & F_DNSKEY)
{
if (crecp->flags & F_DS)
blockdata_free(crecp->addr.sig.keydata);
else
blockdata_free(crecp->addr.key.keydata);
}
else if ((crecp->flags & F_DS) && !(crecp->flags & F_NEG))
blockdata_free(crecp->addr.ds.keydata);
}
#endif
static void cache_free(struct crec *crecp)
{
crecp->flags &= ~F_FORWARD;
crecp->flags &= ~F_REVERSE;
crecp->uid = uid++; /* invalidate CNAMES pointing to this. */
if (uid == -1)
uid++;
crecp->uid = next_uid(); /* invalidate CNAMES pointing to this. */
if (cache_tail)
cache_tail->next = crecp;
@@ -195,9 +221,9 @@ static void cache_free(struct crec *crecp)
big_free = crecp->name.bname;
crecp->flags &= ~F_BIGNAME;
}
#ifdef HAVE_DNSSEC
else if (crecp->flags & (F_DNSKEY | F_DS))
blockdata_free(crecp->addr.key.keydata);
cache_blockdata_free(crecp);
#endif
}
@@ -239,7 +265,7 @@ char *cache_get_name(struct crec *crecp)
char *cache_get_cname_target(struct crec *crecp)
{
if (crecp->addr.cname.uid != -1)
if (crecp->addr.cname.uid != SRC_INTERFACE)
return cache_get_name(crecp->addr.cname.target.cache);
return crecp->addr.cname.target.int_name->name;
@@ -272,7 +298,7 @@ struct crec *cache_enumerate(int init)
static int is_outdated_cname_pointer(struct crec *crecp)
{
if (!(crecp->flags & F_CNAME) || crecp->addr.cname.uid == -1)
if (!(crecp->flags & F_CNAME) || crecp->addr.cname.uid == SRC_INTERFACE)
return 0;
/* NB. record may be reused as DS or DNSKEY, where uid is
@@ -316,27 +342,52 @@ static int cache_scan_free(char *name, struct all_addr *addr, time_t now, unsign
if (flags & F_FORWARD)
{
for (up = hash_bucket(name), crecp = *up; crecp; crecp = crecp->hash_next)
if (is_expired(now, crecp) || is_outdated_cname_pointer(crecp))
{
*up = crecp->hash_next;
if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
{
cache_unlink(crecp);
cache_free(crecp);
}
}
else if ((crecp->flags & F_FORWARD) &&
((flags & crecp->flags & F_TYPE) || ((crecp->flags | flags) & F_CNAME)) &&
hostname_isequal(cache_get_name(crecp), name))
{
if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))
return 0;
*up = crecp->hash_next;
cache_unlink(crecp);
cache_free(crecp);
}
else
{
if (is_expired(now, crecp) || is_outdated_cname_pointer(crecp))
{
*up = crecp->hash_next;
if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
{
cache_unlink(crecp);
cache_free(crecp);
}
continue;
}
if ((crecp->flags & F_FORWARD) && hostname_isequal(cache_get_name(crecp), name))
{
/* Don't delete DNSSEC in favour of a CNAME, they can co-exist */
if ((flags & crecp->flags & (F_IPV4 | F_IPV6)) ||
(((crecp->flags | flags) & F_CNAME) && !(crecp->flags & (F_DNSKEY | F_DS))))
{
if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))
return 0;
*up = crecp->hash_next;
cache_unlink(crecp);
cache_free(crecp);
continue;
}
#ifdef HAVE_DNSSEC
/* Deletion has to be class-sensitive for DS, DNSKEY, RRSIG, also
type-covered sensitive for RRSIG */
if ((flags & (F_DNSKEY | F_DS)) &&
(flags & (F_DNSKEY | F_DS)) == (crecp->flags & (F_DNSKEY | F_DS)) &&
crecp->uid == addr->addr.dnssec.class &&
(!((flags & (F_DS | F_DNSKEY)) == (F_DS | F_DNSKEY)) ||
crecp->addr.sig.type_covered == addr->addr.dnssec.type))
{
if (crecp->flags & F_CONFIG)
return 0;
*up = crecp->hash_next;
cache_unlink(crecp);
cache_free(crecp);
continue;
}
#endif
}
up = &crecp->hash_next;
}
}
else
{
@@ -406,19 +457,21 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
int freed_all = flags & F_REVERSE;
int free_avail = 0;
if (daemon->max_cache_ttl != 0 && daemon->max_cache_ttl < ttl)
ttl = daemon->max_cache_ttl;
/* Don't log keys */
if (flags & (F_IPV4 | F_IPV6))
log_query(flags | F_UPSTREAM, name, addr, NULL);
/* Don't log DNSSEC records here, done elsewhere */
if (flags & (F_IPV4 | F_IPV6 | F_CNAME))
{
log_query(flags | F_UPSTREAM, name, addr, NULL);
/* Don;t mess with TTL for DNSSEC records. */
if (daemon->max_cache_ttl != 0 && daemon->max_cache_ttl < ttl)
ttl = daemon->max_cache_ttl;
}
/* if previous insertion failed give up now. */
if (insert_error)
return NULL;
/* First remove any expired entries and entries for the name/address we
are currently inserting. Fail is we attempt to delete a name from
are currently inserting. Fail if we attempt to delete a name from
/etc/hosts or DHCP. */
if (!cache_scan_free(name, addr, now, flags))
{
@@ -446,14 +499,32 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
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;
}
insert_error = 1;
return NULL;
}
if (freed_all)
{
struct all_addr free_addr = new->addr.addr;;
#ifdef HAVE_DNSSEC
/* For DNSSEC records, addr holds class and type_covered for RRSIG */
if (new->flags & (F_DS | F_DNSKEY))
{
free_addr.addr.dnssec.class = new->uid;
if ((new->flags & (F_DS | F_DNSKEY)) == (F_DS | F_DNSKEY))
free_addr.addr.dnssec.type = new->addr.sig.type_covered;
}
#endif
free_avail = 1; /* Must be free space now. */
cache_scan_free(cache_get_name(new), &new->addr.addr, now, new->flags);
cache_scan_free(cache_get_name(new), &free_addr, now, new->flags);
cache_live_freed++;
}
else
@@ -465,7 +536,7 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
}
/* Check if we need to and can allocate extra memory for a long name.
If that fails, give up now. */
If that fails, give up now, always succeed for DNSSEC records. */
if (name && (strlen(name) > SMALLDNAME-1))
{
if (big_free)
@@ -473,13 +544,13 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
big_name = big_free;
big_free = big_free->next;
}
else if (!bignames_left ||
else if ((bignames_left == 0 && !(flags & (F_DS | F_DNSKEY))) ||
!(big_name = (union bigname *)whine_malloc(sizeof(union bigname))))
{
insert_error = 1;
return NULL;
}
else
else if (bignames_left != 0)
bignames_left--;
}
@@ -502,7 +573,14 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
*cache_get_name(new) = 0;
if (addr)
new->addr.addr = *addr;
{
#ifdef HAVE_DNSSEC
if (flags & (F_DS | F_DNSKEY))
new->uid = addr->addr.dnssec.class;
else
#endif
new->addr.addr = *addr;
}
new->ttd = now + (time_t)ttl;
new->next = new_chain;
@@ -534,10 +612,13 @@ void cache_end_insert(void)
new_chain = NULL;
}
struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsigned short prot)
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;
prot &= ~F_NO_RR;
if (crecp) /* iterating */
ans = crecp->next;
else
@@ -554,6 +635,9 @@ struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsi
if (!is_expired(now, crecp) && !is_outdated_cname_pointer(crecp))
{
if ((crecp->flags & F_FORWARD) &&
#ifdef HAVE_DNSSEC
((crecp->flags & (F_DNSKEY | F_DS)) == (prot & (F_DNSKEY | F_DS))) &&
#endif
(crecp->flags & prot) &&
hostname_isequal(cache_get_name(crecp), name))
{
@@ -582,7 +666,7 @@ struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsi
}
else
{
if (!insert)
if (!insert && !no_rr)
{
insert = up;
ins_flags = crecp->flags & (F_REVERSE | F_IMMORTAL);
@@ -611,7 +695,10 @@ struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsi
if (ans &&
(ans->flags & F_FORWARD) &&
(ans->flags & prot) &&
#ifdef HAVE_DNSSEC
((ans->flags & (F_DNSKEY | F_DS)) == (prot & (F_DNSKEY | F_DS))) &&
#endif
(ans->flags & prot) &&
hostname_isequal(cache_get_name(ans), name))
return ans;
@@ -619,7 +706,7 @@ struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsi
}
struct crec *cache_find_by_addr(struct crec *crecp, struct all_addr *addr,
time_t now, unsigned short prot)
time_t now, unsigned int prot)
{
struct crec *ans;
#ifdef HAVE_IPV6
@@ -692,17 +779,18 @@ static void add_hosts_cname(struct crec *target)
if (hostname_isequal(cache_get_name(target), a->target) &&
(crec = whine_malloc(sizeof(struct crec))))
{
crec->flags = F_FORWARD | F_IMMORTAL | F_NAMEP | F_CONFIG | F_CNAME | F_DNSSECOK;
crec->flags = F_FORWARD | F_IMMORTAL | F_NAMEP | F_CONFIG | F_CNAME;
crec->name.namep = a->alias;
crec->addr.cname.target.cache = target;
crec->addr.cname.uid = target->uid;
crec->uid = next_uid();
cache_hash(crec);
add_hosts_cname(crec); /* handle chains */
}
}
static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrlen,
int index, struct crec **rhash, int hashsz)
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, nameexists = 0;
@@ -806,7 +894,7 @@ static int gettok(FILE *f, char *token)
}
}
static int read_hostsfile(char *filename, int index, int cache_size, struct crec **rhash, int hashsz)
static int read_hostsfile(char *filename, unsigned int index, int cache_size, struct crec **rhash, int hashsz)
{
FILE *f = fopen(filename, "r");
char *token = daemon->namebuff, *domain_suffix = NULL;
@@ -829,14 +917,14 @@ static int read_hostsfile(char *filename, int index, int cache_size, struct crec
if (inet_pton(AF_INET, token, &addr) > 0)
{
flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4 | F_DNSSECOK;
flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4;
addrlen = INADDRSZ;
domain_suffix = get_domain(addr.addr.addr4);
}
#ifdef HAVE_IPV6
else if (inet_pton(AF_INET6, token, &addr) > 0)
{
flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6 | F_DNSSECOK;
flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6;
addrlen = IN6ADDRSZ;
domain_suffix = get_domain6(&addr.addr.addr6);
}
@@ -916,7 +1004,7 @@ void cache_reload(void)
struct cname *a;
struct interface_name *intr;
#ifdef HAVE_DNSSEC
struct dnskey *key;
struct ds_config *ds;
#endif
cache_inserted = cache_live_freed = 0;
@@ -925,8 +1013,7 @@ void cache_reload(void)
for (cache = hash_table[i], up = &hash_table[i]; cache; cache = tmp)
{
#ifdef HAVE_DNSSEC
if (cache->flags & (F_DNSKEY | F_DS))
blockdata_free(cache->addr.key.keydata);
cache_blockdata_free(cache);
#endif
tmp = cache->hash_next;
if (cache->flags & (F_HOSTS | F_CONFIG))
@@ -954,24 +1041,27 @@ void cache_reload(void)
if (hostname_isequal(a->target, intr->name) &&
((cache = whine_malloc(sizeof(struct crec)))))
{
cache->flags = F_FORWARD | F_NAMEP | F_CNAME | F_IMMORTAL | F_CONFIG | F_DNSSECOK;
cache->flags = F_FORWARD | F_NAMEP | F_CNAME | F_IMMORTAL | F_CONFIG;
cache->name.namep = a->alias;
cache->addr.cname.target.int_name = intr;
cache->addr.cname.uid = -1;
cache->addr.cname.uid = SRC_INTERFACE;
cache->uid = next_uid();
cache_hash(cache);
add_hosts_cname(cache); /* handle chains */
}
#ifdef HAVE_DNSSEC
for (key = daemon->dnskeys; key; key = key->next)
for (ds = daemon->ds; ds; ds = ds->next)
if ((cache = whine_malloc(sizeof(struct crec))) &&
(cache->addr.key.keydata = blockdata_alloc(key->key, key->keylen)))
(cache->addr.ds.keydata = blockdata_alloc(ds->digest, ds->digestlen)))
{
cache->flags = F_FORWARD | F_IMMORTAL | F_DNSKEY | F_CONFIG | F_NAMEP;
cache->name.namep = key->name;
cache->uid = key->keylen;
cache->addr.key.algo = key->algo;
cache->addr.key.keytag = dnskey_keytag(key->algo, key->flags, (unsigned char *)key->key, key->keylen);
cache->flags = F_FORWARD | F_IMMORTAL | F_DS | F_CONFIG | F_NAMEP;
cache->name.namep = ds->name;
cache->addr.ds.keylen = ds->digestlen;
cache->addr.ds.algo = ds->algo;
cache->addr.ds.keytag = ds->keytag;
cache->addr.ds.digest = ds->digest_type;
cache->uid = ds->class;
cache_hash(cache);
}
#endif
@@ -990,16 +1080,16 @@ void cache_reload(void)
(cache = whine_malloc(sizeof(struct crec))))
{
cache->name.namep = nl->name;
cache->flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4 | F_NAMEP | F_CONFIG | F_DNSSECOK;
add_hosts_entry(cache, (struct all_addr *)&hr->addr, INADDRSZ, 0, (struct crec **)daemon->packet, revhashsz);
cache->flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4 | F_NAMEP | F_CONFIG;
add_hosts_entry(cache, (struct all_addr *)&hr->addr, INADDRSZ, SRC_CONFIG, (struct crec **)daemon->packet, revhashsz);
}
#ifdef HAVE_IPV6
if (!IN6_IS_ADDR_UNSPECIFIED(&hr->addr6) &&
(cache = whine_malloc(sizeof(struct crec))))
{
cache->name.namep = nl->name;
cache->flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6 | F_NAMEP | F_CONFIG | F_DNSSECOK;
add_hosts_entry(cache, (struct all_addr *)&hr->addr6, IN6ADDRSZ, 0, (struct crec **)daemon->packet, revhashsz);
cache->flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6 | F_NAMEP | F_CONFIG;
add_hosts_entry(cache, (struct all_addr *)&hr->addr6, IN6ADDRSZ, SRC_CONFIG, (struct crec **)daemon->packet, revhashsz);
}
#endif
}
@@ -1012,7 +1102,7 @@ void cache_reload(void)
}
if (!option_bool(OPT_NO_HOSTS))
total_size = read_hostsfile(HOSTSFILE, 0, total_size, (struct crec **)daemon->packet, revhashsz);
total_size = read_hostsfile(HOSTSFILE, SRC_HOSTS, total_size, (struct crec **)daemon->packet, revhashsz);
daemon->addn_hosts = expand_filelist(daemon->addn_hosts);
for (ah = daemon->addn_hosts; ah; ah = ah->next)
@@ -1068,7 +1158,7 @@ static void add_dhcp_cname(struct crec *target, time_t ttd)
if (aliasc)
{
aliasc->flags = F_FORWARD | F_NAMEP | F_DHCP | F_CNAME | F_CONFIG | F_DNSSECOK;
aliasc->flags = F_FORWARD | F_NAMEP | F_DHCP | F_CNAME | F_CONFIG;
if (ttd == 0)
aliasc->flags |= F_IMMORTAL;
else
@@ -1076,6 +1166,7 @@ static void add_dhcp_cname(struct crec *target, time_t ttd)
aliasc->name.namep = a->alias;
aliasc->addr.cname.target.cache = target;
aliasc->addr.cname.uid = target->uid;
aliasc->uid = next_uid();
cache_hash(aliasc);
add_dhcp_cname(aliasc, ttd);
}
@@ -1156,14 +1247,14 @@ void cache_add_dhcp_entry(char *host_name, int prot,
if (crec) /* malloc may fail */
{
crec->flags = flags | F_NAMEP | F_DHCP | F_FORWARD | F_DNSSECOK;
crec->flags = flags | F_NAMEP | F_DHCP | F_FORWARD;
if (ttd == 0)
crec->flags |= F_IMMORTAL;
else
crec->ttd = ttd;
crec->addr.addr = *host_address;
crec->name.namep = host_name;
crec->uid = uid++;
crec->uid = next_uid();
cache_hash(crec);
add_dhcp_cname(crec, ttd);
@@ -1171,10 +1262,106 @@ void cache_add_dhcp_entry(char *host_name, int prot,
}
#endif
int cache_make_stat(struct txt_record *t)
{
static char *buff = NULL;
static int bufflen = 60;
int len;
struct server *serv, *serv1;
char *p;
if (!buff && !(buff = whine_malloc(60)))
return 0;
p = buff;
switch (t->stat)
{
case TXT_STAT_CACHESIZE:
sprintf(buff+1, "%d", daemon->cachesize);
break;
case TXT_STAT_INSERTS:
sprintf(buff+1, "%d", cache_inserted);
break;
case TXT_STAT_EVICTIONS:
sprintf(buff+1, "%d", cache_live_freed);
break;
case TXT_STAT_MISSES:
sprintf(buff+1, "%u", daemon->queries_forwarded);
break;
case TXT_STAT_HITS:
sprintf(buff+1, "%u", daemon->local_answer);
break;
#ifdef HAVE_AUTH
case TXT_STAT_AUTH:
sprintf(buff+1, "%u", daemon->auth_answer);
break;
#endif
case TXT_STAT_SERVERS:
/* sum counts from different records for same server */
for (serv = daemon->servers; serv; serv = serv->next)
serv->flags &= ~SERV_COUNTED;
for (serv = daemon->servers; serv; serv = serv->next)
if (!(serv->flags &
(SERV_NO_ADDR | SERV_LITERAL_ADDRESS | SERV_COUNTED | SERV_USE_RESOLV | SERV_NO_REBIND)))
{
char *new, *lenp;
int port, newlen, bytes_avail, bytes_needed;
unsigned int queries = 0, failed_queries = 0;
for (serv1 = serv; serv1; serv1 = serv1->next)
if (!(serv1->flags &
(SERV_NO_ADDR | SERV_LITERAL_ADDRESS | SERV_COUNTED | SERV_USE_RESOLV | SERV_NO_REBIND)) &&
sockaddr_isequal(&serv->addr, &serv1->addr))
{
serv1->flags |= SERV_COUNTED;
queries += serv1->queries;
failed_queries += serv1->failed_queries;
}
port = prettyprint_addr(&serv->addr, daemon->addrbuff);
lenp = p++; /* length */
bytes_avail = (p - buff) + bufflen;
bytes_needed = snprintf(p, bytes_avail, "%s#%d %u %u", daemon->addrbuff, port, queries, failed_queries);
if (bytes_needed >= bytes_avail)
{
/* expand buffer if necessary */
newlen = bytes_needed + 1 + bufflen - bytes_avail;
if (!(new = whine_malloc(newlen)))
return 0;
memcpy(new, buff, bufflen);
free(buff);
p = new + (p - buff);
lenp = p - 1;
buff = new;
bufflen = newlen;
bytes_avail = (p - buff) + bufflen;
bytes_needed = snprintf(p, bytes_avail, "%s#%d %u %u", daemon->addrbuff, port, queries, failed_queries);
}
*lenp = bytes_needed;
p += bytes_needed;
}
t->txt = (unsigned char *)buff;
t->len = p - buff;
return 1;
}
len = strlen(buff+1);
t->txt = (unsigned char *)buff;
t->len = len + 1;
*buff = len;
return 1;
}
void dump_cache(time_t now)
{
struct server *serv, *serv1;
char *t = "";
my_syslog(LOG_INFO, _("time %lu"), (unsigned long)now);
my_syslog(LOG_INFO, _("cache size %d, %d/%d cache insertions re-used unexpired cache entries."),
@@ -1215,30 +1402,32 @@ void dump_cache(time_t now)
{
struct crec *cache ;
int i;
my_syslog(LOG_INFO, "Host Address Flags Expires");
my_syslog(LOG_INFO, "Host Address Flags Expires");
for (i=0; i<hash_size; i++)
for (cache = hash_table[i]; cache; cache = cache->hash_next)
{
char *a = daemon->addrbuff, *p = daemon->namebuff, *n = cache_get_name(cache);
*a = 0;
if (strlen(n) == 0)
if (strlen(n) == 0 && !(cache->flags & F_REVERSE))
n = "<Root>";
p += sprintf(p, "%-40.40s ", n);
if ((cache->flags & F_CNAME) && !is_outdated_cname_pointer(cache))
a = cache_get_cname_target(cache);
#ifdef HAVE_DNSSEC
else if (cache->flags & F_DNSKEY)
{
a = daemon->addrbuff;
sprintf(a, "%3u %u", cache->addr.key.algo, cache->uid);
}
else if (cache->flags & F_DS)
{
a = daemon->addrbuff;
sprintf(a, "%5u %3u %3u", cache->addr.key.keytag,
cache->addr.key.algo, cache->addr.key.digest);
if (cache->flags & F_DNSKEY)
/* RRSIG */
sprintf(a, "%5u %3u %s", cache->addr.sig.keytag,
cache->addr.sig.algo, querystr("", cache->addr.sig.type_covered));
else 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))
{
@@ -1251,12 +1440,21 @@ void dump_cache(time_t now)
#endif
}
p += sprintf(p, "%-30.30s %s%s%s%s%s%s%s%s%s%s%s%s%s ", a,
cache->flags & F_IPV4 ? "4" : "",
cache->flags & F_IPV6 ? "6" : "",
cache->flags & F_DNSKEY ? "K" : "",
cache->flags & F_DS ? "S" : "",
cache->flags & F_CNAME ? "C" : "",
if (cache->flags & F_IPV4)
t = "4";
else if (cache->flags & F_IPV6)
t = "6";
else if (cache->flags & F_CNAME)
t = "C";
#ifdef HAVE_DNSSEC
else if ((cache->flags & (F_DS | F_DNSKEY)) == (F_DS | F_DNSKEY))
t = "G"; /* DNSKEY and DS set -> RRISG */
else if (cache->flags & F_DS)
t = "S";
else if (cache->flags & F_DNSKEY)
t = "K";
#endif
p += sprintf(p, "%-30.30s %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" : " ",
@@ -1277,11 +1475,13 @@ void dump_cache(time_t now)
}
}
char *record_source(int index)
char *record_source(unsigned int index)
{
struct hostsfile *ah;
if (index == 0)
if (index == SRC_CONFIG)
return "config";
else if (index == SRC_HOSTS)
return HOSTSFILE;
for (ah = daemon->addn_hosts; ah; ah = ah->next)
@@ -1291,14 +1491,45 @@ char *record_source(int index)
return "<unknown>";
}
void querystr(char *desc, char *str, unsigned short type)
char *querystr(char *desc, unsigned short type)
{
unsigned int i;
sprintf(str, "%s[type=%d]", desc, type);
int len = 10; /* strlen("type=xxxxx") */
const char *types = NULL;
static char *buff = NULL;
static int bufflen = 0;
for (i = 0; i < (sizeof(typestr)/sizeof(typestr[0])); i++)
if (typestr[i].type == type)
sprintf(str,"%s[%s]", desc, typestr[i].name);
{
types = typestr[i].name;
len = strlen(types);
break;
}
len += 3; /* braces, terminator */
len += strlen(desc);
if (!buff || bufflen < len)
{
if (buff)
free(buff);
else if (len < 20)
len = 20;
buff = whine_malloc(len);
bufflen = len;
}
if (buff)
{
if (types)
sprintf(buff, "%s[%s]", desc, types);
else
sprintf(buff, "%s[type=%d]", desc, type);
}
return buff ? buff : "";
}
void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
@@ -1335,14 +1566,7 @@ void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
if (flags & F_NEG)
{
if (flags & F_NXDOMAIN)
{
if (flags & F_IPV4)
dest = "NXDOMAIN-IPv4";
else if (flags & F_IPV6)
dest = "NXDOMAIN-IPv6";
else
dest = "NXDOMAIN";
}
dest = "NXDOMAIN";
else
{
if (flags & F_IPV4)
@@ -1385,6 +1609,13 @@ void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
source = arg;
verb = "to";
}
else if (flags & F_IPSET)
{
source = "ipset add";
dest = name;
name = arg;
verb = daemon->addrbuff;
}
else
source = "cached";

View File

@@ -18,7 +18,8 @@
#define MAX_PROCS 20 /* max no children for TCP requests */
#define CHILD_LIFETIME 150 /* secs 'till terminated (RFC1035 suggests > 120s) */
#define EDNS_PKTSZ 4096 /* default max EDNS.0 UDP packet from RFC5625 */
#define KEYBLOCK_LEN 35 /* choose to mininise fragmentation when storing DNSSEC keys */
#define KEYBLOCK_LEN 40 /* choose to mininise 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 FORWARD_TEST 50 /* try all servers every 50 queries */
#define FORWARD_TIME 20 /* or 20 seconds */
@@ -30,7 +31,8 @@
#define PING_CACHE_TIME 30 /* Ping test assumed to be valid this long. */
#define DECLINE_BACKOFF 600 /* disable DECLINEd static addresses for this long */
#define DHCP_PACKET_MAX 16384 /* hard limit on DHCP packet size */
#define SMALLDNAME 40 /* most domain names are smaller than this */
#define SMALLDNAME 50 /* most domain names are smaller than this */
#define CNAME_CHAIN 10 /* chains longer than this atr dropped for loop protection */
#define HOSTSFILE "/etc/hosts"
#define ETHERSFILE "/etc/ethers"
#define DEFLEASE 3600 /* default lease time, 1 hour */
@@ -124,6 +126,11 @@ RESOLVFILE
*/
/* Defining this builds a binary which handles time differently and works better on a system without a
stable RTC (it uses uptime, not epoch time) and writes the DHCP leases file less often to avoid flash wear.
*/
/* #define HAVE_BROKEN_RTC */
/* The default set of options to build. Built with these options, dnsmasq
has no library dependencies other than libc */
@@ -134,8 +141,15 @@ RESOLVFILE
#define HAVE_SCRIPT
#define HAVE_AUTH
#define HAVE_IPSET
/* Build options which require external libraries.
Defining HAVE_<opt>_STATIC as _well_ as HAVE_<opt> will link the library statically.
You can use "make COPTS=-DHAVE_<opt>" instead of editing these.
*/
/* #define HAVE_LUASCRIPT */
/* #define HAVE_BROKEN_RTC */
/* #define HAVE_DBUS */
/* #define HAVE_IDN */
/* #define HAVE_CONNTRACK */

View File

@@ -108,108 +108,6 @@ static void remove_watch(DBusWatch *watch, void *data)
w = data; /* no warning */
}
static void add_update_server(union mysockaddr *addr,
union mysockaddr *source_addr,
const char *interface,
const char *domain)
{
struct server *serv;
/* See if there is a suitable candidate, and unmark */
for (serv = daemon->servers; serv; serv = serv->next)
if ((serv->flags & SERV_FROM_DBUS) &&
(serv->flags & SERV_MARK))
{
if (domain)
{
if (!(serv->flags & SERV_HAS_DOMAIN) || !hostname_isequal(domain, serv->domain))
continue;
}
else
{
if (serv->flags & SERV_HAS_DOMAIN)
continue;
}
serv->flags &= ~SERV_MARK;
break;
}
if (!serv && (serv = whine_malloc(sizeof (struct server))))
{
/* Not found, create a new one. */
memset(serv, 0, sizeof(struct server));
if (domain && !(serv->domain = whine_malloc(strlen(domain)+1)))
{
free(serv);
serv = NULL;
}
else
{
serv->next = daemon->servers;
daemon->servers = serv;
serv->flags = SERV_FROM_DBUS;
if (domain)
{
strcpy(serv->domain, domain);
serv->flags |= SERV_HAS_DOMAIN;
}
}
}
if (serv)
{
if (interface)
strcpy(serv->interface, interface);
else
serv->interface[0] = 0;
if (source_addr->in.sin_family == AF_INET &&
addr->in.sin_addr.s_addr == 0 &&
serv->domain)
serv->flags |= SERV_NO_ADDR;
else
{
serv->flags &= ~SERV_NO_ADDR;
serv->addr = *addr;
serv->source_addr = *source_addr;
}
}
}
static void mark_dbus(void)
{
struct server *serv;
/* mark everything from DBUS */
for (serv = daemon->servers; serv; serv = serv->next)
if (serv->flags & SERV_FROM_DBUS)
serv->flags |= SERV_MARK;
}
static void cleanup_dbus()
{
struct server *serv, *tmp, **up;
/* unlink and free anything still marked. */
for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp)
{
tmp = serv->next;
if (serv->flags & SERV_MARK)
{
server_gone(serv);
*up = serv->next;
if (serv->domain)
free(serv->domain);
free(serv);
}
else
up = &serv->next;
}
}
static void dbus_read_servers(DBusMessage *message)
{
DBusMessageIter iter;
@@ -218,8 +116,8 @@ static void dbus_read_servers(DBusMessage *message)
dbus_message_iter_init(message, &iter);
mark_dbus();
mark_servers(SERV_FROM_DBUS);
while (1)
{
int skip = 0;
@@ -289,13 +187,13 @@ static void dbus_read_servers(DBusMessage *message)
domain = NULL;
if (!skip)
add_update_server(&addr, &source_addr, NULL, domain);
add_update_server(SERV_FROM_DBUS, &addr, &source_addr, NULL, domain);
} while (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING);
}
/* unlink and free anything still marked. */
cleanup_dbus();
cleanup_servers();
}
static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
@@ -319,7 +217,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
strings ? "Expected array of string" : "Expected array of string arrays");
}
mark_dbus();
mark_servers(SERV_FROM_DBUS);
/* array_iter points to each "as" element in the outer array */
dbus_message_iter_recurse(&iter, &array_iter);
@@ -327,6 +225,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
{
const char *str = NULL;
union mysockaddr addr, source_addr;
int flags = 0;
char interface[IF_NAMESIZE];
char *str_addr, *str_domain = NULL;
@@ -416,16 +315,19 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
memset(&interface, 0, sizeof(interface));
/* parse the IP address */
addr_err = parse_server(str_addr, &addr, &source_addr, (char *) &interface, NULL);
if (addr_err)
{
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_NO_ADDR;
if (strings)
{
char *p;
@@ -439,7 +341,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
else
p = NULL;
add_update_server(&addr, &source_addr, interface, str_domain);
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str_domain);
} while ((str_domain = p));
}
else
@@ -454,7 +356,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
dbus_message_iter_get_basic(&string_iter, &str);
dbus_message_iter_next (&string_iter);
add_update_server(&addr, &source_addr, interface, str);
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str);
} while (dbus_message_iter_get_arg_type(&string_iter) == DBUS_TYPE_STRING);
}
@@ -462,7 +364,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
dbus_message_iter_next(&array_iter);
}
cleanup_dbus();
cleanup_servers();
if (dup)
free(dup);

View File

@@ -36,6 +36,7 @@
#define C_IN 1 /* the arpa internet */
#define C_CHAOS 3 /* for chaos net (MIT) */
#define C_HESIOD 4 /* hesiod */
#define C_ANY 255 /* wildcard match */
#define T_A 1
@@ -67,6 +68,7 @@
#define T_RRSIG 46
#define T_NSEC 47
#define T_DNSKEY 48
#define T_NSEC3 50
#define T_TKEY 249
#define T_TSIG 250
#define T_AXFR 252
@@ -76,7 +78,6 @@
#define EDNS0_OPTION_MAC 65001 /* dyndns.org temporary assignment */
#define EDNS0_OPTION_CLIENT_SUBNET 8 /* IANA */
struct dns_header {
u16 id;
u8 hb3,hb4;
@@ -136,24 +137,6 @@ struct dns_header {
(cp) += 4; \
}
#define CHECKED_GETCHAR(var, ptr, len) do { \
if ((len) < 1) return 0; \
var = *ptr++; \
(len) -= 1; \
} while (0)
#define CHECKED_GETSHORT(var, ptr, len) do { \
if ((len) < 2) return 0; \
GETSHORT(var, ptr); \
(len) -= 2; \
} while (0)
#define CHECKED_GETLONG(var, ptr, len) do { \
if ((len) < 4) return 0; \
GETLONG(var, ptr); \
(len) -= 4; \
} while (0)
#define CHECK_LEN(header, pp, plen, len) \
((size_t)((pp) - (unsigned char *)(header) + (len)) <= (plen))

View File

@@ -98,7 +98,10 @@ int main (int argc, char **argv)
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID))
daemon->keyname = safe_malloc(MAXDNAME);
{
daemon->keyname = safe_malloc(MAXDNAME);
daemon->workspacename = safe_malloc(MAXDNAME);
}
#endif
#ifdef HAVE_DHCP
@@ -141,10 +144,18 @@ int main (int argc, char **argv)
}
#endif
if (option_bool(OPT_DNSSEC_VALID))
{
#ifdef HAVE_DNSSEC
if (daemon->cachesize <CACHESIZ && option_bool(OPT_DNSSEC_VALID))
die(_("Cannot reduce cache size from default when DNSSEC enabled"), NULL, EC_BADCONF);
if (!daemon->ds)
die(_("No trust anchors provided for DNSSEC"), NULL, EC_BADCONF);
if (daemon->cachesize < CACHESIZ)
die(_("Cannot reduce cache size from default when DNSSEC enabled"), NULL, EC_BADCONF);
#else
die(_("DNSSEC not available: set HAVE_DNSSEC in src/config.h"), NULL, EC_BADCONF);
#endif
}
#ifndef HAVE_TFTP
if (option_bool(OPT_TFTP))
@@ -296,7 +307,12 @@ int main (int argc, char **argv)
#endif
if (daemon->port != 0)
cache_init();
{
cache_init();
#ifdef HAVE_DNSSEC
blockdata_init();
#endif
}
if (option_bool(OPT_DBUS))
#ifdef HAVE_DBUS
@@ -381,7 +397,7 @@ int main (int argc, char **argv)
piperead = pipefd[0];
pipewrite = pipefd[1];
/* prime the pipe to load stuff first time. */
send_event(pipewrite, EVENT_RELOAD, 0, NULL);
send_event(pipewrite, EVENT_INIT, 0, NULL);
err_pipe[1] = -1;
@@ -646,10 +662,22 @@ int main (int argc, char **argv)
}
#endif
if (option_bool(OPT_LOCAL_SERVICE))
my_syslog(LOG_INFO, _("DNS service limited to local subnets"));
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID))
{
my_syslog(LOG_INFO, _("DNSSEC validation enabled"));
if (option_bool(OPT_DNSSEC_TIME))
my_syslog(LOG_INFO, _("DNSSEC signature timestamps not checked until first cache reload"));
}
#endif
if (log_err != 0)
my_syslog(LOG_WARNING, _("warning: failed to change owner of %s: %s"),
daemon->log_file, strerror(log_err));
if (bind_fallback)
my_syslog(LOG_WARNING, _("setting --bind-interfaces option because of OS limitations"));
@@ -1096,7 +1124,7 @@ static void async_event(int pipe, time_t now)
{
pid_t p;
struct event_desc ev;
int i;
int i, check = 0;
char *msg;
/* NOTE: the memory used to return msg is leaked: use msgs in events only
@@ -1106,12 +1134,36 @@ static void async_event(int pipe, time_t now)
switch (ev.event)
{
case EVENT_RELOAD:
clear_cache_and_reload(now);
if (daemon->port != 0 && daemon->resolv_files && option_bool(OPT_NO_POLL))
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && option_bool(OPT_DNSSEC_TIME))
{
reload_servers(daemon->resolv_files->name);
check_servers();
my_syslog(LOG_INFO, _("now checking DNSSEC signature timestamps"));
reset_option_bool(OPT_DNSSEC_TIME);
}
#endif
/* fall through */
case EVENT_INIT:
clear_cache_and_reload(now);
if (daemon->port != 0)
{
if (daemon->resolv_files && option_bool(OPT_NO_POLL))
{
reload_servers(daemon->resolv_files->name);
check = 1;
}
if (daemon->servers_file)
{
read_servers_file();
check = 1;
}
if (check)
check_servers();
}
#ifdef HAVE_DHCP
rerun_scripts();
#endif

View File

@@ -164,6 +164,7 @@ struct event_desc {
#define EVENT_FORK_ERR 18
#define EVENT_LUA_ERR 19
#define EVENT_TFTP_ERR 20
#define EVENT_INIT 21
/* Exit codes. */
#define EC_GOOD 0
@@ -230,7 +231,11 @@ struct event_desc {
#define OPT_QUIET_DHCP6 43
#define OPT_QUIET_RA 44
#define OPT_DNSSEC_VALID 45
#define OPT_LAST 46
#define OPT_DNSSEC_TIME 46
#define OPT_DNSSEC_DEBUG 47
#define OPT_DNSSEC_NO_SIGN 48
#define OPT_LOCAL_SERVICE 49
#define OPT_LAST 50
/* extra flags for my_syslog, we use a couple of facilities since they are known
not to occupy the same bits as priorities, no matter how syslog.h is set up. */
@@ -243,7 +248,12 @@ struct all_addr {
#ifdef HAVE_IPV6
struct in6_addr addr6;
#endif
/* for log_query */
unsigned int keytag;
/* for cache_insert if RRSIG, DNSKEY, DS */
struct {
unsigned short class, type;
} dnssec;
} addr;
};
@@ -271,10 +281,19 @@ struct naptr {
struct naptr *next;
};
#define TXT_STAT_CACHESIZE 1
#define TXT_STAT_INSERTS 2
#define TXT_STAT_EVICTIONS 3
#define TXT_STAT_MISSES 4
#define TXT_STAT_HITS 5
#define TXT_STAT_AUTH 6
#define TXT_STAT_SERVERS 7
struct txt_record {
char *name;
unsigned char *txt;
unsigned short class, len;
int stat;
struct txt_record *next;
};
@@ -288,10 +307,10 @@ struct cname {
struct cname *next;
};
struct dnskey {
char *name, *key;
int keylen, algo, flags;
struct dnskey *next;
struct ds_config {
char *name, *digest;
int digestlen, class, algo, keytag, digest_type;
struct ds_config *next;
};
#define ADDRLIST_LITERAL 1
@@ -358,18 +377,28 @@ struct crec {
struct crec *cache;
struct interface_name *int_name;
} target;
int uid; /* -1 if union is interface-name */
unsigned int uid; /* 0 if union is interface-name */
} cname;
struct {
struct blockdata *keydata;
unsigned short keylen, flags, keytag;
unsigned char algo;
unsigned char digest; /* DS only */
unsigned short keytag;
} key;
} key;
struct {
struct blockdata *keydata;
unsigned short keylen, keytag;
unsigned char algo;
unsigned char digest;
} ds;
struct {
struct blockdata *keydata;
unsigned short keylen, type_covered, keytag;
char algo;
} sig;
} addr;
time_t ttd; /* time to die */
/* used as keylen ifF_DNSKEY, index to source for F_HOSTS */
int uid;
/* used as class if DNSKEY/DS/RRSIG, index to source for F_HOSTS */
unsigned int uid;
unsigned short flags;
union {
char sname[SMALLDNAME];
@@ -406,10 +435,14 @@ struct crec {
#define F_DNSSEC (1u<<22)
#define F_KEYTAG (1u<<23)
#define F_SECSTAT (1u<<24)
#define F_NO_RR (1u<<25)
#define F_IPSET (1u<<26)
/* composites */
#define F_TYPE (F_IPV4 | F_IPV6 | F_DNSKEY | F_DS) /* Only one may be set */
/* Values of uid in crecs with F_CONFIG bit set. */
#define SRC_INTERFACE 0
#define SRC_CONFIG 1
#define SRC_HOSTS 2
#define SRC_AH 3
/* struct sockaddr is not large enough to hold any address,
@@ -442,6 +475,7 @@ union mysockaddr {
#define SERV_COUNTED 512 /* workspace for log code */
#define SERV_USE_RESOLV 1024 /* forward this domain in the normal way */
#define SERV_NO_REBIND 2048 /* inhibit dns-rebind protection */
#define SERV_FROM_FILE 4096 /* read from --servers-file */
struct serverfd {
int fd;
@@ -508,7 +542,7 @@ struct hostsfile {
struct hostsfile *next;
int flags;
char *fname;
int index; /* matches to cache entries for logging */
unsigned int index; /* matches to cache entries for logging */
};
@@ -518,12 +552,28 @@ struct hostsfile {
#define STAT_BOGUS 3
#define STAT_NEED_DS 4
#define STAT_NEED_KEY 5
#define STAT_TRUNCATED 6
#define STAT_SECURE_WILDCARD 7
#define STAT_NO_SIG 8
#define STAT_NO_DS 9
#define STAT_NEED_DS_NEG 10
#define STAT_CHASE_CNAME 11
#define FREC_NOREBIND 1
#define FREC_CHECKING_DISABLED 2
#define FREC_HAS_SUBNET 4
#define FREC_DNSKEY_QUERY 8
#define FREC_DS_QUERY 16
#define FREC_AD_QUESTION 32
#define FREC_DO_QUESTION 64
#define FREC_ADDED_PHEADER 128
#define FREC_CHECK_NOSIGN 256
#ifdef HAVE_DNSSEC
#define HASH_SIZE 20 /* SHA-1 digest size */
#else
#define HASH_SIZE sizeof(int)
#endif
struct frec {
union mysockaddr source;
@@ -536,10 +586,10 @@ struct frec {
unsigned int iface;
unsigned short orig_id, new_id;
int fd, forwardall, flags;
unsigned int crc;
time_t time;
#ifdef HAVE_DNSSEC
int class;
unsigned char *hash[HASH_SIZE];
#ifdef HAVE_DNSSEC
int class, work_counter;
struct blockdata *stash; /* Saved reply, whilst we validate */
size_t stash_len;
struct frec *dependent; /* Query awaiting internally-generated DNSKEY or DS query */
@@ -842,6 +892,7 @@ extern struct daemon {
unsigned int options, options2;
struct resolvc default_resolv, *resolv_files;
time_t last_resolv;
char *servers_file;
struct mx_srv_record *mxnames;
struct naptr *naptr;
struct txt_record *txt, *rr;
@@ -908,7 +959,7 @@ extern struct daemon {
struct prefix_class *prefix_classes;
#endif
#ifdef HAVE_DNSSEC
struct dnskey *dnskeys;
struct ds_config *ds;
#endif
/* globally used stuff for DNS */
@@ -917,6 +968,7 @@ extern struct daemon {
char *namebuff; /* MAXDNAME size buffer */
#ifdef HAVE_DNSSEC
char *keyname; /* MAXDNAME size buffer */
char *workspacename; /* ditto */
#endif
unsigned int local_answer, queries_forwarded, auth_answer;
struct frec *frec_list;
@@ -932,6 +984,7 @@ extern struct daemon {
pid_t tcp_pids[MAX_PROCS];
struct randfd randomsocks[RANDOM_SOCKS];
int v6pktinfo;
struct addrlist *interface_addrs; /* list of all addresses/prefix lengths associated with all local interfaces */
/* DHCP state */
int dhcpfd, helperfd, pxefd;
@@ -969,13 +1022,13 @@ extern struct daemon {
/* cache.c */
void cache_init(void);
void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg);
char *record_source(int index);
void querystr(char *desc, char *str, unsigned short type);
char *record_source(unsigned int index);
char *querystr(char *desc, unsigned short type);
struct crec *cache_find_by_addr(struct crec *crecp,
struct all_addr *addr, time_t now,
unsigned short prot);
unsigned int prot);
struct crec *cache_find_by_name(struct crec *crecp,
char *name, time_t now, unsigned short prot);
char *name, time_t now, unsigned int prot);
void cache_end_insert(void);
void cache_start_insert(void);
struct crec *cache_insert(char *name, struct all_addr *addr,
@@ -985,16 +1038,17 @@ void cache_add_dhcp_entry(char *host_name, int prot, struct all_addr *host_addre
struct in_addr a_record_from_hosts(char *name, time_t now);
void cache_unhash_dhcp(void);
void dump_cache(time_t now);
int cache_make_stat(struct txt_record *t);
char *cache_get_name(struct crec *crecp);
char *cache_get_cname_target(struct crec *crecp);
struct crec *cache_enumerate(int init);
/* blockdata.c */
#ifdef HAVE_DNSSEC
void blockdata_init(void);
void blockdata_report(void);
struct blockdata *blockdata_alloc(char *data, size_t len);
size_t blockdata_walk(struct blockdata **key, unsigned char **p, size_t cnt);
int blockdata_retrieve(struct blockdata *block, size_t len, void *data);
void *blockdata_retrieve(struct blockdata *block, size_t len, void *data);
void blockdata_free(struct blockdata *blocks);
#endif
@@ -1011,6 +1065,7 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
char *name, int isExtract, int extrabytes);
unsigned char *skip_name(unsigned char *ansp, struct dns_header *header, size_t plen, int extrabytes);
unsigned char *skip_questions(struct dns_header *header, size_t plen);
unsigned char *skip_section(unsigned char *ansp, int count, struct dns_header *header, size_t plen);
unsigned int extract_request(struct dns_header *header, size_t qlen,
char *name, unsigned short *typep);
size_t setup_reply(struct dns_header *header, size_t qlen,
@@ -1018,9 +1073,10 @@ size_t setup_reply(struct dns_header *header, size_t qlen,
unsigned long local_ttl);
int extract_addresses(struct dns_header *header, size_t qlen, char *namebuff,
time_t now, char **ipsets, int is_sign, int checkrebind,
int no_cache, int secure);
int no_cache, int secure, int *doctored);
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);
struct in_addr local_addr, struct in_addr local_netmask,
time_t now, int *ad_reqd, int *do_bit);
int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
struct bogus_addr *addr, time_t now);
unsigned char *find_pseudoheader(struct dns_header *header, size_t plen,
@@ -1055,8 +1111,11 @@ int in_zone(struct auth_zone *zone, char *name, char **cut);
size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, int class, int type, union mysockaddr *addr);
int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t n, char *name, char *keyname, int class);
int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class);
int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class);
int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class, int *neganswer);
int dnssec_chase_cname(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname);
int dnskey_keytag(int alg, int flags, unsigned char *rdata, int rdlen);
size_t filter_rrsigs(struct dns_header *header, size_t plen);
unsigned char* hash_questions(struct dns_header *header, size_t plen, char *name);
/* util.c */
void rand_init(void);
@@ -1083,9 +1142,6 @@ void prettyprint_time(char *buf, unsigned int t);
int prettyprint_addr(union mysockaddr *addr, char *buf);
int parse_hex(char *in, unsigned char *out, int maxlen,
unsigned int *wildcard_mask, int *mac_type);
#ifdef HAVE_DNSSEC
int parse_base64(char *in, char *out);
#endif
int memcmp_masked(unsigned char *a, unsigned char *b, int len,
unsigned int mask);
int expand_buf(struct iovec *iov, size_t size);
@@ -1109,6 +1165,7 @@ void read_opts (int argc, char **argv, char *compile_opts);
char *option_string(int prot, unsigned int opt, unsigned char *val,
int opt_len, char *buf, int buf_len);
void reread_dhcp(void);
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);
@@ -1132,6 +1189,13 @@ int local_bind(int fd, union mysockaddr *addr, char *intname, int is_tcp);
int random_sock(int family);
void pre_allocate_sfds(void);
int reload_servers(char *fname);
void mark_servers(int flag);
void cleanup_servers(void);
void add_update_server(int flags,
union mysockaddr *addr,
union mysockaddr *source_addr,
const char *interface,
const char *domain);
void check_servers(void);
int enumerate_interfaces(int reset);
void create_wildcard_listeners(void);

View File

@@ -1,83 +0,0 @@
/* dnssec-crypto.h is Copyright (c) 2012 Giovanni Bajo <rasky@develer.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 dated June, 1991, or
(at your option) version 3 dated 29 June, 2007.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef DNSSEC_CRYPTO_H
#define DNSSEC_CRYPTO_H
struct blockdata;
/*
* vtable for a signature verification algorithm.
*
* Each algorithm verifies that a certain signature over a (possibly non-contigous)
* array of data has been made with the specified key.
*
* Sample of usage:
*
* // First, set the signature we need to check. Notice: data is not copied
* // nor consumed, so the pointer must stay valid.
* alg->set_signature(sig, 16);
*
* // Second, get push the data through the corresponding digest algorithm;
* // data is consumed immediately, so the buffers can be freed or modified.
* digestalg_begin(alg->get_digestalgo());
* digestalg_add_data(buf1, 123);
* digestalg_add_data(buf2, 45);
* digestalg_add_data(buf3, 678);
* alg->set_digest(digestalg_final());
*
* // Third, verify if we got the correct key for this signature.
* alg->verify(key1, 16);
* alg->verify(key2, 16);
*/
typedef struct VerifyAlgCtx VerifyAlgCtx;
typedef struct
{
int digest_algo;
int (*verify)(VerifyAlgCtx *ctx, struct blockdata *key, unsigned key_len);
} VerifyAlg;
struct VerifyAlgCtx
{
const VerifyAlg *vtbl;
unsigned char *sig;
size_t siglen;
unsigned char digest[64]; /* TODO: if memory problems, use VLA */
};
int verifyalg_supported(int algo);
VerifyAlgCtx* verifyalg_alloc(int algo);
void verifyalg_free(VerifyAlgCtx *a);
int verifyalg_algonum(VerifyAlgCtx *a);
/* Functions to calculate the digest of a key */
/* RFC4034 digest algorithms */
#define DIGESTALG_SHA1 1
#define DIGESTALG_SHA256 2
#define DIGESTALG_MD5 256
#define DIGESTALG_SHA512 257
int digestalg_supported(int algo);
void digestalg_begin(int algo);
void digestalg_add_data(void *data, unsigned len);
void digestalg_add_keydata(struct blockdata *key, size_t len);
unsigned char *digestalg_final(void);
int digestalg_len(void);
#endif /* DNSSEC_CRYPTO_H */

View File

@@ -1,316 +0,0 @@
/* dnssec-openssl.c is Copyright (c) 2012 Giovanni Bajo <rasky@develer.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 dated June, 1991, or
(at your option) version 3 dated 29 June, 2007.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dnsmasq.h"
#ifdef HAVE_DNSSEC
#include "dnssec-crypto.h"
#include <openssl/evp.h>
#include <openssl/rsa.h>
#include <openssl/dsa.h>
#include <openssl/err.h>
#include <string.h>
#define POOL_SIZE 1
static union _Pool
{
VerifyAlgCtx ctx;
} Pool[POOL_SIZE];
static char pool_used = 0;
static void print_hex(unsigned char *data, unsigned len)
{
while (len > 0)
{
printf("%02x", *data++);
--len;
}
printf("\n");
}
static int keydata_to_bn(BIGNUM *ret, struct blockdata **key_data, unsigned char **p, unsigned len)
{
size_t cnt;
BIGNUM temp;
BN_init(ret);
cnt = blockdata_walk(key_data, p, len);
BN_bin2bn(*p, cnt, ret);
len -= cnt;
*p += cnt;
while (len > 0)
{
if (!(cnt = blockdata_walk(key_data, p, len)))
return 0;
BN_lshift(ret, ret, cnt*8);
BN_init(&temp);
BN_bin2bn(*p, cnt, &temp);
BN_add(ret, ret, &temp);
len -= cnt;
*p += cnt;
}
return 1;
}
static int rsasha1_parse_key(BIGNUM *exp, BIGNUM *mod, struct blockdata *key_data, unsigned key_len)
{
unsigned char *p = key_data->key;
size_t exp_len, mod_len;
CHECKED_GETCHAR(exp_len, p, key_len);
if (exp_len == 0)
CHECKED_GETSHORT(exp_len, p, key_len);
if (exp_len >= key_len)
return 0;
mod_len = key_len - exp_len;
return keydata_to_bn(exp, &key_data, &p, exp_len) &&
keydata_to_bn(mod, &key_data, &p, mod_len);
}
static int dsasha1_parse_key(BIGNUM *Q, BIGNUM *P, BIGNUM *G, BIGNUM *Y, struct blockdata *key_data, unsigned key_len)
{
unsigned char *p = key_data->key;
int T;
CHECKED_GETCHAR(T, p, key_len);
return
keydata_to_bn(Q, &key_data, &p, 20) &&
keydata_to_bn(P, &key_data, &p, 64+T*8) &&
keydata_to_bn(G, &key_data, &p, 64+T*8) &&
keydata_to_bn(Y, &key_data, &p, 64+T*8);
}
static int rsa_verify(VerifyAlgCtx *ctx, struct blockdata *key_data, unsigned key_len, int nid, int dlen)
{
int validated = 0;
RSA *rsa = RSA_new();
rsa->e = BN_new();
rsa->n = BN_new();
if (rsasha1_parse_key(rsa->e, rsa->n, key_data, key_len)
&& RSA_verify(nid, ctx->digest, dlen, ctx->sig, ctx->siglen, rsa))
validated = 1;
RSA_free(rsa);
return validated;
}
static int rsamd5_verify(VerifyAlgCtx *ctx, struct blockdata *key_data, unsigned key_len)
{
return rsa_verify(ctx, key_data, key_len, NID_md5, 16);
}
static int rsasha1_verify(VerifyAlgCtx *ctx, struct blockdata *key_data, unsigned key_len)
{
return rsa_verify(ctx, key_data, key_len, NID_sha1, 20);
}
static int rsasha256_verify(VerifyAlgCtx *ctx, struct blockdata *key_data, unsigned key_len)
{
return rsa_verify(ctx, key_data, key_len, NID_sha256, 32);
}
static int rsasha512_verify(VerifyAlgCtx *ctx, struct blockdata *key_data, unsigned key_len)
{
return rsa_verify(ctx, key_data, key_len, NID_sha512, 64);
}
static int dsasha1_verify(VerifyAlgCtx *ctx, struct blockdata *key_data, unsigned key_len)
{
static unsigned char asn1_signature[] =
{
0x30, 0x2E, // sequence
0x02, 21, // large integer (21 bytes)
0x00, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // R
0x02, 21, // large integer (21 bytes)
0x00, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // S
};
int validated = 0;
/* A DSA signature is made of 2 bignums (R & S). We could parse them manually with BN_bin2bn(),
but OpenSSL does not have an API to verify a DSA signature given R and S, and insists
in having a ASN.1 BER sequence (as per RFC3279).
We prepare a hard-coded ASN.1 sequence, and just fill in the R&S numbers in it. */
memcpy(asn1_signature+5, ctx->sig+1, 20);
memcpy(asn1_signature+28, ctx->sig+21, 20);
DSA *dsa = DSA_new();
dsa->q = BN_new();
dsa->p = BN_new();
dsa->g = BN_new();
dsa->pub_key = BN_new();
if (dsasha1_parse_key(dsa->q, dsa->p, dsa->g, dsa->pub_key, key_data, key_len)
&& DSA_verify(0, ctx->digest, 20, asn1_signature, countof(asn1_signature), dsa) > 0)
validated = 1;
DSA_free(dsa);
return validated;
}
#define VALG_UNSUPPORTED() { \
0,0 \
} /**/
#define VALG_VTABLE(alg, digest) { \
digest, \
alg ## _verify \
} /**/
/* Updated registry that merges various RFCs:
https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xml */
static const VerifyAlg valgs[] =
{
VALG_UNSUPPORTED(), /* 0: reserved */
VALG_VTABLE(rsamd5, DIGESTALG_MD5), /* 1: RSAMD5 */
VALG_UNSUPPORTED(), /* 2: DH */
VALG_VTABLE(dsasha1, DIGESTALG_SHA1), /* 3: DSA */
VALG_UNSUPPORTED(), /* 4: ECC */
VALG_VTABLE(rsasha1, DIGESTALG_SHA1), /* 5: RSASHA1 */
VALG_VTABLE(dsasha1, DIGESTALG_SHA1), /* 6: DSA-NSEC3-SHA1 */
VALG_VTABLE(rsasha1, DIGESTALG_SHA1), /* 7: RSASHA1-NSEC3-SHA1 */
VALG_VTABLE(rsasha256, DIGESTALG_SHA256), /* 8: RSASHA256 */
VALG_UNSUPPORTED(), /* 9: unassigned */
VALG_VTABLE(rsasha512, DIGESTALG_SHA512), /* 10: RSASHA512 */
VALG_UNSUPPORTED(), /* 11: unassigned */
VALG_UNSUPPORTED(), /* 12: ECC-GOST */
VALG_UNSUPPORTED(), /* 13: ECDSAP256SHA256 */
VALG_UNSUPPORTED(), /* 14: ECDSAP384SHA384 */
};
/* TODO: remove if we don't need this anymore
(to be rechecked if we ever remove OpenSSL) */
static const int valgctx_size[] =
{
0, /* 0: reserved */
sizeof(VerifyAlgCtx), /* 1: RSAMD5 */
0, /* 2: DH */
sizeof(VerifyAlgCtx), /* 3: DSA */
0, /* 4: ECC */
sizeof(VerifyAlgCtx), /* 5: RSASHA1 */
sizeof(VerifyAlgCtx), /* 6: DSA-NSEC3-SHA1 */
sizeof(VerifyAlgCtx), /* 7: RSASHA1-NSEC3-SHA1 */
sizeof(VerifyAlgCtx), /* 8: RSASHA256 */
0, /* 9: unassigned */
sizeof(VerifyAlgCtx), /* 10: RSASHA512 */
0, /* 11: unassigned */
0, /* 12: ECC-GOST */
0, /* 13: ECDSAP256SHA256 */
0, /* 14: ECDSAP384SHA384 */
};
int verifyalg_supported(int algo)
{
return (algo < countof(valgctx_size) && valgctx_size[algo] != 0);
}
VerifyAlgCtx* verifyalg_alloc(int algo)
{
int i;
VerifyAlgCtx *ret = 0;
if (pool_used == (1<<POOL_SIZE)-1)
ret = whine_malloc(valgctx_size[algo]);
else
for (i = 0; i < POOL_SIZE; ++i)
if (!(pool_used & (1 << i)))
{
ret = (VerifyAlgCtx*)&Pool[i];
pool_used |= 1 << i;
break;
}
if (ret)
ret->vtbl = &valgs[algo];
return ret;
}
void verifyalg_free(VerifyAlgCtx *a)
{
int pool_idx = ((char*)a - (char*)&Pool[0]) / sizeof(Pool[0]);
if (pool_idx < 0 || pool_idx >= POOL_SIZE)
{
free(a);
return;
}
pool_used &= ~(1 << pool_idx);
}
int verifyalg_algonum(VerifyAlgCtx *a)
{
int num = a->vtbl - valgs;
if (num < 0 || num >= countof(valgs))
return -1;
return num;
}
static EVP_MD_CTX digctx;
int digestalg_supported(int algo)
{
return (algo == DIGESTALG_SHA1 ||
algo == DIGESTALG_SHA256 ||
algo == DIGESTALG_MD5 ||
algo == DIGESTALG_SHA512);
}
void digestalg_begin(int algo)
{
EVP_MD_CTX_init(&digctx);
if (algo == DIGESTALG_SHA1)
EVP_DigestInit_ex(&digctx, EVP_sha1(), NULL);
else if (algo == DIGESTALG_SHA256)
EVP_DigestInit_ex(&digctx, EVP_sha256(), NULL);
else if (algo == DIGESTALG_SHA512)
EVP_DigestInit_ex(&digctx, EVP_sha512(), NULL);
else if (algo == DIGESTALG_MD5)
EVP_DigestInit_ex(&digctx, EVP_md5(), NULL);
}
int digestalg_len()
{
return EVP_MD_CTX_size(&digctx);
}
void digestalg_add_data(void *data, unsigned len)
{
EVP_DigestUpdate(&digctx, data, len);
}
void digestalg_add_keydata(struct blockdata *key, size_t len)
{
size_t cnt; unsigned char *p = NULL;
while (len)
{
cnt = blockdata_walk(&key, &p, len);
EVP_DigestUpdate(&digctx, p, cnt);
p += cnt;
len -= cnt;
}
}
unsigned char* digestalg_final(void)
{
static unsigned char digest[32];
EVP_DigestFinal(&digctx, digest, NULL);
return digest;
}
#endif /* HAVE_DNSSEC */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -306,7 +306,7 @@ void lease_update_file(time_t now)
file_dirty = 0;
}
/* Set alarm for when the first lease expires + slop. */
/* Set alarm for when the first lease expires. */
next_event = 0;
#ifdef HAVE_DHCP6
@@ -331,8 +331,8 @@ void lease_update_file(time_t now)
for (lease = leases; lease; lease = lease->next)
if (lease->expires != 0 &&
(next_event == 0 || difftime(next_event, lease->expires + 10) > 0.0))
next_event = lease->expires + 10;
(next_event == 0 || difftime(next_event, lease->expires) > 0.0))
next_event = lease->expires;
if (err)
{
@@ -745,14 +745,23 @@ struct dhcp_lease *lease6_allocate(struct in6_addr *addrp, int lease_type)
void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now)
{
time_t exp = now + (time_t)len;
time_t exp;
if (len == 0xffffffff)
{
exp = 0;
len = 0;
}
else
{
exp = now + (time_t)len;
/* Check for 2038 overflow. Make the lease
inifinite in that case, as the least disruptive
thing we can do. */
if (difftime(exp, now) <= 0.0)
exp = 0;
}
if (exp != lease->expires)
{
dns_dirty = 1;

View File

@@ -264,10 +264,10 @@ int iface_enumerate(int family, void *parm, int (*callback)())
if (ifa->ifa_flags & IFA_F_DEPRECATED)
flags |= IFACE_DEPRECATED;
if (ifa->ifa_flags & IFA_F_PERMANENT)
flags |= IFACE_PERMANENT;
if (!(ifa->ifa_flags & IFA_F_TEMPORARY))
flags |= IFACE_PERMANENT;
if (addrp && callback_ok)
if (!((*callback)(addrp, (int)(ifa->ifa_prefixlen), (int)(ifa->ifa_scope),
(int)(ifa->ifa_index), flags,

View File

@@ -268,7 +268,40 @@ static int iface_allowed(struct iface_param *param, int if_index, char *label,
if (!label)
label = ifr.ifr_name;
/* maintain a list of all addresses on all interfaces for --local-service option */
if (option_bool(OPT_LOCAL_SERVICE))
{
struct addrlist *al;
if (param->spare)
{
al = param->spare;
param->spare = al->next;
}
else
al = whine_malloc(sizeof(struct addrlist));
if (al)
{
al->next = daemon->interface_addrs;
daemon->interface_addrs = al;
al->prefixlen = prefixlen;
if (addr->sa.sa_family == AF_INET)
{
al->addr.addr.addr4 = addr->in.sin_addr;
al->flags = 0;
}
#ifdef HAVE_IPV6
else
{
al->addr.addr.addr6 = addr->in6.sin6_addr;
al->flags = ADDRLIST_IPV6;
}
#endif
}
}
#ifdef HAVE_IPV6
if (addr->sa.sa_family != AF_INET6 || !IN6_IS_ADDR_LINKLOCAL(&addr->in6.sin6_addr))
@@ -565,6 +598,15 @@ int enumerate_interfaces(int reset)
intname->addr = NULL;
}
/* Remove list of addresses of local interfaces */
for (addr = daemon->interface_addrs; addr; addr = tmp)
{
tmp = addr->next;
addr->next = spare;
spare = addr;
}
daemon->interface_addrs = NULL;
#ifdef HAVE_AUTH
/* remove addresses stored against auth_zone subnets, but not
ones configured as address literals */
@@ -1254,87 +1296,187 @@ void pre_allocate_sfds(void)
}
}
void mark_servers(int flag)
{
struct server *serv;
/* mark everything with argument flag */
for (serv = daemon->servers; serv; serv = serv->next)
if (serv->flags & flag)
serv->flags |= SERV_MARK;
}
void cleanup_servers(void)
{
struct server *serv, *tmp, **up;
/* unlink and free anything still marked. */
for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp)
{
tmp = serv->next;
if (serv->flags & SERV_MARK)
{
server_gone(serv);
*up = serv->next;
if (serv->domain)
free(serv->domain);
free(serv);
}
else
up = &serv->next;
}
}
void add_update_server(int flags,
union mysockaddr *addr,
union mysockaddr *source_addr,
const char *interface,
const char *domain)
{
struct server *serv, *next = NULL;
char *domain_str = NULL;
/* See if there is a suitable candidate, and unmark */
for (serv = daemon->servers; serv; serv = serv->next)
if (serv->flags & SERV_MARK)
{
if (domain)
{
if (!(serv->flags & SERV_HAS_DOMAIN) || !hostname_isequal(domain, serv->domain))
continue;
}
else
{
if (serv->flags & SERV_HAS_DOMAIN)
continue;
}
break;
}
if (serv)
{
domain_str = serv->domain;
next = serv->next;
}
else if ((serv = whine_malloc(sizeof (struct server))))
{
/* Not found, create a new one. */
if (domain && !(domain_str = whine_malloc(strlen(domain)+1)))
{
free(serv);
serv = NULL;
}
else
{
struct server *s;
/* Add to the end of the chain, for order */
if (!daemon->servers)
daemon->servers = serv;
else
{
for (s = daemon->servers; s->next; s = s->next);
s->next = serv;
}
if (domain)
strcpy(domain_str, domain);
}
}
if (serv)
{
memset(serv, 0, sizeof(struct server));
serv->flags = flags;
serv->domain = domain_str;
serv->next = next;
serv->queries = serv->failed_queries = 0;
if (domain)
serv->flags |= SERV_HAS_DOMAIN;
if (interface)
strcpy(serv->interface, interface);
if (addr)
serv->addr = *addr;
if (source_addr)
serv->source_addr = *source_addr;
}
}
void check_servers(void)
{
struct irec *iface;
struct server *new, *tmp, *ret = NULL;
struct server *serv;
int port = 0;
/* interface may be new since startup */
if (!option_bool(OPT_NOWILD))
enumerate_interfaces(0);
for (new = daemon->servers; new; new = tmp)
for (serv = daemon->servers; serv; serv = serv->next)
{
tmp = new->next;
if (!(new->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND)))
if (!(serv->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND)))
{
port = prettyprint_addr(&new->addr, daemon->namebuff);
port = prettyprint_addr(&serv->addr, daemon->namebuff);
/* 0.0.0.0 is nothing, the stack treats it like 127.0.0.1 */
if (new->addr.sa.sa_family == AF_INET &&
new->addr.in.sin_addr.s_addr == 0)
if (serv->addr.sa.sa_family == AF_INET &&
serv->addr.in.sin_addr.s_addr == 0)
{
free(new);
serv->flags |= SERV_MARK;
continue;
}
for (iface = daemon->interfaces; iface; iface = iface->next)
if (sockaddr_isequal(&new->addr, &iface->addr))
if (sockaddr_isequal(&serv->addr, &iface->addr))
break;
if (iface)
{
my_syslog(LOG_WARNING, _("ignoring nameserver %s - local interface"), daemon->namebuff);
free(new);
serv->flags |= SERV_MARK;
continue;
}
/* Do we need a socket set? */
if (!new->sfd &&
!(new->sfd = allocate_sfd(&new->source_addr, new->interface)) &&
if (!serv->sfd &&
!(serv->sfd = allocate_sfd(&serv->source_addr, serv->interface)) &&
errno != 0)
{
my_syslog(LOG_WARNING,
_("ignoring nameserver %s - cannot make/bind socket: %s"),
daemon->namebuff, strerror(errno));
free(new);
serv->flags |= SERV_MARK;
continue;
}
}
/* reverse order - gets it right. */
new->next = ret;
ret = new;
if (!(new->flags & SERV_NO_REBIND))
if (!(serv->flags & SERV_NO_REBIND))
{
if (new->flags & (SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_USE_RESOLV))
if (serv->flags & (SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_USE_RESOLV))
{
char *s1, *s2;
if (!(new->flags & SERV_HAS_DOMAIN))
if (!(serv->flags & SERV_HAS_DOMAIN))
s1 = _("unqualified"), s2 = _("names");
else if (strlen(new->domain) == 0)
else if (strlen(serv->domain) == 0)
s1 = _("default"), s2 = "";
else
s1 = _("domain"), s2 = new->domain;
s1 = _("domain"), s2 = serv->domain;
if (new->flags & SERV_NO_ADDR)
if (serv->flags & SERV_NO_ADDR)
my_syslog(LOG_INFO, _("using local addresses only for %s %s"), s1, s2);
else if (new->flags & SERV_USE_RESOLV)
else if (serv->flags & SERV_USE_RESOLV)
my_syslog(LOG_INFO, _("using standard nameservers for %s %s"), s1, s2);
else if (!(new->flags & SERV_LITERAL_ADDRESS))
else if (!(serv->flags & SERV_LITERAL_ADDRESS))
my_syslog(LOG_INFO, _("using nameserver %s#%d for %s %s"), daemon->namebuff, port, s1, s2);
}
else if (new->interface[0] != 0)
my_syslog(LOG_INFO, _("using nameserver %s#%d(via %s)"), daemon->namebuff, port, new->interface);
else if (serv->interface[0] != 0)
my_syslog(LOG_INFO, _("using nameserver %s#%d(via %s)"), daemon->namebuff, port, serv->interface);
else
my_syslog(LOG_INFO, _("using nameserver %s#%d"), daemon->namebuff, port);
}
}
daemon->servers = ret;
cleanup_servers();
}
/* Return zero if no servers found, in that case we keep polling.
@@ -1343,9 +1485,6 @@ int reload_servers(char *fname)
{
FILE *f;
char *line;
struct server *old_servers = NULL;
struct server *new_servers = NULL;
struct server *serv;
int gotone = 0;
/* buff happens to be MAXDNAME long... */
@@ -1354,28 +1493,9 @@ int reload_servers(char *fname)
my_syslog(LOG_ERR, _("failed to read %s: %s"), fname, strerror(errno));
return 0;
}
/* move old servers to free list - we can reuse the memory
and not risk malloc if there are the same or fewer new servers.
Servers which were specced on the command line go to the new list. */
for (serv = daemon->servers; serv;)
{
struct server *tmp = serv->next;
if (serv->flags & SERV_FROM_RESOLV)
{
serv->next = old_servers;
old_servers = serv;
/* forward table rules reference servers, so have to blow them away */
server_gone(serv);
}
else
{
serv->next = new_servers;
new_servers = serv;
}
serv = tmp;
}
mark_servers(SERV_FROM_RESOLV);
while ((line = fgets(daemon->namebuff, MAXDNAME, f)))
{
union mysockaddr addr, source_addr;
@@ -1434,38 +1554,12 @@ int reload_servers(char *fname)
continue;
#endif
if (old_servers)
{
serv = old_servers;
old_servers = old_servers->next;
}
else if (!(serv = whine_malloc(sizeof (struct server))))
continue;
/* this list is reverse ordered:
it gets reversed again in check_servers */
serv->next = new_servers;
new_servers = serv;
serv->addr = addr;
serv->source_addr = source_addr;
serv->domain = NULL;
serv->interface[0] = 0;
serv->sfd = NULL;
serv->flags = SERV_FROM_RESOLV;
serv->queries = serv->failed_queries = 0;
add_update_server(SERV_FROM_RESOLV, &addr, &source_addr, NULL, NULL);
gotone = 1;
}
/* Free any memory not used. */
while (old_servers)
{
struct server *tmp = old_servers->next;
free(old_servers);
old_servers = tmp;
}
daemon->servers = new_servers;
fclose(f);
cleanup_servers();
return gotone;
}
@@ -1476,7 +1570,8 @@ void newaddress(time_t now)
{
(void)now;
if (option_bool(OPT_CLEVERBIND) || daemon->doing_dhcp6 || daemon->relay6 || daemon->doing_ra)
if (option_bool(OPT_CLEVERBIND) || option_bool(OPT_LOCAL_SERVICE) ||
daemon->doing_dhcp6 || daemon->relay6 || daemon->doing_ra)
enumerate_interfaces(0);
if (option_bool(OPT_CLEVERBIND))

View File

@@ -64,83 +64,88 @@ struct myoption {
#define OPTSTRING "951yZDNLERKzowefnbvhdkqr:m:p:c:l:s:i:t:u:g:a:x:S:C:A:T:H:Q:I:B:F:G:O:M:X:V:U:j:P:J:W:Y:2:4:6:7:8:0:3:"
/* options which don't have a one-char version */
#define LOPT_RELOAD 256
#define LOPT_NO_NAMES 257
#define LOPT_TFTP 258
#define LOPT_SECURE 259
#define LOPT_PREFIX 260
#define LOPT_PTR 261
#define LOPT_BRIDGE 262
#define LOPT_TFTP_MAX 263
#define LOPT_FORCE 264
#define LOPT_NOBLOCK 265
#define LOPT_LOG_OPTS 266
#define LOPT_MAX_LOGS 267
#define LOPT_CIRCUIT 268
#define LOPT_REMOTE 269
#define LOPT_SUBSCR 270
#define LOPT_INTNAME 271
#define LOPT_BANK 272
#define LOPT_DHCP_HOST 273
#define LOPT_APREF 274
#define LOPT_OVERRIDE 275
#define LOPT_TFTPPORTS 276
#define LOPT_REBIND 277
#define LOPT_NOLAST 278
#define LOPT_OPTS 279
#define LOPT_DHCP_OPTS 280
#define LOPT_MATCH 281
#define LOPT_BROADCAST 282
#define LOPT_NEGTTL 283
#define LOPT_ALTPORT 284
#define LOPT_SCRIPTUSR 285
#define LOPT_LOCAL 286
#define LOPT_NAPTR 287
#define LOPT_MINPORT 288
#define LOPT_DHCP_FQDN 289
#define LOPT_CNAME 290
#define LOPT_PXE_PROMT 291
#define LOPT_PXE_SERV 292
#define LOPT_TEST 293
#define LOPT_TAG_IF 294
#define LOPT_PROXY 295
#define LOPT_GEN_NAMES 296
#define LOPT_MAXTTL 297
#define LOPT_NO_REBIND 298
#define LOPT_LOC_REBND 299
#define LOPT_ADD_MAC 300
#define LOPT_DNSSEC 301
#define LOPT_INCR_ADDR 302
#define LOPT_CONNTRACK 303
#define LOPT_FQDN 304
#define LOPT_LUASCRIPT 305
#define LOPT_RA 306
#define LOPT_DUID 307
#define LOPT_HOST_REC 308
#define LOPT_TFTP_LC 309
#define LOPT_RR 310
#define LOPT_CLVERBIND 311
#define LOPT_MAXCTTL 312
#define LOPT_AUTHZONE 313
#define LOPT_AUTHSERV 314
#define LOPT_AUTHTTL 315
#define LOPT_AUTHSOA 316
#define LOPT_AUTHSFS 317
#define LOPT_AUTHPEER 318
#define LOPT_IPSET 319
#define LOPT_SYNTH 320
#define LOPT_RELOAD 256
#define LOPT_NO_NAMES 257
#define LOPT_TFTP 258
#define LOPT_SECURE 259
#define LOPT_PREFIX 260
#define LOPT_PTR 261
#define LOPT_BRIDGE 262
#define LOPT_TFTP_MAX 263
#define LOPT_FORCE 264
#define LOPT_NOBLOCK 265
#define LOPT_LOG_OPTS 266
#define LOPT_MAX_LOGS 267
#define LOPT_CIRCUIT 268
#define LOPT_REMOTE 269
#define LOPT_SUBSCR 270
#define LOPT_INTNAME 271
#define LOPT_BANK 272
#define LOPT_DHCP_HOST 273
#define LOPT_APREF 274
#define LOPT_OVERRIDE 275
#define LOPT_TFTPPORTS 276
#define LOPT_REBIND 277
#define LOPT_NOLAST 278
#define LOPT_OPTS 279
#define LOPT_DHCP_OPTS 280
#define LOPT_MATCH 281
#define LOPT_BROADCAST 282
#define LOPT_NEGTTL 283
#define LOPT_ALTPORT 284
#define LOPT_SCRIPTUSR 285
#define LOPT_LOCAL 286
#define LOPT_NAPTR 287
#define LOPT_MINPORT 288
#define LOPT_DHCP_FQDN 289
#define LOPT_CNAME 290
#define LOPT_PXE_PROMT 291
#define LOPT_PXE_SERV 292
#define LOPT_TEST 293
#define LOPT_TAG_IF 294
#define LOPT_PROXY 295
#define LOPT_GEN_NAMES 296
#define LOPT_MAXTTL 297
#define LOPT_NO_REBIND 298
#define LOPT_LOC_REBND 299
#define LOPT_ADD_MAC 300
#define LOPT_DNSSEC 301
#define LOPT_INCR_ADDR 302
#define LOPT_CONNTRACK 303
#define LOPT_FQDN 304
#define LOPT_LUASCRIPT 305
#define LOPT_RA 306
#define LOPT_DUID 307
#define LOPT_HOST_REC 308
#define LOPT_TFTP_LC 309
#define LOPT_RR 310
#define LOPT_CLVERBIND 311
#define LOPT_MAXCTTL 312
#define LOPT_AUTHZONE 313
#define LOPT_AUTHSERV 314
#define LOPT_AUTHTTL 315
#define LOPT_AUTHSOA 316
#define LOPT_AUTHSFS 317
#define LOPT_AUTHPEER 318
#define LOPT_IPSET 319
#define LOPT_SYNTH 320
#ifdef OPTION6_PREFIX_CLASS
#define LOPT_PREF_CLSS 321
#define LOPT_PREF_CLSS 321
#endif
#define LOPT_RELAY 323
#define LOPT_RA_PARAM 324
#define LOPT_ADD_SBNET 325
#define LOPT_QUIET_DHCP 326
#define LOPT_QUIET_DHCP6 327
#define LOPT_QUIET_RA 328
#define LOPT_SEC_VALID 329
#define LOPT_DNSKEY 330
#define LOPT_RELAY 323
#define LOPT_RA_PARAM 324
#define LOPT_ADD_SBNET 325
#define LOPT_QUIET_DHCP 326
#define LOPT_QUIET_DHCP6 327
#define LOPT_QUIET_RA 328
#define LOPT_SEC_VALID 329
#define LOPT_TRUST_ANCHOR 330
#define LOPT_DNSSEC_DEBUG 331
#define LOPT_REV_SERV 332
#define LOPT_SERVERS_FILE 333
#define LOPT_DNSSEC_CHECK 334
#define LOPT_LOCAL_SERVICE 335
#define LOPT_DNSSEC_TIME 336
#ifdef HAVE_GETOPT_LONG
static const struct option opts[] =
@@ -157,6 +162,7 @@ static const struct myoption opts[] =
{ "user", 2, 0, 'u' },
{ "group", 2, 0, 'g' },
{ "resolv-file", 2, 0, 'r' },
{ "servers-file", 1, 0, LOPT_SERVERS_FILE },
{ "mx-host", 1, 0, 'm' },
{ "mx-target", 1, 0, 't' },
{ "cache-size", 2, 0, 'c' },
@@ -171,6 +177,7 @@ static const struct myoption opts[] =
{ "domain-suffix", 1, 0, 's' },
{ "interface", 1, 0, 'i' },
{ "listen-address", 1, 0, 'a' },
{ "local-service", 0, 0, LOPT_LOCAL_SERVICE },
{ "bogus-priv", 0, 0, 'b' },
{ "bogus-nxdomain", 1, 0, 'B' },
{ "selfmx", 0, 0, 'e' },
@@ -178,6 +185,7 @@ static const struct myoption opts[] =
{ "pid-file", 2, 0, 'x' },
{ "strict-order", 0, 0, 'o' },
{ "server", 1, 0, 'S' },
{ "rev-server", 1, 0, LOPT_REV_SERV },
{ "local", 1, 0, LOPT_LOCAL },
{ "address", 1, 0, 'A' },
{ "conf-file", 2, 0, 'C' },
@@ -277,7 +285,10 @@ static const struct myoption opts[] =
{ "ipset", 1, 0, LOPT_IPSET },
{ "synth-domain", 1, 0, LOPT_SYNTH },
{ "dnssec", 0, 0, LOPT_SEC_VALID },
{ "dnskey", 1, 0, LOPT_DNSKEY },
{ "trust-anchor", 1, 0, LOPT_TRUST_ANCHOR },
{ "dnssec-debug", 0, 0, LOPT_DNSSEC_DEBUG },
{ "dnssec-check-unsigned", 0, 0, LOPT_DNSSEC_CHECK },
{ "dnssec-no-timecheck", 0, 0, LOPT_DNSSEC_TIME },
#ifdef OPTION6_PREFIX_CLASS
{ "dhcp-prefix-class", 1, 0, LOPT_PREF_CLSS },
#endif
@@ -346,7 +357,9 @@ static struct {
{ 'Q', ARG_ONE, "<integer>", gettext_noop("Force the originating port for upstream DNS queries."), 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 },
{ 'S', ARG_DUP, "/<domain>/<ipaddr>", gettext_noop("Specify address(es) of upstream servers with optional domains."), NULL },
{ LOPT_REV_SERV, ARG_DUP, "<addr>/<prefix>,<ipaddr>", gettext_noop("Specify address of upstream servers for reverse address queries"), NULL },
{ LOPT_LOCAL, ARG_DUP, "/<domain>/", gettext_noop("Never forward queries to specified domains."), NULL },
{ 's', ARG_DUP, "<domain>[,<range>]", gettext_noop("Specify the domain to be assigned in DHCP leases."), NULL },
{ 't', ARG_ONE, "<host_name>", gettext_noop("Specify default target in an MX record."), NULL },
@@ -428,10 +441,11 @@ static struct {
{ LOPT_AUTHPEER, ARG_DUP, "<ipaddr>[,<ipaddr>...]", gettext_noop("Peers which are allowed to do zone transfer"), NULL },
{ LOPT_IPSET, ARG_DUP, "/<domain>/<ipset>[,<ipset>...]", gettext_noop("Specify ipsets to which matching domains should be added"), NULL },
{ LOPT_SYNTH, ARG_DUP, "<domain>,<range>,[<prefix>]", gettext_noop("Specify a domain and address range for synthesised names"), NULL },
#ifdef HAVE_DNSSEC
{ LOPT_SEC_VALID, OPT_DNSSEC_VALID, NULL, gettext_noop("Activate DNSSEC validation"), NULL },
{ LOPT_DNSKEY, ARG_DUP, "<domain>,<algo>,<key>", gettext_noop("Specify trust anchor DNSKEY"), NULL },
#endif
{ LOPT_TRUST_ANCHOR, ARG_DUP, "<domain>,[<class>],...", gettext_noop("Specify trust anchor key digest."), NULL },
{ LOPT_DNSSEC_DEBUG, OPT_DNSSEC_DEBUG, NULL, gettext_noop("Disable upstream checking for DNSSEC debugging."), NULL },
{ LOPT_DNSSEC_CHECK, OPT_DNSSEC_NO_SIGN, NULL, gettext_noop("Ensure answers without DNSSEC are in unsigned zones."), NULL },
{ LOPT_DNSSEC_TIME, OPT_DNSSEC_TIME, NULL, gettext_noop("Don't check DNSSEC signature timestamps until first cache-reload"), NULL },
#ifdef OPTION6_PREFIX_CLASS
{ LOPT_PREF_CLSS, ARG_DUP, "set:tag,<class>", gettext_noop("Specify DHCPv6 prefix class"), NULL },
#endif
@@ -439,6 +453,7 @@ static struct {
{ LOPT_QUIET_DHCP, OPT_QUIET_DHCP, NULL, gettext_noop("Do not log routine DHCP."), NULL },
{ LOPT_QUIET_DHCP6, OPT_QUIET_DHCP6, NULL, gettext_noop("Do not log routine DHCPv6."), NULL },
{ LOPT_QUIET_RA, OPT_QUIET_RA, NULL, gettext_noop("Do not log RA."), NULL },
{ LOPT_LOCAL_SERVICE, OPT_LOCAL_SERVICE, NULL, gettext_noop("Accept queries only from directly-connected networks"), NULL },
{ 0, 0, NULL, NULL, NULL }
};
@@ -590,20 +605,37 @@ static int atoi_check16(char *a, int *res)
return 1;
}
static void add_txt(char *name, char *txt)
#ifdef HAVE_DNSSEC
static int atoi_check8(char *a, int *res)
{
if (!(atoi_check(a, res)) ||
*res < 0 ||
*res > 0xff)
return 0;
return 1;
}
#endif
static void add_txt(char *name, char *txt, int stat)
{
size_t len = strlen(txt);
struct txt_record *r = opt_malloc(sizeof(struct txt_record));
if (txt)
{
size_t len = strlen(txt);
r->txt = opt_malloc(len+1);
r->len = len+1;
*(r->txt) = len;
memcpy((r->txt)+1, txt, len);
}
r->stat = stat;
r->name = opt_string_alloc(name);
r->next = daemon->txt;
daemon->txt = r;
r->class = C_CHAOS;
r->txt = opt_malloc(len+1);
r->len = len+1;
*(r->txt) = len;
memcpy((r->txt)+1, txt, len);
}
static void do_usage(void)
@@ -674,6 +706,13 @@ char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_a
char *scope_id;
#endif
if (!arg || strlen(arg) == 0)
{
*flags |= SERV_NO_ADDR;
*interface = 0;
return NULL;
}
if ((source = split_chr(arg, '@')) && /* is there a source. */
(portno = split_chr(source, '#')) &&
!atoi_check16(portno, &source_port))
@@ -752,6 +791,54 @@ char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_a
return NULL;
}
static struct server *add_rev4(struct in_addr addr, int msize)
{
struct server *serv = opt_malloc(sizeof(struct server));
in_addr_t a = ntohl(addr.s_addr) >> 8;
char *p;
memset(serv, 0, sizeof(struct server));
p = serv->domain = opt_malloc(25); /* strlen("xxx.yyy.zzz.in-addr.arpa")+1 */
if (msize == 24)
p += sprintf(p, "%d.", a & 0xff);
a = a >> 8;
if (msize != 8)
p += sprintf(p, "%d.", a & 0xff);
a = a >> 8;
p += sprintf(p, "%d.in-addr.arpa", a & 0xff);
serv->flags = SERV_HAS_DOMAIN;
serv->next = daemon->servers;
daemon->servers = serv;
return serv;
}
static struct server *add_rev6(struct in6_addr *addr, int msize)
{
struct server *serv = opt_malloc(sizeof(struct server));
char *p;
int i;
memset(serv, 0, sizeof(struct server));
p = serv->domain = opt_malloc(73); /* strlen("32*<n.>ip6.arpa")+1 */
for (i = msize-1; i >= 0; i -= 4)
{
int dig = ((unsigned char *)addr)[i>>3];
p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
}
p += sprintf(p, "ip6.arpa");
serv->flags = SERV_HAS_DOMAIN;
serv->next = daemon->servers;
daemon->servers = serv;
return serv;
}
#ifdef HAVE_DHCP
static int is_tag_prefix(char *arg)
@@ -1315,7 +1402,7 @@ void reset_option_bool(unsigned int opt)
daemon->options2 &= ~(1u << (opt - 32));
}
static int one_opt(int option, char *arg, char *errstr, char *gen_err, int command_line)
static int one_opt(int option, char *arg, char *errstr, char *gen_err, int command_line, int servers_only)
{
int i;
char *comma;
@@ -1518,6 +1605,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
daemon->resolv_files = list;
break;
}
case LOPT_SERVERS_FILE:
daemon->servers_file = opt_string_alloc(arg);
break;
case 'm': /* --mx-host */
{
@@ -1581,7 +1672,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
case 'H': /* --addn-hosts */
{
struct hostsfile *new = opt_malloc(sizeof(struct hostsfile));
static int hosts_index = 1;
static unsigned int hosts_index = SRC_AH;
new->fname = opt_string_alloc(arg);
new->index = hosts_index++;
new->flags = 0;
@@ -1814,34 +1905,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
ret_err(gen_err);
else
{
/* generate the equivalent of
local=/<domain>/
local=/xxx.yyy.zzz.in-addr.arpa/ */
struct server *serv = opt_malloc(sizeof(struct server));
in_addr_t a = ntohl(new->start.s_addr) >> 8;
char *p;
memset(serv, 0, sizeof(struct server));
serv->domain = d;
serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
serv->next = daemon->servers;
daemon->servers = serv;
serv = opt_malloc(sizeof(struct server));
memset(serv, 0, sizeof(struct server));
p = serv->domain = opt_malloc(25); /* strlen("xxx.yyy.zzz.in-addr.arpa")+1 */
if (msize == 24)
p += sprintf(p, "%d.", a & 0xff);
a = a >> 8;
if (msize != 8)
p += sprintf(p, "%d.", a & 0xff);
a = a >> 8;
p += sprintf(p, "%d.in-addr.arpa", a & 0xff);
serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
serv->next = daemon->servers;
daemon->servers = serv;
/* generate the equivalent of
local=/<domain>/
local=/xxx.yyy.zzz.in-addr.arpa/ */
struct server *serv = add_rev4(new->start, msize);
serv->flags |= SERV_NO_ADDR;
}
}
}
@@ -1877,29 +1945,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
/* generate the equivalent of
local=/<domain>/
local=/xxx.yyy.zzz.ip6.arpa/ */
struct server *serv = opt_malloc(sizeof(struct server));
char *p;
memset(serv, 0, sizeof(struct server));
serv->domain = d;
serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
serv->next = daemon->servers;
daemon->servers = serv;
serv = opt_malloc(sizeof(struct server));
memset(serv, 0, sizeof(struct server));
p = serv->domain = opt_malloc(73); /* strlen("32*<n.>ip6.arpa")+1 */
for (i = msize-1; i >= 0; i -= 4)
{
int dig = ((unsigned char *)&new->start6)[i>>3];
p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
}
p += sprintf(p, "ip6.arpa");
serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
serv->next = daemon->servers;
daemon->servers = serv;
struct server *serv = add_rev6(&new->start6, msize);
serv->flags |= SERV_NO_ADDR;
}
}
}
@@ -2126,6 +2173,9 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
memset(newlist, 0, sizeof(struct server));
}
if (servers_only && option == 'S')
newlist->flags |= SERV_FROM_FILE;
if (option == 'A')
{
newlist->flags |= SERV_LITERAL_ADDRESS;
@@ -2170,6 +2220,40 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
break;
}
case LOPT_REV_SERV: /* --rev-server */
{
char *string;
int size;
struct server *serv;
struct in_addr addr4;
#ifdef HAVE_IPV6
struct in6_addr addr6;
#endif
unhide_metas(arg);
if (!arg || !(comma=split(arg)) || !(string = split_chr(arg, '/')) || !atoi_check(string, &size))
ret_err(gen_err);
if (inet_pton(AF_INET, arg, &addr4))
serv = add_rev4(addr4, size);
#ifdef HAVE_IPV6
else if (inet_pton(AF_INET6, arg, &addr6))
serv = add_rev6(&addr6, size);
#endif
else
ret_err(gen_err);
string = parse_server(comma, &serv->addr, &serv->source_addr, serv->interface, &serv->flags);
if (string)
ret_err(string);
if (servers_only)
serv->flags |= SERV_FROM_FILE;
break;
}
case LOPT_IPSET: /* --ipset */
#ifndef HAVE_IPSET
ret_err(_("recompile with HAVE_IPSET defined to enable ipset directives"));
@@ -3533,7 +3617,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
new->next = daemon->txt;
daemon->txt = new;
new->class = C_IN;
new->stat = 0;
if (!(new->name = canonicalise_opt(arg)))
ret_err(_("bad TXT record"));
@@ -3675,26 +3760,56 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
}
#ifdef HAVE_DNSSEC
case LOPT_DNSKEY:
case LOPT_TRUST_ANCHOR:
{
struct dnskey *new = opt_malloc(sizeof(struct dnskey));
char *key64, *algo;
if (!(comma = split(arg)) || !(algo = split(comma)) || !(key64 = split(algo)) ||
!atoi_check16(comma, &new->flags) || !atoi_check16(algo, &new->algo) ||
!(new->name = canonicalise_opt(arg)))
ret_err(_("bad DNSKEY"));
/* Upper bound on length */
new->key = opt_malloc((3*strlen(key64)/4));
unhide_metas(key64);
if ((new->keylen = parse_base64(key64, new->key)) == -1)
ret_err(_("bad base64 in DNSKEY"));
struct ds_config *new = opt_malloc(sizeof(struct ds_config));
char *cp, *cp1, *keyhex, *digest, *algo = NULL;
int len;
new->next = daemon->dnskeys;
daemon->dnskeys = new;
new->class = C_IN;
if ((comma = split(arg)) && (algo = split(comma)))
{
int class = 0;
if (strcmp(comma, "IN") == 0)
class = C_IN;
else if (strcmp(comma, "CH") == 0)
class = C_CHAOS;
else if (strcmp(comma, "HS") == 0)
class = C_HESIOD;
if (class != 0)
{
new->class = class;
comma = algo;
algo = split(comma);
}
}
if (!comma || !algo || !(digest = split(algo)) || !(keyhex = split(digest)) ||
!atoi_check16(comma, &new->keytag) ||
!atoi_check8(algo, &new->algo) ||
!atoi_check8(digest, &new->digest_type) ||
!(new->name = canonicalise_opt(arg)))
ret_err(_("bad trust anchor"));
/* Upper bound on length */
len = (2*strlen(keyhex))+1;
new->digest = opt_malloc(len);
unhide_metas(keyhex);
/* 4034: "Whitespace is allowed within digits" */
for (cp = keyhex; *cp; )
if (isspace(*cp))
for (cp1 = cp; *cp1; cp1++)
*cp1 = *(cp1+1);
else
cp++;
if ((new->digestlen = parse_hex(keyhex, (unsigned char *)new->digest, len, NULL, NULL)) == -1)
ret_err(_("bad HEX in trust anchor"));
new->next = daemon->ds;
daemon->ds = new;
break;
}
#endif
@@ -3714,12 +3829,13 @@ static void read_file(char *file, FILE *f, int hard_opt)
while (fgets(buff, MAXDNAME, f))
{
int white, i, option = hard_opt;
int white, i;
volatile int option = (hard_opt == LOPT_REV_SERV) ? 0 : hard_opt;
char *errmess, *p, *arg = NULL, *start;
size_t len;
/* Memory allocation failure longjmps here if mem_recover == 1 */
if (option != 0)
if (option != 0 || hard_opt == LOPT_REV_SERV)
{
if (setjmp(mem_jmp))
continue;
@@ -3820,13 +3936,15 @@ static void read_file(char *file, FILE *f, int hard_opt)
errmess = _("extraneous parameter");
else if (opts[i].has_arg == 1 && !arg)
errmess = _("missing parameter");
else if (hard_opt == LOPT_REV_SERV && option != 'S' && option != LOPT_REV_SERV)
errmess = _("illegal option");
}
oops:
if (errmess)
strcpy(daemon->namebuff, errmess);
if (errmess || !one_opt(option, arg, buff, _("error"), 0))
if (errmess || !one_opt(option, arg, buff, _("error"), 0, hard_opt == LOPT_REV_SERV))
{
sprintf(daemon->namebuff + strlen(daemon->namebuff), _(" at line %d of %s"), lineno, file);
if (hard_opt != 0)
@@ -3911,10 +4029,11 @@ static int one_file(char *file, int hard_opt)
/* expand any name which is a directory */
struct hostsfile *expand_filelist(struct hostsfile *list)
{
int i;
unsigned int i;
struct hostsfile *ah;
for (i = 0, ah = list; ah; ah = ah->next)
/* find largest used index */
for (i = SRC_AH, ah = list; ah; ah = ah->next)
{
if (i <= ah->index)
i = ah->index + 1;
@@ -4008,6 +4127,22 @@ struct hostsfile *expand_filelist(struct hostsfile *list)
return list;
}
void read_servers_file(void)
{
FILE *f;
if (!(f = fopen(daemon->servers_file, "r")))
{
my_syslog(LOG_ERR, _("cannot read %s: %s"), daemon->servers_file, strerror(errno));
return;
}
mark_servers(SERV_FROM_FILE);
cleanup_servers();
read_file(daemon->servers_file, f, LOPT_REV_SERV);
}
#ifdef HAVE_DHCP
void reread_dhcp(void)
@@ -4132,9 +4267,18 @@ 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;
add_txt("version.bind", "dnsmasq-" VERSION );
add_txt("authors.bind", "Simon Kelley");
add_txt("copyright.bind", COPYRIGHT);
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);
while (1)
{
@@ -4201,9 +4345,9 @@ void read_opts(int argc, char **argv, char *compile_opts)
else
{
#ifdef HAVE_GETOPT_LONG
if (!one_opt(option, arg, daemon->namebuff, _("try --help"), 1))
if (!one_opt(option, arg, daemon->namebuff, _("try --help"), 1, 0))
#else
if (!one_opt(option, arg, daemon->namebuff, _("try -w"), 1))
if (!one_opt(option, arg, daemon->namebuff, _("try -w"), 1, 0))
#endif
die(_("bad command line options: %s"), daemon->namebuff, EC_BADCONF);
}
@@ -4335,6 +4479,11 @@ void read_opts(int argc, char **argv, char *compile_opts)
else if (option_bool(OPT_DHCP_FQDN))
die(_("there must be a default domain when --dhcp-fqdn is set"), NULL, EC_BADCONF);
/* If there's access-control config, then ignore --local-service, it's intended
as a system default to keep otherwise unconfigured installations safe. */
if (daemon->if_names || daemon->if_except || daemon->if_addrs || daemon->authserver)
reset_option_bool(OPT_LOCAL_SERVICE);
if (testmode)
{
fprintf(stderr, "dnsmasq: %s.\n", _("syntax check OK"));

View File

@@ -285,10 +285,28 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
/* zero net part of address */
setaddr6part(&local, addr6part(&local) & ~((context->prefix == 64) ? (u64)-1LL : (1LLU << (128 - context->prefix)) - 1LLU));
if ((context->flags &
(CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS)))
do_slaac = 1;
{
do_slaac = 1;
if (context->flags & CONTEXT_DHCP)
{
parm.other = 1;
if (!(context->flags & CONTEXT_RA_STATELESS))
parm.managed = 1;
}
}
else
{
/* don't do RA for non-ra-only unless --enable-ra is set */
if (option_bool(OPT_RA))
{
parm.managed = 1;
parm.other = 1;
}
}
if ((opt = expand(sizeof(struct prefix_opt))))
{
opt->type = ICMP6_OPT_PREFIX;

View File

@@ -337,7 +337,7 @@ unsigned char *skip_questions(struct dns_header *header, size_t plen)
return ansp;
}
static unsigned char *skip_section(unsigned char *ansp, int count, struct dns_header *header, size_t plen)
unsigned char *skip_section(unsigned char *ansp, int count, struct dns_header *header, size_t plen)
{
int i, rdlen;
@@ -360,6 +360,7 @@ static unsigned char *skip_section(unsigned char *ansp, int count, struct dns_he
than CRC the raw bytes, since replies might be compressed differently.
We ignore case in the names for the same reason. Return all-ones
if there is not question section. */
#ifndef HAVE_DNSSEC
unsigned int questions_crc(struct dns_header *header, size_t plen, char *name)
{
int q;
@@ -400,7 +401,7 @@ unsigned int questions_crc(struct dns_header *header, size_t plen, char *name)
return crc;
}
#endif
size_t resize_packet(struct dns_header *header, size_t plen, unsigned char *pheader, size_t hlen)
{
@@ -511,14 +512,17 @@ static size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned
int optno, unsigned char *opt, size_t optlen, int set_do)
{
unsigned char *lenp, *datap, *p;
int rdlen;
int rdlen, is_sign;
if (ntohs(header->arcount) == 0)
if (!(p = find_pseudoheader(header, plen, NULL, NULL, &is_sign)))
{
if (is_sign)
return plen;
/* We are adding the pseudoheader */
if (!(p = skip_questions(header, plen)) ||
!(p = skip_section(p,
ntohs(header->ancount) + ntohs(header->nscount),
ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount),
header, plen)))
return plen;
*p++ = 0; /* empty name */
@@ -531,16 +535,16 @@ static size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned
rdlen = 0;
if (((ssize_t)optlen) > (limit - (p + 4)))
return plen; /* Too big */
header->arcount = htons(1);
header->arcount = htons(ntohs(header->arcount) + 1);
datap = p;
}
else
{
int i, is_sign;
int i;
unsigned short code, len, flags;
/* Must be at the end, if exists */
if (ntohs(header->arcount) != 1 ||
!(p = find_pseudoheader(header, plen, NULL, NULL, &is_sign)) ||
is_sign ||
(!(p = skip_name(p, header, plen, 10))))
return plen;
@@ -597,11 +601,11 @@ static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *p
if (family == parm->l3->sa.sa_family)
{
if (family == AF_INET && memcmp (&parm->l3->in.sin_addr, addrp, INADDRSZ) == 0)
if (family == AF_INET && memcmp(&parm->l3->in.sin_addr, addrp, INADDRSZ) == 0)
match = 1;
#ifdef HAVE_IPV6
else
if (family == AF_INET6 && memcmp (&parm->l3->in6.sin6_addr, addrp, IN6ADDRSZ) == 0)
if (family == AF_INET6 && memcmp(&parm->l3->in6.sin6_addr, addrp, IN6ADDRSZ) == 0)
match = 1;
#endif
}
@@ -750,7 +754,7 @@ int private_net(struct in_addr addr, int ban_localhost)
((ip_addr & 0xFFFF0000) == 0xA9FE0000) /* 169.254.0.0/16 (zeroconf) */ ;
}
static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *header, size_t qlen, char *name)
static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *header, size_t qlen, char *name, int *doctored)
{
int i, qtype, qclass, rdlen;
@@ -795,6 +799,7 @@ static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *
addr.s_addr |= (doctor->out.s_addr & doctor->mask.s_addr);
/* Since we munged the data, the server it came from is no longer authoritative */
header->hb3 &= ~HB3_AA;
*doctored = 1;
memcpy(p, &addr, INADDRSZ);
break;
}
@@ -833,7 +838,7 @@ static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *
return p;
}
static int find_soa(struct dns_header *header, size_t qlen, char *name)
static int find_soa(struct dns_header *header, size_t qlen, char *name, int *doctored)
{
unsigned char *p;
int qtype, qclass, rdlen;
@@ -842,7 +847,7 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name)
/* first move to NS section and find TTL from any SOA section */
if (!(p = skip_questions(header, qlen)) ||
!(p = do_doctor(p, ntohs(header->ancount), header, qlen, name)))
!(p = do_doctor(p, ntohs(header->ancount), header, qlen, name, doctored)))
return 0; /* bad packet */
for (i = ntohs(header->nscount); i != 0; i--)
@@ -877,8 +882,8 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name)
return 0; /* bad packet */
}
/* rewrite addresses in additioal section too */
if (!do_doctor(p, ntohs(header->arcount), header, qlen, NULL))
/* rewrite addresses in additional section too */
if (!do_doctor(p, ntohs(header->arcount), header, qlen, NULL, doctored))
return 0;
if (!found_soa)
@@ -892,7 +897,7 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name)
expired and cleaned out that way.
Return 1 if we reject an address because it look like part of dns-rebinding attack. */
int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t now,
char **ipsets, int is_sign, int check_rebind, int no_cache_dnssec, int secure)
char **ipsets, int is_sign, int check_rebind, int no_cache_dnssec, int secure, int *doctored)
{
unsigned char *p, *p1, *endrr, *namep;
int i, j, qtype, qclass, aqtype, aqclass, ardlen, res, searched_soa = 0;
@@ -907,10 +912,14 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
cache_start_insert();
/* find_soa is needed for dns_doctor and logging side-effects, so don't call it lazily if there are any. */
if (daemon->doctors || option_bool(OPT_LOG))
if (daemon->doctors || option_bool(OPT_LOG) || option_bool(OPT_DNSSEC_VALID))
{
searched_soa = 1;
ttl = find_soa(header, qlen, name);
ttl = find_soa(header, qlen, name, doctored);
#ifdef HAVE_DNSSEC
if (*doctored && secure)
return 0;
#endif
}
/* go through the questions. */
@@ -918,7 +927,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
for (i = ntohs(header->qdcount); i != 0; i--)
{
int found = 0, cname_count = 5;
int found = 0, cname_count = CNAME_CHAIN;
struct crec *cpp = NULL;
int flags = RCODE(header) == NXDOMAIN ? F_NXDOMAIN : 0;
int secflag = secure ? F_DNSSECOK : 0;
@@ -979,8 +988,8 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
if (aqtype == T_CNAME)
{
if (!cname_count--)
return 0; /* looped CNAMES */
if (!cname_count-- || secure)
return 0; /* looped CNAMES, or DNSSEC, which we can't cache. */
goto cname_loop;
}
@@ -999,7 +1008,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
if (!searched_soa)
{
searched_soa = 1;
ttl = find_soa(header, qlen, NULL);
ttl = find_soa(header, qlen, NULL, doctored);
}
if (ttl)
cache_insert(NULL, &addr, now, ttl, name_encoding | F_REVERSE | F_NEG | flags | secflag);
@@ -1056,6 +1065,8 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
if (newc)
{
newc->addr.cname.target.cache = NULL;
/* anything other than zero, to avoid being mistaken for CNAME to interface-name */
newc->addr.cname.uid = 1;
if (cpp)
{
cpp->addr.cname.target.cache = newc;
@@ -1091,7 +1102,10 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
{
ipsets_cur = ipsets;
while (*ipsets_cur)
add_to_ipset(*ipsets_cur++, &addr, flags, 0);
{
log_query((flags & (F_IPV4 | F_IPV6)) | F_IPSET, name, &addr, *ipsets_cur);
add_to_ipset(*ipsets_cur++, &addr, flags, 0);
}
}
#endif
@@ -1115,7 +1129,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
if (!searched_soa)
{
searched_soa = 1;
ttl = find_soa(header, qlen, NULL);
ttl = find_soa(header, qlen, NULL, doctored);
}
/* If there's no SOA to get the TTL from, but there is a CNAME
pointing at this, inherit its TTL */
@@ -1147,7 +1161,6 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
/* If the packet holds exactly one query
return F_IPV4 or F_IPV6 and leave the name from the query in name */
unsigned int extract_request(struct dns_header *header, size_t qlen, char *name, unsigned short *typep)
{
unsigned char *p = (unsigned char *)(header+1);
@@ -1233,7 +1246,7 @@ int check_for_local_domain(char *name, time_t now)
struct ptr_record *ptr;
struct naptr *naptr;
if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_CNAME)) &&
if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_CNAME | F_DS | F_NO_RR)) &&
(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
return 1;
@@ -1366,6 +1379,11 @@ int add_resource_record(struct dns_header *header, char *limit, int *truncp, int
p += INADDRSZ;
break;
case 'b':
usval = va_arg(ap, int);
*p++ = usval;
break;
case 's':
usval = va_arg(ap, int);
PUTSHORT(usval, p);
@@ -1438,37 +1456,51 @@ static unsigned long crec_ttl(struct crec *crecp, time_t now)
/* 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)
struct in_addr local_addr, struct in_addr local_netmask,
time_t now, int *ad_reqd, int *do_bit)
{
char *name = daemon->namebuff;
unsigned char *p, *ansp, *pheader;
int qtype, qclass;
unsigned int qtype, qclass;
struct all_addr addr;
int nameoffset;
unsigned short flag;
int q, ans, anscount = 0, addncount = 0;
int dryrun = 0, sec_reqd = 0;
int dryrun = 0, sec_reqd = 0, have_pseudoheader = 0;
int is_sign;
struct crec *crecp;
int nxdomain = 0, auth = 1, trunc = 0, sec_data = 1;
struct mx_srv_record *rec;
size_t len;
/* Don't return AD set if checking disabled. */
if (header->hb4 & HB4_CD)
sec_data = 0;
/* RFC 6840 5.7 */
*ad_reqd = header->hb4 & HB4_AD;
*do_bit = 0;
/* If there is an RFC2671 pseudoheader then it will be overwritten by
partial replies, so we have to do a dry run to see if we can answer
the query. We check to see if the do bit is set, if so we always
forward rather than answering from the cache, which doesn't include
security information. */
security information, unless we're in DNSSEC validation mode. */
if (find_pseudoheader(header, qlen, NULL, &pheader, &is_sign))
{
unsigned short udpsz, flags;
unsigned char *psave = pheader;
have_pseudoheader = 1;
GETSHORT(udpsz, pheader);
pheader += 2; /* ext_rcode */
GETSHORT(flags, pheader);
sec_reqd = flags & 0x8000; /* do bit */
if ((sec_reqd = flags & 0x8000))
*do_bit = 1;/* do bit */
*ad_reqd = 1;
/* If our client is advertising a larger UDP packet size
than we allow, trim it so that we don't get an overlarge
@@ -1518,10 +1550,20 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
ans = 1;
if (!dryrun)
{
unsigned long ttl = daemon->local_ttl;
int ok = 1;
log_query(F_CONFIG | F_RRNAME, name, NULL, "<TXT>");
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->local_ttl, NULL,
T_TXT, t->class, "t", t->len, t->txt))
/* Dynamically generate stat record */
if (t->stat != 0)
{
ttl = 0;
if (!cache_make_stat(t))
ok = 0;
}
if (ok && add_resource_record(header, limit, &trunc, nameoffset, &ansp,
ttl, NULL,
T_TXT, t->class, "t", t->len, t->txt))
anscount++;
}
@@ -1529,6 +1571,98 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
}
}
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && (qtype == T_DNSKEY || qtype == T_DS))
{
int gotone = 0;
struct blockdata *keydata;
/* Do we have RRSIG? Can't do DS or DNSKEY otherwise. */
if (sec_reqd)
{
crecp = NULL;
while ((crecp = cache_find_by_name(crecp, name, now, F_DNSKEY | F_DS)))
if (crecp->uid == qclass && crecp->addr.sig.type_covered == qtype)
break;
}
if (!sec_reqd || crecp)
{
if (qtype == T_DS)
{
crecp = NULL;
while ((crecp = cache_find_by_name(crecp, name, now, F_DS)))
if (crecp->uid == qclass)
{
gotone = 1;
if (!dryrun)
{
if (crecp->flags & F_NEG)
{
if (crecp->flags & F_NXDOMAIN)
nxdomain = 1;
log_query(F_UPSTREAM, name, NULL, "secure no DS");
}
else if ((keydata = blockdata_retrieve(crecp->addr.ds.keydata, crecp->addr.ds.keylen, NULL)))
{
struct all_addr a;
a.addr.keytag = crecp->addr.ds.keytag;
log_query(F_KEYTAG | (crecp->flags & F_CONFIG), name, &a, "DS keytag %u");
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
crec_ttl(crecp, now), &nameoffset,
T_DS, qclass, "sbbt",
crecp->addr.ds.keytag, crecp->addr.ds.algo,
crecp->addr.ds.digest, crecp->addr.ds.keylen, keydata))
anscount++;
}
}
}
}
else /* DNSKEY */
{
crecp = NULL;
while ((crecp = cache_find_by_name(crecp, name, now, F_DNSKEY)))
if (crecp->uid == qclass)
{
gotone = 1;
if (!dryrun && (keydata = blockdata_retrieve(crecp->addr.key.keydata, crecp->addr.key.keylen, NULL)))
{
struct all_addr a;
a.addr.keytag = crecp->addr.key.keytag;
log_query(F_KEYTAG | (crecp->flags & F_CONFIG), name, &a, "DNSKEY keytag %u");
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
crec_ttl(crecp, now), &nameoffset,
T_DNSKEY, qclass, "sbbt",
crecp->addr.key.flags, 3, crecp->addr.key.algo, crecp->addr.key.keylen, keydata))
anscount++;
}
}
}
}
/* Now do RRSIGs */
if (gotone)
{
ans = 1;
auth = 0;
if (!dryrun && sec_reqd)
{
crecp = NULL;
while ((crecp = cache_find_by_name(crecp, name, now, F_DNSKEY | F_DS)))
if (crecp->uid == qclass && crecp->addr.sig.type_covered == qtype &&
(keydata = blockdata_retrieve(crecp->addr.sig.keydata, crecp->addr.sig.keylen, NULL)))
{
add_resource_record(header, limit, &trunc, nameoffset, &ansp,
crec_ttl(crecp, now), &nameoffset,
T_RRSIG, qclass, "t", crecp->addr.sig.keylen, keydata);
anscount++;
}
}
}
}
#endif
if (qclass == C_IN)
{
struct txt_record *t;
@@ -1619,41 +1753,77 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
}
}
else if ((crecp = cache_find_by_addr(NULL, &addr, now, is_arpa)))
do
{
/* 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;
if (crecp->flags & F_NEG)
{
ans = 1;
auth = 0;
if (crecp->flags & F_NXDOMAIN)
nxdomain = 1;
if (!dryrun)
log_query(crecp->flags & ~F_FORWARD, name, &addr, NULL);
}
else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd)
{
ans = 1;
if (!(crecp->flags & (F_HOSTS | F_DHCP)))
auth = 0;
if (!dryrun)
{
log_query(crecp->flags & ~F_FORWARD, cache_get_name(crecp), &addr,
record_source(crecp->uid));
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
crec_ttl(crecp, now), NULL,
T_PTR, C_IN, "d", cache_get_name(crecp)))
anscount++;
}
}
} while ((crecp = cache_find_by_addr(crecp, &addr, now, is_arpa)));
{
if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) && sec_reqd)
{
if (!option_bool(OPT_DNSSEC_VALID) || ((crecp->flags & F_NEG) && (crecp->flags & F_DNSSECOK)))
crecp = NULL;
#ifdef HAVE_DNSSEC
else if (crecp->flags & F_DNSSECOK)
{
int gotsig = 0;
struct crec *rr_crec = NULL;
while ((rr_crec = cache_find_by_name(rr_crec, name, now, F_DS | F_DNSKEY)))
{
if (rr_crec->addr.sig.type_covered == T_PTR && rr_crec->uid == C_IN)
{
char *sigdata = blockdata_retrieve(rr_crec->addr.sig.keydata, rr_crec->addr.sig.keylen, NULL);
gotsig = 1;
if (!dryrun &&
add_resource_record(header, limit, &trunc, nameoffset, &ansp,
rr_crec->ttd - now, &nameoffset,
T_RRSIG, C_IN, "t", crecp->addr.sig.keylen, sigdata))
anscount++;
}
}
if (!gotsig)
crecp = NULL;
}
#endif
}
if (crecp)
{
do
{
/* 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;
if (crecp->flags & F_NEG)
{
ans = 1;
auth = 0;
if (crecp->flags & F_NXDOMAIN)
nxdomain = 1;
if (!dryrun)
log_query(crecp->flags & ~F_FORWARD, name, &addr, NULL);
}
else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd || option_bool(OPT_DNSSEC_VALID))
{
ans = 1;
if (!(crecp->flags & (F_HOSTS | F_DHCP)))
auth = 0;
if (!dryrun)
{
log_query(crecp->flags & ~F_FORWARD, cache_get_name(crecp), &addr,
record_source(crecp->uid));
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
crec_ttl(crecp, now), NULL,
T_PTR, C_IN, "d", cache_get_name(crecp)))
anscount++;
}
}
} while ((crecp = cache_find_by_addr(crecp, &addr, now, is_arpa)));
}
}
else if (is_rev_synth(is_arpa, &addr, name))
{
ans = 1;
@@ -1774,7 +1944,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
}
cname_restart:
if ((crecp = cache_find_by_name(NULL, name, now, flag | F_CNAME)))
if ((crecp = cache_find_by_name(NULL, name, now, flag | F_CNAME | (dryrun ? F_NO_RR : 0))))
{
int localise = 0;
@@ -1793,72 +1963,119 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
} while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME)));
crecp = save;
}
do
{
/* 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)))
break;
if (!(crecp->flags & F_DNSSECOK))
sec_data = 0;
if (crecp->flags & F_CNAME)
/* If the client asked for DNSSEC and we can't provide RRSIGs, either
because we've not doing DNSSEC or the cached answer is signed by negative,
don't answer from the cache, forward instead. */
if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) && sec_reqd)
{
if (!option_bool(OPT_DNSSEC_VALID) || ((crecp->flags & F_NEG) && (crecp->flags & F_DNSSECOK)))
crecp = NULL;
#ifdef HAVE_DNSSEC
else if (crecp->flags & F_DNSSECOK)
{
char *cname_target = cache_get_cname_target(crecp);
/* We're returning validated data, need to return the RRSIG too. */
struct crec *rr_crec = NULL;
int sigtype = type;
/* The signature may have expired even though the data is still in cache,
forward instead of answering from cache if so. */
int gotsig = 0;
if (!dryrun)
if (crecp->flags & F_CNAME)
sigtype = T_CNAME;
while ((rr_crec = cache_find_by_name(rr_crec, name, now, F_DS | F_DNSKEY)))
{
log_query(crecp->flags, name, NULL, record_source(crecp->uid));
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
crec_ttl(crecp, now), &nameoffset,
T_CNAME, C_IN, "d", cname_target))
anscount++;
if (rr_crec->addr.sig.type_covered == sigtype && rr_crec->uid == C_IN)
{
char *sigdata = blockdata_retrieve(rr_crec->addr.sig.keydata, rr_crec->addr.sig.keylen, NULL);
gotsig = 1;
if (!dryrun &&
add_resource_record(header, limit, &trunc, nameoffset, &ansp,
rr_crec->ttd - now, &nameoffset,
T_RRSIG, C_IN, "t", rr_crec->addr.sig.keylen, sigdata))
anscount++;
}
}
strcpy(name, cname_target);
/* check if target interface_name */
if (crecp->addr.cname.uid == -1)
goto intname_restart;
else
goto cname_restart;
if (!gotsig)
crecp = NULL;
}
if (crecp->flags & F_NEG)
{
ans = 1;
auth = 0;
if (crecp->flags & F_NXDOMAIN)
nxdomain = 1;
if (!dryrun)
log_query(crecp->flags, name, NULL, NULL);
}
else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd)
{
/* If we are returning local answers depending on network,
filter here. */
if (localise &&
(crecp->flags & F_HOSTS) &&
!is_same_net(*((struct in_addr *)&crecp->addr), local_addr, local_netmask))
continue;
if (!(crecp->flags & (F_HOSTS | F_DHCP)))
auth = 0;
ans = 1;
if (!dryrun)
{
log_query(crecp->flags & ~F_REVERSE, name, &crecp->addr.addr,
record_source(crecp->uid));
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
crec_ttl(crecp, now), NULL, type, C_IN,
type == T_A ? "4" : "6", &crecp->addr))
anscount++;
}
}
} while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME)));
#endif
}
if (crecp)
do
{
/* 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)))
break;
if (!(crecp->flags & F_DNSSECOK))
sec_data = 0;
if (crecp->flags & F_CNAME)
{
char *cname_target = cache_get_cname_target(crecp);
if (!dryrun)
{
log_query(crecp->flags, name, NULL, record_source(crecp->uid));
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
crec_ttl(crecp, now), &nameoffset,
T_CNAME, C_IN, "d", cname_target))
anscount++;
}
strcpy(name, cname_target);
/* check if target interface_name */
if (crecp->addr.cname.uid == SRC_INTERFACE)
goto intname_restart;
else
goto cname_restart;
}
if (crecp->flags & F_NEG)
{
/* We don't cache NSEC records, so if a DNSSEC-validated negative answer
is cached and the client wants DNSSEC, forward rather than answering from the cache */
if (!sec_reqd || !(crecp->flags & F_DNSSECOK))
{
ans = 1;
auth = 0;
if (crecp->flags & F_NXDOMAIN)
nxdomain = 1;
if (!dryrun)
log_query(crecp->flags, name, NULL, NULL);
}
}
else
{
/* If we are returning local answers depending on network,
filter here. */
if (localise &&
(crecp->flags & F_HOSTS) &&
!is_same_net(*((struct in_addr *)&crecp->addr), local_addr, local_netmask))
continue;
if (!(crecp->flags & (F_HOSTS | F_DHCP)))
auth = 0;
ans = 1;
if (!dryrun)
{
log_query(crecp->flags & ~F_REVERSE, name, &crecp->addr.addr,
record_source(crecp->uid));
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
crec_ttl(crecp, now), NULL, type, C_IN,
type == T_A ? "4" : "6", &crecp->addr))
anscount++;
}
}
} while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME)));
}
else if (is_name_synthetic(flag, name, &addr))
{
@@ -1876,7 +2093,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (qtype == T_CNAME || qtype == T_ANY)
{
if ((crecp = cache_find_by_name(NULL, name, now, F_CNAME)) &&
(qtype == T_CNAME || (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))))
(qtype == T_CNAME || (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG | (dryrun ? F_NO_RR : 0)))))
{
if (!(crecp->flags & F_DNSSECOK))
sec_data = 0;
@@ -1892,7 +2109,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
}
}
}
if (qtype == T_MX || qtype == T_ANY)
{
int found = 0;
@@ -1915,7 +2132,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
}
if (!found && (option_bool(OPT_SELFMX) || option_bool(OPT_LOCALMX)) &&
cache_find_by_name(NULL, name, now, F_HOSTS | F_DHCP))
cache_find_by_name(NULL, name, now, F_HOSTS | F_DHCP | F_NO_RR))
{
ans = 1;
if (!dryrun)
@@ -2060,12 +2277,6 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (trunc)
header->hb3 |= HB3_TC;
header->hb4 &= ~HB4_AD;
if (option_bool(OPT_DNSSEC_VALID) || option_bool(OPT_DNSSEC_PROXY))
if (sec_data)
header->hb4 |= HB4_AD;
if (nxdomain)
SET_RCODE(header, NXDOMAIN);
else
@@ -2073,6 +2284,17 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
header->ancount = htons(anscount);
header->nscount = htons(0);
header->arcount = htons(addncount);
return ansp - (unsigned char *)header;
len = ansp - (unsigned char *)header;
if (have_pseudoheader)
len = add_pseudoheader(header, len, (unsigned char *)limit, 0, NULL, 0, sec_reqd);
if (*ad_reqd && sec_data)
header->hb4 |= HB4_AD;
else
header->hb4 &= ~HB4_AD;
return len;
}

View File

@@ -962,6 +962,8 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
case DHCPDISCOVER:
if (ignore || have_config(config, CONFIG_DISABLE))
{
if (option_bool(OPT_QUIET_DHCP))
return 0;
message = _("ignored");
opt = NULL;
}

View File

@@ -482,66 +482,6 @@ int parse_hex(char *in, unsigned char *out, int maxlen,
return i;
}
#ifdef HAVE_DNSSEC
static int charval(char c)
{
if (c >= 'A' && c <= 'Z')
return c - 'A';
if (c >= 'a' && c <= 'z')
return c - 'a' + 26;
if (c >= '0' && c <= '9')
return c - '0' + 52;
if (c == '+')
return 62;
if (c == '/')
return 63;
if (c == '=')
return -1;
return -2;
}
int parse_base64(char *in, char *out)
{
char *p = out;
int i, val[4];
while (*in)
{
for (i = 0; i < 4; i++)
{
while (*in == ' ')
in++;
if (*in == 0)
return -1;
if ((val[i] = charval(*in++)) == -2)
return -1;
}
while (*in == ' ')
in++;
if (val[1] == -1)
return -1; /* too much padding */
*p++ = (val[0] << 2) | (val[1] >> 4);
if (val[2] != -1)
*p++ = (val[1] << 4) | ( val[2] >> 2);
if (val[3] != -1)
*p++ = (val[2] << 6) | val[3];
}
return p - out;
}
#endif
/* return 0 for no match, or (no matched octets) + 1 */
int memcmp_masked(unsigned char *a, unsigned char *b, int len, unsigned int mask)
{

9
trust-anchors.conf Normal file
View File

@@ -0,0 +1,9 @@
# The root DNSSEC trust anchor, valid as at 30/01/2014
# Note that this is a DS record (ie a hash of the root Zone Signing Key)
# If was downloaded from https://data.iana.org/root-anchors/root-anchors.xml
trust-anchor=.,19036,8,2,49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5