Compare commits

...

189 Commits

Author SHA1 Message Date
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
c3a04081ff [fd00::} and [fe80::] special addresses in DHCPv6 options. 2014-01-11 22:18:19 +00:00
Simon Kelley
ae76242fdf Fix missing RA RDNS option with --dhcp-option=option6:23,[::] 2014-01-10 18:15:16 +00:00
Simon Kelley
4f04476e3b Set AD bit for address replies from /etc/hosts &c 2014-01-10 12:20:38 +00:00
Simon Kelley
1486a9c7f2 Furthet tweak to RRset sort. 2014-01-10 11:44:26 +00:00
Simon Kelley
5ada888507 RFC 4035 5.3.2 wildcard label rules. 2014-01-09 22:25:03 +00:00
Simon Kelley
5f8e58f49b DNSSEC consolidation. 2014-01-09 17:31:19 +00:00
Simon Kelley
b8071a849a Tweak blockdata accounting. 2014-01-09 09:41:33 +00:00
Simon Kelley
b6e9e7c32d Handle digest lengths greater than 1 block. 2014-01-08 21:21:20 +00:00
Simon Kelley
0435d041ea AD into cache fixes. 2014-01-08 18:22:37 +00:00
Simon Kelley
795501bc86 AD bit handling when doing validation. 2014-01-08 18:11:55 +00:00
Simon Kelley
c2207688c0 Memory stats for DNSSEC. 2014-01-08 18:04:20 +00:00
Simon Kelley
98c098bfc7 Move blockdata to it's own file. 2014-01-08 17:31:16 +00:00
Simon Kelley
c47e3ba446 Update copyright for 2014. 2014-01-08 17:07:54 +00:00
Simon Kelley
f1668d2786 New source port for DNSSEC-originated queries. 2014-01-08 16:53:27 +00:00
Simon Kelley
7d7b7b31e5 DNSSEC for TCP queries. 2014-01-08 15:57:36 +00:00
Simon Kelley
3ddacb86e9 Ensure cache is big enough to do DNSSEC. 2014-01-08 14:32:03 +00:00
Simon Kelley
60b68069cf Rationalise DNS packet-buffer size calculations. 2014-01-08 12:10:28 +00:00
Simon Kelley
871417d45d Handle truncated replies in DNSSEC validation. 2014-01-08 11:22:32 +00:00
Simon Kelley
65d1e3bb9b Tweak libraries and make DNSSEC compile optional. 2014-01-08 11:00:01 +00:00
Simon Kelley
0fc2f31368 First functional DNSSEC - highly alpha. 2014-01-08 10:26:58 +00:00
Simon Kelley
c3e0b9b6e7 backup 2013-12-31 13:50:39 +00:00
Simon Kelley
6ea1f23b3f Send correct O and M bits when advertising only deprecated prefixes. 2013-12-19 15:45:12 +00:00
Simon Kelley
963c380d13 Merge branch 'master' into dnssec 2013-12-18 17:45:31 +00:00
Simon Kelley
00238fb019 indentation fix. 2013-12-18 13:24:12 +00:00
Simon Kelley
74e6b52011 Typo in contributor name. Sorry. 2013-12-17 21:33:53 +00:00
Simon Kelley
875b8160f6 Remove unused code. 2013-12-17 17:40:32 +00:00
Simon Kelley
76ff440ebe Ignore ",," in dhcp-host, rather than treating it as ",0," 2013-12-17 16:29:14 +00:00
Simon Kelley
8db957dfbf Fix endless loop with some bogu-nxdomain. Another F_CONFIG botch. 2013-12-17 15:47:10 +00:00
Simon Kelley
9d633048fe Saving progress 2013-12-13 15:36:55 +00:00
Simon Kelley
a9b55837dc Merge branch 'master' into dnssec 2013-12-12 14:53:46 +00:00
Simon Kelley
c352dd8f1a Merge branch 'master' into dnssec 2013-12-12 12:16:17 +00:00
Simon Kelley
3a2371527f Commit to allow master merge. 2013-12-12 12:15:50 +00:00
Simon Kelley
1ee9be4c3f Implement dynamic interface discovery on *BSD 2013-12-09 16:50:19 +00:00
Simon Kelley
65c9b48921 Merge branch 'master' into dnssec 2013-11-17 12:34:04 +00:00
Simon Kelley
e597dba7ec Merge branch 'master' into dnssec 2013-11-15 11:29:21 +00:00
Simon Kelley
06e54b823e Merge branch 'master' into dnssec 2013-11-14 10:39:40 +00:00
Simon Kelley
5a4120dbfb Merge branch 'master' into dnssec
Conflicts:
	src/dnsmasq.h
	src/forward.c
	src/option.c
2013-10-25 13:16:27 +01:00
Simon Kelley
687bac22db Tidy rebase 2013-08-20 15:41:26 +01:00
Giovanni Bajo
8d41ebd8a3 Add copyright banners 2013-08-20 15:41:26 +01:00
Giovanni Bajo
4631dbf68c DSA-NSEC3-SHA1 is an alias of DSA for signature verification. 2013-08-20 15:41:26 +01:00
Simon Kelley
4f9aefc753 Don't fight over namespace with re-implementation of strchrnul() 2013-08-20 15:41:26 +01:00
Giovanni Bajo
4b5287005f Again make errors greppable. 2013-08-20 15:41:25 +01:00
Giovanni Bajo
5c32841934 Implement RSA-SHA512. 2013-08-20 15:41:25 +01:00
Giovanni Bajo
ccd1d32c3a Make testsuite errors greppable. 2013-08-20 15:41:25 +01:00
Giovanni Bajo
75ffc9bf15 Implement RSA-MD5. 2013-08-20 15:41:25 +01:00
Giovanni Bajo
3af1ea8cbc Simplify abstraction of verification algorithms (it was too flexible) 2013-08-20 15:41:25 +01:00
Giovanni Bajo
1f0dc5835b Implement DSA-SHA1 verification algorithm. 2013-08-20 15:41:25 +01:00
Giovanni Bajo
ed1fc98595 Untangle digestalg from verifyalg; better separation, less code duplication. 2013-08-20 15:41:25 +01:00
Giovanni Bajo
b58fb39f24 Since extract_name() does not convert to lowercase, do it temporarly within convert_domain_to_wire(). 2013-08-20 15:41:25 +01:00
Giovanni Bajo
0304d28f7e Parse and match DS records. 2013-08-20 15:41:25 +01:00
Giovanni Bajo
f5adbb90a1 Implement digest algorithm support. 2013-08-20 15:41:24 +01:00
Giovanni Bajo
32b826e2a0 Genericize verifyalg_add_data_domain() (rename to convert_domain_to_wire()). 2013-08-20 15:41:24 +01:00
Giovanni Bajo
0937692dc6 Add rdata description for MX. 2013-08-20 15:41:24 +01:00
Giovanni Bajo
785ee80b93 Describe SOA rdata section. 2013-08-20 15:41:24 +01:00
Giovanni Bajo
f119ed382e Simplify verifyalg_add_rdata() with new canonicalization functions. 2013-08-20 15:41:24 +01:00
Giovanni Bajo
da23c4f960 Simplify rrset_canonical_order() with new canonicalization functions. 2013-08-20 15:41:24 +01:00
Giovanni Bajo
4885d57c58 Add rdata canonicalization functions. 2013-08-20 15:41:24 +01:00
Giovanni Bajo
0db0e0c216 Fix a bug in rdlen update while decompressing a name 2013-08-20 15:41:24 +01:00
Giovanni Bajo
ec2962eacb Fix the macro names. 2013-08-20 15:41:23 +01:00
Giovanni Bajo
0ca895f585 Fix rrset_canonical_order() to correct handle canonicalization of domain names in RDATA. 2013-08-20 15:41:23 +01:00
Giovanni Bajo
6299ffbe60 Start refactoring for correct handling of domain wire-format.
Introduce utility functions and RDATA meta-description.
2013-08-20 15:41:23 +01:00
Giovanni Bajo
7f0485cf53 verifyalg_add_data_domain: fix for root domain (""). 2013-08-20 15:41:23 +01:00
Giovanni Bajo
02bff4f109 Implement RSASHA256. 2013-08-20 15:41:23 +01:00
Giovanni Bajo
d1ca25ca7e Canonicalize NS records. 2013-08-20 15:41:23 +01:00
Giovanni Bajo
23c2176681 Process RRSIGs also in authority and additional sections. 2013-08-20 15:41:23 +01:00
Giovanni Bajo
e83297d0f6 RSASHA1-NSEC3-SHA1 is equivalent to RSASHA1 for the purpose of RRSIG validation. 2013-08-20 15:41:23 +01:00
Giovanni Bajo
41de7442d2 Reformat some code (no semantic difference). 2013-08-20 15:41:23 +01:00
Giovanni Bajo
0852d76b58 Start implementing canonicalization of RDATA wire formats. 2013-08-20 15:41:22 +01:00
Giovanni Bajo
a55ce08cc0 Silence a few warnings. 2013-08-20 15:41:22 +01:00
Giovanni Bajo
dd090561bf Convert to C-style comments. 2013-08-20 15:41:22 +01:00
Giovanni Bajo
28f04fd647 Remove unused variable. 2013-08-20 15:41:22 +01:00
Giovanni Bajo
50a96b62f1 Fix a validation bug when owner != signer.
Since owner and signer are both domain names and share the same
buffer in memory (daemon->namebuff), we need to go through a little
hoop to make sure one doesn't step on the other's toes. We don't
really need to extract the signer name until we have finished
calculating the hash of the RRset, so we postpone its extraction.
2013-08-20 15:41:22 +01:00
Giovanni Bajo
00b963ab72 Improve logging message. 2013-08-20 15:41:22 +01:00
Giovanni Bajo
79333a2498 Fix a bug in extract_name_no_compression.
When the maxlen was exactly equal to the length of the string,
the function was returning 0 because the end-of-buffer check was
misplaced.
2013-08-20 15:41:22 +01:00
Giovanni Bajo
32f82c62c8 Export skip_name function. 2013-08-20 15:41:21 +01:00
Giovanni Bajo
4e076d746f Debug function. 2013-08-20 15:41:21 +01:00
Giovanni Bajo
13e435ebca Bugfix: domain names must go through hash function in DNS format (but uncompressed!) 2013-08-20 15:41:21 +01:00
Giovanni Bajo
4b0eecbb44 Bugfix: rdata flags must go through hash function in network byte order. 2013-08-20 15:41:21 +01:00
Giovanni Bajo
0360a524df Implement RSA verification. 2013-08-20 15:41:21 +01:00
Giovanni Bajo
262ac85107 verify() function must take a keydata chained buffer for input key. 2013-08-20 15:41:21 +01:00
Giovanni Bajo
4c70046d93 Move helper functions to common header file. 2013-08-20 15:41:21 +01:00
Giovanni Bajo
458824dcb4 Helper function to walk through keydata chained blocks. 2013-08-20 15:41:21 +01:00
Giovanni Bajo
a7338645d7 Add a FIXME for missing logic. 2013-08-20 15:41:21 +01:00
Giovanni Bajo
776fd04754 Add cast to silence warning. 2013-08-20 15:41:20 +01:00
Giovanni Bajo
20bccd499f Rework the loop a little (no functionality changes) 2013-08-20 15:41:20 +01:00
Giovanni Bajo
708bcd2dd3 Call valg verify functions (unimplemented for now) 2013-08-20 15:41:20 +01:00
Giovanni Bajo
d0edff7d6e Insert all DNSKEY/DS records into cache in one transaction. 2013-08-20 15:41:20 +01:00
Giovanni Bajo
ccca70cb33 Change some logging messages. 2013-08-20 15:41:20 +01:00
Giovanni Bajo
0d829ebc69 Skip non-signing keys 2013-08-20 15:41:20 +01:00
Giovanni Bajo
4137b84e4e Postpone RRSIG processing after all DNSKEY/DS have been parsed. 2013-08-20 15:41:20 +01:00
Giovanni Bajo
e6c2a670fe Before using a key for validation, also verify that algorithm matches. 2013-08-20 15:41:20 +01:00
Giovanni Bajo
47f99dd2b3 Fix argument in dnssec_parsekey() call. 2013-08-20 15:41:20 +01:00
Giovanni Bajo
6759b99e28 Add function to extract algorithm number from context. 2013-08-20 15:41:20 +01:00
Giovanni Bajo
3471f18130 Start parsing DNSKEY records and insert them into cache. 2013-08-20 15:41:20 +01:00
Giovanni Bajo
2ef843dd16 extract_name_no_compression: strip trailing dot. 2013-08-20 15:41:19 +01:00
Giovanni Bajo
ce2a0f5a6a Macros to simplify tentative parsing. 2013-08-20 15:41:19 +01:00
Giovanni Bajo
adca3e9c4b Refactor to use new VerifyAlg context, and start implementing logic for querying DNSKEYs. 2013-08-20 15:41:19 +01:00
Giovanni Bajo
366dfcb907 Explicitize the context of verification algorithm. 2013-08-20 15:41:19 +01:00
Giovanni Bajo
28c625572b Move general macros in dnsmasq.h 2013-08-20 15:41:19 +01:00
Giovanni Bajo
02f9b76418 Rename key cache field. 2013-08-20 15:41:19 +01:00
Giovanni Bajo
ba8badd6df Fix bug in keydata_alloc() 2013-08-20 15:41:19 +01:00
Giovanni Bajo
0decc869ae Fix rrset qsort comparison function. 2013-08-20 15:41:19 +01:00
Giovanni Bajo
b573aebc09 Add skeleton for RSASHA256. 2013-08-20 15:41:19 +01:00
Giovanni Bajo
d31d057aa3 Remove useless endian-conversion after GETLONG(). 2013-08-20 15:41:18 +01:00
Giovanni Bajo
6445c8ed73 Fix off-by-one in iteration. 2013-08-20 15:41:18 +01:00
Giovanni Bajo
382e38f494 Specify the correct place where to canonicalize RR within RRset. 2013-08-20 15:41:18 +01:00
Giovanni Bajo
9940aba9f6 Initial openssl RSASHA1 implementation (only SHA1 for now). 2013-08-20 15:41:18 +01:00
Giovanni Bajo
7e846b9858 Add openssl support to build machinery. 2013-08-20 15:41:18 +01:00
Giovanni Bajo
d322de0613 Further abstract API of verify crypto. 2013-08-20 15:41:18 +01:00
Giovanni Bajo
b98f771519 Filter out invalid characters in domain names. 2013-08-20 15:41:18 +01:00
Giovanni Bajo
c7a93f6e4e Skip trailing \0 in domain name. 2013-08-20 15:41:18 +01:00
Giovanni Bajo
970ce22b68 Augment verify algorithm table. 2013-08-20 15:41:18 +01:00
Giovanni Bajo
e292e93d35 Initial dnssec structure. 2013-08-20 15:41:18 +01:00
Giovanni Bajo
fa164d459f DNSSEC validation require EDNS0, force larger packet size. 2013-08-20 15:41:17 +01:00
Giovanni Bajo
f53c79c01b Externalize dns parsing functions. 2013-08-20 15:41:17 +01:00
Giovanni Bajo
7dbe193bee Add run-time options to activate dnssec validation. 2013-08-20 15:41:17 +01:00
Giovanni Bajo
a669f012dd Add dnssec RR types 2013-08-20 15:39:44 +01:00
Giovanni Bajo
237724c0c7 Rename existing DNSSEC macros into DNSSEC_PROXY. 2013-08-20 15:39:44 +01:00
Giovanni Bajo
53f84c7f62 Add compile-time macro for DNSSEC support. 2013-08-20 15:39:43 +01:00
45 changed files with 3609 additions and 603 deletions

View File

@@ -1,3 +1,68 @@
version 2.69
Implement dynamic interface discovery on *BSD. This allows
the contructor: syntax to be used in dhcp-range for DHCPv6
on the BSD platform. Thanks to Matthias Andree for
valuable research on how to implement this.
Fix infinite loop associated with some --bogus-nxdomain
configs. Thanks fogobogo for the bug report.
Fix missing RA RDNS option with configuration like
--dhcp-option=option6:23,[::] Thanks to Tsachi Kimeldorfer
for spotting the problem.
Add [fd00::] and [fe80::] as special addresses in DHCPv6
options, analogous to [::]. [fd00::] is replaced with the
actual ULA of the interface on the machine running
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 to over a megabyte, but
saves the size of the shared libraries which are five
times that size.
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.
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.
version 2.68
Use random addresses for DHCPv6 temporary address
allocations, instead of algorithmically determined stable

View File

@@ -1,4 +1,4 @@
# dnsmasq is Copyright (c) 2000-2013 Simon Kelley
# dnsmasq is Copyright (c) 2000-2014 Simon Kelley
#
# 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
@@ -51,30 +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`
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
dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.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)" \
build_libs="$(dbus_libs) $(idn_libs) $(ct_libs) $(lua_libs) $(sunos_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 :
@@ -97,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

@@ -8,7 +8,8 @@ LOCAL_SRC_FILES := bpf.c cache.c dbus.c dhcp.c dnsmasq.c \
netlink.c network.c option.c rfc1035.c \
rfc2131.c tftp.c util.c conntrack.c \
dhcp6.c rfc3315.c dhcp-common.c outpacket.c \
radv.c slaac.c auth.c ipset.c domain.c
radv.c slaac.c auth.c ipset.c domain.c \
dnssec.c dnssec-openssl.c blockdata.c
LOCAL_MODULE := dnsmasq

View File

@@ -2,10 +2,31 @@
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="$*"
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;

8
debian/changelog vendored
View File

@@ -1,7 +1,13 @@
dnsmasq (2.69-1) unstable; urgency=low
* New upstream.
-- 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

34
debian/rules vendored
View File

@@ -17,7 +17,7 @@ CFLAGS += -Wall -W
LDFLAGS = $(shell dpkg-buildflags --get LDFLAGS)
COPTS =
DEB_COPTS = $(COPTS)
TARGET = install-i18n
@@ -29,52 +29,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 +126,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 +173,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,10 @@
# 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
# Uncomment this to filter useless windows-originated DNS requests
# which can trigger dial-on-demand links needlessly.

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
@@ -579,19 +582,35 @@ 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.
.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 --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
@@ -917,9 +936,11 @@ and to set the time-server address to 192.168.0.4, do
.B --dhcp-option = 42,192.168.0.4
or
.B --dhcp-option = option:ntp-server, 192.168.0.4
The special address 0.0.0.0 (or [::] for DHCPv6) is taken to mean "the address of the
machine running dnsmasq". Data types allowed are comma separated
dotted-quad IP addresses, a decimal number, colon-separated hex digits
The special address 0.0.0.0 is taken to mean "the address of the
machine running dnsmasq".
Data types allowed are comma separated
dotted-quad IPv4 addresses, []-wrapped IPv6 addresses, a decimal number, colon-separated hex digits
and a text string. If the optional tags are given then
this option is only sent when all the tags are matched.
@@ -935,7 +956,9 @@ keyword, followed by the option number or option name. The IPv6 option
name space is disjoint from the IPv4 option name space. IPv6 addresses
in options must be bracketed with square brackets, eg.
.B --dhcp-option=option6:ntp-server,[1234::56]
For IPv6, [::] means "the global address of
the machine running dnsmasq", whilst [fd00::] is replaced with the
ULA, if it exists, and [fe80::] with the link-local address.
Be careful: no checking is done that the correct type of data for the
option number is sent, it is quite possible to

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
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

146
src/blockdata.c Normal file
View File

@@ -0,0 +1,146 @@
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
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
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, 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)
{
struct blockdata *block, *ret = NULL;
struct blockdata **prev = &ret;
size_t blen;
while (len > 0)
{
if (!keyblock_free)
blockdata_expand(50);
if (keyblock_free)
{
block = keyblock_free;
keyblock_free = block->next;
blockdata_count++;
}
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);
data += blen;
len -= blen;
*prev = block;
prev = &block->next;
block->next = NULL;
}
return ret;
}
void blockdata_free(struct blockdata *blocks)
{
struct blockdata *tmp;
if (blocks)
{
for (tmp = blocks; tmp->next; tmp = tmp->next)
blockdata_count--;
tmp->next = keyblock_free;
keyblock_free = blocks;
blockdata_count--;
}
}
/* 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;
void *new, *d;
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;
memcpy(d, b->key, blen);
d += blen;
len -= blen;
}
return data;
}
#endif

108
src/bpf.c
View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
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
@@ -19,9 +19,9 @@
#if defined(HAVE_BSD_NETWORK) || defined(HAVE_SOLARIS_NETWORK)
#include <ifaddrs.h>
#if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
#include <sys/param.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/route.h>
#include <net/if_dl.h>
#include <netinet/if_ether.h>
@@ -29,7 +29,9 @@
# include <net/if_var.h>
#endif
#include <netinet/in_var.h>
#include <netinet6/in6_var.h>
#ifdef HAVE_IPV6
# include <netinet6/in6_var.h>
#endif
#ifndef SA_SIZE
#define SA_SIZE(sa) \
@@ -38,6 +40,13 @@
1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1) ) )
#endif
#ifdef HAVE_BSD_NETWORK
static int del_family = 0;
static struct all_addr del_addr;
#endif
#if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
int arp_enumerate(void *parm, int (*callback)())
{
int mib[6];
@@ -88,7 +97,7 @@ int arp_enumerate(void *parm, int (*callback)())
return 1;
}
#endif
#endif /* defined(HAVE_BSD_NETWORK) && !defined(__APPLE__) */
int iface_enumerate(int family, void *parm, int (*callback)())
@@ -129,6 +138,10 @@ int iface_enumerate(int family, void *parm, int (*callback)())
{
struct in_addr addr, netmask, broadcast;
addr = ((struct sockaddr_in *) addrs->ifa_addr)->sin_addr;
#ifdef HAVE_BSD_NETWORK
if (del_family == AF_INET && del_addr.addr.addr4.s_addr == addr.s_addr)
continue;
#endif
netmask = ((struct sockaddr_in *) addrs->ifa_netmask)->sin_addr;
if (addrs->ifa_broadaddr)
broadcast = ((struct sockaddr_in *) addrs->ifa_broadaddr)->sin_addr;
@@ -146,6 +159,10 @@ int iface_enumerate(int family, void *parm, int (*callback)())
int i, j, prefix = 0;
u32 valid = 0xffffffff, preferred = 0xffffffff;
int flags = 0;
#ifdef HAVE_BSD_NETWORK
if (del_family == AF_INET6 && IN6_ARE_ADDR_EQUAL(&del_addr.addr.addr6, addr))
continue;
#endif
#if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
struct in6_ifreq ifr6;
@@ -226,7 +243,7 @@ int iface_enumerate(int family, void *parm, int (*callback)())
return ret;
}
#endif
#endif /* defined(HAVE_BSD_NETWORK) || defined(HAVE_SOLARIS_NETWORK) */
#if defined(HAVE_BSD_NETWORK) && defined(HAVE_DHCP)
@@ -345,6 +362,87 @@ void send_via_bpf(struct dhcp_packet *mess, size_t len,
while (writev(daemon->dhcp_raw_fd, iov, 4) == -1 && retry_send());
}
#endif /* defined(HAVE_BSD_NETWORK) && defined(HAVE_DHCP) */
#ifdef HAVE_BSD_NETWORK
void route_init(void)
{
/* AF_UNSPEC: all addr families */
daemon->routefd = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC);
if (daemon->routefd == -1 || !fix_fd(daemon->routefd))
die(_("cannot create PF_ROUTE socket: %s"), NULL, EC_BADNET);
}
void route_sock(time_t now)
{
struct if_msghdr *msg;
int rc = recv(daemon->routefd, daemon->packet, daemon->packet_buff_sz, 0);
if (rc < 4)
return;
msg = (struct if_msghdr *)daemon->packet;
if (rc < msg->ifm_msglen)
return;
if (msg->ifm_version != RTM_VERSION)
{
static int warned = 0;
if (!warned)
{
my_syslog(LOG_WARNING, _("Unknown protocol version from route socket"));
warned = 1;
}
}
else if (msg->ifm_type == RTM_NEWADDR)
{
del_family = 0;
newaddress(now);
}
else if (msg->ifm_type == RTM_DELADDR)
{
/* There's a race in the kernel, such that if we run iface_enumerate() immediately
we get a DELADDR event, the deleted address still appears. Here we store the deleted address
in a static variable, and omit it from the set returned by iface_enumerate() */
int mask = ((struct ifa_msghdr *)msg)->ifam_addrs;
int maskvec[] = { RTA_DST, RTA_GATEWAY, RTA_NETMASK, RTA_GENMASK,
RTA_IFP, RTA_IFA, RTA_AUTHOR, RTA_BRD };
int of;
unsigned int i;
for (i = 0, of = sizeof(struct ifa_msghdr); of < rc && i < sizeof(maskvec)/sizeof(maskvec[0]); i++)
if (mask & maskvec[i])
{
struct sockaddr *sa = (struct sockaddr *)((char *)msg + of);
size_t diff = (sa->sa_len != 0) ? sa->sa_len : sizeof(long);
if (maskvec[i] == RTA_IFA)
{
del_family = sa->sa_family;
if (del_family == AF_INET)
del_addr.addr.addr4 = ((struct sockaddr_in *)sa)->sin_addr;
#ifdef HAVE_IPV6
else if (del_family == AF_INET6)
del_addr.addr.addr6 = ((struct sockaddr_in6 *)sa)->sin6_addr;
#endif
else
del_family = 0;
}
of += diff;
/* round up as needed */
if (diff & (sizeof(long) - 1))
of += sizeof(long) - (diff & (sizeof(long) - 1));
}
newaddress(now);
}
}
#endif /* HAVE_BSD_NETWORK */

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
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
@@ -25,9 +25,6 @@ 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;
#ifdef HAVE_DNSSEC
static struct keydata *keyblock_free = NULL;
#endif
/* type->string mapping: this is also used by the name-hash function as a mixing table. */
static const struct {
@@ -56,7 +53,11 @@ static const struct {
{ 38, "A6" },
{ 39, "DNAME" },
{ 41, "OPT" },
{ 43, "DS" },
{ 46, "RRSIG" },
{ 47, "NSEC" },
{ 48, "DNSKEY" },
{ 50, "NSEC3" },
{ 249, "TKEY" },
{ 250, "TSIG" },
{ 251, "IXFR" },
@@ -171,7 +172,22 @@ 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)
blockdata_free(crecp->addr.ds.keydata);
}
#endif
static void cache_free(struct crec *crecp)
{
crecp->flags &= ~F_FORWARD;
@@ -196,9 +212,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))
keydata_free(crecp->addr.key.keydata);
cache_blockdata_free(crecp);
#endif
}
@@ -317,27 +333,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
{
@@ -410,16 +451,16 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
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))
/* Don't log keys here, done elsewhere */
if (flags & (F_IPV4 | F_IPV6 | F_CNAME))
log_query(flags | F_UPSTREAM, name, addr, NULL);
/* 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))
{
@@ -447,14 +488,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
@@ -466,7 +525,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)
@@ -474,13 +533,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--;
}
@@ -535,10 +594,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
@@ -555,6 +617,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))
{
@@ -583,7 +648,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);
@@ -612,7 +677,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;
@@ -620,7 +688,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
@@ -916,12 +984,18 @@ void cache_reload(void)
struct name_list *nl;
struct cname *a;
struct interface_name *intr;
#ifdef HAVE_DNSSEC
struct ds_config *ds;
#endif
cache_inserted = cache_live_freed = 0;
for (i=0; i<hash_size; i++)
for (cache = hash_table[i], up = &hash_table[i]; cache; cache = tmp)
{
#ifdef HAVE_DNSSEC
cache_blockdata_free(cache);
#endif
tmp = cache->hash_next;
if (cache->flags & (F_HOSTS | F_CONFIG))
{
@@ -955,6 +1029,22 @@ void cache_reload(void)
cache_hash(cache);
add_hosts_cname(cache); /* handle chains */
}
#ifdef HAVE_DNSSEC
for (ds = daemon->ds; ds; ds = ds->next)
if ((cache = whine_malloc(sizeof(struct crec))) &&
(cache->addr.ds.keydata = blockdata_alloc(ds->digest, ds->digestlen)))
{
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
/* borrow the packet buffer for a temporary by-address hash */
memset(daemon->packet, 0, daemon->packet_buff_sz);
@@ -1155,6 +1245,7 @@ void cache_add_dhcp_entry(char *host_name, int prot,
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."),
@@ -1164,6 +1255,9 @@ void dump_cache(time_t now)
#ifdef HAVE_AUTH
my_syslog(LOG_INFO, _("queries for authoritative zones %u"), daemon->auth_answer);
#endif
#ifdef HAVE_DNSSEC
blockdata_report();
#endif
/* sum counts from different records for same server */
for (serv = daemon->servers; serv; serv = serv->next)
@@ -1192,35 +1286,43 @@ 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, *p = daemon->namebuff;
p += sprintf(p, "%-40.40s ", cache_get_name(cache));
if ((cache->flags & F_NEG) && (cache->flags & F_FORWARD))
a = "";
else if (cache->flags & F_CNAME)
{
a = "";
if (!is_outdated_cname_pointer(cache))
a = cache_get_cname_target(cache);
}
char *a = daemon->addrbuff, *p = daemon->namebuff, *n = cache_get_name(cache);
*a = 0;
if (strlen(n) == 0 && !(cache->flags & F_REVERSE))
n = "<Root>";
p += sprintf(p, "%-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_DS)
{
if (cache->flags & F_DNSKEY)
{
/* RRSIG */
a = daemon->addrbuff;
sprintf(a, "%5u %3u %s", cache->addr.sig.keytag,
cache->addr.sig.algo, querystr("", cache->addr.sig.type_covered));
}
else
{
a = daemon->addrbuff;
sprintf(a, "%5u %3u %3u", cache->addr.ds.keytag,
cache->addr.ds.algo, cache->addr.ds.digest);
}
}
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 %u", cache->addr.key.flags_or_keyid,
cache->addr.key.algo, cache->addr.key.digest, cache->uid);
sprintf(a, "%5u %3u %3u", cache->addr.key.keytag,
cache->addr.key.algo, cache->addr.key.flags);
}
#endif
else
else if (!(cache->flags & F_NEG) || !(cache->flags & F_FORWARD))
{
a = daemon->addrbuff;
if (cache->flags & F_IPV4)
@@ -1231,12 +1333,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" : " ",
@@ -1271,14 +1382,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)
@@ -1291,13 +1433,20 @@ void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
if (addr)
{
if (flags & F_KEYTAG)
sprintf(daemon->addrbuff, arg, addr->addr.keytag);
else
{
#ifdef HAVE_IPV6
inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
addr, daemon->addrbuff, ADDRSTRLEN);
inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
addr, daemon->addrbuff, ADDRSTRLEN);
#else
strncpy(daemon->addrbuff, inet_ntoa(addr->addr.addr4), ADDRSTRLEN);
strncpy(daemon->addrbuff, inet_ntoa(addr->addr.addr4), ADDRSTRLEN);
#endif
}
}
else
dest = arg;
if (flags & F_REVERSE)
{
@@ -1308,14 +1457,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)
@@ -1339,6 +1481,8 @@ void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
source = arg;
else if (flags & F_UPSTREAM)
source = "reply";
else if (flags & F_SECSTAT)
source = "validation";
else if (flags & F_AUTH)
source = "auth";
else if (flags & F_SERVER)
@@ -1351,6 +1495,11 @@ void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
source = arg;
verb = "from";
}
else if (flags & F_DNSSEC)
{
source = arg;
verb = "to";
}
else
source = "cached";
@@ -1360,50 +1509,4 @@ void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
my_syslog(LOG_INFO, "%s %s %s %s", source, name, verb, dest);
}
#ifdef HAVE_DNSSEC
struct keydata *keydata_alloc(char *data, size_t len)
{
struct keydata *block, *ret = NULL;
struct keydata **prev = &ret;
while (len > 0)
{
if (keyblock_free)
{
block = keyblock_free;
keyblock_free = block->next;
}
else
block = whine_malloc(sizeof(struct keydata));
if (!block)
{
/* failed to alloc, free partial chain */
keydata_free(ret);
return NULL;
}
memcpy(block->key, data, len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len);
data += KEYBLOCK_LEN;
len -= KEYBLOCK_LEN;
*prev = block;
prev = &block->next;
block->next = NULL;
}
return ret;
}
void keydata_free(struct keydata *blocks)
{
struct keydata *tmp;
if (blocks)
{
for (tmp = blocks; tmp->next; tmp = tmp->next);
tmp->next = keyblock_free;
keyblock_free = blocks;
}
}
#endif

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
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
@@ -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 140 /* choose to mininise fragmentation when storing DNSSEC keys */
#define KEYBLOCK_LEN 35 /* 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 */
@@ -124,6 +125,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,11 +140,19 @@ 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 */
/* #define HAVE_DNSSEC */
/* Default locations for important system files. */
@@ -384,7 +398,12 @@ static char *compile_opts =
#ifndef HAVE_AUTH
"no-"
#endif
"auth";
"auth "
#ifndef HAVE_DNSSEC
"no-"
#endif
"DNSSEC";
#endif

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
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

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
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

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
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
@@ -399,13 +399,13 @@ void dhcp_update_configs(struct dhcp_config *configs)
if (cache_find_by_name(crec, config->hostname, 0, cacheflags))
{
/* use primary (first) address */
while (crec && !(crec->flags & F_REVERSE))
crec = cache_find_by_name(crec, config->hostname, 0, cacheflags);
if (!crec)
continue; /* should be never */
inet_ntop(prot, &crec->addr.addr, daemon->addrbuff, ADDRSTRLEN);
my_syslog(MS_DHCP | LOG_WARNING, _("%s has more than one address in hostsfile, using %s for DHCP"),
config->hostname, daemon->addrbuff);
while (crec && !(crec->flags & F_REVERSE))
crec = cache_find_by_name(crec, config->hostname, 0, cacheflags);
if (!crec)
continue; /* should be never */
inet_ntop(prot, &crec->addr.addr, daemon->addrbuff, ADDRSTRLEN);
my_syslog(MS_DHCP | LOG_WARNING, _("%s has more than one address in hostsfile, using %s for DHCP"),
config->hostname, daemon->addrbuff);
}
if (prot == AF_INET &&

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
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

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
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

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
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

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
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
@@ -23,7 +23,7 @@
struct iface_param {
struct dhcp_context *current;
struct dhcp_relay *relay;
struct in6_addr fallback, relay_local;
struct in6_addr fallback, relay_local, ll_addr, ula_addr;
int ind, addr_match;
};
@@ -158,6 +158,8 @@ void dhcp6_packet(time_t now)
parm.ind = if_index;
parm.addr_match = 0;
memset(&parm.fallback, 0, IN6ADDRSZ);
memset(&parm.ll_addr, 0, IN6ADDRSZ);
memset(&parm.ula_addr, 0, IN6ADDRSZ);
for (context = daemon->dhcp6; context; context = context->next)
if (IN6_IS_ADDR_UNSPECIFIED(&context->start6) && context->prefix == 0)
@@ -210,7 +212,7 @@ void dhcp6_packet(time_t now)
lease_prune(NULL, now); /* lose any expired leases */
port = dhcp6_reply(parm.current, if_index, ifr.ifr_name, &parm.fallback,
sz, &from.sin6_addr, now);
&parm.ll_addr, &parm.ula_addr, sz, &from.sin6_addr, now);
lease_update_file(now);
lease_update_dns(0);
@@ -309,6 +311,11 @@ static int complete_context6(struct in6_addr *local, int prefix,
if (if_index == param->ind)
{
if (IN6_IS_ADDR_LINKLOCAL(local))
param->ll_addr = *local;
else if (IN6_IS_ADDR_ULA(local))
param->ula_addr = *local;
if (!IN6_IS_ADDR_LOOPBACK(local) &&
!IN6_IS_ADDR_LINKLOCAL(local) &&
!IN6_IS_ADDR_MULTICAST(local))
@@ -720,7 +727,6 @@ void dhcp_construct_contexts(time_t now)
if (context->flags & CONTEXT_GC && !(context->flags & CONTEXT_OLD))
{
if ((context->flags & (CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS)) ||
option_bool(OPT_RA))
{

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
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
@@ -36,20 +36,38 @@
#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
#define T_NS 2
#define T_NS 2
#define T_MD 3
#define T_MF 4
#define T_CNAME 5
#define T_SOA 6
#define T_MB 7
#define T_MG 8
#define T_MR 9
#define T_PTR 12
#define T_MINFO 14
#define T_MX 15
#define T_TXT 16
#define T_RP 17
#define T_AFSDB 18
#define T_RT 21
#define T_SIG 24
#define T_PX 26
#define T_AAAA 28
#define T_NXT 30
#define T_SRV 33
#define T_NAPTR 35
#define T_KX 36
#define T_DNAME 39
#define T_OPT 41
#define T_DS 43
#define T_RRSIG 46
#define T_NSEC 47
#define T_DNSKEY 48
#define T_TKEY 249
#define T_TSIG 250
#define T_AXFR 252
@@ -59,7 +77,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;
@@ -78,6 +95,8 @@ struct dns_header {
#define HB4_RCODE 0x0f
#define OPCODE(x) (((x)->hb3 & HB3_OPCODE) >> 3)
#define SET_OPCODE(x, code) (x)->hb3 = ((x)->hb3 & ~HB3_OPCODE) | code
#define RCODE(x) ((x)->hb4 & HB4_RCODE)
#define SET_RCODE(x, code) (x)->hb4 = ((x)->hb4 & ~HB4_RCODE) | code
@@ -117,3 +136,8 @@ struct dns_header {
(cp) += 4; \
}
#define CHECK_LEN(header, pp, plen, len) \
((size_t)((pp) - (unsigned char *)(header) + (len)) <= (plen))
#define ADD_RDLEN(header, pp, plen, len) \
(!CHECK_LEN(header, pp, plen, len) ? 0 : (((pp) += (len)), 1))

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
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
@@ -81,15 +81,25 @@ int main (int argc, char **argv)
umask(022); /* known umask, create leases and pid files as 0644 */
read_opts(argc, argv, compile_opts);
if (daemon->edns_pktsz < PACKETSZ)
daemon->edns_pktsz = PACKETSZ;
#ifdef HAVE_DNSSEC
/* Enforce min packet big enough for DNSSEC */
if (option_bool(OPT_DNSSEC_VALID) && daemon->edns_pktsz < EDNS_PKTSZ)
daemon->edns_pktsz = EDNS_PKTSZ;
#endif
daemon->packet_buff_sz = daemon->edns_pktsz > DNSMASQ_PACKETSZ ?
daemon->edns_pktsz : DNSMASQ_PACKETSZ;
daemon->packet = safe_malloc(daemon->packet_buff_sz);
daemon->addrbuff = safe_malloc(ADDRSTRLEN);
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID))
daemon->keyname = safe_malloc(MAXDNAME);
#endif
#ifdef HAVE_DHCP
if (!daemon->lease_file)
@@ -131,6 +141,19 @@ int main (int argc, char **argv)
}
#endif
if (option_bool(OPT_DNSSEC_VALID))
{
#ifdef HAVE_DNSSEC
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))
die(_("TFTP server not available: set HAVE_TFTP in src/config.h"), NULL, EC_BADCONF);
@@ -182,7 +205,7 @@ int main (int argc, char **argv)
daemon->doing_dhcp6 = 1;
if (context->flags & CONTEXT_RA)
daemon->doing_ra = 1;
#ifndef HAVE_LINUX_NETWORK
#if !defined(HAVE_LINUX_NETWORK) && !defined(HAVE_BSD_NETWORK)
if (context->flags & CONTEXT_TEMPLATE)
die (_("dhcp-range constructor not available on this platform"), NULL, EC_BADCONF);
#endif
@@ -220,13 +243,15 @@ int main (int argc, char **argv)
ipset_init();
#endif
#ifdef HAVE_LINUX_NETWORK
#if defined(HAVE_LINUX_NETWORK)
netlink_init();
if (option_bool(OPT_NOWILD) && option_bool(OPT_CLEVERBIND))
die(_("cannot set --bind-interfaces and --bind-dynamic"), NULL, EC_BADCONF);
#elif defined(HAVE_BSD_NETWORK)
route_init();
#endif
if (option_bool(OPT_NOWILD) && option_bool(OPT_CLEVERBIND))
die(_("cannot set --bind-interfaces and --bind-dynamic"), NULL, EC_BADCONF);
if (!enumerate_interfaces(1) || !enumerate_interfaces(0))
die(_("failed to find list of interfaces: %s"), NULL, EC_MISC);
@@ -279,7 +304,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
@@ -628,11 +658,16 @@ int main (int argc, char **argv)
my_syslog(LOG_INFO, _("DBus support enabled: bus connection pending"));
}
#endif
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID))
my_syslog(LOG_INFO, _("DNSSEC validation enabled"));
#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"));
@@ -808,11 +843,14 @@ int main (int argc, char **argv)
}
#endif
#ifdef HAVE_LINUX_NETWORK
#if defined(HAVE_LINUX_NETWORK)
FD_SET(daemon->netlinkfd, &rset);
bump_maxfd(daemon->netlinkfd, &maxfd);
#elif defined(HAVE_BSD_NETWORK)
FD_SET(daemon->routefd, &rset);
bump_maxfd(daemon->routefd, &maxfd);
#endif
FD_SET(piperead, &rset);
bump_maxfd(piperead, &maxfd);
@@ -867,9 +905,12 @@ int main (int argc, char **argv)
warn_bound_listeners();
}
#ifdef HAVE_LINUX_NETWORK
#if defined(HAVE_LINUX_NETWORK)
if (FD_ISSET(daemon->netlinkfd, &rset))
netlink_multicast(now);
#elif defined(HAVE_BSD_NETWORK)
if (FD_ISSET(daemon->routefd, &rset))
route_sock(now);
#endif
/* Check for changes to resolv files once per second max. */
@@ -1304,7 +1345,7 @@ static int set_dns_listeners(time_t now, fd_set *set, int *maxfdp)
/* will we be able to get memory? */
if (daemon->port != 0)
get_new_frec(now, &wait);
get_new_frec(now, &wait, 0);
for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
{

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
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
@@ -14,7 +14,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define COPYRIGHT "Copyright (c) 2000-2013 Simon Kelley"
#define COPYRIGHT "Copyright (c) 2000-2014 Simon Kelley"
#ifndef NO_LARGEFILE
/* Ensure we can use files >2GB (log files may grow this big) */
@@ -50,12 +50,16 @@
#include <getopt.h>
#include "config.h"
#include "ip6addr.h"
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned long long u64;
#define countof(x) (long)(sizeof(x) / sizeof(x[0]))
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#include "dns-protocol.h"
#include "dhcp-protocol.h"
#ifdef HAVE_DHCP6
@@ -213,7 +217,7 @@ struct event_desc {
#define OPT_NO_OVERRIDE 30
#define OPT_NO_REBIND 31
#define OPT_ADD_MAC 32
#define OPT_DNSSEC 33
#define OPT_DNSSEC_PROXY 33
#define OPT_CONSEC_ADDR 34
#define OPT_CONNTRACK 35
#define OPT_FQDN_UPDATE 36
@@ -225,7 +229,10 @@ struct event_desc {
#define OPT_QUIET_DHCP 42
#define OPT_QUIET_DHCP6 43
#define OPT_QUIET_RA 44
#define OPT_LAST 45
#define OPT_DNSSEC_VALID 45
#define OPT_DNSSEC_PERMISS 46
#define OPT_DNSSEC_DEBUG 47
#define OPT_LAST 48
/* 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. */
@@ -238,6 +245,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;
};
@@ -282,6 +295,12 @@ struct cname {
struct cname *next;
};
struct ds_config {
char *name, *digest;
int digestlen, class, algo, keytag, digest_type;
struct ds_config *next;
};
#define ADDRLIST_LITERAL 1
#define ADDRLIST_IPV6 2
@@ -331,8 +350,8 @@ union bigname {
union bigname *next; /* freelist */
};
struct keydata {
struct keydata *next;
struct blockdata {
struct blockdata *next;
unsigned char key[KEYBLOCK_LEN];
};
@@ -349,14 +368,24 @@ struct crec {
int uid; /* -1 if union is interface-name */
} cname;
struct {
struct keydata *keydata;
struct blockdata *keydata;
unsigned short keylen, flags, keytag;
unsigned char algo;
unsigned char digest; /* DS only */
unsigned short flags_or_keyid; /* flags for DNSKEY, keyid for DS */
} 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 if F_DS or F_DNSKEY, index to source for F_HOSTS */
/* used as class if DNSKEY/DS/RRSIG, index to source for F_HOSTS */
int uid;
unsigned short flags;
union {
@@ -391,10 +420,10 @@ struct crec {
#define F_QUERY (1u<<19)
#define F_NOERR (1u<<20)
#define F_AUTH (1u<<21)
/* composites */
#define F_TYPE (F_IPV4 | F_IPV6 | F_DNSKEY | F_DS) /* Only one may be set */
#define F_DNSSEC (1u<<22)
#define F_KEYTAG (1u<<23)
#define F_SECSTAT (1u<<24)
#define F_NO_RR (1u<<25)
/* struct sockaddr is not large enough to hold any address,
@@ -496,9 +525,27 @@ struct hostsfile {
int index; /* matches to cache entries for logging */
};
/* DNSSEC status values. */
#define STAT_SECURE 1
#define STAT_INSECURE 2
#define STAT_BOGUS 3
#define STAT_NEED_DS 4
#define STAT_NEED_KEY 5
#define STAT_TRUNCATED 6
#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
#ifdef HAVE_DNSSEC
#define HASH_SIZE 20 /* SHA-1 digest size */
#else
#define HASH_SIZE sizeof(int)
#endif
struct frec {
union mysockaddr source;
@@ -511,8 +558,15 @@ struct frec {
unsigned int iface;
unsigned short orig_id, new_id;
int fd, forwardall, flags;
unsigned int crc;
time_t time;
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 */
struct frec *blocking_query; /* Query which is blocking us. */
#endif
struct frec *next;
};
@@ -561,7 +615,7 @@ struct dhcp_lease {
struct in6_addr addr6;
int iaid;
struct slaac_address {
struct in6_addr addr, local;
struct in6_addr addr;
time_t ping_time;
int backoff; /* zero -> confirmed */
struct slaac_address *next;
@@ -875,11 +929,17 @@ extern struct daemon {
#ifdef OPTION6_PREFIX_CLASS
struct prefix_class *prefix_classes;
#endif
#ifdef HAVE_DNSSEC
struct ds_config *ds;
#endif
/* globally used stuff for DNS */
char *packet; /* packet buffer */
int packet_buff_sz; /* size of above */
char *namebuff; /* MAXDNAME size buffer */
#ifdef HAVE_DNSSEC
char *keyname; /* MAXDNAME size buffer */
#endif
unsigned int local_answer, queries_forwarded, auth_answer;
struct frec *frec_list;
struct serverfd *sfds;
@@ -900,7 +960,7 @@ extern struct daemon {
#if defined(HAVE_LINUX_NETWORK)
int netlinkfd;
#elif defined(HAVE_BSD_NETWORK)
int dhcp_raw_fd, dhcp_icmp_fd;
int dhcp_raw_fd, dhcp_icmp_fd, routefd;
#endif
struct iovec dhcp_packet;
char *dhcp_buff, *dhcp_buff2, *dhcp_buff3;
@@ -932,12 +992,12 @@ extern struct daemon {
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 *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,
@@ -950,9 +1010,14 @@ void dump_cache(time_t now);
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
struct keydata *keydata_alloc(char *data, size_t len);
void keydata_free(struct keydata *blocks);
void blockdata_init(void);
void blockdata_report(void);
struct blockdata *blockdata_alloc(char *data, size_t len);
void *blockdata_retrieve(struct blockdata *block, size_t len, void *data);
void blockdata_free(struct blockdata *blocks);
#endif
/* domain.c */
@@ -964,6 +1029,10 @@ int is_name_synthetic(int flags, char *name, struct all_addr *addr);
int is_rev_synth(int flag, struct all_addr *addr, char *name);
/* rfc1035.c */
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 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,
@@ -971,9 +1040,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 checking_disabled);
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 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,
@@ -984,6 +1054,9 @@ size_t resize_packet(struct dns_header *header, size_t plen,
unsigned char *pheader, size_t hlen);
size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysockaddr *l3);
size_t add_source_addr(struct dns_header *header, size_t plen, char *limit, union mysockaddr *source);
#ifdef HAVE_DNSSEC
size_t add_do_bit(struct dns_header *header, size_t plen, char *limit);
#endif
int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer);
int add_resource_record(struct dns_header *header, char *limit, int *truncp,
int nameoffset, unsigned char **pp, unsigned long ttl,
@@ -1001,6 +1074,14 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen,
int in_zone(struct auth_zone *zone, char *name, char **cut);
#endif
/* dnssec.c */
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 dnskey_keytag(int alg, int flags, unsigned char *rdata, int rdlen);
unsigned char* hash_questions(struct dns_header *header, size_t plen, char *name);
/* util.c */
void rand_init(void);
unsigned short rand16(void);
@@ -1061,7 +1142,7 @@ void receive_query(struct listener *listen, time_t now);
unsigned char *tcp_request(int confd, time_t now,
union mysockaddr *local_addr, struct in_addr netmask, int auth_dns);
void server_gone(struct server *server);
struct frec *get_new_frec(time_t now, int *wait);
struct frec *get_new_frec(time_t now, int *wait, int force);
int send_from(int fd, int nowild, char *packet, size_t len,
union mysockaddr *to, struct all_addr *source,
unsigned int iface);
@@ -1090,6 +1171,10 @@ int set_ipv6pktinfo(int fd);
#ifdef HAVE_DHCP6
void join_multicast(int dienow);
#endif
#if defined(HAVE_LINUX_NETWORK) || defined(HAVE_BSD_NETWORK)
void newaddress(time_t now);
#endif
/* dhcp.c */
#ifdef HAVE_DHCP
@@ -1177,6 +1262,8 @@ void netlink_multicast(time_t now);
void init_bpf(void);
void send_via_bpf(struct dhcp_packet *mess, size_t len,
struct in_addr iface_addr, struct ifreq *ifr);
void route_init(void);
void route_sock(time_t now);
#endif
/* bpf.c or netlink.c */
@@ -1249,7 +1336,8 @@ void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac,
/* rfc3315.c */
#ifdef HAVE_DHCP6
unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name,
struct in6_addr *fallback, size_t sz, struct in6_addr *client_addr, time_t now);
struct in6_addr *fallback, struct in6_addr *ll_addr, struct in6_addr *ula_addr,
size_t sz, struct in6_addr *client_addr, time_t now);
void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, struct in6_addr *peer_address, u32 scope_id);
unsigned short relay_reply6( struct sockaddr_in6 *peer, ssize_t sz, char *arrival_interface);

1454
src/dnssec.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
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

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
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
@@ -16,11 +16,11 @@
#include "dnsmasq.h"
static struct frec *lookup_frec(unsigned short id, unsigned int crc);
static struct frec *lookup_frec(unsigned short id, void *hash);
static struct frec *lookup_frec_by_sender(unsigned short id,
union mysockaddr *addr,
unsigned int crc);
static unsigned short get_id(unsigned int crc);
void *hash);
static unsigned short get_id(void);
static void free_frec(struct frec *f);
static struct randfd *allocate_rfd(int family);
@@ -234,24 +234,67 @@ static unsigned int search_servers(time_t now, struct all_addr **addrpp,
static int forward_query(int udpfd, union mysockaddr *udpaddr,
struct all_addr *dst_addr, unsigned int dst_iface,
struct dns_header *header, size_t plen, time_t now, struct frec *forward)
struct dns_header *header, size_t plen, time_t now,
struct frec *forward, int ad_reqd)
{
char *domain = NULL;
int type = 0, norebind = 0;
struct all_addr *addrp = NULL;
unsigned int crc = questions_crc(header, plen, daemon->namebuff);
unsigned int flags = 0;
unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL);
struct server *start = NULL;
/* RFC 4035: sect 4.6 para 2 */
header->hb4 &= ~HB4_AD;
#ifdef HAVE_DNSSEC
void *hash = hash_questions(header, plen, daemon->namebuff);
#else
unsigned int crc = questions_crc(header, plen, daemon->namebuff);
void *hash = &crc;
#endif
unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL);
/* may be no servers available. */
if (!daemon->servers)
forward = NULL;
else if (forward || (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, crc)))
else if (forward || (hash && (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, hash))))
{
#ifdef HAVE_DNSSEC
/* If we've already got an answer to this query, but we're awaiting keys for vaildation,
there's no point retrying the query, retry the key query instead...... */
if (forward->blocking_query)
{
int fd;
while (forward->blocking_query)
forward = forward->blocking_query;
blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
plen = forward->stash_len;
if (forward->sentto->addr.sa.sa_family)
log_query(F_DNSSEC | F_IPV4, "retry", (struct all_addr *)&forward->sentto->addr.in.sin_addr, "dnssec");
#ifdef HAVE_IPV6
else
log_query(F_DNSSEC | F_IPV6, "retry", (struct all_addr *)&forward->sentto->addr.in6.sin6_addr, "dnssec");
#endif
if (forward->sentto->sfd)
fd = forward->sentto->sfd->fd;
else
{
#ifdef HAVE_IPV6
if (forward->sentto->addr.sa.sa_family == AF_INET6)
fd = forward->rfd6->fd;
else
#endif
fd = forward->rfd4->fd;
}
while (sendto(fd, (char *)header, plen, 0,
&forward->sentto->addr.sa,
sa_len(&forward->sentto->addr)) == -1 && retry_send());
return 1;
}
#endif
/* retry on existing query, send to all available servers */
domain = forward->sentto->domain;
forward->sentto->failed_queries++;
@@ -270,7 +313,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
if (gotname)
flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);
if (!flags && !(forward = get_new_frec(now, NULL)))
if (!flags && !(forward = get_new_frec(now, NULL, 0)))
/* table full - server failure. */
flags = F_NEG;
@@ -280,15 +323,20 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
forward->dest = *dst_addr;
forward->iface = dst_iface;
forward->orig_id = ntohs(header->id);
forward->new_id = get_id(crc);
forward->new_id = get_id();
forward->fd = udpfd;
forward->crc = crc;
memcpy(forward->hash, hash, HASH_SIZE);
forward->forwardall = 0;
forward->flags = 0;
if (norebind)
forward->flags |= FREC_NOREBIND;
if (header->hb4 & HB4_CD)
forward->flags |= FREC_CHECKING_DISABLED;
if (ad_reqd)
forward->flags |= FREC_AD_QUESTION;
#ifdef HAVE_DNSSEC
forward->work_counter = DNSSEC_WORK;
#endif
header->id = htons(forward->new_id);
@@ -330,11 +378,11 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
int forwarded = 0;
if (option_bool(OPT_ADD_MAC))
plen = add_mac(header, plen, ((char *) header) + PACKETSZ, &forward->source);
plen = add_mac(header, plen, ((char *) header) + daemon->packet_buff_sz, &forward->source);
if (option_bool(OPT_CLIENT_SUBNET))
{
size_t new = add_source_addr(header, plen, ((char *) header) + PACKETSZ, &forward->source);
size_t new = add_source_addr(header, plen, ((char *) header) + daemon->packet_buff_sz, &forward->source);
if (new != plen)
{
plen = new;
@@ -342,6 +390,17 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
}
}
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID))
{
plen = add_do_bit(header, plen, ((char *) header) + daemon->packet_buff_sz);
/* For debugging, set Checking Disabled, otherwise, have the upstream check too,
this allows it to select auth servers when one is returning bad data. */
if (option_bool(OPT_DNSSEC_DEBUG))
header->hb4 |= HB4_CD;
}
#endif
while (1)
{
/* only send to servers dealing with our domain.
@@ -447,13 +506,15 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
}
static size_t process_reply(struct dns_header *header, time_t now, struct server *server, size_t n, int check_rebind,
int checking_disabled, int check_subnet, union mysockaddr *query_source)
int no_cache, int cache_secure, int ad_reqd, int check_subnet, union mysockaddr *query_source)
{
unsigned char *pheader, *sizep;
char **sets = 0;
int munged = 0, is_sign;
size_t plen;
(void)ad_reqd;
#ifdef HAVE_IPSET
/* Similar algorithm to search_servers. */
struct ipsets *ipset_pos;
@@ -465,10 +526,11 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
char *matchstart = daemon->namebuff + namelen - domainlen;
if (namelen >= domainlen && hostname_isequal(matchstart, ipset_pos->domain) &&
(domainlen == 0 || namelen == domainlen || *(matchstart - 1) == '.' ) &&
domainlen >= matchlen) {
matchlen = domainlen;
sets = ipset_pos->sets;
}
domainlen >= matchlen)
{
matchlen = domainlen;
sets = ipset_pos->sets;
}
}
#endif
@@ -478,15 +540,13 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
if ((pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign)))
{
if (!is_sign)
{
unsigned short udpsz;
unsigned char *psave = sizep;
GETSHORT(udpsz, sizep);
if (udpsz > daemon->edns_pktsz)
PUTSHORT(daemon->edns_pktsz, psave);
}
unsigned short udpsz;
unsigned char *psave = sizep;
GETSHORT(udpsz, sizep);
if (!is_sign && udpsz > daemon->edns_pktsz)
PUTSHORT(daemon->edns_pktsz, psave);
if (check_subnet && !check_source(header, plen, pheader, query_source))
{
@@ -494,12 +554,11 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
return 0;
}
}
/* RFC 4035 sect 4.6 para 3 */
if (!is_sign && !option_bool(OPT_DNSSEC))
header->hb4 &= ~HB4_AD;
if (!is_sign && !option_bool(OPT_DNSSEC_PROXY))
header->hb4 &= ~HB4_AD;
if (OPCODE(header) != QUERY || (RCODE(header) != NOERROR && RCODE(header) != NXDOMAIN))
return n;
@@ -512,16 +571,19 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
if (!option_bool(OPT_LOG))
server->flags |= SERV_WARNED_RECURSIVE;
}
if (daemon->bogus_addr && RCODE(header) != NXDOMAIN &&
check_for_bogus_wildcard(header, n, daemon->namebuff, daemon->bogus_addr, now))
{
munged = 1;
SET_RCODE(header, NXDOMAIN);
header->hb3 &= ~HB3_AA;
cache_secure = 0;
}
else
{
int doctored = 0;
if (RCODE(header) == NXDOMAIN &&
extract_request(header, n, daemon->namebuff, NULL) &&
check_for_local_domain(daemon->namebuff, now))
@@ -532,15 +594,38 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
munged = 1;
header->hb3 |= HB3_AA;
SET_RCODE(header, NOERROR);
cache_secure = 0;
}
if (extract_addresses(header, n, daemon->namebuff, now, sets, is_sign, check_rebind, checking_disabled))
if (extract_addresses(header, n, daemon->namebuff, now, sets, is_sign, check_rebind, no_cache, cache_secure, &doctored))
{
my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff);
munged = 1;
cache_secure = 0;
}
if (doctored)
cache_secure = 0;
}
#ifdef HAVE_DNSSEC
if (no_cache && !(header->hb4 & HB4_CD))
{
if (!option_bool(OPT_DNSSEC_DEBUG))
{
/* Bogus reply, turn into SERVFAIL */
SET_RCODE(header, SERVFAIL);
munged = 1;
}
}
if (option_bool(OPT_DNSSEC_VALID))
header->hb4 &= ~HB4_AD;
if (!(header->hb4 & HB4_CD) && ad_reqd && cache_secure)
header->hb4 |= HB4_AD;
#endif
/* do this after extract_addresses. Ensure NODATA reply and remove
nameserver info. */
@@ -566,10 +651,14 @@ void reply_query(int fd, int family, time_t now)
union mysockaddr serveraddr;
struct frec *forward;
socklen_t addrlen = sizeof(serveraddr);
ssize_t n = recvfrom(fd, daemon->packet, daemon->edns_pktsz, 0, &serveraddr.sa, &addrlen);
ssize_t n = recvfrom(fd, daemon->packet, daemon->packet_buff_sz, 0, &serveraddr.sa, &addrlen);
size_t nn;
struct server *server;
void *hash;
#ifndef HAVE_DNSSEC
unsigned int crc;
#endif
/* packet buffer overwritten */
daemon->srv_save = NULL;
@@ -587,14 +676,19 @@ void reply_query(int fd, int family, time_t now)
break;
header = (struct dns_header *)daemon->packet;
#ifdef HAVE_DNSSEC
hash = hash_questions(header, n, daemon->namebuff);
#else
hash = &crc;
crc = questions_crc(header, n, daemon->namebuff);
#endif
if (!server ||
n < (int)sizeof(struct dns_header) || !(header->hb3 & HB3_QR) ||
!(forward = lookup_frec(ntohs(header->id), questions_crc(header, n, daemon->namebuff))))
!(forward = lookup_frec(ntohs(header->id), hash)))
return;
server = forward->sentto;
if ((RCODE(header) == SERVFAIL || RCODE(header) == REFUSED) &&
!option_bool(OPT_ORDER) &&
forward->forwardall == 0)
@@ -614,11 +708,13 @@ void reply_query(int fd, int family, time_t now)
if ((nn = resize_packet(header, (size_t)n, pheader, plen)))
{
header->hb3 &= ~(HB3_QR | HB3_TC);
forward_query(-1, NULL, NULL, 0, header, nn, now, forward);
forward_query(-1, NULL, NULL, 0, header, nn, now, forward, 0);
return;
}
}
}
server = forward->sentto;
if ((forward->sentto->flags & SERV_TYPE) == 0)
{
@@ -640,7 +736,7 @@ void reply_query(int fd, int family, time_t now)
if (!option_bool(OPT_ALL_SERVERS))
daemon->last_server = server;
}
/* If the answer is an error, keep the forward record in place in case
we get a good reply from another server. Kill it when we've
had replies from all to avoid filling the forwarding table when
@@ -648,13 +744,190 @@ void reply_query(int fd, int family, time_t now)
if (forward->forwardall == 0 || --forward->forwardall == 1 ||
(RCODE(header) != REFUSED && RCODE(header) != SERVFAIL))
{
int check_rebind = !(forward->flags & FREC_NOREBIND);
int check_rebind = 0, no_cache_dnssec = 0, cache_secure = 0;
if (!option_bool(OPT_NO_REBIND))
check_rebind = 0;
if (option_bool(OPT_NO_REBIND))
check_rebind = !(forward->flags & FREC_NOREBIND);
if ((nn = process_reply(header, now, server, (size_t)n, check_rebind, forward->flags & FREC_CHECKING_DISABLED,
forward->flags & FREC_HAS_SUBNET, &forward->source)))
/* Don't cache replies where DNSSEC validation was turned off, either
the upstream server told us so, or the original query specified it. */
if ((header->hb4 & HB4_CD) || (forward->flags & FREC_CHECKING_DISABLED))
no_cache_dnssec = 1;
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && !(forward->flags & FREC_CHECKING_DISABLED))
{
int status;
/* We've had a reply already, which we're validating. Ignore this duplicate */
if (forward->blocking_query)
return;
if (header->hb3 & HB3_TC)
{
/* Truncated answer can't be validated.
If this is an answer to a DNSSEC-generated query, we still
need to get the client to retry over TCP, so return
an answer with the TC bit set, even if the actual answer fits.
*/
status = STAT_TRUNCATED;
}
else if (forward->flags & FREC_DNSKEY_QUERY)
status = dnssec_validate_by_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
else if (forward->flags & FREC_DS_QUERY)
status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
else
status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class);
/* Can't validate, as we're missing key data. Put this
answer aside, whilst we get that. */
if (status == STAT_NEED_DS || status == STAT_NEED_KEY)
{
struct frec *new, *orig;
/* Free any saved query */
if (forward->stash)
blockdata_free(forward->stash);
/* Now save reply pending receipt of key data */
if (!(forward->stash = blockdata_alloc((char *)header, n)))
return;
forward->stash_len = n;
anotherkey:
/* Find the original query that started it all.... */
for (orig = forward; orig->dependent; orig = orig->dependent);
if (--orig->work_counter == 0 || !(new = get_new_frec(now, NULL, 1)))
status = STAT_INSECURE;
else
{
int fd;
struct frec *next = new->next;
*new = *forward; /* copy everything, then overwrite */
new->next = next;
new->blocking_query = NULL;
new->rfd4 = NULL;
#ifdef HAVE_IPV6
new->rfd6 = NULL;
#endif
new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY);
new->dependent = forward; /* to find query awaiting new one. */
forward->blocking_query = new; /* for garbage cleaning */
/* validate routines leave name of required record in daemon->keyname */
if (status == STAT_NEED_KEY)
{
new->flags |= FREC_DNSKEY_QUERY;
nn = dnssec_generate_query(header, ((char *) header) + daemon->packet_buff_sz,
daemon->keyname, forward->class, T_DNSKEY, &server->addr);
}
else
{
new->flags |= FREC_DS_QUERY;
nn = dnssec_generate_query(header,((char *) header) + daemon->packet_buff_sz,
daemon->keyname, forward->class, T_DS, &server->addr);
}
if ((hash = hash_questions(header, nn, daemon->namebuff)))
memcpy(new->hash, hash, HASH_SIZE);
new->new_id = get_id();
header->id = htons(new->new_id);
/* Save query for retransmission */
new->stash = blockdata_alloc((char *)header, nn);
new->stash_len = nn;
/* Don't resend this. */
daemon->srv_save = NULL;
if (server->sfd)
fd = server->sfd->fd;
else
{
fd = -1;
#ifdef HAVE_IPV6
if (server->addr.sa.sa_family == AF_INET6)
{
if (new->rfd6 || (new->rfd6 = allocate_rfd(AF_INET6)))
fd = new->rfd6->fd;
}
else
#endif
{
if (new->rfd4 || (new->rfd4 = allocate_rfd(AF_INET)))
fd = new->rfd4->fd;
}
}
if (fd != -1)
{
while (sendto(fd, (char *)header, nn, 0, &server->addr.sa, sa_len(&server->addr)) == -1 && retry_send());
server->queries++;
}
return;
}
}
/* Ok, we reached far enough up the chain-of-trust that we can validate something.
Now wind back down, pulling back answers which wouldn't previously validate
and validate them with the new data. Note that if an answer needs multiple
keys to validate, we may find another key is needed, in which case we set off
down another branch of the tree. Once we get to the original answer
(FREC_DNSSEC_QUERY not set) and it validates, return it to the original requestor. */
while (forward->dependent)
{
struct frec *prev = forward->dependent;
free_frec(forward);
forward = prev;
forward->blocking_query = NULL; /* already gone */
blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
n = forward->stash_len;
if (status == STAT_SECURE)
{
if (forward->flags & FREC_DNSKEY_QUERY)
status = dnssec_validate_by_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
else if (forward->flags & FREC_DS_QUERY)
status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
else
status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class);
if (status == STAT_NEED_DS || status == STAT_NEED_KEY)
goto anotherkey;
}
}
if (status == STAT_TRUNCATED)
header->hb3 |= HB3_TC;
else
{
char *result;
if (forward->work_counter == 0)
result = "ABANDONED";
else
result = (status == STAT_SECURE ? "SECURE" : (status == STAT_INSECURE ? "INSECURE" : "BOGUS"));
log_query(F_KEYTAG | F_SECSTAT, "result", NULL, result);
}
no_cache_dnssec = 0;
if (status == STAT_SECURE)
cache_secure = 1;
else if (status == STAT_BOGUS)
no_cache_dnssec = 1;
}
#endif
/* restore CD bit to the value in the query */
if (forward->flags & FREC_CHECKING_DISABLED)
header->hb4 |= HB4_CD;
else
header->hb4 &= ~HB4_CD;
if ((nn = process_reply(header, now, server, (size_t)n, check_rebind, no_cache_dnssec, cache_secure,
forward->flags & FREC_AD_QUESTION, forward->flags & FREC_HAS_SUBNET, &forward->source)))
{
header->id = htons(forward->orig_id);
header->hb4 |= HB4_RA; /* recursion if available */
@@ -855,13 +1128,11 @@ void receive_query(struct listener *listen, time_t now)
if (extract_request(header, (size_t)n, daemon->namebuff, &type))
{
char types[20];
#ifdef HAVE_AUTH
struct auth_zone *zone;
#endif
querystr(auth_dns ? "auth" : "query", types, type);
char *types = querystr(auth_dns ? "auth" : "query", type);
if (listen->family == AF_INET)
log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
(struct all_addr *)&source_addr.in.sin_addr, types);
@@ -887,7 +1158,7 @@ void receive_query(struct listener *listen, time_t now)
#ifdef HAVE_AUTH
if (auth_dns)
{
m = answer_auth(header, ((char *) header) + PACKETSZ, (size_t)n, now, &source_addr, local_auth);
m = answer_auth(header, ((char *) header) + daemon->packet_buff_sz, (size_t)n, now, &source_addr, local_auth);
if (m >= 1)
{
send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND),
@@ -898,8 +1169,9 @@ void receive_query(struct listener *listen, time_t now)
else
#endif
{
m = answer_request(header, ((char *) header) + PACKETSZ, (size_t)n,
dst_addr_4, netmask, now);
int ad_reqd;
m = answer_request(header, ((char *) header) + daemon->packet_buff_sz, (size_t)n,
dst_addr_4, netmask, now, &ad_reqd);
if (m >= 1)
{
@@ -908,13 +1180,86 @@ void receive_query(struct listener *listen, time_t now)
daemon->local_answer++;
}
else if (forward_query(listen->fd, &source_addr, &dst_addr, if_index,
header, (size_t)n, now, NULL))
header, (size_t)n, now, NULL, ad_reqd))
daemon->queries_forwarded++;
else
daemon->local_answer++;
}
}
#ifdef HAVE_DNSSEC
static int tcp_key_recurse(time_t now, int status, struct dns_header *header, size_t n,
int class, char *name, char *keyname, struct server *server, int *keycount)
{
/* Recurse up the key heirarchy */
int new_status;
/* limit the amount of work we do, to avoid cycling forever on loops in the DNS */
if (--(*keycount) == 0)
return STAT_INSECURE;
if (status == STAT_NEED_KEY)
new_status = dnssec_validate_by_ds(now, header, n, name, keyname, class);
else if (status == STAT_NEED_DS)
new_status = dnssec_validate_ds(now, header, n, name, keyname, class);
else
new_status = dnssec_validate_reply(now, header, n, name, keyname, &class);
/* Can't validate because we need a key/DS whose name now in keyname.
Make query for same, and recurse to validate */
if (new_status == STAT_NEED_DS || new_status == STAT_NEED_KEY)
{
size_t m;
unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ + sizeof(u16));
unsigned char *payload = &packet[2];
struct dns_header *new_header = (struct dns_header *)payload;
u16 *length = (u16 *)packet;
unsigned char c1, c2;
if (!packet)
return STAT_INSECURE;
another_tcp_key:
m = dnssec_generate_query(new_header, ((char *) new_header) + 65536, keyname, class,
new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS, &server->addr);
*length = htons(m);
if (!read_write(server->tcpfd, packet, m + sizeof(u16), 0) ||
!read_write(server->tcpfd, &c1, 1, 1) ||
!read_write(server->tcpfd, &c2, 1, 1) ||
!read_write(server->tcpfd, payload, (c1 << 8) | c2, 1))
new_status = STAT_INSECURE;
else
{
m = (c1 << 8) | c2;
if (tcp_key_recurse(now, new_status, new_header, m, class, name, keyname, server, keycount) == STAT_SECURE)
{
/* Reached a validated record, now try again at this level.
Note that we may get ANOTHER NEED_* if an answer needs more than one key.
If so, go round again. */
if (status == STAT_NEED_KEY)
new_status = dnssec_validate_by_ds(now, header, n, name, keyname, class);
else if (status == STAT_NEED_DS)
new_status = dnssec_validate_ds(now, header, n, name, keyname, class);
else
new_status = dnssec_validate_reply(now, header, n, name, keyname, &class);
if (new_status == STAT_NEED_DS || new_status == STAT_NEED_KEY)
goto another_tcp_key;
}
}
free(packet);
}
return new_status;
}
#endif
/* The daemon forks before calling this: it should deal with one connection,
blocking as neccessary, and then return. Note, need to be a bit careful
about resources for debug mode, when the fork is suppressed: that's
@@ -927,7 +1272,7 @@ unsigned char *tcp_request(int confd, time_t now,
#ifdef HAVE_AUTH
int local_auth = 0;
#endif
int checking_disabled, check_subnet;
int checking_disabled, ad_question, check_subnet, no_cache_dnssec = 0, cache_secure = 0;
size_t m;
unsigned short qtype;
unsigned int gotname;
@@ -960,18 +1305,15 @@ unsigned char *tcp_request(int confd, time_t now,
check_subnet = 0;
/* save state of "cd" flag in query */
checking_disabled = header->hb4 & HB4_CD;
if ((checking_disabled = header->hb4 & HB4_CD))
no_cache_dnssec = 1;
/* RFC 4035: sect 4.6 para 2 */
header->hb4 &= ~HB4_AD;
if ((gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype)))
{
char types[20];
#ifdef HAVE_AUTH
struct auth_zone *zone;
#endif
querystr(auth_dns ? "auth" : "query", types, qtype);
char *types = querystr(auth_dns ? "auth" : "query", qtype);
if (peer_addr.sa.sa_family == AF_INET)
log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
@@ -1008,7 +1350,7 @@ unsigned char *tcp_request(int confd, time_t now,
{
/* m > 0 if answered from cache */
m = answer_request(header, ((char *) header) + 65536, (size_t)size,
dst_addr_4, netmask, now);
dst_addr_4, netmask, now, &ad_question);
/* Do this by steam now we're not in the select() loop */
check_log_writer(NULL);
@@ -1044,8 +1386,13 @@ unsigned char *tcp_request(int confd, time_t now,
if (!flags && last_server)
{
struct server *firstsendto = NULL;
#ifdef HAVE_DNSSEC
unsigned char *newhash, hash[HASH_SIZE];
if ((newhash = hash_questions(header, (unsigned int)size, daemon->keyname)))
memcpy(hash, newhash, HASH_SIZE);
#else
unsigned int crc = questions_crc(header, (unsigned int)size, daemon->namebuff);
#endif
/* Loop round available servers until we succeed in connecting to one.
Note that this code subtley ensures that consecutive queries on this connection
which can go to the same server, do so. */
@@ -1080,6 +1427,14 @@ unsigned char *tcp_request(int confd, time_t now,
continue;
}
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID))
{
size = add_do_bit(header, size, ((char *) header) + 65536);
header->hb4 |= HB4_CD;
}
#endif
#ifdef HAVE_CONNTRACK
/* Copy connection mark of incoming query to outgoing connection. */
if (option_bool(OPT_CONNTRACK))
@@ -1103,7 +1458,8 @@ unsigned char *tcp_request(int confd, time_t now,
if (!read_write(last_server->tcpfd, packet, size + sizeof(u16), 0) ||
!read_write(last_server->tcpfd, &c1, 1, 1) ||
!read_write(last_server->tcpfd, &c2, 1, 1))
!read_write(last_server->tcpfd, &c2, 1, 1) ||
!read_write(last_server->tcpfd, payload, (c1 << 8) | c2, 1))
{
close(last_server->tcpfd);
last_server->tcpfd = -1;
@@ -1111,8 +1467,6 @@ unsigned char *tcp_request(int confd, time_t now,
}
m = (c1 << 8) | c2;
if (!read_write(last_server->tcpfd, payload, m, 1))
return packet;
if (!gotname)
strcpy(daemon->namebuff, "query");
@@ -1124,6 +1478,34 @@ unsigned char *tcp_request(int confd, time_t now,
log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff,
(struct all_addr *)&last_server->addr.in6.sin6_addr, NULL);
#endif
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && !checking_disabled)
{
int keycount = DNSSEC_WORK; /* Limit to number of DNSSEC questions, to catch loops and avoid filling cache. */
int status = tcp_key_recurse(now, STAT_TRUNCATED, header, m, 0, daemon->namebuff, daemon->keyname, last_server, &keycount);
char *result;
if (keycount == 0)
result = "ABANDONED";
else
result = (status == STAT_SECURE ? "SECURE" : (status == STAT_INSECURE ? "INSECURE" : "BOGUS"));
log_query(F_KEYTAG | F_SECSTAT, "result", NULL, result);
if (status == STAT_BOGUS)
no_cache_dnssec = 1;
if (status == STAT_SECURE)
cache_secure = 1;
}
#endif
/* restore CD bit to the value in the query */
if (checking_disabled)
header->hb4 |= HB4_CD;
else
header->hb4 &= ~HB4_CD;
/* There's no point in updating the cache, since this process will exit and
lose the information after a few queries. We make this call for the alias and
@@ -1131,10 +1513,24 @@ unsigned char *tcp_request(int confd, time_t now,
/* If the crc of the question section doesn't match the crc we sent, then
someone might be attempting to insert bogus values into the cache by
sending replies containing questions and bogus answers. */
if (crc == questions_crc(header, (unsigned int)m, daemon->namebuff))
m = process_reply(header, now, last_server, (unsigned int)m,
option_bool(OPT_NO_REBIND) && !norebind, checking_disabled,
check_subnet, &peer_addr);
#ifdef HAVE_DNSSEC
newhash = hash_questions(header, (unsigned int)m, daemon->namebuff);
if (!newhash || memcmp(hash, newhash, HASH_SIZE) != 0)
{
m = 0;
break;
}
#else
if (crc != questions_crc(header, (unsigned int)m, daemon->namebuff))
{
m = 0;
break;
}
#endif
m = process_reply(header, now, last_server, (unsigned int)m,
option_bool(OPT_NO_REBIND) && !norebind, no_cache_dnssec,
cache_secure, ad_question, check_subnet, &peer_addr);
break;
}
@@ -1168,6 +1564,11 @@ static struct frec *allocate_frec(time_t now)
f->flags = 0;
#ifdef HAVE_IPV6
f->rfd6 = NULL;
#endif
#ifdef HAVE_DNSSEC
f->dependent = NULL;
f->blocking_query = NULL;
f->stash = NULL;
#endif
daemon->frec_list = f;
}
@@ -1210,7 +1611,6 @@ static struct randfd *allocate_rfd(int family)
return NULL; /* doom */
}
static void free_frec(struct frec *f)
{
if (f->rfd4 && --(f->rfd4->refcount) == 0)
@@ -1226,13 +1626,29 @@ static void free_frec(struct frec *f)
f->rfd6 = NULL;
#endif
#ifdef HAVE_DNSSEC
if (f->stash)
{
blockdata_free(f->stash);
f->stash = NULL;
}
/* Anything we're waiting on is pointless now, too */
if (f->blocking_query)
free_frec(f->blocking_query);
f->blocking_query = NULL;
f->dependent = NULL;
#endif
}
/* if wait==NULL return a free or older than TIMEOUT record.
else return *wait zero if one available, or *wait is delay to
when the oldest in-use record will expire. Impose an absolute
limit of 4*TIMEOUT before we wipe things (for random sockets) */
struct frec *get_new_frec(time_t now, int *wait)
limit of 4*TIMEOUT before we wipe things (for random sockets).
If force is set, always return a result, even if we have
to allocate above the limit. */
struct frec *get_new_frec(time_t now, int *wait, int force)
{
struct frec *f, *oldest, *target;
int count;
@@ -1281,7 +1697,7 @@ struct frec *get_new_frec(time_t now, int *wait)
}
/* none available, calculate time 'till oldest record expires */
if (count > daemon->ftabsize)
if (!force && count > daemon->ftabsize)
{
static time_t last_log = 0;
@@ -1305,13 +1721,13 @@ struct frec *get_new_frec(time_t now, int *wait)
}
/* crc is all-ones if not known. */
static struct frec *lookup_frec(unsigned short id, unsigned int crc)
static struct frec *lookup_frec(unsigned short id, void *hash)
{
struct frec *f;
for(f = daemon->frec_list; f; f = f->next)
if (f->sentto && f->new_id == id &&
(f->crc == crc || crc == 0xffffffff))
(!hash || memcmp(hash, f->hash, HASH_SIZE) == 0))
return f;
return NULL;
@@ -1319,14 +1735,14 @@ static struct frec *lookup_frec(unsigned short id, unsigned int crc)
static struct frec *lookup_frec_by_sender(unsigned short id,
union mysockaddr *addr,
unsigned int crc)
void *hash)
{
struct frec *f;
for(f = daemon->frec_list; f; f = f->next)
if (f->sentto &&
f->orig_id == id &&
f->crc == crc &&
memcmp(hash, f->hash, HASH_SIZE) == 0 &&
sockaddr_isequal(&f->source, addr))
return f;
@@ -1350,13 +1766,13 @@ void server_gone(struct server *server)
}
/* return unique random ids. */
static unsigned short get_id(unsigned int crc)
static unsigned short get_id(void)
{
unsigned short ret = 0;
do
ret = rand16();
while (lookup_frec(ret, crc));
while (lookup_frec(ret, NULL));
return ret;
}

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
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

34
src/ip6addr.h Normal file
View File

@@ -0,0 +1,34 @@
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
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/>.
*/
#define IN6_IS_ADDR_ULA(a) \
((((__const uint32_t *) (a))[0] & htonl (0xff000000)) \
== htonl (0xfd000000))
#define IN6_IS_ADDR_ULA_ZERO(a) \
(((__const uint32_t *) (a))[0] == htonl (0xfd000000) \
&& ((__const uint32_t *) (a))[1] == 0 \
&& ((__const uint32_t *) (a))[2] == 0 \
&& ((__const uint32_t *) (a))[3] == 0)
#define IN6_IS_ADDR_LINK_LOCAL_ZERO(a) \
(((__const uint32_t *) (a))[0] == htonl (0xfe800000) \
&& ((__const uint32_t *) (a))[1] == 0 \
&& ((__const uint32_t *) (a))[2] == 0 \
&& ((__const uint32_t *) (a))[3] == 0)

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
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
@@ -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

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
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

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
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
@@ -39,7 +39,6 @@ static struct iovec iov;
static u32 netlink_pid;
static int nl_async(struct nlmsghdr *h);
static void nl_newaddress(time_t now);
void netlink_init(void)
{
@@ -203,7 +202,7 @@ int iface_enumerate(int family, void *parm, int (*callback)())
/* handle async new interface address arrivals, these have to be done
after we complete as we're not re-entrant */
if (newaddr)
nl_newaddress(dnsmasq_time());
newaddress(dnsmasq_time());
return callback_ok;
}
@@ -265,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,
@@ -351,7 +350,7 @@ void netlink_multicast(time_t now)
fcntl(daemon->netlinkfd, F_SETFL, flags);
if (newaddr)
nl_newaddress(now);
newaddress(now);
}
static int nl_async(struct nlmsghdr *h)
@@ -399,30 +398,6 @@ static int nl_async(struct nlmsghdr *h)
return 0;
}
static void nl_newaddress(time_t now)
{
(void)now;
if (option_bool(OPT_CLEVERBIND) || daemon->doing_dhcp6 || daemon->relay6 || daemon->doing_ra)
enumerate_interfaces(0);
if (option_bool(OPT_CLEVERBIND))
create_bound_listeners(0);
#ifdef HAVE_DHCP6
if (daemon->doing_dhcp6 || daemon->relay6 || daemon->doing_ra)
join_multicast(0);
if (daemon->doing_dhcp6 || daemon->doing_ra)
dhcp_construct_contexts(now);
if (daemon->doing_dhcp6)
lease_find_interfaces(now);
#endif
}
#endif

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
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
@@ -489,7 +489,7 @@ static int iface_allowed_v6(struct in6_addr *local, int prefix,
addr.in6.sin6_scope_id = if_index;
else
addr.in6.sin6_scope_id = 0;
return iface_allowed((struct iface_param *)vparam, if_index, NULL, &addr, netmask, prefix, !!(flags & IFACE_TENTATIVE));
}
#endif
@@ -681,7 +681,7 @@ static int make_sock(union mysockaddr *addr, int type, int dienow)
close (fd);
errno = errsav;
if (dienow)
{
/* failure to bind addresses given by --listen-address at this point
@@ -1470,7 +1470,31 @@ int reload_servers(char *fname)
return gotone;
}
#if defined(HAVE_LINUX_NETWORK) || defined(HAVE_BSD_NETWORK)
/* Called when addresses are added or deleted from an interface */
void newaddress(time_t now)
{
(void)now;
if (option_bool(OPT_CLEVERBIND) || daemon->doing_dhcp6 || daemon->relay6 || daemon->doing_ra)
enumerate_interfaces(0);
if (option_bool(OPT_CLEVERBIND))
create_bound_listeners(0);
#ifdef HAVE_DHCP6
if (daemon->doing_dhcp6 || daemon->relay6 || daemon->doing_ra)
join_multicast(0);
if (daemon->doing_dhcp6 || daemon->doing_ra)
dhcp_construct_contexts(now);
if (daemon->doing_dhcp6)
lease_find_interfaces(now);
#endif
}
#endif

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
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
@@ -138,7 +138,9 @@ struct myoption {
#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
#ifdef HAVE_GETOPT_LONG
static const struct option opts[] =
@@ -274,6 +276,9 @@ static const struct myoption opts[] =
{ "auth-peer", 1, 0, LOPT_AUTHPEER },
{ "ipset", 1, 0, LOPT_IPSET },
{ "synth-domain", 1, 0, LOPT_SYNTH },
{ "dnssec", 0, 0, LOPT_SEC_VALID },
{ "trust-anchor", 1, 0, LOPT_TRUST_ANCHOR },
{ "dnssec-debug", 0, 0, LOPT_DNSSEC_DEBUG },
#ifdef OPTION6_PREFIX_CLASS
{ "dhcp-prefix-class", 1, 0, LOPT_PREF_CLSS },
#endif
@@ -407,7 +412,7 @@ static struct {
{ LOPT_TEST, 0, NULL, gettext_noop("Check configuration syntax."), NULL },
{ LOPT_ADD_MAC, OPT_ADD_MAC, NULL, gettext_noop("Add requestor's MAC address to forwarded DNS queries."), NULL },
{ LOPT_ADD_SBNET, ARG_ONE, "<v4 pref>[,<v6 pref>]", gettext_noop("Add requestor's IP subnet to forwarded DNS queries."), NULL },
{ LOPT_DNSSEC, OPT_DNSSEC, NULL, gettext_noop("Proxy DNSSEC validation results from upstream nameservers."), NULL },
{ LOPT_DNSSEC, OPT_DNSSEC_PROXY, NULL, gettext_noop("Proxy DNSSEC validation results from upstream nameservers."), NULL },
{ LOPT_INCR_ADDR, OPT_CONSEC_ADDR, NULL, gettext_noop("Attempt to allocate sequential IP addresses to DHCP clients."), NULL },
{ LOPT_CONNTRACK, OPT_CONNTRACK, NULL, gettext_noop("Copy connection-track mark from queries to upstream connections."), NULL },
{ LOPT_FQDN, OPT_FQDN_UPDATE, NULL, gettext_noop("Allow DHCP clients to do their own DDNS updates."), NULL },
@@ -424,6 +429,9 @@ 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 },
{ LOPT_SEC_VALID, OPT_DNSSEC_VALID, NULL, gettext_noop("Activate DNSSEC validation"), NULL },
{ 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 },
#ifdef OPTION6_PREFIX_CLASS
{ LOPT_PREF_CLSS, ARG_DUP, "set:tag,<class>", gettext_noop("Specify DHCPv6 prefix class"), NULL },
#endif
@@ -582,6 +590,16 @@ static int atoi_check16(char *a, int *res)
return 1;
}
static int atoi_check8(char *a, int *res)
{
if (!(atoi_check(a, res)) ||
*res < 0 ||
*res > 0xff)
return 0;
return 1;
}
static void add_txt(char *name, char *txt)
{
@@ -2732,7 +2750,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
else
{
char *cp, *lastp = NULL, last = 0;
int fac = 1;
int fac = 1, isdig = 0;
if (strlen(a[j]) > 1)
{
@@ -2763,9 +2781,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
}
for (cp = a[j]; *cp; cp++)
if (!isdigit((unsigned char)*cp) && *cp != ' ')
if (isdigit((unsigned char)*cp))
isdig = 1;
else if (*cp != ' ')
break;
if (*cp)
{
if (lastp)
@@ -2787,7 +2807,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
new->domain = strip_hostname(new->hostname);
}
}
else
else if (isdig)
{
new->lease_time = atoi(a[j]) * fac;
/* Leases of a minute or less confuse
@@ -3663,9 +3683,64 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
daemon->host_records_tail = new;
break;
}
#ifdef HAVE_DNSSEC
case LOPT_TRUST_ANCHOR:
{
struct ds_config *new = opt_malloc(sizeof(struct ds_config));
char *cp, *cp1, *keyhex, *digest, *algo = NULL;
int len;
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
default:
ret_err(_("unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DBus support)"));
ret_err(_("unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DNSSEC/DBus support)"));
}

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
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

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
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

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
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
@@ -31,8 +31,8 @@ struct ra_param {
int ind, managed, other, found_context, first;
char *if_name;
struct dhcp_netid *tags;
struct in6_addr link_local, link_global;
unsigned int pref_time, adv_interval;
struct in6_addr link_local, link_global, ula;
unsigned int glob_pref_time, link_pref_time, ula_pref_time, adv_interval;
};
struct search_param {
@@ -206,6 +206,7 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
struct dhcp_opt *opt_cfg;
struct ra_interface *ra_param = find_iface_param(iface_name);
int done_dns = 0, old_prefix = 0;
unsigned int min_pref_time;
#ifdef HAVE_LINUX_NETWORK
FILE *f;
#endif
@@ -228,7 +229,7 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
parm.if_name = iface_name;
parm.first = 1;
parm.now = now;
parm.pref_time = 0;
parm.glob_pref_time = parm.link_pref_time = parm.ula_pref_time = 0;
parm.adv_interval = calc_interval(ra_param);
/* set tag with name == interface */
@@ -245,6 +246,18 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
if (!iface_enumerate(AF_INET6, &parm, add_prefixes))
return;
/* Find smallest preferred time within address classes,
to use as lifetime for options. This is a rather arbitrary choice. */
min_pref_time = 0xffffffff;
if (parm.glob_pref_time != 0 && parm.glob_pref_time < min_pref_time)
min_pref_time = parm.glob_pref_time;
if (parm.ula_pref_time != 0 && parm.ula_pref_time < min_pref_time)
min_pref_time = parm.ula_pref_time;
if (parm.link_pref_time != 0 && parm.link_pref_time < min_pref_time)
min_pref_time = parm.link_pref_time;
/* Look for constructed contexts associated with addresses which have gone,
and advertise them with preferred_time == 0 RFC 6204 4.3 L-13 */
for (up = &daemon->dhcp6, context = daemon->dhcp6; context; context = tmp)
@@ -272,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;
@@ -340,22 +371,48 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
if (opt_cfg->opt == OPTION6_DNS_SERVER)
{
struct in6_addr *a = (struct in6_addr *)opt_cfg->val;
struct in6_addr *a;
int len;
done_dns = 1;
if (opt_cfg->len == 0 || (IN6_IS_ADDR_UNSPECIFIED(a) && parm.pref_time != 0))
if (opt_cfg->len == 0)
continue;
put_opt6_char(ICMP6_OPT_RDNSS);
put_opt6_char((opt_cfg->len/8) + 1);
put_opt6_short(0);
put_opt6_long(parm.pref_time);
/* zero means "self" */
for (i = 0; i < opt_cfg->len; i += IN6ADDRSZ, a++)
if (IN6_IS_ADDR_UNSPECIFIED(a))
put_opt6(&parm.link_global, IN6ADDRSZ);
else
put_opt6(a, IN6ADDRSZ);
/* reduce len for any addresses we can't substitute */
for (a = (struct in6_addr *)opt_cfg->val, len = opt_cfg->len, i = 0;
i < opt_cfg->len; i += IN6ADDRSZ, a++)
if ((IN6_IS_ADDR_UNSPECIFIED(a) && parm.glob_pref_time == 0) ||
(IN6_IS_ADDR_ULA_ZERO(a) && parm.ula_pref_time == 0) ||
(IN6_IS_ADDR_LINK_LOCAL_ZERO(a) && parm.link_pref_time == 0))
len -= IN6ADDRSZ;
if (len != 0)
{
put_opt6_char(ICMP6_OPT_RDNSS);
put_opt6_char((len/8) + 1);
put_opt6_short(0);
put_opt6_long(min_pref_time);
for (a = (struct in6_addr *)opt_cfg->val, i = 0; i < opt_cfg->len; i += IN6ADDRSZ, a++)
if (IN6_IS_ADDR_UNSPECIFIED(a))
{
if (parm.glob_pref_time != 0)
put_opt6(&parm.link_global, IN6ADDRSZ);
}
else if (IN6_IS_ADDR_ULA_ZERO(a))
{
if (parm.ula_pref_time != 0)
put_opt6(&parm.ula, IN6ADDRSZ);
}
else if (IN6_IS_ADDR_LINK_LOCAL_ZERO(a))
{
if (parm.link_pref_time != 0)
put_opt6(&parm.link_local, IN6ADDRSZ);
}
else
put_opt6(a, IN6ADDRSZ);
}
}
if (opt_cfg->opt == OPTION6_DOMAIN_SEARCH && opt_cfg->len != 0)
@@ -365,7 +422,7 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
put_opt6_char(ICMP6_OPT_DNSSL);
put_opt6_char(len + 1);
put_opt6_short(0);
put_opt6_long(parm.pref_time);
put_opt6_long(min_pref_time);
put_opt6(opt_cfg->val, opt_cfg->len);
/* pad */
@@ -374,13 +431,13 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
}
}
if (daemon->port == NAMESERVER_PORT && !done_dns && parm.pref_time != 0)
if (daemon->port == NAMESERVER_PORT && !done_dns && parm.link_pref_time != 0)
{
/* default == us, as long as we are supplying DNS service. */
put_opt6_char(ICMP6_OPT_RDNSS);
put_opt6_char(3);
put_opt6_short(0);
put_opt6_long(parm.pref_time);
put_opt6_long(min_pref_time);
put_opt6(&parm.link_local, IN6ADDRSZ);
}
@@ -426,7 +483,16 @@ static int add_prefixes(struct in6_addr *local, int prefix,
if (if_index == param->ind)
{
if (IN6_IS_ADDR_LINKLOCAL(local))
param->link_local = *local;
{
/* Can there be more than one LL address?
Select the one with the longest preferred time
if there is. */
if (preferred > param->link_pref_time)
{
param->link_pref_time = preferred;
param->link_local = *local;
}
}
else if (!IN6_IS_ADDR_LOOPBACK(local) &&
!IN6_IS_ADDR_MULTICAST(local))
{
@@ -516,11 +582,22 @@ static int add_prefixes(struct in6_addr *local, int prefix,
/* configured time is ceiling */
if (!constructed || preferred > time)
preferred = time;
if (preferred > param->pref_time)
if (IN6_IS_ADDR_ULA(local))
{
param->pref_time = preferred;
param->link_global = *local;
if (preferred > param->ula_pref_time)
{
param->ula_pref_time = preferred;
param->ula = *local;
}
}
else
{
if (preferred > param->glob_pref_time)
{
param->glob_pref_time = preferred;
param->link_global = *local;
}
}
if (real_prefix != 0)
@@ -546,7 +623,6 @@ static int add_prefixes(struct in6_addr *local, int prefix,
if (!option_bool(OPT_QUIET_RA))
my_syslog(MS_DHCP | LOG_INFO, "RTR-ADVERT(%s) %s", param->if_name, daemon->addrbuff);
}
}
}
}

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
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
@@ -16,13 +16,6 @@
#include "dnsmasq.h"
#define CHECK_LEN(header, pp, plen, len) \
((size_t)((pp) - (unsigned char *)(header) + (len)) <= (plen))
#define ADD_RDLEN(header, pp, plen, len) \
(!CHECK_LEN(header, pp, plen, len) ? 0 : (((pp) += (len)), 1))
int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
char *name, int isExtract, int extrabytes)
{
@@ -274,7 +267,7 @@ int in_arpa_name_2_addr(char *namein, struct all_addr *addrp)
return 0;
}
static unsigned char *skip_name(unsigned char *ansp, struct dns_header *header, size_t plen, int extrabytes)
unsigned char *skip_name(unsigned char *ansp, struct dns_header *header, size_t plen, int extrabytes)
{
while(1)
{
@@ -367,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;
@@ -407,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)
{
@@ -500,7 +494,7 @@ unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t
else if (is_sign &&
i == arcount - 1 &&
class == C_ANY &&
(type == T_SIG || type == T_TSIG))
type == T_TSIG)
*is_sign = 1;
}
@@ -515,50 +509,64 @@ struct macparm {
};
static size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit,
int optno, unsigned char *opt, size_t optlen)
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 */
PUTSHORT(T_OPT, p);
PUTSHORT(daemon->edns_pktsz, p); /* max packet length */
PUTLONG(0, p); /* extended RCODE */
PUTSHORT(0, p); /* extended RCODE and version */
PUTSHORT(set_do ? 0x8000 : 0, p); /* DO flag */
lenp = p;
PUTSHORT(0, p); /* RDLEN */
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;
unsigned short code, len;
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;
p += 8; /* skip UDP length and RCODE */
p += 6; /* skip UDP length and RCODE */
GETSHORT(flags, p);
if (set_do)
{
p -=2;
PUTSHORT(flags | 0x8000, p);
}
lenp = p;
GETSHORT(rdlen, p);
if (!CHECK_LEN(header, p, plen, rdlen))
return plen; /* bad packet */
datap = p;
/* no option to add */
if (optno == 0)
return plen;
/* check if option already there */
for (i = 0; i + 4 < rdlen; i += len + 4)
{
@@ -573,10 +581,13 @@ static size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned
return plen; /* Too big */
}
PUTSHORT(optno, p);
PUTSHORT(optlen, p);
memcpy(p, opt, optlen);
p += optlen;
if (optno != 0)
{
PUTSHORT(optno, p);
PUTSHORT(optlen, p);
memcpy(p, opt, optlen);
p += optlen;
}
PUTSHORT(p - datap, lenp);
return p - (unsigned char *)header;
@@ -602,7 +613,7 @@ static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *p
if (!match)
return 1; /* continue */
parm->plen = add_pseudoheader(parm->header, parm->plen, parm->limit, EDNS0_OPTION_MAC, (unsigned char *)mac, maclen);
parm->plen = add_pseudoheader(parm->header, parm->plen, parm->limit, EDNS0_OPTION_MAC, (unsigned char *)mac, maclen, 0);
return 0; /* done */
}
@@ -681,9 +692,16 @@ size_t add_source_addr(struct dns_header *header, size_t plen, char *limit, unio
struct subnet_opt opt;
len = calc_subnet_opt(&opt, source);
return add_pseudoheader(header, plen, (unsigned char *)limit, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len);
return add_pseudoheader(header, plen, (unsigned char *)limit, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0);
}
#ifdef HAVE_DNSSEC
size_t add_do_bit(struct dns_header *header, size_t plen, char *limit)
{
return add_pseudoheader(header, plen, (unsigned char *)limit, 0, NULL, 0, 1);
}
#endif
int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer)
{
/* Section 9.2, Check that subnet option in reply matches. */
@@ -736,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;
@@ -781,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;
}
@@ -819,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;
@@ -828,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--)
@@ -863,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)
@@ -878,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 checking_disabled)
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;
@@ -893,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 = 0;
#endif
}
/* go through the questions. */
@@ -907,8 +930,9 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
int found = 0, cname_count = 5;
struct crec *cpp = NULL;
int flags = RCODE(header) == NXDOMAIN ? F_NXDOMAIN : 0;
int secflag = secure ? F_DNSSECOK : 0;
unsigned long cttl = ULONG_MAX, attl;
namep = p;
if (!extract_name(header, qlen, &p, name, 1, 4))
return 0; /* bad packet */
@@ -966,10 +990,11 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
{
if (!cname_count--)
return 0; /* looped CNAMES */
secflag = 0; /* no longer DNSSEC */
goto cname_loop;
}
cache_insert(name, &addr, now, cttl, name_encoding | F_REVERSE);
cache_insert(name, &addr, now, cttl, name_encoding | secflag | F_REVERSE);
found = 1;
}
@@ -984,10 +1009,10 @@ 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);
cache_insert(NULL, &addr, now, ttl, name_encoding | F_REVERSE | F_NEG | flags | secflag);
}
}
else
@@ -1037,7 +1062,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
{
if (!cname_count--)
return 0; /* looped CNAMES */
newc = cache_insert(name, NULL, now, attl, F_CNAME | F_FORWARD);
newc = cache_insert(name, NULL, now, attl, F_CNAME | F_FORWARD | secflag);
if (newc)
{
newc->addr.cname.target.cache = NULL;
@@ -1080,7 +1105,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
}
#endif
newc = cache_insert(name, &addr, now, attl, flags | F_FORWARD);
newc = cache_insert(name, &addr, now, attl, flags | F_FORWARD | secflag);
if (newc && cpp)
{
cpp->addr.cname.target.cache = newc;
@@ -1100,13 +1125,13 @@ 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 */
if (ttl || cpp)
{
newc = cache_insert(name, NULL, now, ttl ? ttl : cttl, F_FORWARD | F_NEG | flags);
newc = cache_insert(name, NULL, now, ttl ? ttl : cttl, F_FORWARD | F_NEG | flags | secflag);
if (newc && cpp)
{
cpp->addr.cname.target.cache = newc;
@@ -1118,15 +1143,13 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
}
/* Don't put stuff from a truncated packet into the cache.
Don't cache replies where DNSSEC validation was turned off, either
the upstream server told us so, or the original query specified it.
Don't cache replies from non-recursive nameservers, since we may get a
reply containing a CNAME but not its target, even though the target
does exist. */
if (!(header->hb3 & HB3_TC) &&
!(header->hb4 & HB4_CD) &&
(header->hb4 & HB4_RA) &&
!checking_disabled)
!no_cache_dnssec)
cache_end_insert();
return 0;
@@ -1134,7 +1157,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);
@@ -1220,7 +1242,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_NO_RR)) &&
(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
return 1;
@@ -1283,7 +1305,7 @@ int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
/* Found a bogus address. Insert that info here, since there no SOA record
to get the ttl from in the normal processing */
cache_start_insert();
cache_insert(name, NULL, now, ttl, F_IPV4 | F_FORWARD | F_NEG | F_NXDOMAIN | F_CONFIG);
cache_insert(name, NULL, now, ttl, F_IPV4 | F_FORWARD | F_NEG | F_NXDOMAIN);
cache_end_insert();
return 1;
@@ -1353,6 +1375,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);
@@ -1425,7 +1452,8 @@ 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)
{
char *name = daemon->namebuff;
unsigned char *p, *ansp, *pheader;
@@ -1434,28 +1462,39 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
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;
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;
/* 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 */
*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
@@ -1516,6 +1555,88 @@ 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 && (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;
@@ -1606,38 +1727,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_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;
@@ -1758,7 +1918,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;
@@ -1777,69 +1937,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_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 == -1)
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))
{
@@ -1857,8 +2067,11 @@ 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;
ans = 1;
if (!dryrun)
{
@@ -1870,7 +2083,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
}
}
}
if (qtype == T_MX || qtype == T_ANY)
{
int found = 0;
@@ -1893,7 +2106,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)
@@ -2037,7 +2250,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
/* truncation */
if (trunc)
header->hb3 |= HB3_TC;
if (nxdomain)
SET_RCODE(header, NXDOMAIN);
else
@@ -2045,6 +2258,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

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
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

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
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
@@ -24,7 +24,7 @@ struct state {
int clid_len, iaid, ia_type, interface, hostname_auth, lease_allocate;
char *client_hostname, *hostname, *domain, *send_domain;
struct dhcp_context *context;
struct in6_addr *link_address, *fallback;
struct in6_addr *link_address, *fallback, *ll_addr, *ula_addr;
unsigned int xid, fqdn_flags;
char *iface_name;
void *packet_options, *end;
@@ -73,7 +73,8 @@ static void calculate_times(struct dhcp_context *context, unsigned int *min_time
unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name,
struct in6_addr *fallback, size_t sz, struct in6_addr *client_addr, time_t now)
struct in6_addr *fallback, struct in6_addr *ll_addr, struct in6_addr *ula_addr,
size_t sz, struct in6_addr *client_addr, time_t now)
{
struct dhcp_vendor *vendor;
int msg_type;
@@ -93,6 +94,8 @@ unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *if
state.interface = interface;
state.iface_name = iface_name;
state.fallback = fallback;
state.ll_addr = ll_addr;
state.ula_addr = ula_addr;
state.mac_len = 0;
state.tags = NULL;
state.link_address = NULL;
@@ -1269,36 +1272,59 @@ static struct dhcp_netid *add_options(struct state *state, int do_refresh)
continue;
}
if (opt_cfg->opt == OPTION6_DNS_SERVER)
{
done_dns = 1;
if (opt_cfg->len == 0)
continue;
}
if (opt_cfg->opt == OPTION6_REFRESH_TIME)
done_refresh = 1;
o = new_opt6(opt_cfg->opt);
if (opt_cfg->flags & DHOPT_ADDR6)
{
int j;
struct in6_addr *a = (struct in6_addr *)opt_cfg->val;
for (j = 0; j < opt_cfg->len; j+=IN6ADDRSZ, a++)
{
/* zero means "self" (but not in vendorclass options.) */
if (IN6_IS_ADDR_UNSPECIFIED(a))
{
if (!add_local_addrs(state->context))
put_opt6(state->fallback, IN6ADDRSZ);
int len, j;
struct in6_addr *a;
if (opt_cfg->opt == OPTION6_DNS_SERVER)
done_dns = 1;
for (a = (struct in6_addr *)opt_cfg->val, len = opt_cfg->len, j = 0;
j < opt_cfg->len; j += IN6ADDRSZ, a++)
if ((IN6_IS_ADDR_ULA_ZERO(a) && IN6_IS_ADDR_UNSPECIFIED(state->ula_addr)) ||
(IN6_IS_ADDR_LINK_LOCAL_ZERO(a) && IN6_IS_ADDR_UNSPECIFIED(state->ll_addr)))
len -= IN6ADDRSZ;
if (len != 0)
{
o = new_opt6(opt_cfg->opt);
for (a = (struct in6_addr *)opt_cfg->val, j = 0; j < opt_cfg->len; j+=IN6ADDRSZ, a++)
{
if (IN6_IS_ADDR_UNSPECIFIED(a))
{
if (!add_local_addrs(state->context))
put_opt6(state->fallback, IN6ADDRSZ);
}
else if (IN6_IS_ADDR_ULA_ZERO(a))
{
if (!IN6_IS_ADDR_UNSPECIFIED(state->ula_addr))
put_opt6(state->ula_addr, IN6ADDRSZ);
}
else if (IN6_IS_ADDR_LINK_LOCAL_ZERO(a))
{
if (!IN6_IS_ADDR_UNSPECIFIED(state->ll_addr))
put_opt6(state->ll_addr, IN6ADDRSZ);
}
else
put_opt6(a, IN6ADDRSZ);
}
else
put_opt6(a, IN6ADDRSZ);
}
end_opt6(o);
}
}
else
{
o = new_opt6(opt_cfg->opt);
if (opt_cfg->val)
put_opt6(opt_cfg->val, opt_cfg->len);
end_opt6(o);
}
else if (opt_cfg->val)
put_opt6(opt_cfg->val, opt_cfg->len);
end_opt6(o);
}
if (daemon->port == NAMESERVER_PORT && !done_dns)

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
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
@@ -93,7 +93,6 @@ void slaac_add_addrs(struct dhcp_lease *lease, time_t now, int force)
slaac->ping_time = now;
slaac->backoff = 1;
slaac->addr = addr;
slaac->local = context->local6;
/* Do RA's to prod it */
ra_start_unsolicted(now, context);
}

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
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

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
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
@@ -109,10 +109,10 @@ static int check_name(char *in)
if (in[l-1] == '.')
{
if (l == 1) return 0;
in[l-1] = 0;
nowhite = 1;
}
for (; (c = *in); in++)
{
if (c == '.')

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