Compare commits

...

172 Commits

Author SHA1 Message Date
Simon Kelley
e1791f36ea Fix logging of DNSSEC queries in TCP mode. Destination server address was misleading. 2018-10-06 23:23:23 +01:00
Simon Kelley
0fdf3c1f61 Fix dhcp-match-name to match hostname, not complete FQDN.
Also do name matching for DHCPv6.
2018-10-05 23:35:54 +01:00
Simon Kelley
ee1df06aab Tweak strategy for confirming SLAAC addresses.
The code which conirms possible SLAAC addresses associated with
hosts known from DHCPv4 addresses keeps trying at longer and longer
intervals essentially forever, EXCEPT if sending an ICMP ping results
in a HOSTUNREACH error, which terminates the process immediately.

It turns out that this is too drastic. Routing changes associated
with addressing changes can cause temporary HOSTUNREACH problems,
even when an address has not gone forever. Therefore continue
trying in the face of HOSTUNREACH for the first part of the
process. HOSTUNREACH errors will still terminate the process
after it reaches the slow tail of retries.

Thanks to Andrey Vakhitov for help diagnosing this.
2018-10-05 22:22:41 +01:00
Simon Kelley
1e87eba424 Clarify manpage for --auth-sec-servers 2018-10-05 16:49:31 +01:00
Simon Kelley
08933475ab Make interface spec optional in --auth-server.
But make auth-server required when any auth-zones are defined.

The "glue record" field in auth-server is needed to synthesise
SOA and NS records in auth zones, so the --auth-server has to
be specified. If makes sense, however to define one or more
auth-zones that appear within the normal recursive DNS service
without actually acting as an authoritative DNS server on
any interface. Hence making the interface field optional.
2018-10-05 16:44:05 +01:00
Simon Kelley
7cbf497da4 Example config file fix for CERT Vulnerability VU#598349. 2018-09-26 18:04:38 +01:00
Simon Kelley
3a610a007f Finesse allocation of memory for "struct crec" cache entries.
These normally have enough space for a name of up to SMALLDNAME characters.
When used to hold /etc/hosts entries, they are allocated with just enough
bytes for the name held. When used to hold other configured stuff, (CNAMES
DS records. DHCP names etc), the name is replaced by a pointer to a string
held elsewhere, and F_NAMEP set. Hence only enough space to hold a char *
is needed, rather than SMALLDNAME bytes.
2018-09-26 16:50:35 +01:00
Simon Kelley
48b090cb5c Fix b6f926fbef to not SEGV on startup (rarely).
Many thanks to Kristian Evensen  for finding and diagnosing this.

We can't copy the whole of a crec structure in make_non_terminals, since
crec structures allocated to represent /etc/hosts entries are allocated with
just enough space for the actual name they contain, not the full
SMALLDNAME bytes declared in struct crec. Using structure copy therefore
copies beyond the end of the allocated source and, just occaisionally,
into unmapped memory, resulting in a SEGV.

Since the crecs we're making here always have F_NAMEP set, we're not
interested in copying the name field from the source anyway, we use the
namep part of the union and set it to point some way into the name
of the source crec to get the super-domain that we're representing.

The fix is therefore to copy the relevant fields of the crec, rather
than copying the whole and overwriting.
2018-09-26 12:53:59 +01:00
Simon Kelley
4139298d28 Change behavior when RD bit unset in queries.
Change anti cache-snooping behaviour with queries with the
recursion-desired bit unset. Instead to returning SERVFAIL, we
now always forward, and never answer from the cache. This
allows "dig +trace" command to work.
2018-09-19 22:27:11 +01:00
Simon Kelley
51cc10fa54 Add warning about 0.0.0.0 and :: addresses to man page. 2018-09-19 12:49:43 +01:00
Simon Kelley
ea6cc33804 Handle memory allocation failure in make_non_terminals()
Thanks to Kristian Evensen for spotting the problem.
2018-09-18 23:21:17 +01:00
Simon Kelley
ad03967ee4 Add debian/tmpfiles.conf 2018-09-17 23:54:13 +01:00
Simon Kelley
f4fd07d303 Debian bugfix. 2018-09-17 23:45:32 +01:00
Simon Kelley
e3c08a34a7 Debian packaging fix. (restorecon) 2018-09-17 23:20:00 +01:00
Simon Kelley
118011fe2b Debian packaging fix. (tmpfiles.d) 2018-09-17 23:15:37 +01:00
Simon Kelley
af3bd07355 Man page typo. 2018-09-08 15:08:22 +01:00
Simon Kelley
d68209978a Picky changes to 47b45b2967 2018-09-04 23:00:11 +01:00
Petr Menšík
47b45b2967 Fix lengths of interface names
Use helper function similar to copy correctly limited names into
buffers.
2018-09-04 22:47:58 +01:00
Petr Menšík
2b38e3823b Minor improvements in lease-tools
Limit max interface name to fit into buffer.
Make sure pointer have to be always positive.
Close socket after received reply.
2018-09-04 22:36:23 +01:00
Petr Menšík
282eab7952 Mark die function as never returning
Improves static analysis output and reduces false positives.
2018-09-04 22:32:51 +01:00
Simon Kelley
c346f61535 Handle ANY queries in context of da8b6517de 2018-09-04 21:14:18 +01:00
Simon Kelley
03212e533b Manpage typo. 2018-09-04 17:52:28 +01:00
Simon Kelley
da8b6517de Implement --address=/example.com/#
as (more efficient) syntactic sugar for --address=/example.com/0.0.0.0 and --address=/example.com/::
2018-09-03 23:18:36 +01:00
Simon Kelley
c5db8f93ec Tidy 7f876b64c22b2b18412e2e3d8506ee33e42db7c 2018-08-23 23:06:00 +01:00
Simon Kelley
974a6d087a Add --caa-record 2018-08-23 23:01:16 +01:00
Simon Kelley
b758b67c37 Improve logging of RRs from --dns-rr. 2018-08-23 21:41:23 +01:00
Simon Kelley
9bafdc62b7 Tidy up file parsing code. 2018-08-21 22:53:38 +01:00
Simon Kelley
97f876b64c Properly deal with unaligned addresses in DHCPv6 packets.
Thanks to Vladislav Grishenko for spotting this.
2018-08-21 22:06:36 +01:00
Simon Kelley
cbfbd173c4 Fix broken DNSSEC records in previous. 2018-08-21 18:25:18 +01:00
Simon Kelley
b6f926fbef Don't return NXDOMAIN to empty non-terminals.
When a record is defined locally, eg an A record for one.two.example then
we already know that if we forward, eg an AAAA query for one.two.example,
and get back NXDOMAIN, then we need to alter that to NODATA. This is handled
by  check_for_local_domain(). But, if we forward two.example, because
one.two.example exists, then the answer to two.example should also be
a NODATA.

For most local records this is easy, just to substring matching.
for A, AAAA and CNAME records that are in the cache, it's more difficult.
The cache has no efficient way to find such records. The fix is to
insert empty (none of F_IPV4, F_IPV6 F_CNAME set) records for each
non-terminal.

The same considerations apply in auth mode, and the same basic mechanism
is used there too.
2018-08-21 17:46:52 +01:00
Simon Kelley
c822620967 Add --dhcp-name-match 2018-08-08 23:46:03 +01:00
Simon Kelley
397c0502e2 Handle case of --auth-zone but no --auth-server. 2018-08-04 21:04:59 +01:00
Simon Kelley
1682d15a74 Add missing EDNS0 section.
EDNS0 section missing in replies to EDNS0-containing queries where
answer generated from --local=/<domain>/
2018-08-03 20:38:18 +01:00
Simon Kelley
dd33e98da0 Fix crash parsing a --synth-domain with no prefix.
Problem introduced in 2.79/6b2b564ac34cb3c862f168e6b1457f9f0b9ca69c
2018-07-30 14:55:39 +01:00
Simon Kelley
c16d966ad3 Add copyright to src/metrics.h 2018-07-29 22:31:02 +01:00
Simon Kelley
1dfed16071 Remove C99 only code. 2018-07-29 22:16:41 +01:00
Simon Kelley
6f835ed6c8 Format fixes - ubus.c 2018-07-29 22:15:36 +01:00
Kevin Darbyshire-Bryant
9d6fd1727e dnsmasq.c fix OPT_UBUS option usage
Signed-off-by: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
2018-07-29 22:11:12 +01:00
Simon Kelley
8c1b6a5fd7 New metrics and ubus files. 2018-07-21 22:12:32 +01:00
Julian Kornberger
8dcdb33be9 Add --enable-ubus option. 2018-07-21 22:11:08 +01:00
Julian Kornberger
aba8bbb6e3 Add collection of metrics
Data can be retreived via D-Bus und U-Bus
2018-07-21 21:55:08 +01:00
Julian Kornberger
caf4d571e6 Add OpenWRT ubus patch
Originally written by John Crispin <john@phrozen.org>
2018-07-21 21:45:03 +01:00
Simon Kelley
3b6eb197a8 Log DNSSEC trust anchors at startup. 2018-07-20 21:23:56 +01:00
Simon Kelley
f3e57877ed Trivial comment change. 2018-07-20 21:10:48 +01:00
Simon Kelley
c851c695db Log failure to confirm an address in DHCPv6. 2018-07-20 17:35:24 +01:00
Simon Kelley
a3bd7e73d3 Fix missing fatal errors when parsing some command-line/config options. 2018-07-19 22:00:08 +01:00
Olivier Gayot
ab5ceaf74a Document the --help option in the french manual
The option was already described in the original manual page but was not
replicated in the french translation.

Reviewed-By: Nicolas Cavallari <nicolas.cavallari@green-communications.fr>
2018-07-19 21:17:06 +01:00
Olivier Gayot
1f2f69d4f6 Fix recurrent minor spelling mistake in french manual
The use of "peut-être" should be spelled "peut être" (without the
hyphen) unless it can be replaced by "sans doute".

It is roughly the same difference between "maybe" and "may be".

As for "doit-être", it should always be spelled "doit être".

Reviewed-By: Nicolas Cavallari <nicolas.cavallari@green-communications.fr>
2018-07-19 21:16:41 +01:00
Olivier Gayot
f361b39dea Fix some mistakes in french translation of the manual
Various typos were present along with spelling mistakes and grammar
errors. Some sentences were missing a few words to be easily
understandable.

Many of them probably remain though.

Reviewed-By: Nicolas Cavallari <nicolas.cavallari@green-communications.fr>
2018-07-19 21:16:04 +01:00
Simon Kelley
eb1fe15ca8 When replacing cache entries, preserve CNAMES which target them.
When inserting new cache records, we first delete existing records
of the same name/type, to maintain consistency. This has the side effect
of deleting any CNAMES which have the records as target. So

cname1.example CNAME record.example
cname2.example CNAME record.example

looking up cname2.example will push it into the cache, and also
push record.example. Doing that deletes any cache of cname1.example.

This changeset avoids that problem by making sure that when
deleting record.example, and re-insterting it (with the same name -important),
it uses the same struct crec, with the same uid. This preserves the existing cnames.
2018-07-18 20:59:52 +01:00
Simon Kelley
45d8a2435e Introduce UID_NONE value, for cache records which are not the target of a CNAME. 2018-07-17 21:01:14 +01:00
Simon Kelley
706d84fd10 Merge branch 'master' of ssh://thekelleys.org.uk/var/local/git/dnsmasq 2018-07-16 18:07:15 +01:00
Simon Kelley
a997ca0da0 Fix sometimes missing DNSSEC RRs when DNSSEC validation not enabled.
Dnsmasq does pass on the do-bit, and return DNSSEC RRs, irrespective
of of having DNSSEC validation compiled in or enabled.

The thing to understand here is that the cache does not store all the
DNSSEC RRs, and dnsmasq doesn't have the (very complex) logic required
to determine the set of DNSSEC RRs required in an answer. Therefore if
the client wants the DNSSEC RRs, the query can not be answered from
the cache. When DNSSEC validation is enabled, any query with the
do-bit set is never answered from the cache, unless the domain is
known not to be signed: the query is always forwarded. This ensures
that the DNSEC RRs are included.

The same thing should be true when DNSSEC validation is not enabled,
but there's a bug in the logic.

line 1666 of src/rfc1035.c looks like this

 if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || !do_bit || !(crecp->flags & F_DNSSECOK))

{ ...answer from cache ... }

So local stuff (hosts, DHCP, ) get answered. If the do_bit is not set
then the query is answered, and if the domain is known not to be
signed, the query is answered.

Unfortunately, if DNSSEC validation is not turned on then the
F_DNSSECOK bit is not valid, and it's always zero, so the question
always gets answered from the cache, even when the do-bit is set.

This code should look like that at line 1468, dealing with PTR queries

  if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) ||
      !do_bit ||
      (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK)))

where the F_DNSSECOK bit is only used when validation is enabled.
2018-06-29 14:39:41 +01:00
Peter Pöschl
9268b5d677 Man page: uniform formatting style for options.
* Always use the long option form, except when options are introduced.
* Render options in bold, with '--' prefix.
2018-06-12 17:04:54 +01:00
Paul Maddock
51e4eeeb04 Fix address-dependent domains for IPv6.
Thanks to Paul Maddock for spotting this.
It seems to have been broken forever.
2018-06-12 16:37:40 +01:00
Simon Kelley
05ff659a3c Fix stupid infinite loop introduced by preceding commit. 2018-06-12 16:03:09 +01:00
Simon Kelley
db0f488ea8 Handle some corner cases in RA contructed interfaces with addresses changing interface.
Thanks to Vladislav Grishenko for work on this.
2018-06-07 21:37:02 +01:00
Geert Stappers
7dcca6c622 Warn about the impact of cache-size on performance. 2018-06-02 18:54:04 +01:00
Simon Kelley
090856c7e6 Allow zone transfer in authoritative mode whenever auth-peer is specified. 2018-06-02 18:37:07 +01:00
Simon Kelley
cc5cc8f1e0 Sane error message when pcap file header is wrong. 2018-06-02 14:45:17 +01:00
Simon Kelley
c488b68e75 Handle standard and contructed dhcp-ranges on the same interface. 2018-06-02 13:06:00 +01:00
Simon Kelley
1f1873aadd Log warning on very large cachesize config, instead of truncating it. 2018-05-11 23:38:23 +01:00
Maarten de Vries
0a496f059c Do unsolicited RAs for interfaces which appear after dnsmasq startup.
I noticed that dnsmasq often wasn't sending any unsolicited RAs for me.

This turned out to happen when the interface (a bridge interface) wasn't
created yet at the time dnsmasq started. When dnsmasq is started after
the interface is created, it sends RAs as expected. I assume this also
extends to other types of virtual interfaces that are created after
dnsmasq starts.

Digging into the source, it seems to be caused by a missing call to
ra_start_unsolicited for non-template contexts in construct_worker from
src/dhcp6.c. The attached patch adds that call, but only if the
interface index or address changed to prevent doing fast RAs for no reason.

I tested it on my own server and it appears to work as expected. When
the interface is created and configured, dnsmasq does fast RAs for a
while and then settles into slow RAs.
2018-05-11 23:20:58 +01:00
Simon Kelley
e27825b0ef Fix logging in previous. 2018-05-11 17:20:47 +01:00
Simon Kelley
1f60a18ea1 Retry SERVFAIL DNSSEC queries to a different server, if possible. 2018-05-11 16:44:16 +01:00
Simon Kelley
a0088e8364 Handle query retry on REFUSED or SERVFAIL for DNSSEC-generated queries. 2018-05-10 21:43:14 +01:00
Simon Kelley
34e26e14c5 Retry query to other servers on receipt of SERVFAIL rcode. 2018-05-10 20:54:57 +01:00
Simon Kelley
6b17335209 Add packet-dump debugging facility. 2018-05-08 18:32:14 +01:00
Simon Kelley
07ed585c38 Add logging for DNS error returns from upstream and local configuration. 2018-05-04 21:52:22 +01:00
Simon Kelley
0669ee7a69 Fix DHCP broken-ness when --no-ping AND --dhcp-sequential-ip are set. 2018-05-04 16:46:24 +01:00
Simon Kelley
f84e674d8a Be persistent with broken-upstream-DNSSEC warnings. 2018-05-04 16:29:57 +01:00
Simon Kelley
7f0084316a Handle DNSSEC-unaware upstream servers better. 2018-04-15 20:01:49 +01:00
Simon Kelley
a6918530ce Change default for dnssec-check-unsigned. 2018-04-15 16:20:52 +01:00
Simon Kelley
4e72fec660 Fix DNSSEC without dnssec-check-unsigned.
An oversight meant that non-existance checking was being done
anyway.

(Should probably alter the default for this.)
2018-04-11 22:49:31 +01:00
Simon Kelley
4441cf762c Fix DNS server fd garbage collection.
If we're talking to upstream servers from a fixed port, specified by query-port
we create the fds to do this once, before dropping root, so that ports <1024 can be used.

But we call check_servers() before reading /etc/resolv.conf, so if the only servers
are in resolv.conf, at that point there will be no servers, and the fds get garbage
collected away, only to be recreated (but without root) after we read /etc/resolv.conf

Make pre-allocated server fds immortal, to avoid this problem.
2018-04-10 21:39:54 +01:00
Simon Kelley
e83915d10d Set V6ONLY on DNS upstream socket.
If query-port is set, we create sockets bound to the wildcard address and the query port for
IPv4 and IPv6, but the IPv6 one fails, because is covers IPv4 as well, and an IPv4 socket
already exists (it gets created first). Set V6ONLY to avoid this.
2018-04-10 21:27:26 +01:00
Kevin Darbyshire-Bryant
6d37924b86 Makefile: Stop creating '-d' directory
Install-common section was creating superfluous '-d' directory in build
location.

Split the directory creation into individual install commands to cope
with cross platform differences of interpreting subsequent '-d'
arguments.  e.g. GNU appears to be fine.  Apple creates the stray
directory.

Signed-off-by: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
2018-04-07 23:00:56 +01:00
Simon Kelley
f3d7974e86 Fixed bug number in changelog s/833596/883596/ 2018-03-26 13:23:59 +01:00
Simon Kelley
734d53176f Add RFC4039 rapid commit support. 2018-03-23 23:09:53 +00:00
Simon Kelley
9a7be47614 Add lintian override in Debian package. 2018-03-23 20:45:53 +00:00
Simon Kelley
26e27d0015 Symbolic link for /usr/share/doc/dnsmasq-base-lua in Debian package. 2018-03-18 17:29:12 +00:00
Simon Kelley
94b6878821 Tidy crypto.c of old library compat. Now need libnettle 3. 2018-03-17 18:39:23 +00:00
Simon Kelley
8b96552f0d Fix compiler warning. 2018-03-10 20:44:17 +00:00
Simon Kelley
ae290659de Fix debian/changelog syntax. 2018-03-10 20:28:32 +00:00
Simon Kelley
6b2b564ac3 Enhance --synth-domain to allow names with sequential integers. 2018-03-10 20:25:57 +00:00
Simon Kelley
4f7bb57e97 Fix deletion of dhcp-options from inotify dynamic files.
These were not deleted except in the case that a dhcp-optsfile
option was coincidentally provided.
2018-03-08 18:47:08 +00:00
Petr Menšík
56f0623930 Allow trailing dot in CNAME.
I got reported bug in Fedora [1], that cname is broken in new releases.
At first I though this was false report, but there is still new
regression in cname handling.

Before, it accepted alias with trailing dot. Not it would accept only
target, but not alias.

cname=alias.,target

is no longer valid. The issue is it will count size to skip after
canonicalize. If that ignores trailing dot, next name would be "". And
that is invalid and refused, dnsmasq refuses to start.

I also think that any whitespace like tab should be possible after
comma. So this fixes also 30858e3b9b.
2018-03-06 23:13:32 +00:00
Simon Kelley
f3223fbff6 Fix nettle_hash() function to avoid ABI incompatibilities.
The way of accessing the list of available hashes on nettle was
vulnerable to breaking if the version of libnettle in use was
different to the version dnsmasq was compiled against.
Change to a new system if libnettle >= 3.4 is in use.
Older versions if nettle are still OK, once 3.4 is reached,
the ABi problem is fixed. Thanks to Petr Menšík for clues on this.
2018-03-06 22:55:36 +00:00
Simon Kelley
4c4f4c2649 Debian dependency tweaking for new dnsmasq-base-lua package. 2018-02-16 22:43:29 +00:00
Ville Skyttä
773af304ea Man page typo fix. 2018-02-16 21:47:55 +00:00
Simon Kelley
4cc944b0d6 Merge branch 'master' of ssh://thekelleys.org.uk/var/local/git/dnsmasq 2018-02-16 21:30:21 +00:00
Simon Kelley
87e00feb01 Compiler warning fixes. 2018-02-16 21:29:32 +00:00
Simon Kelley
e7a4af8903 Compiler warning fixes. 2018-02-16 21:27:35 +00:00
Simon Kelley
2d69d6146d Add liblua-dev to Debian build-depends. 2018-02-16 21:11:17 +00:00
Simon Kelley
30e4a9441e Debian package: add dnsmasq-base-lua binary package. 2018-02-16 19:56:56 +00:00
Simon Kelley
232a8f3569 Merge messages for release. 2018-02-14 23:02:34 +00:00
Simon Kelley
1721453d51 Remove special handling of A-for-A queries. 2018-02-14 22:56:09 +00:00
yiwenchen
499d8dde2b Fix boundary for test introduced in 3e3f1029c9ec6c63e430ff51063a6301d4b2262
This fixes breakage of  DHCPv6 relay.
2018-02-14 22:26:54 +00:00
Andy Hawkins
6f1cbfd000 Fix debian/readme typo. 2018-02-14 21:35:56 +00:00
Andy Hawkins
55ecde7f1b Inotify: Ignore backup files created by editors
Use strlen to determine the length of the filename returned by
inotify, as in->len refers to the length of the buffer containing
the name, not the length of the name itself.

http://lists.thekelleys.org.uk/pipermail/dnsmasq-discuss/2018q1/011950.html

Signed-off-by: Andy Hawkins <andy@gently.org.uk>

Patch further modified by simon@thekelleys.org to avoid
out-of-bounds array access with an empty string, call strlen once,
and reverse order of filename verifcation and resolv-file test.
2018-02-14 18:36:47 +00:00
Simon Kelley
6b54d69a85 Make failure to chown() pidfile a warning. 2018-02-08 21:32:16 +00:00
Simon Kelley
246a31cd73 Change ownership of pid file, to keep systemd happy. 2018-02-06 17:27:55 +00:00
Simon Kelley
83e4b73596 Remove confusion between --user and --script-user. 2018-02-06 16:57:15 +00:00
Simon Kelley
6340ca734f Tweak heuristic for initial DNSSEC memory allocation. 2018-01-30 21:39:01 +00:00
Simon Kelley
baf553db0c Default min-port to 1024 to avoid reserved ports. 2018-01-29 23:04:06 +00:00
Kurt H Maier
486bcd5a7b Simplify and correct bindtodevice(). 2018-01-26 15:10:59 +00:00
Simon Kelley
be9a74d2f8 Close Debian bug for CVE-2017-15107. 2018-01-26 14:36:32 +00:00
Leon M. George
ffcbc0f011 Example config typo fixes. 2018-01-26 13:17:41 +00:00
Simon Kelley
a969ba6e2a Special case NSEC processing for root DS record, to avoid spurious BOGUS. 2018-01-20 23:08:38 +00:00
Simon Kelley
f1781728af Add homepage to Debian control file. 2018-01-20 22:18:54 +00:00
Simon Kelley
cd7df612b1 Fix DNSSEC validation errors introduced in 4fe6744a22 2018-01-20 00:10:55 +00:00
Simon Kelley
c1a4e257a3 Try to be a little more clever at falling back to smaller DNS packet sizes. 2018-01-19 22:00:05 +00:00
Simon Kelley
4fe6744a22 DNSSEC fix for wildcard NSEC records. CVE-2017-15107 applies.
It's OK for NSEC records to be expanded from wildcards,
but in that case, the proof of non-existence is only valid
starting at the wildcard name, *.<domain> NOT the name expanded
from the wildcard. Without this check it's possible for an
attacker to craft an NSEC which wrongly proves non-existence
in a domain which includes a wildcard for NSEC.
2018-01-19 12:39:46 +00:00
Neil Jerram
3bd4c47f31 Remove limit on length of command-line options. 2018-01-18 22:49:38 +00:00
Artem Poloznikov
98196c4931 Typo fix. 2018-01-18 22:14:26 +00:00
Simon Kelley
22cd860124 Allow more than one --bridge-interface option to refer to an interface. 2018-01-14 22:57:14 +00:00
Simon Kelley
3c973ad92d Use SIGINT (instead of overloading SIGHUP) to turn on DNSSEC time validation. 2018-01-14 21:40:56 +00:00
Ville Skyttä
faaf306a63 Spelling fixes. 2018-01-14 17:32:52 +00:00
Geert Stappers
c7e6aea81b Change references to gPXE to iPXE.
Development of EtherBoot gPXE was always development
of iPXE core developer Michael Brown.

http://git.etherboot.org/?p=gpxe.git was last updated in 2011
https://git.ipxe.org/ipxe.git is well alive

This  s/gPXE/iPXE/ reflects that.

Signed-off-by: Geert Stappers <stappers@stappers.nl>
2018-01-13 17:56:37 +00:00
Simon Kelley
e541245987 Handle duplicate RRs in DNSSEC validation.
RFC 4034 says:
  [RFC2181] specifies that an RRset is not allowed to contain duplicate
  records (multiple RRs with the same owner name, class, type, and
  RDATA).  Therefore, if an implementation detects duplicate RRs when
  putting the RRset in canonical form, it MUST treat this as a protocol
  error.  If the implementation chooses to handle this protocol error
  in the spirit of the robustness principle (being liberal in what it
  accepts), it MUST remove all but one of the duplicate RR(s) for the
  purposes of calculating the canonical form of the RRset.

We chose to handle this robustly, having found at least one recursive
server in the wild which returns duplicate NSEC records in the AUTHORITY
section of an answer generated from a wildcard record. sort_rrset() is
therefore modified to delete duplicate RRs which are detected almost
for free during the bubble-sort process.

Thanks to Toralf Förster for helping to diagnose this problem.
2018-01-06 22:16:31 +00:00
Simon Kelley
84a01bee10 Bump year in Debian copyright notice. 2018-01-03 15:16:09 +00:00
Simon Kelley
d1ced3ae38 Update copyrights to 2018. 2018-01-01 22:18:03 +00:00
Simon Kelley
a6cee69af4 Fix exit code from dhcp_release6. 2017-12-14 22:40:48 +00:00
Simon Kelley
0039920ab6 Severely fix code formating of contrib/lease-tools/dhcp_release6.c 2017-12-14 22:29:31 +00:00
Simon Kelley
39d8550a80 Run Debian startup regex in "C" locale. 2017-12-14 21:23:34 +00:00
Simon Kelley
ef3d137a64 Fix infinite retries in strict-order mode.
If all configured dns servers return refused in
 response to a query; dnsmasq will end up in an infinite loop
 retransmitting the dns query resulting into high CPU load.
 Problem is caused by the dns refuse retransmission logic which does
 not check for the end of a dns server list iteration in strict mode.
 Having one configured dns server returning a refused reply easily
 triggers this problem in strict order mode. This was introduced in
 9396752c11

 Thanks to Hans Dedecker <dedeckeh@gmail.com> for spotting this
 and the initial patch.
2017-12-05 22:37:29 +00:00
Simon Kelley
8c707e1e37 Make 373e917389 compile without DNSSEC. 2017-12-05 22:28:10 +00:00
Simon Kelley
373e917389 Fix a6004d7f17 to cope with >256 RRs in answer section. 2017-12-01 22:40:56 +00:00
Josh Soref
74f0f9a042 Commment language tweaks. 2017-12-01 21:38:27 +00:00
李三0159
ed6bdb0967 Man page typos. 2017-11-30 16:47:01 +00:00
Simon Kelley
c88af046b7 Modify doc.html to mention git-over-http is now available. 2017-11-15 21:23:43 +00:00
Dr. Markus Waldeck
ae0187d454 Fix trust-anchor regexp in Debian init script. 2017-11-07 22:53:06 +00:00
Simon Kelley
0c50e3ddc8 Bump version in Debian package. 2017-11-07 22:49:20 +00:00
Petr Menšík
075366ad6e Open inotify socket only when used.
Some of our Openstack users run quite large number of dnsmasq instances
on single host. They started hitting default limit of inotify socket
number on single system after upgrade to more recent version. System
defaults of sysctl fs.inotify.max_user_instances is 128. They reached
limit of 116 dnsmasq instances, then more instances failed to start.

I was surprised they have any use case for such high number of
instances. They use one dnsmasq for one virtual network.

I found simple way to avoid hitting low system limit. They do not use
resolv.conf for name server configuration or any dhcp hosts or options
directory. Created inotify socket is never used in that case. Simple
patch attached.

I know we can raise inotify system limit. I think better is to not waste
resources that are left unused.
2017-11-05 16:05:39 +00:00
Simon Kelley
8e8b2d6f63 Release notes update. 2017-10-30 23:21:52 +00:00
Simon Kelley
087eb76140 Always return a SERVFAIL response to DNS queries with RD=0.
Unless we are acting in authoritative mode, obviously.

To do otherwise may allows cache snooping, see.
http://cs.unc.edu/~fabian/course_papers/cache_snooping.pdf
2017-10-30 23:16:54 +00:00
Simon Kelley
ebedcbaeb8 Typo in printf format string added in 22dee512f3 2017-10-29 20:54:17 +00:00
Simon Kelley
0954a977c9 Remove RSA/MD5 DNSSEC algorithm.
This is set to status DoNotImplement in RFC 6944.
2017-10-27 23:26:51 +01:00
Simon Kelley
b77efc1948 Tidy DNSSEC algorithm table use. 2017-10-27 23:23:53 +01:00
Simon Kelley
3b0cb34710 Fix manpage which said ZSK but meant KSK. 2017-10-27 22:54:35 +01:00
Simon Kelley
aa6f832d61 Add a few DNS RRs to the table. 2017-10-27 22:52:26 +01:00
Simon Kelley
ad9c6f06c5 Add support for Ed25519 DNSSEC signature algorithm. 2017-10-27 22:13:49 +01:00
Simon Kelley
a6004d7f17 Fix caching logic for validated answers.
The current logic is naive in the case that there is more than
one RRset in an answer (Typically, when a non-CNAME query is answered
by one or more CNAME RRs, and then then an answer RRset.)

If all the RRsets validate, then they are cached and marked as validated,
but if any RRset doesn't validate, then the AD flag is not set (good) and
ALL the RRsets are cached marked as not validated.

This breaks when, eg, the answer contains a validated CNAME, pointing
to a non-validated answer. A subsequent query for the CNAME without do
will get an answer with the AD flag wrongly reset, and worse, the same
query with do will get a cached answer without RRSIGS, rather than
being forwarded.

The code now records the validation of individual RRsets and that
is used to correctly set the "validated" bits in the cache entries.
2017-10-25 17:48:19 +01:00
Simon Kelley
c366717e66 Tidy up add_resource_record() buffer size checks.
Mainly code-size and readability fixes.

Also return NULL from do_rfc1035_name() when limit exceeded, so
that truncated bit gets set in answer.
2017-10-13 23:26:29 +01:00
Simon Kelley
22dee512f3 Log DNS server max packet size reduction. 2017-10-13 22:54:00 +01:00
Simon Kelley
6fd5d79e73 Fix logic on EDNS0 headers.
The logic to determine is an EDNS0 header was added was wrong. It compared
the packet length before and after the operations on the EDNS0 header,
but these can include adding options to an existing EDNS0 header. So
a query may have an existing EDNS0 header, which is extended, and logic
thinks that it had a header added de-novo.

Replace this with a simpler system. Check if the packet has an EDSN0 header,
do the updates/additions, and then check again. If it didn't have one
initially, but it has one laterly, that's the correct condition
to strip the header from a reply, and to assume that the client
cannot handle packets larger than 512 bytes.
2017-10-13 22:26:40 +01:00
Simon Kelley
9d6918d32c Use IP[V6]_UNICAST_IF socket option instead of SO_BINDTODEVICE for DNS.
dnsmasq allows to specify a interface for each name server passed with
the -S option or pushed through D-Bus; when an interface is set,
queries to the server will be forced via that interface.

Currently dnsmasq uses SO_BINDTODEVICE to enforce that traffic goes
through the given interface; SO_BINDTODEVICE also guarantees that any
response coming from other interfaces is ignored.

This can cause problems in some scenarios: consider the case where
eth0 and eth1 are in the same subnet and eth0 has a name server ns0
associated.  There is no guarantee that the response to a query sent
via eth0 to ns0 will be received on eth0 because the local router may
have in the ARP table the MAC address of eth1 for the IP of eth0. This
can happen because Linux sends ARP responses for all the IPs of the
machine through all interfaces. The response packet on the wrong
interface will be dropped because of SO_BINDTODEVICE and the
resolution will fail.

To avoid this situation, dnsmasq should only restrict queries, but not
responses, to the given interface. A way to do this on Linux is with
the IP_UNICAST_IF and IPV6_UNICAST_IF socket options which were added
in kernel 3.4 and, respectively, glibc versions 2.16 and 2.26.

Reported-by: Hector Martin <marcan@marcan.st>
Signed-off-by: Beniamino Galvani <bgalvani@redhat.com>
2017-10-13 17:55:09 +01:00
Simon Kelley
a49c5c2265 Fix search_servers() segfault with DNSSEC.
--address=/example.com/<IP> would segfault if query on example.com
was generated as part of DNSSEC validation.
2017-10-10 22:04:59 +01:00
Simon Kelley
30858e3b9b Spaces in CNAME options break parsing.
cname = wibble,wobble

works, but

cname =	wibble, wobble

fails. Bug introduced in 2.77. commit a1d973f987
2017-10-09 22:36:11 +01:00
Simon Kelley
30df7efc96 Merge i18n messages. 2017-10-02 14:13:51 +01:00
Simon Kelley
3e8c42cba5 Debian changlelog update. 2017-09-29 17:39:26 +01:00
Simon Kelley
62cb936cb7 Security fix, CVE-2017-14491, DNS heap buffer overflow.
Further fix to 0549c73b7e
Handles case when RR name is not a pointer to the question,
only occurs for some auth-mode replies, therefore not
detected by fuzzing (?)
2017-09-26 22:00:11 +01:00
Simon Kelley
39921d03ba Update credits for Google security team. 2017-09-26 18:43:19 +01:00
Simon Kelley
6a0b00f0d6 Misc code cleanups arising from Google analysis.
No security impleications or CVEs.
2017-09-25 20:19:55 +01:00
Simon Kelley
51eadb692a Security fix, CVE-2017-14495, OOM in DNS response creation.
Fix out-of-memory Dos vulnerability. An attacker which can
send malicious DNS queries to dnsmasq can trigger memory
allocations in the add_pseudoheader function
The allocated memory is never freed which leads to a DoS
through memory exhaustion. dnsmasq is vulnerable only
if one of the following option is specified:
--add-mac, --add-cpe-id or --add-subnet.
2017-09-25 20:16:50 +01:00
Simon Kelley
897c113fda Security fix, CVE-2017-14496, Integer underflow in DNS response creation.
Fix DoS in DNS. Invalid boundary checks in the
add_pseudoheader function allows a memcpy call with negative
size An attacker which can send malicious DNS queries
to dnsmasq can trigger a DoS remotely.
dnsmasq is vulnerable only if one of the following option is
specified: --add-mac, --add-cpe-id or --add-subnet.
2017-09-25 20:11:58 +01:00
Simon Kelley
33e3f1029c Security fix, CVE-2017-14494, Infoleak handling DHCPv6 forwarded requests.
Fix information leak in DHCPv6. A crafted DHCPv6 packet can
cause dnsmasq to forward memory from outside the packet
buffer to a DHCPv6 server when acting as a relay.
2017-09-25 20:05:11 +01:00
Simon Kelley
3d4ff1ba84 Security fix, CVE-2017-14493, DHCPv6 - Stack buffer overflow.
Fix stack overflow in DHCPv6 code. An attacker who can send
a DHCPv6 request to dnsmasq can overflow the stack frame and
crash or control dnsmasq.
2017-09-25 19:59:54 +01:00
Simon Kelley
24036ea507 Security fix, CVE-2017-14492, DHCPv6 RA heap overflow.
Fix heap overflow in IPv6 router advertisement code.
This is a potentially serious security hole, as a
crafted RA request can overflow a buffer and crash or
control dnsmasq. Attacker must be on the local network.
2017-09-25 19:59:27 +01:00
Simon Kelley
0549c73b7e Security fix, CVE-2017-14491 DNS heap buffer overflow.
Fix heap overflow in DNS code. This is a potentially serious
security hole. It allows an attacker who can make DNS
requests to dnsmasq, and who controls the contents of
a domain, which is thereby queried, to overflow
(by 2 bytes) a heap buffer and either crash, or
even take control of, dnsmasq.
2017-09-25 18:17:11 +01:00
Christian Hesse
b697fbb7f1 Do not include stdio.h before dnsmasq.h
We define some constants in dnsmasq.h, which have an influence on
stdio.h. So do not include stdio.h before dnsmasq.h.
2017-09-25 17:36:24 +01:00
Rasmus Ahlberg
96e063c43d Update contrib/try-all-ns. 2017-09-25 17:30:59 +01:00
Chris Novakovic
4e841da1a6 Fix broken translations after commit 730c6745 2017-09-25 17:21:49 +01:00
Simon Kelley
09ce307bdb Disable libIDN2 underscore workaround with libIDN or fixed libIDN2. 2017-09-25 16:53:55 +01:00
Simon Kelley
a3303e196e Don't return arcount=1 if EDNS0 RR won't fit in the packet.
Omitting the EDNS0 RR but setting arcount gives a malformed packet.
Also, don't accept UDP packet size less than 512 in recieved EDNS0.
2017-09-07 20:45:00 +01:00
Simon Kelley
63437ffbb5 Fix CVE-2017-13704, which resulted in a crash on a large DNS query.
A DNS query recieved by UDP which exceeds 512 bytes (or the EDNS0 packet size,
if different.) is enough to cause SIGSEGV.
2017-09-06 22:34:21 +01:00
Simon Kelley
69a815aa8f Fix loss of undercores in domain names when using libidn2.
libidn2 strips underscores from international domain names
when encoding them. Indeed, it strips underscores even if
no encoding is necessary, which breaks SRV records.

Don't submit domain names to IDN encoding if they contain
one or more underscores to fix this.
2017-07-08 21:20:16 +01:00
Simon Kelley
1d224949cc Remove ping-check of configured DHCP address.
This was added in 5ce3e76fbf but
it trips over too many buggy clients that leave an interface configured
even in DHCPDISCOVER case.
2017-07-08 20:52:55 +01:00
Simon Kelley
391f708a09 Man page tweak. 2017-07-08 20:48:51 +01:00
Rosen Penev
cbd29e5da8 Printf related fixes. 2017-06-27 22:29:51 +01:00
Rosen Penev
50a2841d34 Fix function declarations. 2017-06-27 22:27:02 +01:00
Hans Dedecker
9396752c11 Try other servers if first returns REFUSED when --strict-order active.
If a DNS server replies REFUSED for a given DNS query in strict order mode
no failover to the next DNS server is triggered as the failover logic only
covers non strict mode.
As a result the client will be returned the REFUSED reply without first
falling back to the secondary DNS server(s).

Make failover support work as well for strict mode config in case REFUSED is
replied by deleting the strict order check and rely only on forwardall being
equal to 0 which is the case in non strict mode when a single server has been
contacted or when strict order mode has been configured.
2017-06-27 22:08:47 +01:00
83 changed files with 9796 additions and 7121 deletions

5
.gitignore vendored
View File

@@ -7,9 +7,8 @@ src/.copts_*
contrib/lease-tools/dhcp_lease_time
contrib/lease-tools/dhcp_release
contrib/lease-tools/dhcp_release6
debian/base/
debian/daemon/
debian/files
debian/substvars
debian/utils-substvars
debian/utils/
debian/trees/
debian/build/

213
CHANGELOG
View File

@@ -1,3 +1,140 @@
version 2.80
Add support for RFC 4039 DHCP rapid commit. Thanks to Ashram Method
for the initial patch and motivation.
Alter the default for dnssec-check-unsigned. Versions of
dnsmasq prior to 2.80 defaulted to not checking unsigned
replies, and used --dnssec-check-unsigned to switch
this on. Such configurations will continue to work as before,
but those which used the default of no checking will need to be
altered to explicitly select no checking. The new default is
because switching off checking for unsigned replies is
inherently dangerous. Not only does it open the possiblity of forged
replies, but it allows everything to appear to be working even
when the upstream namesevers do not support DNSSEC, and in this
case no DNSSEC validation at all is occuring.
Fix DHCP broken-ness when --no-ping AND --dhcp-sequential-ip
are set. Thanks to Daniel Miess for help with this.
Add a facilty to store DNS packets sent/recieved in a
pcap-format file for later debugging. The file location
is given by the --dumpfile option, and a bitmap controlling
which packets should be dumped is given by the --dumpmask
option.
Handle the case of both standard and constructed dhcp-ranges on the
same interface better. We don't now contruct a dhcp-range if there's
already one specified. This allows the specified interface to
have different parameters and avoids advertising the same
prefix twice. Thanks to Luis Marsano for spotting this case.
Allow zone transfer in authoritative mode if auth-peer is specified,
even if auth-sec-servers is not. Thanks to Raphaël Halimi for
the suggestion.
Fix bug which sometimes caused dnsmasq to wrongly return answers
without DNSSEC RRs to queries with the do-bit set, but only when
DNSSEC validation was not enabled.
Thanks to Petr Menšík for spotting this.
Fix missing fatal errors with some malformed options
(server, local, address, rebind-domain-ok, ipset, alias).
Thanks to Eugene Lozovoy for spotting the problem.
Fix crash on startup with a --synth-domain which has no prefix.
Introduced in 2.79. Thanks to Andreas Engel for the bug report.
Fix missing EDNS0 section in some replies generated by local
DNS configuration which confused systemd-resolvd. Thanks to
Steve Dodd for characterising the problem.
Add --dhcp-name-match config option.
Add --caa-record config option.
Implement --address=/example.com/# as (more efficient) syntactic
sugar for --address=/example.com/0.0.0.0 and
--address=/example.com/::
Returning null addresses is a useful technique for ad-blocking.
Thanks to Peter Russell for the suggestion.
Change anti cache-snooping behaviour with queries with the
recursion-desired bit unset. Instead to returning SERVFAIL, we
now always forward, and never answer from the cache. This
allows "dig +trace" command to work.
Include in the example config file a formulation which
stops DHCP clients from claiming the DNS name "wpad".
This is a fix for the CERT Vulnerability VU#598349.
version 2.79
Fix parsing of CNAME arguments, which are confused by extra spaces.
Thanks to Diego Aguirre for spotting the bug.
Where available, use IP_UNICAST_IF or IPV6_UNICAST_IF to bind
upstream servers to an interface, rather than SO_BINDTODEVICE.
Thanks to Beniamino Galvani for the patch.
Always return a SERVFAIL answer to DNS queries without the
recursion desired bit set, UNLESS acting as an authoritative
DNS server. This avoids a potential route to cache snooping.
Add support for Ed25519 signatures in DNSSEC validation.
No longer support RSA/MD5 signatures in DNSSEC validation,
since these are not secure. This behaviour is mandated in
RFC-6944.
Fix incorrect error exit code from dhcp_release6 utility.
Thanks Gaudenz Steinlin for the bug report.
Use SIGINT (instead of overloading SIGHUP) to turn on DNSSEC
time validation when --dnssec-no-timecheck is in use.
Note that this is an incompatible change from earlier releases.
Allow more than one --bridge-interface option to refer to an
interface, so that we can use
--bridge-interface=int1,alias1
--bridge-interface=int1,alias2
as an alternative to
--bridge-interface=int1,alias1,alias2
Thanks to Neil Jerram for work on this.
Fix for DNSSEC with wildcard-derived NSEC records.
It's OK for NSEC records to be expanded from wildcards,
but in that case, the proof of non-existence is only valid
starting at the wildcard name, *.<domain> NOT the name expanded
from the wildcard. Without this check it's possible for an
attacker to craft an NSEC which wrongly proves non-existence.
Thanks to Ralph Dolmans for finding this, and co-ordinating
the vulnerability tracking and fix release.
CVE-2017-15107 applies.
Remove special handling of A-for-A DNS queries. These
are no longer a significant problem in the global DNS.
http://cs.northwestern.edu/~ychen/Papers/DNS_ToN15.pdf
Thanks to Mattias Hellström for the initial patch.
Fix failure to delete dynamically created dhcp options
from files in -dhcp-optsdir directories. Thanks to
Lindgren Fredrik for the bug report.
Add to --synth-domain the ability to create names using
sequential numbers, as well as encodings of IP addresses.
For instance,
--synth-domain=thekelleys.org.uk,192.168.0.50,192.168.0.70,internal-*
creates 21 domain names of the form
internal-4.thekelleys.org.uk over the address range given, with
internal-0.thekelleys.org.uk being 192.168.0.50 and
internal-20.thekelleys.org.uk being 192.168.0.70
Thanks to Andy Hawkins for the suggestion.
Tidy up Crypto code, removing workarounds for ancient
versions of libnettle. We now require libnettle 3.
version 2.78
Fix logic of appending ".<layer>" to PXE basename. Thanks to Chris
Novakovic for the patch.
@@ -13,6 +150,76 @@ version 2.78
ff325644c7afae2588583f935f4ea9b9694eb52e. Thanks to
John Fitzgibbon for the diagnosis and patch.
Try other servers if first returns REFUSED when
--strict-order active. Thanks to Hans Dedecker
for the patch
Fix regression in 2.77, ironically added as a security
improvement, which resulted in a crash when a DNS
query exceeded 512 bytes (or the EDNS0 packet size,
if different.) Thanks to Christian Kujau, Arne Woerner
Juan Manuel Fernandez and Kevin Darbyshire-Bryant for
chasing this one down. CVE-2017-13704 applies.
Fix heap overflow in DNS code. This is a potentially serious
security hole. It allows an attacker who can make DNS
requests to dnsmasq, and who controls the contents of
a domain, which is thereby queried, to overflow
(by 2 bytes) a heap buffer and either crash, or
even take control of, dnsmasq.
CVE-2017-14491 applies.
Credit to Felix Wilhelm, Fermin J. Serna, Gabriel Campana
Kevin Hamacher and Ron Bowes of the Google Security Team for
finding this.
Fix heap overflow in IPv6 router advertisement code.
This is a potentially serious security hole, as a
crafted RA request can overflow a buffer and crash or
control dnsmasq. Attacker must be on the local network.
CVE-2017-14492 applies.
Credit to Felix Wilhelm, Fermin J. Serna, Gabriel Campana
and Kevin Hamacher of the Google Security Team for
finding this.
Fix stack overflow in DHCPv6 code. An attacker who can send
a DHCPv6 request to dnsmasq can overflow the stack frame and
crash or control dnsmasq.
CVE-2017-14493 applies.
Credit to Felix Wilhelm, Fermin J. Serna, Gabriel Campana
Kevin Hamacher and Ron Bowes of the Google Security Team for
finding this.
Fix information leak in DHCPv6. A crafted DHCPv6 packet can
cause dnsmasq to forward memory from outside the packet
buffer to a DHCPv6 server when acting as a relay.
CVE-2017-14494 applies.
Credit to Felix Wilhelm, Fermin J. Serna, Gabriel Campana
Kevin Hamacher and Ron Bowes of the Google Security Team for
finding this.
Fix DoS in DNS. Invalid boundary checks in the
add_pseudoheader function allows a memcpy call with negative
size An attacker which can send malicious DNS queries
to dnsmasq can trigger a DoS remotely.
dnsmasq is vulnerable only if one of the following option is
specified: --add-mac, --add-cpe-id or --add-subnet.
CVE-2017-14496 applies.
Credit to Felix Wilhelm, Fermin J. Serna, Gabriel Campana
Kevin Hamacher and Ron Bowes of the Google Security Team for
finding this.
Fix out-of-memory Dos vulnerability. An attacker which can
send malicious DNS queries to dnsmasq can trigger memory
allocations in the add_pseudoheader function
The allocated memory is never freed which leads to a DoS
through memory exhaustion. dnsmasq is vulnerable only
if one of the following option is specified:
--add-mac, --add-cpe-id or --add-subnet.
CVE-2017-14495 applies.
Credit to Felix Wilhelm, Fermin J. Serna, Gabriel Campana
Kevin Hamacher and Ron Bowes of the Google Security Team for
finding this.
version 2.77
Generate an error when configured with a CNAME loop,
@@ -1456,7 +1663,7 @@ version 2.56
By default, setting an IPv4 address for a domain but not
an IPv6 address causes dnsmasq to return
an NODATA reply for IPv6 (or vice-versa). So
a NODATA reply for IPv6 (or vice-versa). So
--address=/google.com/1.2.3.4 stops IPv6 queries for
*google.com from being forwarded. Make it possible to
override this behaviour by defining the semantics if the
@@ -1962,13 +2169,13 @@ version 2.47
Support arbitrarily encapsulated DHCP options, suggestion
and initial patch from Samium Gromoff. This is useful for
(eg) gPXE, which expect all its private options to be
(eg) iPXE, which expect all its private options to be
encapsulated inside a single option 175. So, eg,
dhcp-option = encap:175, 190, "iscsi-client0"
dhcp-option = encap:175, 191, "iscsi-client0-secret"
will provide iSCSI parameters to gPXE.
will provide iSCSI parameters to iPXE.
Enhance --dhcp-match to allow testing of the contents of a
client-sent option, as well as its presence. This

View File

@@ -1010,7 +1010,7 @@ release 2.9
but to the address of another interface were ignored
unless the loopback interface was explicitly configured.
2) on OpenBSD failure to configure one interface now
causes a fatal error on startup rather than an huge
causes a fatal error on startup rather than a huge
stream of log messages. Thanks to Erik Jan Tromp for
finding that bug.
@@ -2067,7 +2067,7 @@ version 2.36
kernel. Thanks to Philip Wall for the bug report.
Added --dhcp-bridge option, but only to the FreeBSD
build. This fixes an oddity with a a particular bridged
build. This fixes an oddity with a particular bridged
network configuration on FreeBSD. Thanks to Luigi Rizzo
for the patch.
@@ -2273,7 +2273,7 @@ version 2.40
this.
Use client-id as hash-seed for DHCP address allocation
with Firewire and InfiniBand, as these don't supply an MAC
with Firewire and InfiniBand, as these don't supply a MAC
address.
Tweaked TFTP file-open code to make it behave sensibly
@@ -2433,7 +2433,7 @@ version 2.41
Add --dhcp-match flag, to check for arbitrary options in
DHCP messages from clients. This enables use of dnsmasq
with gPXE. Thanks to Rance Hall for the suggestion.
with iPXE. Thanks to Rance Hall for the suggestion.
Added --dhcp-broadcast, to force broadcast replies to DHCP
clients which need them but are too dumb or too old to

6
FAQ
View File

@@ -9,7 +9,7 @@ A: The high ports that dnsmasq opens are for replies from the upstream
from port 53 the replies would be _to_ port 53 and get blocked.
This is not a security hole since dnsmasq will only accept replies to that
port: queries are dropped. The replies must be to oustanding queries
port: queries are dropped. The replies must be to outstanding queries
which dnsmasq has forwarded, otherwise they are dropped too.
Addendum: dnsmasq now has the option "query-port" (-Q), which allows
@@ -297,7 +297,7 @@ A: Dnsmasq from v2.63 can operate in one of three different "networking
by dnsmasq when in --bind-interfaces mode. In wildcard or bind-dynamic
mode, such interfaces are handled normally.
A --interface specification for a non-existent interface is a fatal
An --interface specification for a non-existent interface is a fatal
error at start-up when in --bind-interfaces mode, by just generates a
warning in wildcard or bind-dynamic mode.
@@ -320,7 +320,7 @@ A: Yes, new releases of dnsmasq are always announced through
Q: What does the dhcp-authoritative option do?
A: The DHCP spec says that when a DHCP server recieves a renewal request
A: The DHCP spec says that when a DHCP server receives a renewal request
from a client it has no knowledge of, it should just ignore it.
This is because it's supported to have more than one DHCP server
on a network, and another DHCP server may be dealing with the client.

View File

@@ -53,6 +53,7 @@ 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`
ubus_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_UBUS $(PKG_CONFIG) --copy -lubox -lubus`
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`
idn2_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LIBIDN2 $(PKG_CONFIG) --cflags libidn2`
@@ -76,16 +77,16 @@ objs = cache.o rfc1035.o util.o option.o forward.o network.o \
helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o \
dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o \
domain.o dnssec.o blockdata.o tables.o loop.o inotify.o \
poll.o rrfilter.o edns0.o arp.o
poll.o rrfilter.o edns0.o arp.o crypto.o dump.o ubus.o metrics.o
hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \
dns-protocol.h radv-protocol.h ip6addr.h
dns-protocol.h radv-protocol.h ip6addr.h metrics.h
all : $(BUILDDIR)
@cd $(BUILDDIR) && $(MAKE) \
top="$(top)" \
build_cflags="$(version) $(dbus_cflags) $(idn2_cflags) $(idn_cflags) $(ct_cflags) $(lua_cflags) $(nettle_cflags)" \
build_libs="$(dbus_libs) $(idn2_libs) $(idn_libs) $(ct_libs) $(lua_libs) $(sunos_libs) $(nettle_libs) $(gmp_libs)" \
build_libs="$(dbus_libs) $(idn2_libs) $(idn_libs) $(ct_libs) $(lua_libs) $(sunos_libs) $(nettle_libs) $(gmp_libs) $(ubus_libs)" \
-f $(top)/Makefile dnsmasq
mostly_clean :
@@ -100,7 +101,8 @@ clean : mostly_clean
install : all install-common
install-common :
$(INSTALL) -d $(DESTDIR)$(BINDIR) -d $(DESTDIR)$(MANDIR)/man8
$(INSTALL) -d $(DESTDIR)$(BINDIR)
$(INSTALL) -d $(DESTDIR)$(MANDIR)/man8
$(INSTALL) -m 644 $(MAN)/dnsmasq.8 $(DESTDIR)$(MANDIR)/man8
$(INSTALL) -m 755 $(BUILDDIR)/dnsmasq $(DESTDIR)$(BINDIR)

View File

@@ -10,7 +10,8 @@ LOCAL_SRC_FILES := bpf.c cache.c dbus.c dhcp.c dnsmasq.c \
dhcp6.c rfc3315.c dhcp-common.c outpacket.c \
radv.c slaac.c auth.c ipset.c domain.c \
dnssec.c dnssec-openssl.c blockdata.c tables.c \
loop.c inotify.c poll.c rrfilter.c edns0.c arp.c
loop.c inotify.c poll.c rrfilter.c edns0.c arp.c \
crypto.c dump.c ubus.c
LOCAL_MODULE := dnsmasq

View File

@@ -83,7 +83,7 @@ static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt
if (p >= end - 2)
return NULL; /* malformed packet */
opt_len = option_len(p);
if (p >= end - (2 + opt_len))
if (end - p >= (2 + opt_len))
return NULL; /* malformed packet */
if (*p == opt && opt_len >= minsize)
return p;
@@ -206,13 +206,13 @@ int main(int argc, char **argv)
{
unsigned int x;
if ((x = t/86400))
printf("%dd", x);
printf("%ud", x);
if ((x = (t/3600)%24))
printf("%dh", x);
printf("%uh", x);
if ((x = (t/60)%60))
printf("%dm", x);
printf("%um", x);
if ((x = t%60))
printf("%ds", x);
printf("%us", x);
}
return 0;
}

View File

@@ -270,7 +270,8 @@ int main(int argc, char **argv)
/* This voodoo fakes up a packet coming from the correct interface, which really matters for
a DHCP server */
strcpy(ifr.ifr_name, argv[1]);
strncpy(ifr.ifr_name, argv[1], sizeof(ifr.ifr_name)-1);
ifr.ifr_name[sizeof(ifr.ifr_name)-1] = '\0';
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) == -1)
{
perror("cannot setup interface");

View File

@@ -46,7 +46,8 @@ typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
enum DHCP6_TYPES{
enum DHCP6_TYPES
{
SOLICIT = 1,
ADVERTISE = 2,
REQUEST = 3,
@@ -61,8 +62,10 @@ enum DHCP6_TYPES{
RELAY_FORW = 12,
RELAY_REPL = 13
};
enum DHCP6_OPTIONS{
};
enum DHCP6_OPTIONS
{
CLIENTID = 1,
SERVERID = 2,
IA_NA = 3,
@@ -82,25 +85,27 @@ enum DHCP6_OPTIONS{
INTERFACE_ID = 18,
RECONF_MSG = 19,
RECONF_ACCEPT = 20,
};
};
enum DHCP6_STATUSES{
enum DHCP6_STATUSES
{
SUCCESS = 0,
UNSPEC_FAIL = 1,
NOADDR_AVAIL=2,
NO_BINDING = 3,
NOT_ON_LINK = 4,
USE_MULTICAST =5
};
};
static struct option longopts[] = {
{"ip", required_argument, 0, 'a'},
{"server-id", required_argument, 0, 's'},
{"client-id", required_argument, 0, 'c'},
{"iface", required_argument, 0, 'n'},
{"iaid", required_argument, 0, 'i'},
{"dry-run", no_argument, 0, 'd'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0}
{"ip", required_argument, 0, 'a' },
{"server-id", required_argument, 0, 's' },
{"client-id", required_argument, 0, 'c' },
{"iface", required_argument, 0, 'n' },
{"iaid", required_argument, 0, 'i' },
{"dry-run", no_argument, 0, 'd' },
{"help", no_argument, 0, 'h' },
{0, 0, 0, 0 }
};
const short DHCP6_CLIENT_PORT = 546;
@@ -108,207 +113,230 @@ const short DHCP6_SERVER_PORT = 547;
const char* DHCP6_MULTICAST_ADDRESS = "ff02::1:2";
struct dhcp6_option{
uint16_t type;
uint16_t len;
char value[1024];
struct dhcp6_option {
uint16_t type;
uint16_t len;
char value[1024];
};
struct dhcp6_iaaddr_option{
uint16_t type;
uint16_t len;
struct in6_addr ip;
uint32_t preferred_lifetime;
uint32_t valid_lifetime;
struct dhcp6_iaaddr_option {
uint16_t type;
uint16_t len;
struct in6_addr ip;
uint32_t preferred_lifetime;
uint32_t valid_lifetime;
};
struct dhcp6_iana_option{
uint16_t type;
uint16_t len;
uint32_t iaid;
uint32_t t1;
uint32_t t2;
char options[1024];
struct dhcp6_iana_option {
uint16_t type;
uint16_t len;
uint32_t iaid;
uint32_t t1;
uint32_t t2;
char options[1024];
};
struct dhcp6_packet{
size_t len;
char buf[2048];
} ;
struct dhcp6_packet {
size_t len;
char buf[2048];
};
size_t pack_duid(const char* str, char* dst){
char* tmp = strdup(str);
char* tmp_to_free = tmp;
char *ptr;
uint8_t write_pos = 0;
while ((ptr = strtok (tmp, ":"))) {
dst[write_pos] = (uint8_t) strtol(ptr, NULL, 16);
write_pos += 1;
tmp = NULL;
size_t pack_duid(const char* str, char* dst)
{
char* tmp = strdup(str);
char* tmp_to_free = tmp;
char *ptr;
uint8_t write_pos = 0;
while ((ptr = strtok (tmp, ":")))
{
dst[write_pos] = (uint8_t) strtol(ptr, NULL, 16);
write_pos += 1;
tmp = NULL;
}
free(tmp_to_free);
return write_pos;
free(tmp_to_free);
return write_pos;
}
struct dhcp6_option create_client_id_option(const char* duid){
struct dhcp6_option option;
option.type = htons(CLIENTID);
bzero(option.value, sizeof(option.value));
option.len = htons(pack_duid(duid, option.value));
return option;
struct dhcp6_option create_client_id_option(const char* duid)
{
struct dhcp6_option option;
option.type = htons(CLIENTID);
bzero(option.value, sizeof(option.value));
option.len = htons(pack_duid(duid, option.value));
return option;
}
struct dhcp6_option create_server_id_option(const char* duid){
struct dhcp6_option option;
option.type = htons(SERVERID);
bzero(option.value, sizeof(option.value));
option.len = htons(pack_duid(duid, option.value));
return option;
struct dhcp6_option create_server_id_option(const char* duid)
{
struct dhcp6_option option;
option.type = htons(SERVERID);
bzero(option.value, sizeof(option.value));
option.len = htons(pack_duid(duid, option.value));
return option;
}
struct dhcp6_iaaddr_option create_iaadr_option(const char* ip){
struct dhcp6_iaaddr_option result;
result.type =htons(IAADDR);
/* no suboptions needed here, so length is 24 */
result.len = htons(24);
result.preferred_lifetime = 0;
result.valid_lifetime = 0;
int s = inet_pton(AF_INET6, ip, &(result.ip));
if (s <= 0) {
if (s == 0)
fprintf(stderr, "Not in presentation format");
else
perror("inet_pton");
exit(EXIT_FAILURE);
}
return result;
}
struct dhcp6_iana_option create_iana_option(const char * iaid, struct dhcp6_iaaddr_option ia_addr){
struct dhcp6_iana_option result;
result.type = htons(IA_NA);
result.iaid = htonl(atoi(iaid));
result.t1 = 0;
result.t2 = 0;
result.len = htons(12 + ntohs(ia_addr.len) + 2 * sizeof(uint16_t));
memcpy(result.options, &ia_addr, ntohs(ia_addr.len) + 2 * sizeof(uint16_t));
return result;
struct dhcp6_iaaddr_option create_iaadr_option(const char* ip)
{
struct dhcp6_iaaddr_option result;
result.type =htons(IAADDR);
/* no suboptions needed here, so length is 24 */
result.len = htons(24);
result.preferred_lifetime = 0;
result.valid_lifetime = 0;
int s = inet_pton(AF_INET6, ip, &(result.ip));
if (s <= 0) {
if (s == 0)
fprintf(stderr, "Not in presentation format");
else
perror("inet_pton");
exit(EXIT_FAILURE);
}
return result;
}
struct dhcp6_packet create_release_packet(const char* iaid, const char* ip, const char* client_id, const char* server_id){
struct dhcp6_packet result;
bzero(result.buf, sizeof(result.buf));
/* message_type */
result.buf[0] = RELEASE;
/* tx_id */
bzero(result.buf+1, 3);
struct dhcp6_option client_option = create_client_id_option(client_id);
struct dhcp6_option server_option = create_server_id_option(server_id);
struct dhcp6_iaaddr_option iaaddr_option = create_iaadr_option(ip);
struct dhcp6_iana_option iana_option = create_iana_option(iaid, iaaddr_option);
int offset = 4;
memcpy(result.buf + offset, &client_option, ntohs(client_option.len) + 2*sizeof(uint16_t));
offset += (ntohs(client_option.len)+ 2 *sizeof(uint16_t) );
memcpy(result.buf + offset, &server_option, ntohs(server_option.len) + 2*sizeof(uint16_t) );
offset += (ntohs(server_option.len)+ 2* sizeof(uint16_t));
memcpy(result.buf + offset, &iana_option, ntohs(iana_option.len) + 2*sizeof(uint16_t) );
offset += (ntohs(iana_option.len)+ 2* sizeof(uint16_t));
result.len = offset;
return result;
struct dhcp6_iana_option create_iana_option(const char * iaid, struct dhcp6_iaaddr_option ia_addr)
{
struct dhcp6_iana_option result;
result.type = htons(IA_NA);
result.iaid = htonl(atoi(iaid));
result.t1 = 0;
result.t2 = 0;
result.len = htons(12 + ntohs(ia_addr.len) + 2 * sizeof(uint16_t));
memcpy(result.options, &ia_addr, ntohs(ia_addr.len) + 2 * sizeof(uint16_t));
return result;
}
uint16_t parse_iana_suboption(char* buf, size_t len){
size_t current_pos = 0;
char option_value[1024];
while (current_pos < len) {
uint16_t option_type, option_len;
memcpy(&option_type,buf + current_pos, sizeof(uint16_t));
memcpy(&option_len,buf + current_pos + sizeof(uint16_t), sizeof(uint16_t));
option_type = ntohs(option_type);
option_len = ntohs(option_len);
current_pos += 2 * sizeof(uint16_t);
if (option_type == STATUS_CODE){
uint16_t status;
memcpy(&status, buf + current_pos, sizeof(uint16_t));
status = ntohs(status);
if (status != SUCCESS){
memcpy(option_value, buf + current_pos + sizeof(uint16_t) , option_len - sizeof(uint16_t));
option_value[option_len-sizeof(uint16_t)] ='\0';
fprintf(stderr, "Error: %s\n", option_value);
struct dhcp6_packet create_release_packet(const char* iaid, const char* ip, const char* client_id, const char* server_id)
{
struct dhcp6_packet result;
bzero(result.buf, sizeof(result.buf));
/* message_type */
result.buf[0] = RELEASE;
/* tx_id */
bzero(result.buf+1, 3);
struct dhcp6_option client_option = create_client_id_option(client_id);
struct dhcp6_option server_option = create_server_id_option(server_id);
struct dhcp6_iaaddr_option iaaddr_option = create_iaadr_option(ip);
struct dhcp6_iana_option iana_option = create_iana_option(iaid, iaaddr_option);
int offset = 4;
memcpy(result.buf + offset, &client_option, ntohs(client_option.len) + 2*sizeof(uint16_t));
offset += (ntohs(client_option.len)+ 2 *sizeof(uint16_t) );
memcpy(result.buf + offset, &server_option, ntohs(server_option.len) + 2*sizeof(uint16_t) );
offset += (ntohs(server_option.len)+ 2* sizeof(uint16_t));
memcpy(result.buf + offset, &iana_option, ntohs(iana_option.len) + 2*sizeof(uint16_t) );
offset += (ntohs(iana_option.len)+ 2* sizeof(uint16_t));
result.len = offset;
return result;
}
uint16_t parse_iana_suboption(char* buf, size_t len)
{
size_t current_pos = 0;
char option_value[1024];
while (current_pos < len)
{
uint16_t option_type, option_len;
memcpy(&option_type,buf + current_pos, sizeof(uint16_t));
memcpy(&option_len,buf + current_pos + sizeof(uint16_t), sizeof(uint16_t));
option_type = ntohs(option_type);
option_len = ntohs(option_len);
current_pos += 2 * sizeof(uint16_t);
if (option_type == STATUS_CODE)
{
uint16_t status;
memcpy(&status, buf + current_pos, sizeof(uint16_t));
status = ntohs(status);
if (status != SUCCESS)
{
memcpy(option_value, buf + current_pos + sizeof(uint16_t) , option_len - sizeof(uint16_t));
option_value[option_len-sizeof(uint16_t)] ='\0';
fprintf(stderr, "Error: %s\n", option_value);
}
return status;
return status;
}
}
return -2;
return -2;
}
int16_t parse_packet(char* buf, size_t len){
uint8_t type = buf[0];
/*skipping tx id. you need it, uncomment following line
uint16_t tx_id = ntohs((buf[1] <<16) + (buf[2] <<8) + buf[3]);
*/
size_t current_pos = 4;
if (type != REPLY ){
return NOT_REPLY_CODE;
}
char option_value[1024];
while (current_pos < len) {
uint16_t option_type, option_len;
memcpy(&option_type,buf + current_pos, sizeof(uint16_t));
memcpy(&option_len,buf + current_pos + sizeof(uint16_t), sizeof(uint16_t));
option_type = ntohs(option_type);
option_len = ntohs(option_len);
current_pos += 2 * sizeof(uint16_t);
if (option_type == STATUS_CODE){
uint16_t status;
memcpy(&status, buf + current_pos, sizeof(uint16_t));
status = ntohs(status);
if (status != SUCCESS){
memcpy(option_value, buf + current_pos +sizeof(uint16_t) , option_len -sizeof(uint16_t));
fprintf(stderr, "Error: %d %s\n", status, option_value);
return status;
}
int16_t parse_packet(char* buf, size_t len)
{
int16_t ret = -1;
uint8_t type = buf[0];
/*skipping tx id. you need it, uncomment following line
uint16_t tx_id = ntohs((buf[1] <<16) + (buf[2] <<8) + buf[3]);
*/
size_t current_pos = 4;
if (type != REPLY )
return NOT_REPLY_CODE;
char option_value[1024];
while (current_pos < len)
{
uint16_t option_type, option_len;
memcpy(&option_type,buf + current_pos, sizeof(uint16_t));
memcpy(&option_len,buf + current_pos + sizeof(uint16_t), sizeof(uint16_t));
option_type = ntohs(option_type);
option_len = ntohs(option_len);
current_pos += 2 * sizeof(uint16_t);
if (option_type == STATUS_CODE)
{
uint16_t status;
memcpy(&status, buf + current_pos, sizeof(uint16_t));
status = ntohs(status);
if (status != SUCCESS)
{
memcpy(option_value, buf + current_pos +sizeof(uint16_t) , option_len -sizeof(uint16_t));
fprintf(stderr, "Error: %d %s\n", status, option_value);
return status;
}
/* Got success status, return that if there's no specific error in an IA_NA. */
ret = SUCCESS;
}
if (option_type == IA_NA ){
uint16_t result = parse_iana_suboption(buf + current_pos +24, option_len -24);
if (result){
return result;
}
}
current_pos += option_len;
if (option_type == IA_NA )
{
uint16_t result = parse_iana_suboption(buf + current_pos +24, option_len -24);
if (result)
return result;
}
current_pos += option_len;
}
return -1;
return ret;
}
void usage(const char* arg, FILE* stream){
const char* usage_string ="--ip IPv6 --iface IFACE --server-id SERVER_ID --client-id CLIENT_ID --iaid IAID [--dry-run] | --help";
fprintf (stream, "Usage: %s %s\n", arg, usage_string);
void usage(const char* arg, FILE* stream)
{
const char* usage_string ="--ip IPv6 --iface IFACE --server-id SERVER_ID --client-id CLIENT_ID --iaid IAID [--dry-run] | --help";
fprintf (stream, "Usage: %s %s\n", arg, usage_string);
}
int send_release_packet(const char* iface, struct dhcp6_packet* packet){
struct sockaddr_in6 server_addr, client_addr;
char response[1400];
int sock = socket(PF_INET6, SOCK_DGRAM, 0);
int i = 0;
if (sock < 0) {
perror("creating socket");
return -1;
int send_release_packet(const char* iface, struct dhcp6_packet* packet)
{
struct sockaddr_in6 server_addr, client_addr;
char response[1400];
int sock = socket(PF_INET6, SOCK_DGRAM, 0);
int i = 0;
if (sock < 0)
{
perror("creating socket");
return -1;
}
if (setsockopt(sock, SOL_SOCKET, 25, iface, strlen(iface)) == -1) {
if (setsockopt(sock, SOL_SOCKET, 25, iface, strlen(iface)) == -1)
{
perror("SO_BINDTODEVICE");
close(sock);
return -1;
}
}
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin6_family = AF_INET6;
client_addr.sin6_family = AF_INET6;
@@ -320,126 +348,152 @@ int send_release_packet(const char* iface, struct dhcp6_packet* packet){
inet_pton(AF_INET6, DHCP6_MULTICAST_ADDRESS, &server_addr.sin6_addr);
server_addr.sin6_port = htons(DHCP6_SERVER_PORT);
int16_t recv_size = 0;
for (i = 0; i < 5; i++) {
if (sendto(sock, packet->buf, packet->len, 0,
(struct sockaddr *)&server_addr,
sizeof(server_addr)) < 0) {
perror("sendto failed");
for (i = 0; i < 5; i++)
{
if (sendto(sock, packet->buf, packet->len, 0, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
{
perror("sendto failed");
exit(4);
}
}
recv_size = recvfrom(sock, response, sizeof(response), MSG_DONTWAIT, NULL, 0);
if (recv_size == -1){
if (errno == EAGAIN){
sleep(1);
continue;
}else {
if (recv_size == -1)
{
if (errno == EAGAIN)
{
sleep(1);
continue;
}
else
{
perror("recvfrom");
}
}
}
}
int16_t result = parse_packet(response, recv_size);
if (result == NOT_REPLY_CODE){
if (result == NOT_REPLY_CODE)
{
sleep(1);
continue;
}
}
close(sock);
return result;
}
}
close(sock);
fprintf(stderr, "Response timed out\n");
return -1;
return -1;
}
int main(int argc, char * const argv[]) {
const char* UNINITIALIZED = "";
const char* iface = UNINITIALIZED;
const char* ip = UNINITIALIZED;
const char* client_id = UNINITIALIZED;
const char* server_id = UNINITIALIZED;
const char* iaid = UNINITIALIZED;
int dry_run = 0;
while (1) {
int option_index = 0;
int c = getopt_long(argc, argv, "a:s:c:n:i:hd", longopts, &option_index);
if (c == -1){
break;
}
switch(c){
case 0:
if (longopts[option_index].flag !=0){
break;
}
printf ("option %s", longopts[option_index].name);
if (optarg)
printf (" with arg %s", optarg);
printf ("\n");
break;
case 'i':
iaid = optarg;
break;
case 'n':
iface = optarg;
break;
case 'a':
ip = optarg;
break;
case 'c':
client_id = optarg;
break;
case 'd':
dry_run = 1;
break;
case 's':
server_id = optarg;
break;
case 'h':
usage(argv[0], stdout);
return 0;
case '?':
usage(argv[0], stderr);
return -1;
default:
abort();
}
int main(int argc, char * const argv[])
{
const char* UNINITIALIZED = "";
const char* iface = UNINITIALIZED;
const char* ip = UNINITIALIZED;
const char* client_id = UNINITIALIZED;
const char* server_id = UNINITIALIZED;
const char* iaid = UNINITIALIZED;
int dry_run = 0;
while (1)
{
int option_index = 0;
int c = getopt_long(argc, argv, "a:s:c:n:i:hd", longopts, &option_index);
if (c == -1)
break;
switch(c)
{
case 0:
if (longopts[option_index].flag !=0)
break;
printf ("option %s", longopts[option_index].name);
if (optarg)
printf (" with arg %s", optarg);
printf ("\n");
break;
case 'i':
iaid = optarg;
break;
case 'n':
iface = optarg;
break;
case 'a':
ip = optarg;
break;
case 'c':
client_id = optarg;
break;
case 'd':
dry_run = 1;
break;
case 's':
server_id = optarg;
break;
case 'h':
usage(argv[0], stdout);
return 0;
case '?':
usage(argv[0], stderr);
return -1;
default:
abort();
}
}
if (iaid == UNINITIALIZED){
fprintf(stderr, "Missing required iaid parameter\n");
usage(argv[0], stderr);
return -1;
if (iaid == UNINITIALIZED)
{
fprintf(stderr, "Missing required iaid parameter\n");
usage(argv[0], stderr);
return -1;
}
if (server_id == UNINITIALIZED){
if (server_id == UNINITIALIZED)
{
fprintf(stderr, "Missing required server-id parameter\n");
usage(argv[0], stderr);
return -1;
}
if (client_id == UNINITIALIZED){
}
if (client_id == UNINITIALIZED)
{
fprintf(stderr, "Missing required client-id parameter\n");
usage(argv[0], stderr);
return -1;
}
if (ip == UNINITIALIZED){
}
if (ip == UNINITIALIZED)
{
fprintf(stderr, "Missing required ip parameter\n");
usage(argv[0], stderr);
return -1;
}
if (iface == UNINITIALIZED){
fprintf(stderr, "Missing required iface parameter\n");
}
if (iface == UNINITIALIZED)
{
fprintf(stderr, "Missing required iface parameter\n");
usage(argv[0], stderr);
return -1;
}
}
struct dhcp6_packet packet = create_release_packet(iaid, ip, client_id, server_id);
if (dry_run){
if (dry_run)
{
uint16_t i;
for(i=0;i<packet.len;i++){
printf("%hhx", packet.buf[i]);
}
for(i=0; i<packet.len; i++)
printf("%hhx", packet.buf[i]);
printf("\n");
return 0;
}
}
return send_release_packet(iface, &packet);
}

View File

@@ -3,7 +3,7 @@
STATUS_FILE="/tmp/dnsmasq-ip-mac.status"
# Script for dnsmasq lease-change hook.
# Maintains the above file with a IP address/MAC address pairs,
# Maintains the above file with an IP address/MAC address pairs,
# one lease per line. Works with IPv4 and IPv6 leases, file is
# atomically updated, so no races for users of the data.

View File

@@ -0,0 +1,10 @@
Hi,
I updated the try-all-ns patch to work with the latest version of git. Ended up implementing it on top of master, 2.78test2-7-g63437ff. As that specific if-clause has been changed in the last few commits, it's not compatible for 2.77, sadly.
Find the patch attached.
Regards,
Rasmus Ahlberg
Software Developer, R&D
Electrolux Small Appliances

View File

@@ -0,0 +1,20 @@
diff --git a/src/forward.c b/src/forward.c
index e3fa94b..ecf3b98 100644
--- a/src/forward.c
+++ b/src/forward.c
@@ -789,9 +789,12 @@ void reply_query(int fd, int family, time_t now)
/* Note: if we send extra options in the EDNS0 header, we can't recreate
the query from the reply. */
- if (RCODE(header) == REFUSED &&
- forward->forwardall == 0 &&
- !(forward->flags & FREC_HAS_EXTRADATA))
+ if ((RCODE(header) == REFUSED &&
+ forward->forwardall == 0 &&
+ !(forward->flags & FREC_HAS_EXTRADATA)) ||
+ /* If strict-order is set, try next server on NXDOMAIN reply */
+ (RCODE(header) == NXDOMAIN && option_bool(OPT_ORDER) &&
+ server->next != NULL))
/* for broken servers, attempt to send to another one. */
{
unsigned char *pheader;

View File

@@ -243,6 +243,10 @@ IPv4 or IPv6 address of the lease to remove.
Note that this function will trigger the DhcpLeaseRemoved signal and the
configured DHCP lease script will be run with the "del" action.
GetMetrics
----------
Returns an array with various metrics for DNS and DHCP.
2. SIGNALS

42
debian/changelog vendored
View File

@@ -1,8 +1,44 @@
dnsmasq (2.78-1) unstable; urgency=low
dnsmasq (2.80-1) unstable; urgency=low
* New upstream. (closes: #837602) (closes: #794640) (closes: #794636)
* Close old bugs, long agp fixed. (closes: #802845) (closes: #754299)
* Provide usr/lib/tmpfiles.d/dnsmasq.conf. (closes: #872396)
* Run restorecon on /run/dnsmasq for SE Linux. (closes: #872397)
-- Simon Kelley <simon@thekelleys.org.uk> Mon, 17 Sep 2018 23:11:25 +0000
dnsmasq (2.79-1) unstable; urgency=low
* New upstream. (closes: #888200)
* Fix trust-anchor regex in init script. (closes: #884347)
* Fix exit code for dhcp_release6 (closes: #883596)
* Add project homepage to control file. (closes: #887764)
* New binary package dnsmasq-base-lua, includes Lua support.
* Remove hardwired shlibs dependency for libnettle 3.3 and
fix code to avoid ABI breakage as long as compiled against
libnettle 3.4 or later. (closes: #891315)
-- Simon Kelley <simon@thekelleys.org.uk> Fri, 16 Feb 2018 19:54:22 +0000
dnsmasq (2.78-3) unstable; urgency=high
* Make failure of pidfile chown a warning. (closes: #889857)
-- Simon Kelley <simon@thekelleys.org.uk> Thu, 8 Feb 2018 21:26:30 +0000
dnsmasq (2.78-2) unstable; urgency=high
* Change ownership of pid file, to keep systemd happy. (closes: #889336)
-- Simon Kelley <simon@thekelleys.org.uk> Tue, 6 Feb 2018 17:21:30 +0000
dnsmasq (2.78-1) unstable; urgency=high
* New upstream.
Security fixes for CVE-2017-13704 (closes: #877102)
Security fixes for CVE-2017-14491 - CVE-2017-14496 inclusive.
-- Simon Kelley <simon@thekelleys.org.uk> Sun, 25 Jun 2017 21:34:00 +0000
-- Simon Kelley <simon@thekelleys.org.uk> Sun, 29 Sep 2017 21:34:00 +0000
dnsmasq (2.77-2) unstable; urgency=low
@@ -17,7 +53,7 @@ dnsmasq (2.77-1) unstable; urgency=low
includes port=0 to disable DNS.
* Handle gratuitous format change in /usr/share/dns/root.ds
(closes: #858506) (closes: #860064)
* Add lsb-base dependancy.
* Add lsb-base dependency.
-- Simon Kelley <simon@thekelleys.org.uk> Tue, 11 Apr 2017 14:19:20 +0000

24
debian/control vendored
View File

@@ -3,13 +3,15 @@ Section: net
Priority: optional
Build-depends: gettext, libnetfilter-conntrack-dev [linux-any],
libidn11-dev, libdbus-1-dev (>=0.61), libgmp-dev,
nettle-dev (>=2.4-3), libbsd-dev [!linux-any]
nettle-dev (>=2.4-3), libbsd-dev [!linux-any],
liblua5.2-dev
Maintainer: Simon Kelley <simon@thekelleys.org.uk>
Homepage: http://www.thekelleys.org.uk/dnsmasq/doc.html
Standards-Version: 3.9.8
Package: dnsmasq
Architecture: all
Depends: netbase, dnsmasq-base(>= ${binary:Version}),
Depends: netbase, dnsmasq-base,
init-system-helpers (>= 1.18~), lsb-base (>= 3.0-6)
Suggests: resolvconf
Conflicts: resolvconf (<<1.15)
@@ -27,13 +29,29 @@ Package: dnsmasq-base
Architecture: any
Depends: adduser, ${shlibs:Depends}
Breaks: dnsmasq (<< 2.63-1~)
Replaces: dnsmasq (<< 2.63-1~)
Replaces: dnsmasq (<< 2.63-1~), dnsmasq-base
Recommends: dns-root-data
Provides: dnsmasq-base
Conflicts: dnsmasq-base-lua
Description: Small caching DNS proxy and DHCP/TFTP server
This package contains the dnsmasq executable and documentation, but
not the infrastructure required to run it as a system daemon. For
that, install the dnsmasq package.
Package: dnsmasq-base-lua
Architecture: any
Depends: adduser, ${shlibs:Depends}
Breaks: dnsmasq (<< 2.63-1~)
Replaces: dnsmasq (<< 2.63-1~), dnsmasq-base
Recommends: dns-root-data
Provides: dnsmasq-base
Conflicts: dnsmasq-base
Description: Small caching DNS proxy and DHCP/TFTP server
This package contains the dnsmasq executable and documentation, but
not the infrastructure required to run it as a system daemon. For
that, install the dnsmasq package. This package is an alternative
to dnsmasq-base which includes the LUA interpreter.
Package: dnsmasq-utils
Architecture: linux-any
Depends: ${shlibs:Depends}

2
debian/copyright vendored
View File

@@ -1,4 +1,4 @@
dnsmasq is Copyright (c) 2000-2016 Simon Kelley
dnsmasq is Copyright (c) 2000-2018 Simon Kelley
It was downloaded from: http://www.thekelleys.org.uk/dnsmasq/

5
debian/init vendored
View File

@@ -111,7 +111,7 @@ DNSMASQ_OPTS="$DNSMASQ_OPTS --local-service"
ROOT_DS="/usr/share/dns/root.ds"
if [ -f $ROOT_DS ]; then
DNSMASQ_OPTS="$DNSMASQ_OPTS `sed -rne "s/^([.a-ZA-Z0-9]+)([[:space:]]+[0-9]+)*([[:space:]]+IN)*[[:space:]]+DS[[:space:]]+/--trust-anchor=\1,/;s/[[:space:]]+/,/gp" $ROOT_DS | tr '\n' ' '`"
DNSMASQ_OPTS="$DNSMASQ_OPTS `env LC_ALL=C sed -rne "s/^([.a-zA-Z0-9]+)([[:space:]]+[0-9]+)*([[:space:]]+IN)*[[:space:]]+DS[[:space:]]+/--trust-anchor=\1,/;s/[[:space:]]+/,/gp" $ROOT_DS | tr '\n' ' '`"
fi
start()
@@ -127,7 +127,8 @@ start()
mkdir /run/dnsmasq || return 2
chown dnsmasq:nogroup /run/dnsmasq || return 2
fi
[ -x /sbin/restorecon ] && /sbin/restorecon /run/dnsmasq
start-stop-daemon --start --quiet --pidfile /run/dnsmasq/$NAME.pid --exec $DAEMON --test > /dev/null || return 1
start-stop-daemon --start --quiet --pidfile /run/dnsmasq/$NAME.pid --exec $DAEMON -- \
-x /run/dnsmasq/$NAME.pid \

3
debian/lintian-override vendored Normal file
View File

@@ -0,0 +1,3 @@
# dnsmasq-base and dnsmasq-base-lua are mutually exclusive and both
# provide /usr/share/doc/dnsmasq-base
dnsmasq-base-lua binary: usr-share-doc-symlink-without-dependency dnsmasq-base

2
debian/readme vendored
View File

@@ -54,7 +54,7 @@ Notes on configuring dnsmasq as packaged for Debian.
nodhcp : omit DHCP support.
nodhcp6 : omit DHCPv6 support.
noscript : omit lease-change script support.
use_lua : provide support for lease-change scripts written
uselua : provide support for lease-change scripts written
in Lua.
noipv6 : omit IPv6 support.
nodbus : omit DBus support.

266
debian/rules vendored
View File

@@ -1,6 +1,6 @@
#!/usr/bin/make -f
# debian/rules file - for dnsmasq.
# Copyright 2001-2011 by Simon Kelley
# Copyright 2001-2018 by Simon Kelley
# Based on the sample in the debian hello package which carries the following:
# Copyright 1994,1995 by Ian Jackson.
# I hereby give you perpetual unlimited permission to copy,
@@ -103,128 +103,172 @@ ifneq ($(DEB_HOST_ARCH_OS),linux)
LDFLAGS += -lbsd
endif
clean:
$(checkdir)
rm -rf debian/daemon debian/base debian/utils debian/*~ debian/files debian/substvars debian/utils-substvars
make clean
make -C contrib/lease-tools clean
binary-indep: checkroot
$(checkdir)
rm -rf debian/daemon
define build_tree
rm -rf $1
install -m 755 \
-d debian/daemon/DEBIAN \
-d debian/daemon/usr/share/doc \
-d debian/daemon/etc/init.d \
-d debian/daemon/etc/dnsmasq.d \
-d debian/daemon/etc/resolvconf/update.d \
-d debian/daemon/usr/lib/resolvconf/dpkg-event.d \
-d debian/daemon/usr/share/dnsmasq \
-d debian/daemon/etc/default \
-d debian/daemon/lib/systemd/system \
-d debian/daemon/etc/insserv.conf.d
install -m 644 debian/conffiles debian/daemon/DEBIAN
install -m 755 debian/postinst debian/postrm debian/prerm debian/daemon/DEBIAN
install -m 755 debian/init debian/daemon/etc/init.d/dnsmasq
install -m 755 debian/resolvconf debian/daemon/etc/resolvconf/update.d/dnsmasq
install -m 755 debian/resolvconf-package debian/daemon/usr/lib/resolvconf/dpkg-event.d/dnsmasq
install -m 644 debian/installed-marker debian/daemon/usr/share/dnsmasq
install -m 644 debian/default debian/daemon/etc/default/dnsmasq
install -m 644 dnsmasq.conf.example debian/daemon/etc/dnsmasq.conf
install -m 644 debian/readme.dnsmasq.d debian/daemon/etc/dnsmasq.d/README
install -m 644 debian/systemd.service debian/daemon/lib/systemd/system/dnsmasq.service
install -m 644 debian/insserv debian/daemon/etc/insserv.conf.d/dnsmasq
ln -s $(package) debian/daemon/usr/share/doc/dnsmasq
cd debian/daemon && find . -type f ! -regex '.*DEBIAN/.*' -printf '%P\0' | LC_ALL=C sort -z | xargs -r0 md5sum > DEBIAN/md5sums
dpkg-gencontrol $(PACKAGE_VERSION) -T -pdnsmasq -Pdebian/daemon
find debian/daemon -depth -newermt '$(BUILD_DATE)' -print0 | xargs -0r touch --no-dereference --date='$(BUILD_DATE)'
chown -R root.root debian/daemon
chmod -R g-ws debian/daemon
dpkg --build debian/daemon ..
-d $1/DEBIAN \
-d $1/etc/dbus-1/system.d \
-d $1/usr/share/doc/$(package) \
-d $1/usr/share/doc/$(package)/examples \
-d $1/usr/share/$(package) \
-d $1/var/lib/misc
binary-arch: checkroot
$(checkdir)
rm -rf debian/base
install -m 755 \
-d debian/base/DEBIAN \
-d debian/base/etc/dbus-1/system.d \
-d debian/base/usr/share/doc/$(package) \
-d debian/base/usr/share/doc/$(package)/examples \
-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="$(DEB_COPTS)" CC=$(CC) PKG_CONFIG=$(PKG_CONFIG)
ifeq (,$(findstring nodocs,$(DEB_BUILD_OPTIONS)))
endef
define add_docs
# Need to remove paypal links in Debian Package for policy reasons.
sed -e /\<H2\>Donations/Q -e /icon.png/d doc.html -e /favicon.ico/d >debian/base/usr/share/doc/$(package)/doc.html
echo "</BODY>" >>debian/base/usr/share/doc/$(package)/doc.html
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 -9n debian/base/usr/share/doc/$(package)/FAQ
install -m 644 CHANGELOG debian/base/usr/share/doc/$(package)/changelog
gzip -9n debian/base/usr/share/doc/$(package)/changelog
install -m 644 CHANGELOG.archive debian/base/usr/share/doc/$(package)/changelog.archive
gzip -9n debian/base/usr/share/doc/$(package)/changelog.archive
install -m 644 dbus/DBus-interface debian/base/usr/share/doc/$(package)/.
gzip -9n debian/base/usr/share/doc/$(package)/DBus-interface
endif
install -m 644 debian/dnsmasq-base.conffiles debian/base/DEBIAN/conffiles
install -m 755 debian/dnsmasq-base.postinst debian/base/DEBIAN/postinst
install -m 755 debian/dnsmasq-base.postrm debian/base/DEBIAN/postrm
install -m 644 debian/changelog debian/base/usr/share/doc/$(package)/changelog.Debian
gzip -9n debian/base/usr/share/doc/$(package)/changelog.Debian
install -m 644 debian/readme debian/base/usr/share/doc/$(package)/README.Debian
install -m 644 debian/copyright debian/base/usr/share/doc/$(package)/copyright
install -m 644 debian/dbus.conf debian/base/etc/dbus-1/system.d/dnsmasq.conf
gzip -9n debian/base/usr/share/man/man8/dnsmasq.8
for f in debian/base/usr/share/man/*; do \
sed -e /\<H2\>Donations/Q -e /icon.png/d doc.html -e /favicon.ico/d >$1/usr/share/doc/$(package)/doc.html
echo "</BODY>" >>$1/usr/share/doc/$(package)/doc.html
install -m 644 setup.html $1/usr/share/doc/$(package)/.
install -m 644 dnsmasq.conf.example $1/usr/share/doc/$(package)/examples/.
install -m 644 FAQ $1/usr/share/doc/$(package)/.
gzip -9n $1/usr/share/doc/$(package)/FAQ
install -m 644 CHANGELOG $1/usr/share/doc/$(package)/changelog
gzip -9n $1/usr/share/doc/$(package)/changelog
install -m 644 CHANGELOG.archive $1/usr/share/doc/$(package)/changelog.archive
gzip -9n $1/usr/share/doc/$(package)/changelog.archive
install -m 644 dbus/DBus-interface $1/usr/share/doc/$(package)/.
gzip -9n $1/usr/share/doc/$(package)/DBus-interface
gzip -9n $1/usr/share/man/man8/dnsmasq.8
for f in $1/usr/share/man/*; do \
if [ -f $$f/man8/dnsmasq.8 ]; then \
gzip -9n $$f/man8/dnsmasq.8 ; \
fi \
done
ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
$(DEB_HOST_GNU_TYPE)-strip -R .note -R .comment debian/base/usr/sbin/dnsmasq
endef
define add_files
install -m 644 trust-anchors.conf $1/usr/share/$(package)/.
install -m 644 debian/dnsmasq-base.conffiles $1/DEBIAN/conffiles
install -m 755 debian/dnsmasq-base.postinst $1/DEBIAN/postinst
install -m 755 debian/dnsmasq-base.postrm $1/DEBIAN/postrm
install -m 644 debian/changelog $1/usr/share/doc/$(package)/changelog.Debian
gzip -9n $1/usr/share/doc/$(package)/changelog.Debian
install -m 644 debian/readme $1/usr/share/doc/$(package)/README.Debian
install -m 644 debian/copyright $1/usr/share/doc/$(package)/copyright
install -m 644 debian/dbus.conf $1/etc/dbus-1/system.d/dnsmasq.conf
endef
clean:
$(checkdir)
make BUILDDIR=debian/build/no-lua clean
make BUILDDIR=debian/build/lua clean
make -C contrib/lease-tools clean
rm -rf debian/build debian/trees debian/*~ debian/files debian/substvars debian/utils-substvars
binary-indep: checkroot
$(checkdir)
rm -rf debian/trees/daemon
install -m 755 \
-d debian/trees/daemon/DEBIAN \
-d debian/trees/daemon/usr/share/doc \
-d debian/trees/daemon/etc/init.d \
-d debian/trees/daemon/etc/dnsmasq.d \
-d debian/trees/daemon/etc/resolvconf/update.d \
-d debian/trees/daemon/usr/lib/resolvconf/dpkg-event.d \
-d debian/trees/daemon/usr/share/dnsmasq \
-d debian/trees/daemon/etc/default \
-d debian/trees/daemon/lib/systemd/system \
-d debian/trees/daemon/usr/lib/tmpfiles.d \
-d debian/trees/daemon/etc/insserv.conf.d
install -m 644 debian/conffiles debian/trees/daemon/DEBIAN
install -m 755 debian/postinst debian/postrm debian/prerm debian/trees/daemon/DEBIAN
install -m 755 debian/init debian/trees/daemon/etc/init.d/dnsmasq
install -m 755 debian/resolvconf debian/trees/daemon/etc/resolvconf/update.d/dnsmasq
install -m 755 debian/resolvconf-package debian/trees/daemon/usr/lib/resolvconf/dpkg-event.d/dnsmasq
install -m 644 debian/installed-marker debian/trees/daemon/usr/share/dnsmasq
install -m 644 debian/default debian/trees/daemon/etc/default/dnsmasq
install -m 644 dnsmasq.conf.example debian/trees/daemon/etc/dnsmasq.conf
install -m 644 debian/readme.dnsmasq.d debian/trees/daemon/etc/dnsmasq.d/README
install -m 644 debian/systemd.service debian/trees/daemon/lib/systemd/system/dnsmasq.service
install -m 644 debian/tmpfiles.conf debian/trees/daemon/usr/lib/tmpfiles.d/dnsmasq.conf
install -m 644 debian/insserv debian/trees/daemon/etc/insserv.conf.d/dnsmasq
ln -s $(package) debian/trees/daemon/usr/share/doc/dnsmasq
cd debian/trees/daemon && find . -type f ! -regex '.*DEBIAN/.*' -printf '%P\0' | LC_ALL=C sort -z | xargs -r0 md5sum > DEBIAN/md5sums
dpkg-gencontrol $(PACKAGE_VERSION) -T -pdnsmasq -Pdebian/trees/daemon
find debian/trees/daemon -depth -newermt '$(BUILD_DATE)' -print0 | xargs -0r touch --no-dereference --date='$(BUILD_DATE)'
chown -R root.root debian/trees/daemon
chmod -R g-ws debian/trees/daemon
dpkg --build debian/trees/daemon ..
binary-arch: checkroot
$(call build_tree,debian/trees/base)
make $(TARGET) BUILDDIR=debian/build/no-lua PREFIX=/usr DESTDIR=`pwd`/debian/trees/base CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" COPTS="$(DEB_COPTS)" CC=$(CC) PKG_CONFIG=$(PKG_CONFIG)
ifeq (,$(findstring nodocs,$(DEB_BUILD_OPTIONS)))
$(call add_docs,debian/trees/base)
else
rm -rf debian/trees/base/usr/share/man
endif
cd debian/base && find . -type f ! -regex '.*DEBIAN/.*' -printf '%P\0' | LC_ALL=C sort -z | xargs -r0 md5sum > DEBIAN/md5sums
dpkg-shlibdeps --warnings=1 debian/base/usr/sbin/dnsmasq
dpkg-gencontrol $(PACKAGE_VERSION) -pdnsmasq-base -Pdebian/base
find debian/base -depth -newermt '$(BUILD_DATE)' -print0 | xargs -0r touch --no-dereference --date='$(BUILD_DATE)'
chown -R root.root debian/base
chmod -R g-ws debian/base
dpkg --build debian/base ..
$(call add_files,debian/trees/base)
ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
$(DEB_HOST_GNU_TYPE)-strip -R .note -R .comment debian/trees/base/usr/sbin/dnsmasq
endif
cd debian/trees/base && find . -type f ! -regex '.*DEBIAN/.*' -printf '%P\0' | LC_ALL=C sort -z | xargs -r0 md5sum > DEBIAN/md5sums
dpkg-shlibdeps --warnings=1 debian/trees/base/usr/sbin/dnsmasq
dpkg-gencontrol $(PACKAGE_VERSION) -pdnsmasq-base -Pdebian/trees/base
find debian/trees/base -depth -newermt '$(BUILD_DATE)' -print0 | xargs -0r touch --no-dereference --date='$(BUILD_DATE)'
chown -R root.root debian/trees/base
chmod -R g-ws debian/trees/base
dpkg --build debian/trees/base ..
$(call build_tree,debian/trees/lua-base)
make $(TARGET) BUILDDIR=debian/build/lua PREFIX=/usr DESTDIR=`pwd`/debian/trees/lua-base CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" COPTS="-DHAVE_LUASCRIPT $(DEB_COPTS)" CC=$(CC) PKG_CONFIG=$(PKG_CONFIG)
ifeq (,$(findstring nodocs,$(DEB_BUILD_OPTIONS)))
$(call add_docs,debian/trees/lua-base)
else
rm -rf debian/trees/lua-base/usr/share/man
endif
$(call add_files,debian/trees/lua-base)
install -m 755 -d debian/trees/lua-base/usr/share/lintian/overrides
install -m 644 debian/lintian-override debian/trees/lua-base/usr/share/lintian/overrides/dnsmasq-base-lua
ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
$(DEB_HOST_GNU_TYPE)-strip -R .note -R .comment debian/trees/lua-base/usr/sbin/dnsmasq
endif
ln -s $(package) debian/trees/lua-base/usr/share/doc/dnsmasq-base-lua
cd debian/trees/lua-base && find . -type f ! -regex '.*DEBIAN/.*' -printf '%P\0' | LC_ALL=C sort -z | xargs -r0 md5sum > DEBIAN/md5sums
dpkg-shlibdeps --warnings=1 debian/trees/lua-base/usr/sbin/dnsmasq
dpkg-gencontrol $(PACKAGE_VERSION) -pdnsmasq-base-lua -Pdebian/trees/lua-base
find debian/trees/lua-base -depth -newermt '$(BUILD_DATE)' -print0 | xargs -0r touch --no-dereference --date='$(BUILD_DATE)'
chown -R root.root debian/trees/lua-base
chmod -R g-ws debian/trees/lua-base
dpkg --build debian/trees/lua-base ..
ifeq ($(DEB_HOST_ARCH_OS),linux)
rm -rf debian/utils
install -m 755 -d debian/utils/DEBIAN \
-d debian/utils/usr/share/man/man1 \
-d debian/utils/usr/bin \
-d debian/utils/usr/share/doc/dnsmasq-utils
make -C contrib/lease-tools PREFIX=/usr DESTDIR=`pwd`/debian/utils CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" COPTS="$(DEB_COPTS)" CC=$(CC) PKG_CONFIG=$(PKG_CONFIG)
install -m 755 contrib/lease-tools/dhcp_release debian/utils/usr/bin/dhcp_release
install -m 644 contrib/lease-tools/dhcp_release.1 debian/utils/usr/share/man/man1/dhcp_release.1
gzip -9n debian/utils/usr/share/man/man1/dhcp_release.1
install -m 755 contrib/lease-tools/dhcp_release6 debian/utils/usr/bin/dhcp_release6
install -m 644 contrib/lease-tools/dhcp_release6.1 debian/utils/usr/share/man/man1/dhcp_release6.1
gzip -9n debian/utils/usr/share/man/man1/dhcp_release6.1
install -m 755 contrib/lease-tools/dhcp_lease_time debian/utils/usr/bin/dhcp_lease_time
install -m 644 contrib/lease-tools/dhcp_lease_time.1 debian/utils/usr/share/man/man1/dhcp_lease_time.1
install -m 644 debian/copyright debian/utils/usr/share/doc/dnsmasq-utils/copyright
install -m 644 debian/changelog debian/utils/usr/share/doc/dnsmasq-utils/changelog.Debian
gzip -9n debian/utils/usr/share/doc/dnsmasq-utils/changelog.Debian
gzip -9n debian/utils/usr/share/man/man1/dhcp_lease_time.1
rm -rf debian/trees/utils
install -m 755 -d debian/trees/utils/DEBIAN \
-d debian/trees/utils/usr/bin \
-d debian/trees/utils/usr/share/doc/dnsmasq-utils
ifeq (,$(findstring nodocs,$(DEB_BUILD_OPTIONS)))
install -m 755 -d debian/trees/utils/usr/share/man/man1
endif
make -C contrib/lease-tools PREFIX=/usr DESTDIR=`pwd`/debian/trees/utils CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" COPTS="$(DEB_COPTS)" CC=$(CC) PKG_CONFIG=$(PKG_CONFIG)
install -m 755 contrib/lease-tools/dhcp_release debian/trees/utils/usr/bin/dhcp_release
install -m 755 contrib/lease-tools/dhcp_release6 debian/trees/utils/usr/bin/dhcp_release6
install -m 755 contrib/lease-tools/dhcp_lease_time debian/trees/utils/usr/bin/dhcp_lease_time
ifeq (,$(findstring nodocs,$(DEB_BUILD_OPTIONS)))
install -m 644 contrib/lease-tools/dhcp_release.1 debian/trees/utils/usr/share/man/man1/dhcp_release.1
gzip -9n debian/trees/utils/usr/share/man/man1/dhcp_release.1
install -m 644 contrib/lease-tools/dhcp_release6.1 debian/trees/utils/usr/share/man/man1/dhcp_release6.1
gzip -9n debian/trees/utils/usr/share/man/man1/dhcp_release6.1
install -m 644 contrib/lease-tools/dhcp_lease_time.1 debian/trees/utils/usr/share/man/man1/dhcp_lease_time.1
gzip -9n debian/trees/utils/usr/share/man/man1/dhcp_lease_time.1
endif
install -m 644 debian/copyright debian/trees/utils/usr/share/doc/dnsmasq-utils/copyright
install -m 644 debian/changelog debian/trees/utils/usr/share/doc/dnsmasq-utils/changelog.Debian
gzip -9n debian/trees/utils/usr/share/doc/dnsmasq-utils/changelog.Debian
ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
$(DEB_HOST_GNU_TYPE)-strip -R .note -R .comment debian/utils/usr/bin/dhcp_release
$(DEB_HOST_GNU_TYPE)-strip -R .note -R .comment debian/utils/usr/bin/dhcp_release6
$(DEB_HOST_GNU_TYPE)-strip -R .note -R .comment debian/utils/usr/bin/dhcp_lease_time
$(DEB_HOST_GNU_TYPE)-strip -R .note -R .comment debian/trees/utils/usr/bin/dhcp_release
$(DEB_HOST_GNU_TYPE)-strip -R .note -R .comment debian/trees/utils/usr/bin/dhcp_release6
$(DEB_HOST_GNU_TYPE)-strip -R .note -R .comment debian/trees/utils/usr/bin/dhcp_lease_time
endif
cd debian/utils && find . -type f ! -regex '.*DEBIAN/.*' -printf '%P\0' | LC_ALL=C sort -z | xargs -r0 md5sum > DEBIAN/md5sums
dpkg-shlibdeps -Tdebian/utils-substvars debian/utils/usr/bin/dhcp_release debian/utils/usr/bin/dhcp_lease_time
dpkg-gencontrol $(PACKAGE_VERSION) -Tdebian/utils-substvars -pdnsmasq-utils -Pdebian/utils
find debian/utils -depth -newermt '$(BUILD_DATE)' -print0 | xargs -0r touch --no-dereference --date='$(BUILD_DATE)'
chown -R root.root debian/utils
chmod -R g-ws debian/utils
dpkg --build debian/utils ..
cd debian/trees/utils && find . -type f ! -regex '.*DEBIAN/.*' -printf '%P\0' | LC_ALL=C sort -z | xargs -r0 md5sum > DEBIAN/md5sums
dpkg-shlibdeps -Tdebian/utils-substvars debian/trees/utils/usr/bin/dhcp_release debian/trees/utils/usr/bin/dhcp_release6 debian/trees/utils/usr/bin/dhcp_lease_time
dpkg-gencontrol $(PACKAGE_VERSION) -Tdebian/utils-substvars -pdnsmasq-utils -Pdebian/trees/utils
find debian/trees/utils -depth -newermt '$(BUILD_DATE)' -print0 | xargs -0r touch --no-dereference --date='$(BUILD_DATE)'
chown -R root.root debian/trees/utils
chmod -R g-ws debian/trees/utils
dpkg --build debian/trees/utils ..
endif
define checkdir

1
debian/shlibs.local vendored
View File

@@ -1 +0,0 @@
libnettle 6 libnettle6 (>= 3.3)

1
debian/tmpfiles.conf vendored Normal file
View File

@@ -0,0 +1 @@
d /run/dnsmasq 755 dnsmasq nogroup

View File

@@ -90,7 +90,7 @@
# server=10.1.2.3@eth1
# and this sets the source (ie local) address used to talk to
# 10.1.2.3 to 192.168.1.1 port 55 (there must be a interface with that
# 10.1.2.3 to 192.168.1.1 port 55 (there must be an interface with that
# IP on the machine, obviously).
# server=10.1.2.3@192.168.1.1#55
@@ -288,7 +288,7 @@
# Give a fixed IPv6 address and name to client with
# DUID 00:01:00:01:16:d2:83:fc:92:d4:19:e2:d8:b2
# Note the MAC addresses CANNOT be used to identify DHCPv6 clients.
# Note also the they [] around the IPv6 address are obligatory.
# Note also that the [] around the IPv6 address are obligatory.
#dhcp-host=id:00:01:00:01:16:d2:83:fc:92:d4:19:e2:d8:b2, fred, [1234::5]
# Ignore any clients which are not specified in dhcp-host lines
@@ -354,11 +354,11 @@
# Set option 58 client renewal time (T1). Defaults to half of the
# lease time if not specified. (RFC2132)
#dhcp-option=option:T1:1m
#dhcp-option=option:T1,1m
# Set option 59 rebinding time (T2). Defaults to 7/8 of the
# lease time if not specified. (RFC2132)
#dhcp-option=option:T2:2m
#dhcp-option=option:T2,2m
# Set the NTP time server address to be the same machine as
# is running dnsmasq
@@ -436,22 +436,22 @@
#dhcp-option-force=211,30i
# Set the boot filename for netboot/PXE. You will only need
# this is you want to boot machines over the network and you will need
# a TFTP server; either dnsmasq's built in TFTP server or an
# this if you want to boot machines over the network and you will need
# a TFTP server; either dnsmasq's built-in TFTP server or an
# external one. (See below for how to enable the TFTP server.)
#dhcp-boot=pxelinux.0
# The same as above, but use custom tftp-server instead machine running dnsmasq
#dhcp-boot=pxelinux,server.name,192.168.1.100
# Boot for Etherboot gPXE. The idea is to send two different
# filenames, the first loads gPXE, and the second tells gPXE what to
# load. The dhcp-match sets the gpxe tag for requests from gPXE.
#dhcp-match=set:gpxe,175 # gPXE sends a 175 option.
#dhcp-boot=tag:!gpxe,undionly.kpxe
#dhcp-boot=mybootimage
# Boot for iPXE. The idea is to send two different
# filenames, the first loads iPXE, and the second tells iPXE what to
# load. The dhcp-match sets the ipxe tag for requests from iPXE.
#dhcp-boot=undionly.kpxe
#dhcp-match=set:ipxe,175 # iPXE sends a 175 option.
#dhcp-boot=tag:ipxe,http://boot.ipxe.org/demo/boot.php
# Encapsulated options for Etherboot gPXE. All the options are
# Encapsulated options for iPXE. All the options are
# encapsulated within option 175
#dhcp-option=encap:175, 1, 5b # priority code
#dhcp-option=encap:175, 176, 1b # no-proxydhcp
@@ -547,6 +547,14 @@
# http://www.isc.org/files/auth.html
#dhcp-authoritative
# Set the DHCP server to enable DHCPv4 Rapid Commit Option per RFC 4039.
# In this mode it will respond to a DHCPDISCOVER message including a Rapid Commit
# option with a DHCPACK including a Rapid Commit option and fully committed address
# and configuration information. This must only be enabled if either the server is
# the only server for the subnet, or multiple servers are present and they each
# commit a binding for all clients.
#dhcp-rapid-commit
# Run an executable when a DHCP lease is created or destroyed.
# The arguments sent to the script are "add" or "del",
# then the MAC address, the IP address and finally the hostname
@@ -664,3 +672,8 @@
# Include all files in a directory which end in .conf
#conf-dir=/etc/dnsmasq.d/,*.conf
# If a DHCP client claims that its name is "wpad", ignore that.
# This fixes a security hole. see CERT Vulnerability VU#598349
#dhcp-name-match=set:wpad-ignore,wpad
#dhcp-ignore-names=tag:wpad-ignore

View File

@@ -66,6 +66,10 @@ the repo, or get a copy using git protocol with the command
<PRE><TT>git clone git://thekelleys.org.uk/dnsmasq.git </TT></PRE>
or
<PRE><TT>git clone http://thekelleys.org.uk/git/dnsmasq.git </TT></PRE>
<H2>License.</H2>
Dnsmasq is distributed under the GPL, version 2 or version 3 at your discretion. See the files COPYING and COPYING-v3 in the distribution
for details.

File diff suppressed because it is too large Load Diff

View File

@@ -478,7 +478,8 @@ la traza reversa direcci
.TP
.B \-c, --cache-size=<tamaño de caché>
Fijar el tamaño del caché de dnsmasq. El predeterminado es 150 nombres.
Fijar el tamaño a cero deshabilita el caché.
Fijar el tamaño a cero deshabilita el caché. Nota: el gran tamaño de
caché afecta el rendimiento.
.TP
.B \-N, --no-negcache
Deshabilitar caché negativo. El caché negativo le permite a dnsmasq

View File

@@ -10,7 +10,7 @@ est un serveur à faible empreinte mémoire faisant DNS, TFTP, PXE, annonces de
routeurs et DHCP. Il offre à la fois les services DNS et DHCP pour un réseau
local (LAN).
.PP
Dnsmasq accepte les requêtes DNS et y réponds soit en utilisant un petit cache
Dnsmasq accepte les requêtes DNS et y répond soit en utilisant un petit cache
local, soit en effectuant une requête à un serveur DNS récursif externe (par
exemple celui de votre fournisseur d'accès internet). Il charge le contenu du
fichier /etc/hosts afin que les noms locaux n'apparaissant pas dans les DNS
@@ -19,25 +19,25 @@ pour les hôtes présents dans le service DHCP. Il peut aussi agir en temps que
serveur DNS faisant autorité pour un ou plusieurs domaines, permettant à des
noms locaux d'apparaitre dans le DNS global.
.PP
Le serveur DHCP Dnsmasq DHCP supporte les définitions d'adresses statiques et les
Le serveur DHCP de Dnsmasq supporte les définitions d'adresses statiques et les
réseaux multiples. Il fournit par défaut un jeu raisonnable de paramètres DHCP,
et peut être configuré pour fournir n'importe quelle option DHCP.
Il inclut un serveur TFTP sécurisé en lecture seule permettant le démarrage via
le réseau/PXE de clients DHCP et supporte également le protocole BOOTP. Le
support PXE est complet, et comprend un mode proxy permettant de fournir des
informations PXE aux clients alors que l'allocation DHCP est effectuée par un
autre serveur.
informations PXE aux clients alors que l'allocation d'adresse via DHCP est
effectuée par un autre serveur.
.PP
Le serveur DHCPv6 de dnsmasq possède non seulement les mêmes fonctionalités
Le serveur DHCPv6 de dnsmasq possède non seulement les mêmes fonctionnalités
que le serveur DHCPv4, mais aussi le support des annonces de routeurs ainsi
qu'une fonctionalité permettant l'addition de ressources AAAA pour des
qu'une fonctionnalité permettant l'addition de ressources AAAA pour des
clients utilisant DHCPv4 et la configuration IPv6 sans état (stateless
autoconfiguration).
Il inclut le support d'allocations d'adresses (à la fois en DHCPv6 et en
annonces de routeurs - RA) pour des sous-réseaux dynamiquement délégués via
une délégation de préfixe DHCPv6.
.PP
Dnsmasq est developpé pour de petits systèmes embarqués. It tends à avoir
Dnsmasq est développé pour de petits systèmes embarqués. Il tend à avoir
l'empreinte mémoire la plus faible possible pour les fonctions supportées,
et permet d'exclure les fonctions inutiles du binaire compilé.
.SH OPTIONS
@@ -46,16 +46,23 @@ Dans ce cas, la fonction correspondante sera désactivée. Par exemple
.B --pid-file=
(sans paramètre après le =) désactive l'écriture du fichier PID.
Sur BSD, à moins que le logiciel ne soit compilé avec la bibliothèque GNU
getopt, la forme longue des options ne fonctionne pas en ligne de commande; Elle
getopt, la forme longue des options ne fonctionne pas en ligne de commande; elle
est toujours supportée dans le fichier de configuration.
.TP
.B --test
Vérifie la syntaxe du ou des fichiers de configurations. Se termine avec le
Vérifie la syntaxe du ou des fichiers de configuration. Se termine avec le
code de retour 0 si tout est OK, ou un code différent de 0 dans le cas
contraire. Ne démarre pas Dnsmasq.
.TP
.B \-w, --help
Affiche toutes les options de ligne de commande.
.B --help dhcp
affiche les options de configuration connues pour DHCPv4, et
.B --help dhcp6
affiche les options de configuration connues pour DHCPv6.
.TP
.B \-h, --no-hosts
Ne pas charger les noms du fichier /etc/hosts.
Ne pas charger les noms d'hôtes du fichier /etc/hosts.
.TP
.B \-H, --addn-hosts=<fichier>
Fichiers d'hôtes additionnels. Lire le fichier spécifié en plus de /etc/hosts.
@@ -68,7 +75,7 @@ fichiers contenus dans ce répertoire.
.B \-E, --expand-hosts
Ajoute le nom de domaine aux noms simples (ne contenant pas de point dans le
nom) contenus dans le fichier /etc/hosts, de la même façon que pour le service
DHCP. Notez que cela ne s'applique pas au nom de domaine dans les CNAME, les
DHCP. Notez que cela ne s'applique pas aux noms de domaine dans les CNAME, les
enregistrements PTR, TXT, etc...
.TP
.B \-T, --local-ttl=<durée>
@@ -86,7 +93,7 @@ Les réponses négatives provenant des serveurs amonts contiennent normalement
une information de durée de vie (time-to-live) dans les enregistrements SOA,
information dont dnsmasq se sert pour mettre la réponse en cache. Si la réponse
du serveur amont omet cette information, dnsmasq ne cache pas la réponse. Cette
option permet de doner une valeur de durée de vie par défaut (en secondes) que
option permet de donner une valeur de durée de vie par défaut (en secondes) que
dnsmasq utilise pour mettre les réponses négatives dans son cache, même en
l'absence d'enregistrement SOA.
.TP
@@ -194,7 +201,7 @@ au dessus de la valeur spécifiée. Utile pour des systèmes derrière des dispo
garde-barrières ("firewalls").
.TP
.B \-i, --interface=<nom d'interface>
N'écouter que sur l'interface réseau spécifiée. Dnsmasq aujoute automatiquement
N'écouter que sur l'interface réseau spécifiée. Dnsmasq ajoute automatiquement
l'interface locale ("loopback") à la liste des interfaces lorsque l'option
.B --interface
est utilisée.
@@ -205,13 +212,13 @@ ou
n'est donnée, Dnsmasq écoutera sur toutes les interfaces disponibles sauf
celle(s) spécifiée(s) par l'option
.B --except-interface.
Les alias d'interfaces IP (e-g "eth1:0") ne peuvent être utilisés ni avec
Les alias d'interfaces IP (par exemple "eth1:0") ne peuvent être utilisés ni avec
.B --interface
ni
.B \--except-interface.
Utiliser l'option
.B --listen-address
à la place. Un simple joker, consistant d'un '*' final, peut-être utilisé dans
à la place. Un simple joker, consistant en un '*' final, peut être utilisé dans
les options
.B \--interface
et
@@ -229,10 +236,10 @@ sont fournies n'importe pas, et que l'option
.B --except-interface
l'emporte toujours sur les autres.
.TP
.B --auth-server=<domaine>,<interface>|<addresse IP>
.B --auth-server=<domaine>,<interface>|<adresse IP>
Active le mode DNS faisant autorité pour les requêtes arrivant sur cette
interface ou sur cette adresse. Noter que l'interface ou l'adresse n'ont
pas besoin d'être mentionées ni dans
pas besoin d'être mentionnées ni dans
.B --interface
ni dans
.B --listen-address
@@ -253,7 +260,7 @@ Ecouter sur la ou les adresse(s) IP spécifiée(s). Les options
.B \--interface
et
.B \--listen-address
peuvent-être spécifiées simultanément, auquel cas un jeu d'interfaces et
peuvent être spécifiées simultanément, auquel cas un jeu d'interfaces et
d'adresses seront utilisées. Notez que si
aucune option
.B \--interface
@@ -265,7 +272,7 @@ nécessaire de fournir explicitement son adresse IP, 127.0.0.1 via l'option
.B \--listen-address.
.TP
.B \-z, --bind-interfaces
Sur les systèmes qui le supporte, Dnsmasq s'associe avec l'interface joker
Sur les systèmes qui le supportent, Dnsmasq s'associe avec l'interface joker
("wildcard"), même lorsqu'il ne doit écouter que sur certaines interfaces. Par
la suite, il rejette les requêtes auxquelles il ne doit pas répondre. Cette
situation présente l'avantage de fonctionner même lorsque les interfaces vont
@@ -302,7 +309,7 @@ le réseau auquel ils sont attachés). Cette possibilité est actuellement limit
.TP
.B \-b, --bogus-priv
Fausse résolution inverse pour les réseaux privés. Toutes les requêtes DNS
inverses pour des adresses IP privées (ie 192.168.x.x, etc...) qui ne sont pas
inverses pour des adresses IP privées (192.168.x.x, etc...) qui ne sont pas
trouvées dans /etc/hosts ou dans le fichier de baux DHCP se voient retournées
une réponse "pas de tel domaine" ("no such domain") au lieu d'être transmises
aux serveurs de nom amont ("upstream server").
@@ -317,7 +324,7 @@ modifiera 1.2.3.56 en 6.7.8.56 et 1.2.3.67 en 6.7.8.67.
Cette fonctionnalité correspond à ce que les routeurs Cisco PIX appellent
"bidouillage DNS" ("DNS doctoring"). Si l'ancienne IP est donnée sous la forme
d'une gamme d'adresses, alors seules les adresses dans cette gamme seront
ecrites, et non le sous-réseau dans son ensemble. Ainsi,
écrites, et non le sous-réseau dans son ensemble. Ainsi,
.B --alias=192.168.0.10-192.168.0.40,10.0.0.0,255.255.255.0
fait correspondre 192.168.0.10->192.168.0.40 à 10.0.0.10->10.0.0.40
.TP
@@ -380,7 +387,7 @@ effectuer ses requêtes à tous les serveurs disponibles. Le résultat renvoyé
au client sera celui fournit par le premier serveur ayant répondu.
.TP
.B --stop-dns-rebind
Rejete (et enregistre dans le journal d'activité) les adresses dans la gamme
Rejette (et enregistre dans le journal d'activité) les adresses dans la gamme
d'adresses IP privée (au sens RFC1918) qui pourraient être renvoyées par les
serveurs amonts suite à une résolution de nom. Cela bloque les attaques cherchant
à détourner de leur usage les logiciels de navigation web ('browser') en s'en
@@ -389,13 +396,13 @@ servant pour découvrir les machines situées sur le réseau local.
.B --rebind-localhost-ok
Exclue 127.0.0/8 des vérifications de réassociation DNS. Cette gamme d'adresses
est retournée par les serveurs Realtime Blackhole (RBL, utilisés dans la
lutte contre le spam), la bloquer peut entraîner des disfonctionnements de ces
lutte contre le spam), la bloquer peut entraîner des dysfonctionnements de ces
services.
.TP
.B --rebind-domain-ok=[<domaine>]|[[/<domaine>/[<domaine>/]
Ne pas détecter ni bloquer les actions de type dns-rebind pour ces domaines.
Cette option peut prendre comme valeur soit un nom de domaine soit plusieurs
noms de domains entourés par des '/', selon une syntaxe similaire à l'option
noms de domaine entourés par des '/', selon une syntaxe similaire à l'option
--server, c-à-d :
.B --rebind-domain-ok=/domaine1/domaine2/domaine3/
.TP
@@ -474,7 +481,7 @@ serveur de nom. Il doit s'agir d'une des adresses IP appartenant à la machine s
laquelle tourne Dnsmasq ou sinon la ligne sera ignorée et une erreur sera
consignée dans le journal des événements, ou alors d'un nom d'interface. Si un nom
d'interface est donné, alors les requêtes vers le serveur de nom seront envoyées
depuis cette interface; si une adresse ip est donnée, alors l'adresse source de
depuis cette interface; si une adresse IP est donnée, alors l'adresse source de
la requête sera l'adresse en question. L'option query-port est ignorée pour tous
les serveurs ayant une adresse source spécifiée, mais il est possible de la donner
directement dans la spécification de l'adresse source. Forcer les requêtes à être
@@ -510,7 +517,7 @@ d'IP netfilter (ipset) indiqués. Domaines et sous-domaines sont résolus de la
même façon que pour --address. Ces groupes d'IP doivent déjà exister. Voir
ipset(8) pour plus de détails.
.TP
.B \-m, --mx-host=<nom de l'hôte>[[,<nom du MX>],<préference>]
.B \-m, --mx-host=<nom de l'hôte>[[,<nom du MX>],<préférence>]
Spécifie un enregistrement de type MX pour <nom de l'hôte> retournant le nom
donné dans <nom du MX> (s'il est présent), ou sinon le nom spécifié dans
l'option
@@ -612,7 +619,7 @@ l'enregistrement est donnée dans les données hexadécimales, qui peuvent
être de la forme 01:23:45, 01 23 45,+012345 ou n'importe quelle combinaison.
.TP
.B --interface-name=<nom>,<interface>
Définit un entregistrement DNS associant le nom avec l'adresse primaire sur
Définit un enregistrement DNS associant le nom avec l'adresse primaire sur
l'interface donnée en argument. Cette option spécifie un enregistrement de type
A pour le nom donné en argument de la même façon que s'il était défini par une
ligne de /etc/hosts, sauf que l'adresse n'est pas constante mais dépendante de
@@ -638,7 +645,7 @@ IPv6 pouvant commencer par '::', mais les noms DNS ne pouvant pas commencer
par '-', si aucun préfixe n'est donné, un zéro est ajouté en début de nom.
Ainsi, ::1 devient 0--1.
La plage d'adresses peut-être de la forme
La plage d'adresses peut être de la forme
<adresse IP>,<adresse IP> ou <adresse IP>/<masque réseau>
.TP
.B --add-mac
@@ -647,7 +654,7 @@ amonts. Cela peut être utilisé dans un but de filtrage DNS par les serveurs
amonts. L'adresse MAC peut uniquement être ajoutée si le requêteur est sur le
même sous-réseau que le serveur dnsmasq. Veuillez noter que le mécanisme
utilisé pour effectuer cela (une option EDNS0) n'est pas encore standardisée,
aussi cette fonctionalité doit être considérée comme expérimentale. Notez
aussi cette fonctionnalité doit être considérée comme expérimentale. Notez
également qu'exposer les adresses MAC de la sorte peut avoir des implications
en termes de sécurité et de vie privée. L'avertissement donné pour --add-subnet
s'applique également ici.
@@ -659,19 +666,20 @@ longueur du préfixe : 32 (ou 128 dans le cas d'IPv6) transmet la totalité
de l'adresse, 0 n'en transmet aucun mais marque néanmoins la requête ce qui
fait qu'aucun serveur amont ne rajoutera d'adresse client. La valeur par
défaut est zéro et pour IPv4 et pour IPv6. A noter que les serveurs amonts
peuvent-être configurés pour retourner des valeurs différentes en fonction
peuvent être configurés pour retourner des valeurs différentes en fonction
de cette information mais que le cache de dnsmasq n'en tient pas compte.
Si une instance de dnsmasq est configurée de telle maniêre que des valeurs
différentes pourraient-être rencontrés, alors le cache devrait être désactivé.
Si une instance de dnsmasq est configurée de telle manière que des valeurs
différentes pourraient être rencontrées, alors le cache devrait être désactivé.
.TP
.B \-c, --cache-size=<taille>
Définit la taille du cache de Dnsmasq. La valeur par défaut est de 150 noms.
Définir une valeur de zéro désactive le cache.
Définir une valeur de zéro désactive le cache. Remarque: la taille importante
du cache a un impact sur les performances.
.TP
.B \-N, --no-negcache
Désactive le "cache négatif". Le "cache négatif" permet à Dnsmasq de se souvenir
des réponses de type "no such domain" fournies par les serveurs DNS en amont et
de fournir les réponses sans avoir à re-transmettre les requêtes aux serveurs
de fournir les réponses sans avoir à retransmettre les requêtes aux serveurs
amont.
.TP
.B \-0, --dns-forward-max=<nombre de requêtes>
@@ -683,17 +691,17 @@ son journal des requêtes, ce qui peut générer un nombre important de requête
simultanées.
.TP
.B --proxy-dnssec
Un resolveur sur une machine cliente peut effectuer la validation DNSSEC de
Un résolveur sur une machine cliente peut effectuer la validation DNSSEC de
deux façons : il peut effectuer lui-même les opérations de chiffrements sur
la réponse reçue, ou il peut laisser le serveur récursif amont faire la
validation et positionner un drapeau dans la réponse au cas où celle-ci est
correcte. Dnsmasq n'est pas un validateur DNSSEC, aussi il ne peut effectuer
la validation comme un serveur de nom récursif, cependant il peut retransmettre
les résultats de validation de ses serveurs amonts. Cette option permet
l'activation de cette fonctionalité. Vous ne devriez utiliser cela que si vous
l'activation de cette fonctionnalité. Vous ne devriez utiliser cela que si vous
faites confiance aux serveurs amonts
.I ainsi que le réseau entre vous et eux.
Si vous utilisez le premier mode DNSSEC, la validation par le resolveur des
Si vous utilisez le premier mode DNSSEC, la validation par le résolveur des
clients, cette option n'est pas requise. Dnsmasq retourne toujours toutes les
données nécessaires par un client pour effectuer la validation lui-même.
.TP
@@ -703,12 +711,12 @@ Définie une zone DNS pour laquelle dnsmasq agit en temps que serveur faisant
autorité. Les enregistrements DNS définis localement et correspondant à ce
domaine seront fournis. Les enregistrements A et AAAA doivent se situer dans
l'un des sous-réseaux définis, ou dans un réseau correspondant à une plage DHCP
(ce comportement peut-être désactivé par
(ce comportement peut être désactivé par
.B constructor-noauth:
). Le ou les sous-réseaux sont également utilisé(s) pour définir les domaines
in-addr.arpa et ip6.arpa servant à l'interrogation DNS inverse. Si la longueur
de préfixe n'est pas spécifiée, elle sera par défaut de 24 pour IPv4 et 64 pour
IPv6. Dans le cas d'IPv4, la longueur du masque de réseau devrait-être de 8, 16
IPv6. Dans le cas d'IPv4, la longueur du masque de réseau devrait être de 8, 16
ou 24, sauf si en cas de mise en place d'une délégation de la zone in-addr.arpa
conforme au RFC 2317.
.TP
@@ -719,7 +727,7 @@ optionnel, les valeurs par défaut devant convenir à la majorité des cas.
.TP
.B --auth-sec-servers=<domaine>[,<domaine>[,<domaine>...]]
Spécifie un ou plusieurs serveur de nom secondaires pour une zone pour
laquelle dnsmasq fait autorité. Ces serveurs doivent-être configurés pour
laquelle dnsmasq fait autorité. Ces serveurs doivent être configurés pour
récupérer auprès de dnsmasq les informations liées à la zone au travers d'un
transfert de zone, et répondre aux requêtes pour toutes les zones pour
lesquelles dnsmasq fait autorité.
@@ -733,7 +741,7 @@ seront acceptées pour tous les serveurs secondaires.
.B --conntrack
Lis le marquage de suivi de connexion Linux associé aux requêtes DNS entrantes
et positionne la même marque au trafic amont utilisé pour répondre à ces
requétes. Cela permet au trafic généré par Dnsmasq d'étre associé aux requêtes
requêtes. Cela permet au trafic généré par Dnsmasq d'être associé aux requêtes
l'ayant déclenché, ce qui est pratique pour la gestion de la bande passante
(accounting) et le filtrage (firewall). Dnsmasq doit pour cela être compilé
avec le support conntrack, le noyau doit également inclure conntrack et être
@@ -742,7 +750,7 @@ configuré pour cela. Cette option ne peut pas être combinée avec
.TP
.B \-F, --dhcp-range=[tag:<label>[,tag:<label>],][set:<label>],]<adresse de début>[,<adresse de fin>][,<mode>][,<masque de réseau>[,<broadcast>]][,<durée de bail>]
.TP
.B \-F, --dhcp-range=[tag:<label>[,tag:<label>],][set:<label>],]<addresse IPv6 de début>[,<adresse IPv6 de fin>|constructor:<interface>][,<mode>][,<longueur de préfixe>][,<durée de bail>]
.B \-F, --dhcp-range=[tag:<label>[,tag:<label>],][set:<label>],]<adresse IPv6 de début>[,<adresse IPv6 de fin>|constructor:<interface>][,<mode>][,<longueur de préfixe>][,<durée de bail>]
Active le serveur DHCP. Les adresses seront données dans la plage comprise entre
<adresse de début> et <adresse de fin> et à partir des adresses définies
@@ -755,7 +763,7 @@ durée indéterminée. Si aucune valeur n'est donnée, une durée de bail par d
de une heure est appliquée. La valeur minimum pour un bail DHCP est de 2
minutes.
Pour les plages IPv6, la durée de bail peut-être égale au mot-clef "deprecated"
Pour les plages IPv6, la durée de bail peut être égale au mot-clef "deprecated"
(obsolète); Cela positionne la durée de vie préférée envoyée dans les baux DHCP
ou les annonces routeurs à zéro, ce qui incite les clients à utiliser d'autres
adresses autant que possible, pour toute nouvelle connexion, en préalable à
@@ -795,7 +803,7 @@ adresses assignées à l'interface. Par exemple
provoque la recherche d'adresses de la forme <réseau>::1 sur eth0 et crée une
plage allant de <réseau>::1 à <réseau>:400. Si une interface est assignée à
plus d'un réseau, les plages correspondantes seront automatiquement créées,
rendues obsolètes puis supprimées lorsque l'adress est rendue obsolète puis
rendues obsolètes puis supprimées lorsque l'adresse est rendue obsolète puis
supprimée. Le nom de l'interface peut être spécifié avec un caractère joker '*'
final.
@@ -809,7 +817,7 @@ obsolètes ne conviennent pas.
Si une plage dhcp-range est uniquement utilisée pour du DHCP sans-état
("stateless") ou de l'autoconfiguration sans état ("SLAAC"), alors l'adresse
peut-être indiquée sous la forme '::'
peut être indiquée sous la forme '::'
.B --dhcp-range=::,constructor:eth0
@@ -851,7 +859,7 @@ et
.B pxe-service
pour plus de détails).
Pour IPv6, le mode peut-être une combinaison des valeurs
Pour IPv6, le mode peut être une combinaison des valeurs
.B ra-only, slaac, ra-names, ra-stateless, off-link.
.B ra-only
@@ -883,7 +891,7 @@ connectés (et non ceux pour lesquels DHCP se fait via relai), et ne
fonctionnera pas si un hôte utilise les "extensions de vie privée"
("privacy extensions").
.B ra-names
peut-être combiné avec
peut être combiné avec
.B ra-stateless
et
.B slaac.
@@ -914,7 +922,7 @@ sous-réseau qu'une plage dhcp-range valide. Pour les sous-réseaux qui n'ont pa
besoin d'adresses dynamiquement allouées, utiliser le mot-clef "static" dans la
déclaration de plage d'adresses dhcp-range.
Il est possible d'utiliser des identifiants clients (appellé "DUID client" dans
Il est possible d'utiliser des identifiants clients (appelés "DUID client" dans
le monde IPv6) plutôt que des adresses matérielles pour identifier les hôtes,
en préfixant ceux-ci par 'id:'. Ainsi,
.B --dhcp-host=id:01:02:03:04,.....
@@ -926,7 +934,7 @@ ceci :
Un seul
.B dhcp-host
peut contenir une adresse IPv4, une adresse IPv6, ou les deux en même temps.
Les adresses IPv6 doivent-être mises entre crochets comme suit :
Les adresses IPv6 doivent être mises entre crochets comme suit :
.B --dhcp-host=laptop,[1234::56]
Les adresses IPv6 peuvent ne contenir que la partie identifiant de client :
.B --dhcp-host=laptop,[::56]
@@ -945,7 +953,7 @@ identifiant client mais pas les autres.
Si un nom apparaît dans /etc/hosts, l'adresse associée peut être allouée à un
bail DHCP mais seulement si une option
.B --dhcp-host
spécifiant le nom existe par ailleurs. Seul un nom d'hôte peut-être donné dans
spécifiant le nom existe par ailleurs. Seul un nom d'hôte peut être donné dans
une option
.B dhcp-host
, mais les alias sont possibles au travers de l'utilisation des CNAMEs. (Voir
@@ -973,7 +981,7 @@ Les adresses ethernet (mais pas les identifiants clients) peuvent être définie
avec des octets joker, ainsi par exemple
.B --dhcp-host=00:20:e0:3b:13:*,ignore
demande à Dnsmasq d'ignorer une gamme d'adresses matérielles. Il est à noter
que "*" doit-être précédé d'un caractère d'échappement ou mis entre guillemets
que "*" doit être précédé d'un caractère d'échappement ou mis entre guillemets
lorsque spécifié en option de ligne de commande, mais pas dans le fichier de
configuration.
@@ -1059,14 +1067,14 @@ qu'aux réseaux dont tous les labels coïncident avec ceux de la requête.
Un traitement spécial est effectué sur les chaînes de caractères fournies pour
l'option 119, conformément à la RFC 3397. Les chaînes de caractères ou les
adresses IP sous forme de 4 chiffres séparés par des points donnés en arguments
de l'option 120 sont traités conforméments à la RFC 3361. Les adresses IP sous
de l'option 120 sont traités conformément à la RFC 3361. Les adresses IP sous
forme de 4 chiffres séparés par des points suivies par une barre montante "/",
puis une taille de masque sont encodés conforméments à la RFC 3442.
puis une taille de masque sont encodés conformément à la RFC 3442.
Les options IPv6 sont fournies en utilisant le mot-clef
.B option6:
suivi par le numéro d'option ou le nom d'option. L'espace de nommage des options
IPv6 est disjint de l'espace de nommage des options IPv4. Les adresses IPv6
IPv6 est disjoint de l'espace de nommage des options IPv4. Les adresses IPv6
en option doivent être entourées de crochets, comme par exemple :
.B --dhcp-option=option6:ntp-server,[1234::56]
@@ -1075,7 +1083,7 @@ adéquat sont envoyées pour un numéro d'option donné, il est tout à fait pos
de persuader Dnsmasq de générer des paquets DHCP illégaux par une utilisation
incorrecte de cette option. Lorsque la valeur est un nombre décimal, Dnsmasq
doit déterminer la taille des données. Cela est fait en examinant le numéro de
l'option et/ou la valeur, mais peut-être évité en rajoutant un suffixe d'une
l'option et/ou la valeur, mais peut être évité en rajoutant un suffixe d'une
lettre comme suit :
b = un octet, s = 2 octets, i = 4 octets. Cela sert essentiellement pour des
options encapsulées de classes de vendeurs (voir plus bas), pour lesquelles
@@ -1088,7 +1096,7 @@ d'une chaîne de caractères comme nom de serveur TFTP, il est nécessaire de fa
comme suit :
.B --dhcp-option=66,"1.2.3.4"
Les options encapsulées de classes de vendeurs peuvent-être aussi spécifiées
Les options encapsulées de classes de vendeurs peuvent être aussi spécifiées
(pour IPv4 seulement) en utilisant
.B --dhcp-option
: par exemple
@@ -1105,7 +1113,7 @@ par le client. Il est possible d'omettre complètement une classe de vendeur :
.B --dhcp-option=vendor:,1,0.0.0.0
Dans ce cas l'option encapsulée est toujours envoyée.
En IPv4, les options peuvent-être encapsulées au sein d'autres options :
En IPv4, les options peuvent être encapsulées au sein d'autres options :
par exemple
.B --dhcp-option=encap:175, 190, "iscsi-client0"
enverra l'option 175, au sein de laquelle se trouve l'option 190.
@@ -1128,7 +1136,7 @@ une option encapsulée.
Cela fonctionne exactement de la même façon que
.B --dhcp-option
sauf que cette option sera toujours envoyée, même si le client ne la demande pas
dans la liste de paramêtres requis. Cela est parfois nécessaire, par exemple lors
dans la liste de paramètres requis. Cela est parfois nécessaire, par exemple lors
de la fourniture d'options à PXELinux.
.TP
.B --dhcp-no-override
@@ -1149,10 +1157,10 @@ Toutes les requêtes DHCP arrivant sur cette interface seront relayées au
serveur DHCP distant correspondant à l'adresse de serveur indiquée. Il est
possible de relayer depuis une unique adresse locale vers différents serveurs
distant en spécifiant plusieurs fois l'option dhcp-relay avec la même adresse
locale et différentes adresses de serveur. L'adresse de serveur doit-être
sous forme numérique. Dans le cas de DHCPv6, l'adresse de serveur peut-être
locale et différentes adresses de serveur. L'adresse de serveur doit être
sous forme numérique. Dans le cas de DHCPv6, l'adresse de serveur peut être
l'adresse de multicast ff05::1:3 correspondant à tous les serveurs DHCP. Dans
ce cas, l'interface doittre spécifiée et ne peut comporter de caractère
ce cas, l'interface doit être spécifiée et ne peut comporter de caractère
joker. Elle sera utilisée pour indiquer l'interface à partir de laquelle le
multicast pourra atteindre le serveur DHCP.
@@ -1181,14 +1189,14 @@ vice-versa.
Associe une chaîne de classe de vendeur à un label. La plupart
des clients DHCP fournissent une "classe de vendeur" ("vendor class") qui
représente, d'une certaine façon, le type d'hôte. Cette option associe des
classes de vendeur à des labels, de telle sorte que des options DHCP peuvent-être
fournie de manière sélective aux différentes classes d'hôtes. Par exemple,
classes de vendeur à des labels, de telle sorte que des options DHCP peuvent être
fournies de manière sélective aux différentes classes d'hôtes. Par exemple,
.B dhcp-vendorclass=set:printers,Hewlett-Packard JetDirect
ou
.B dhcp-vendorclass=printers,Hewlett-Packard JetDirect
permet de n'allouer des options qu'aux imprimantes HP de la manière suivante :
.B --dhcp-option=tag:printers,3,192.168.4.4
La chaîne de caractères de la classe de vendeur founie en argument est cherchée
La chaîne de caractères de la classe de vendeur fournie en argument est cherchée
en temps que sous-chaîne de caractères au sein de la classe de vendeur fournie
par le client, de façon à permettre la recherche d'un sous-ensemble de la chaîne
de caractères ("fuzzy matching"). Le préfixe set: est optionnel mais autorisé
@@ -1218,7 +1226,7 @@ matérielle coïncide avec les critères définis.
.TP
.B --dhcp-circuitid=set:<label>,<identifiant de circuit>, --dhcp-remoteid=set:<label>,<identifiant distant>
Associe des options de relais DHCP issus de la RFC3046 à des labels.
Cette information peut-être fournie par des relais DHCP. L'identifiant
Cette information peut être fournie par des relais DHCP. L'identifiant
de circuit ou l'identifiant distant est normalement fourni sous la forme d'une
chaîne de valeurs hexadécimales séparées par des ":", mais il est également
possible qu'elle le soit sous la forme d'une simple chaîne de caractères. Si
@@ -1231,7 +1239,7 @@ est supporté en IPv6 (mais non dhcp-circuitid).
(IPv4 et IPv6) Associe des options de relais DHCP issues de la RFC3993 à des
labels.
.TP
.B --dhcp-proxy[=<adresse ip>]......
.B --dhcp-proxy[=<adresse IP>]......
(IPv4 seulement) Un agent relai DHCP normal est uniquement utilisé pour faire
suivre les éléments initiaux de l'interaction avec le serveur DHCP. Une fois
que le client est configuré, il communique directement avec le serveur. Cela
@@ -1252,7 +1260,7 @@ interactions avec les relais dont l'adresse est dans la liste seront affectées.
Si aucune valeur n'est spécifiée, associe le label si le client
envoie une option DHCP avec le numéro ou le nom spécifié. Lorsqu'une valeur est
fournie, positionne le label seulement dans le cas où l'option est fournie et
correspond à la valeur. La valeur peut-être de la forme "01:ff:*:02", auquel
correspond à la valeur. La valeur peut être de la forme "01:ff:*:02", auquel
cas le début de l'option doit correspondre (en respectant les jokers). La
valeur peut aussi être de la même forme que dans
.B dhcp-option
@@ -1262,14 +1270,14 @@ valeur peut aussi être de la même forme que dans
--dhcp-match=set:efi-ia32,option:client-arch,6
spécifie le label "efi-ia32" si le numéro 6 apparaît dnas la liste
d'architectures envoyé par le client au sein de l'option 93. (se réferer
d'architectures envoyé par le client au sein de l'option 93. (se référer
au RFC 4578 pour plus de détails). Si la valeur est un chaine de caractères,
celle-ci est recherchée (correspondance en temps que sous-chaîne).
Pour la forme particulière vi-encap:<numéro d'entreprise>, la comparaison se
fait avec les classes de vendeur "identifiant de vendeur" ("vendor-identifying
vendor classes") pour l'entreprise dont le numéro est fourni en option.
Veuillez vous réferer à la RFC 3925 pour plus de détail.
Veuillez vous référer à la RFC 3925 pour plus de détails.
.TP
.B --tag-if=set:<label>[,set:<label>[,tag:<label>[,tag:<label>]]]
Effectue une opération booléenne sur les labels. Si tous les labels
@@ -1280,7 +1288,7 @@ Si aucun tag:<label> n'est spécifié, alors tous les labels fournis par
set:<label> sont positionnés.
N'importe quel nombre de set: ou tag: peuvent être fournis, et l'ordre est sans
importance.
Les lignes tag-if sont executées dans l'ordre, ce qui fait que si un label dans
Les lignes tag-if sont exécutées dans l'ordre, ce qui fait que si un label dans
tag:<label> est un label positionné par une rêgle
.B tag-if,
la ligne qui positionne le label doit précéder celle qui le teste.
@@ -1320,17 +1328,17 @@ le cas de certains vieux clients BOOTP.
(IPv4 seulement) Spécifie les options BOOTP devant être retournées par le
serveur DHCP. Le nom de serveur ainsi que l'adresse sont optionnels : s'ils
ne sont pas fournis, le nom est laissé vide et l'adresse fournie est celle de
la machine sur laquelle s'exécute Dnsmasq. Si Dnsmasq founit un service TFTP (voir
la machine sur laquelle s'exécute Dnsmasq. Si Dnsmasq fournit un service TFTP (voir
.B --enable-tftp
), alors seul un nom de fichier est requis ici pour permettre un démarrage par
le réseau.
Si d'éventuels labels sont fournis, ils doivent coïncider avec
ceux du client pour que cet élement de configuration lui soit envoyé.
ceux du client pour que cet élément de configuration lui soit envoyé.
Une adresse de serveur TFTP peut être spécifiée à la place de l'adresse IP,
sous la forme d'un nom de domaine qui sera cherché dans le fichier /etc/hosts.
Ce nom peut être associé dans /etc/hosts avec plusieurs adresses IP, auquel cas
celles-ci seront utilisées tour à tour (algorithme round-robin).
Cela peut-être utiliser pour équilibrer la charge tftp sur plusieurs serveurs.
Cela peut être utilisé pour équilibrer la charge tftp sur plusieurs serveurs.
.TP
.B --dhcp-sequential-ip
Dnsmasq est conçu pour choisir l'adresse IP des clients DHCP en utilisant
@@ -1357,7 +1365,7 @@ Ceci spécifie l'option de démarrage qui apparaitra dans un menu de démarrage
PXE. <CSA> est le type du système client. Seuls des types de services valides
apparaitront dans un menu. Les types connus sont x86PC, PC98, IA64_EFI, Alpha,
Arc_x86, Intel_Lean_Client, IA32_EFI, BC_EFI, Xscale_EFI et X86-64_EFI;
D'autres types peuvent-être spécifiés sous la forme d'une valeur entière. Le
D'autres types peuvent être spécifiés sous la forme d'une valeur entière. Le
paramètre après le texte correspondant à l'entrée dans le menu peut être un nom
de fichier, auquel cas Dnsmasq agit comme un serveur de démarrage et indique au
client PXE qu'il faut télécharger ce fichier via TFTP, soit depuis ce serveur
@@ -1376,7 +1384,7 @@ démarrage n'est fournie (ou qu'une valeur de 0 est donnée pour le type de
service), alors l'entrée de menu provoque l'interruption du démarrage par
le réseau et la poursuite du démarrage sur un média local. L'adresse de serveur
peut être donnée sous la forme de nom de domaine qui est recherché dans
/etc/hosts. Ce nom peut-être associé à plusieurs adresses IP, qui dans ce cas
/etc/hosts. Ce nom peut être associé à plusieurs adresses IP, qui dans ce cas
sont utilisées à tour de rôle (en "round-robin").
.TP
.B --pxe-prompt=[tag:<label>,]<invite>[,<délai>]
@@ -1435,7 +1443,7 @@ Utiliser cette option avec précaution, une adresse allouée à un client BOOTP
étant perpétuelle, et de fait n'est plus disponibles pour d'autres hôtes. Si
aucun argument n'est donné, alors cette option permet une allocation dynamique
dans tous les cas. Si des arguments sont spécifiés, alors l'allocation ne se
fait que lorsque tous les identifiants coïncident. Il est possible de répeter
fait que lorsque tous les identifiants coïncident. Il est possible de répéter
cette option avec plusieurs jeux d'arguments.
.TP
.B \-5, --no-ping
@@ -1560,7 +1568,7 @@ Tous les descripteurs de fichiers sont fermés, sauf stdin, stdout et stderr qui
sont ouverts sur /dev/null (sauf en mode déverminage).
Le script n'est pas lancé de manière concurrente : au plus une instance du
script est executée à la fois (dnsmasq attends qu'une instance de script se
script est exécutée à la fois (dnsmasq attend qu'une instance de script se
termine avant de lancer la suivante). Les changements dans la base des baux
nécessitant le lancement du script sont placé en attente dans une queue jusqu'à
terminaison d'une instance du script en cours. Si cette mise en queue fait que
@@ -1575,7 +1583,7 @@ le script sera invoqué avec une action "old" pour tous les baux existants.
Il existe deux autres actions pouvant apparaître comme argument au script :
"init" et "tftp". D'autres sont susceptibles d'être rajoutées dans le futur,
aussi les scripts devraient-être écrits de sorte à ignorer les actions
aussi les scripts devraient être écrits de sorte à ignorer les actions
inconnues. "init" est décrite ci-dessous dans
.B --leasefile-ro.
L'action "tftp" est invoquée lorsqu'un transfert de fichier TFTP s'est
@@ -1588,7 +1596,7 @@ Spécifie un script écrit en Lua, devant être exécuté lorsque des baux sont
créés, détruits ou modifiés. Pour utiliser cette option, dnsmasq doit être
compilé avec avec le support de Lua. L'interpréteur Lua est initialisé une
seule fois, lorsque dnsmasq démarre, ce qui fait que les variables globales
persistent entre les évênements liés aux baux. Le code Lua doit définir une
persistent entre les événements liés aux baux. Le code Lua doit définir une
fonction
.B lease
et peut fournir des fonctions
@@ -1637,7 +1645,7 @@ et
.B --dhcp-scriptuser
Spécifie l'utilisateur sous lequel le script shell lease-change ou le script
doivent être exécutés. La valeur par défaut correspond à l'utilisateur root
mais peut-être changée par le biais de cette option.
mais peut être changée par le biais de cette option.
.TP
.B \-9, --leasefile-ro
Supprimer complètement l'usage du fichier servant de base de donnée pour les
@@ -1649,8 +1657,8 @@ biais de l'option
être complètement gérée par le script sur un stockage externe. En addition aux
actions décrites dans
.B --dhcp-script,
le script de changement d'état de bail est appellé une fois, au lancement de
Dnsmasq, avec pour seul argument "init". Lorsqu'appellé de la sorte, le script
le script de changement d'état de bail est appelé une fois, au lancement de
Dnsmasq, avec pour seul argument "init". Lorsqu'appelé de la sorte, le script
doit fournir l'état de la base de baux, dans le format de fichier de baux de
Dnsmasq, sur sa sortie standard (stdout) et retourner un code de retour de 0.
Positionner cette option provoque également une invocation du script de
@@ -1692,20 +1700,20 @@ positionné à la première valeur de la directive "search" du fichier
/etc/resolv.conf (ou équivalent).
La gamme d'adresses peut être de la forme
<adresse ip>,<adresse ip> ou <adresse ip>/<masque de réseau> voire une simple
<adresse ip>. Voir
<adresse IP>,<adresse IP> ou <adresse IP>/<masque de réseau> voire une simple
<adresse IP>. Voir
.B --dhcp-fqdn
qui peut changer le comportement de dnsmasq relatif aux domaines.
Si la gamme d'adresse est fournie sous la forme
<adresse ip>/<taille de réseau>, alors le drapeau "local" peut-être rajouté
qui a pour effect d'ajouter --local-declarations aux requêtes DNS directes et
<adresse IP>/<taille de réseau>, alors le drapeau "local" peut être rajouté
qui a pour effet d'ajouter --local-declarations aux requêtes DNS directes et
inverses. C-à-d
.B --domain=thekelleys.org.uk,192.168.0.0/24,local
est identique à
.B --domain=thekelleys.org.uk,192.168.0.0/24
--local=/thekelleys.org.uk/ --local=/0.168.192.in-addr.arpa/
La taille de réseau doit-être de 8, 16 ou 24 pour être valide.
La taille de réseau doit être de 8, 16 ou 24 pour être valide.
.TP
.B --dhcp-fqdn
Dans le mode par défaut, dnsmasq insère les noms non-qualifiés des clients
@@ -1717,8 +1725,8 @@ ce nom est transféré au nouveau client. Si
est spécifié, ce comportement change : les noms non qualifiés ne sont plus
rajoutés dans le DNS, seuls les noms qualifiés le sont. Deux clients DHCP
avec le même nom peuvent tous les deux garder le nom, pour peu que la partie
relative au domaine soit différente (c-à-d que les noms pleinements qualifiés
diffèrent). Pour d'assurer que tous les noms ont une partie domaine, il doit-y
relative au domaine soit différente (c-à-d que les noms pleinement qualifiés
diffèrent). Pour s'assurer que tous les noms ont une partie domaine, il doit y
avoir au moins un
.B --domain
sans gamme d'adresses de spécifié lorsque l'option
@@ -1735,7 +1743,7 @@ Windows de la mise à jour de serveurs Active Directory. Voir la RFC 4702 pour
plus de détails.
.TP
.B --enable-ra
Active la fonctionalité d'annonces routeurs IPv6 ("IPv6 Router Advertisement").
Active la fonctionnalité d'annonces routeurs IPv6 ("IPv6 Router Advertisement").
DHCPv6 ne gère pas la configuration complète du réseau de la même façon que
DHCPv4. La découverte de routeurs et la découverte (éventuelle) de préfixes pour
la création autonome d'adresse sont gérées par un protocole différent.
@@ -1747,7 +1755,7 @@ dhcp-range et, par défaut, fournir comme valeur de routeur et de DNS récursif
la valeur d'adresse link-local appropriée parmi celles de la machine sur
laquelle tourne dnsmasq.
Par défaut, les bits "managed address" sont positionnés, et le bit "use SLAAC"
("utiliser SLAAC") est réinitialisé. Cela peut-être changé pour des
("utiliser SLAAC") est réinitialisé. Cela peut être changé pour des
sous-réseaux donnés par le biais du mot clef de mode décris dans
.B --dhcp-range.
Les paramètres DNS du RFC6106 sont inclus dans les annonces. Par défaut,
@@ -1759,16 +1767,16 @@ DNSSL.
.B --ra-param=<interface>,[mtu:<valeur>|<interface>|off,][high,|low,]<intervalle d'annonce routeur>[,<durée de vie route>]
Configure pour une interface donnée des valeurs pour les annonces routeurs
différentes des valeurs par défaut. La valeur par défaut du champ priorité
pour le routeur peut-être changée de "medium" (moyen) à "high" (haute) ou
pour le routeur peut être changée de "medium" (moyen) à "high" (haute) ou
"low" (basse). Par exemple :
.B --ra-param=eth0,high,0.
Un intervalle (en secondes) entre les annonces routeur peut-être fourni par :
Un intervalle (en secondes) entre les annonces routeur peut être fourni par :
.B --ra-param=eth0,60.
La durée de vie de la route peut-être changée ou mise à zéro, auquel cas
La durée de vie de la route peut être changée ou mise à zéro, auquel cas
le routeur peut annoncer les préfixes mais pas de route :
.B --ra-parm=eth0,0,0
(une valeur de zéro pour l'intervalle signifie qu'il garde la valeur par défaut).
Ces quatre paramètres peuvent-être configurés en une fois :
Ces quatre paramètres peuvent être configurés en une fois :
.B --ra-param=eth0,mtu:1280,low,60,1200
La valeur pour l'interface peut inclure un caractère joker.
.TP
@@ -1776,7 +1784,7 @@ La valeur pour l'interface peut inclure un caractère joker.
Active la fonction serveur TFTP. Celui-ci est de manière délibérée limité aux
fonctions nécessaires au démarrage par le réseau ("net-boot") d'un client. Seul
un accès en lecture est possible; les extensions tsize et blksize sont supportées
(tsize est seulement supporté en mode octet). Sans argument optionel, le service
(tsize est seulement supportée en mode octet). Sans argument optionnel, le service
TFTP est fourni sur les mêmes interfaces que le service DHCP. Si une liste
d'interfaces est fournie, cela définit les interfaces sur lesquelles le
service TFTP sera activé.
@@ -1847,9 +1855,9 @@ Un serveur TFTP écoute sur le port prédéfini 69 ("well-known port") pour
l'initiation de la connexion, mais utilise également un port dynamiquement
alloué pour chaque connexion. Normalement, ces ports sont alloués par
le système d'exploitation, mais cette option permet de spécifier une gamme
de ports à utiliser pour les transferts TFTP. Cela peut-être utile si
de ports à utiliser pour les transferts TFTP. Cela peut être utile si
TFTP doit traverser un dispositif garde-barrière ("firewall"). La valeur
de début pour la plage de port ne peut-être inférieure à 1025 sauf si
de début pour la plage de port ne peut être inférieure à 1025 sauf si
dnsmasq tourne en temps que super-utilisateur ("root"). Le nombre de
connexions TFTP concurrentes est limitée par la taille de la gamme de
ports ainsi spécifiée.
@@ -1859,8 +1867,8 @@ Un serveur TFTP écoute sur un numéro de port bien connu (69) pour l'initiation
de la connexion, et alloue dynamiquement un port pour chaque connexion. Ces
numéros de ports sont en principe alloués par le système d'exploitation, mais
cette option permet de spécifier une gamme de ports à utiliser pour les
transferts TFTP. Cela peut-être utile lorsque ceux-ci doivent traverser un
dispositif garde-barrière ("firewall"). Le début de la plage ne peut-être
transferts TFTP. Cela peut être utile lorsque ceux-ci doivent traverser un
dispositif garde-barrière ("firewall"). Le début de la plage ne peut être
inférieur à 1024 à moins que Dnsmasq ne fonctionne en temps que
super-utilisateur ("root"). Le nombre maximal de connexions TFTP concurrentes
est limitée par la taille de la plage de ports ainsi définie.
@@ -1895,7 +1903,7 @@ par "--". Les lignes commençant par # sont des commentaires et sont ignorées.
Pour les options qui ne peuvent-être spécifiées qu'une seule fois, celle du
fichier de configuration prends le pas sur celle fournie en ligne de commande.
Il est possible d'utiliser des guillemets afin d'éviter que les ",",":","." et
"#" ne soit interprêtés, et il est possible d'utiliser les séquences
"#" ne soient interprétés, et il est possible d'utiliser les séquences
d'échappement suivantes : \\\\ \\" \\t \\e \\b \\r et \\n. Elles correspondent
respectivement à la barre oblique descendante ("anti-slash"), guillemets doubles,
tabulation, caractère d'échappement ("escape"), suppression ("backspace"), retour ("return") et
@@ -1940,8 +1948,8 @@ traces dans un fichier (voir
.B --log-facility
), alors
.B Dnsmasq
ferme et re-rouvre le fichier de traces. Il faut noter que pendant cette
opération Dnsmasq ne s'exécute pas en temps que "root". Lorsqu'il créé un
ferme et rouvre le fichier de traces. Il faut noter que pendant cette
opération Dnsmasq ne s'exécute pas en tant que "root". Lorsqu'il créé un
fichier de traces pour la première fois, Dnsmasq change le propriétaire du
fichier afin de le faire appartenir à l'utilisateur non "root" sous lequel
Dnsmasq s'exécute. Le logiciel de rotation de fichiers de trace logrotate doit
@@ -1983,7 +1991,7 @@ qu'une connexion PPP ne soit établie. Dans ce cas, Dnsmasq vérifie régulière
pour voir si un fichier
.I /etc/resolv.conf
est créé. Dnsmasq peut être configuré pour lire plus d'un fichier resolv.conf.
Cela est utile sur un ordinateur portable où PPP et DHCP peuvent-être utilisés :
Cela est utile sur un ordinateur portable où PPP et DHCP peuvent être utilisés :
Dnsmasq peut alors être configuré pour lire à la fois
.I /etc/ppp/resolv.conf
et
@@ -2007,7 +2015,7 @@ ou alors en mettant leurs adresses dans un autre fichier, par exemple
.I /etc/resolv.dnsmasq
et en lançant Dnsmasq avec l'option
.B \-r /etc/resolv.dnsmasq.
Cette deuxième technique permet la mise-à-jour dynamique des addresses de
Cette deuxième technique permet la mise-à-jour dynamique des adresses de
serveurs DNS amont par le biais de PPP ou DHCP.
.PP
Les adresses dans /etc/hosts prennent le dessus sur celles fournies par le
@@ -2067,10 +2075,10 @@ et pour affecter l'option envoyée, sur la base de la plage sélectionnée.
Ce système a évolué d'un système plus ancien et aux possibilités plus limitées,
et pour des raisons de compatibilité "net:" peut être utilisé à la place de
"tag:" et "set:" peut-être omis (à l'exception de
"tag:" et "set:" peut être omis (à l'exception de
.B dhcp-host,
où "net:" peut-être utilisé à la place de "set:"). Pour les mêmes raisons, '#'
peut-être utilisé à la place de '!' pour indiquer la négation.
où "net:" peut être utilisé à la place de "set:"). Pour les mêmes raisons, '#'
peut être utilisé à la place de '!' pour indiquer la négation.
.PP
Le serveur DHCP intégré dans Dnsmasq fonctionne également en temps que serveur
BOOTP, pour peu que l'adresse MAC et l'adresse IP des clients soient fournies,
@@ -2097,7 +2105,7 @@ scénarios de complexité croissante. Le pré-requis pour chacun de ces scénari
est l'existence d'une adresse IP globalement disponible, d'un enregistrement de
type A ou AAAA pointant vers cette adresse, ainsi que d'un serveur DNS externe
capable d'effectuer la délégation de la zone en question. Pour la première
partie de ces explications, nous allons appeller serveur.exemple.com
partie de ces explications, nous allons appeler serveur.exemple.com
l'enregistrement A (ou AAAA) de l'adresse globalement accessible, et
notre.zone.com la zone pour laquelle dnsmasq fait autorité.
@@ -2138,11 +2146,11 @@ notre.zone.com NS our.zone.com
.fi
L'enregistrement A pour notre.zone.com est dorénavant un enregistrement "colle"
qui résoud le problème de poule et d'oeuf consistant à trouver l'adresse IP
qui résout le problème de poule et d'oeuf consistant à trouver l'adresse IP
du serveur de nom pour notre.zone.com lorsque l'enregistrement se trouve dans
la zone en question. Il s'agit du seul rôle de cet enregistrement : comme dnsmasq
fait désormais autorité pour notre.zone.com, il doit également fournir cet
enregistrement. Si l'adresse externe est statique, cela peut-être réalisé par
enregistrement. Si l'adresse externe est statique, cela peut être réalisé par
le biais d'une entrée dans
.B /etc/hosts
ou via un
@@ -2194,7 +2202,7 @@ spécifiques, vous pouvez le faire via :
Dnsmasq joue le rôle de serveur faisant autorité pour les domaines in-addr.arpa
et ip6.arpa associés aux sous-réseaux définis dans la déclaration de zone
auth-zone, ce qui fait que les requêtes DNS inversées (de l'adresse vers
le nom) peuvent-simplement être configurées avec un enregistrement NS
le nom) peuvent simplement être configurées avec un enregistrement NS
adéquat. Par exemple, comme nous définissons plus haut les adresses
1.2.3.0/24 :
.nf
@@ -2243,7 +2251,7 @@ celui fourni par
.B --domain.
Si l'option
.B --dhcp-fqdn
est fournie, alors les noms pleinemenet qualifiés associés aux baux DHCP
est fournie, alors les noms pleinement qualifiés associés aux baux DHCP
sont utilisés, dès lors qu'ils correspondent au nom de domaine associé
à la zone.
@@ -2282,7 +2290,7 @@ Dnsmasq est capable de gérer le DNS et DHCP pour au moins un millier de clients
Pour cela, la durée des bail ne doit pas être très courte (moins d'une heure).
La valeur de
.B --dns-forward-max
peut-être augmentée : commencer par la rendre égale au nombre de clients et
peut être augmentée : commencer par la rendre égale au nombre de clients et
l'augmenter si le DNS semble lent. Noter que la performance du DNS dépends
également de la performance des serveurs amonts. La taille du cache DNS peut-
être augmentée : la limite en dur est de 10000 entrées et la valeur par défaut
@@ -2306,7 +2314,7 @@ Il est possible d'utiliser Dnsmasq pour bloquer la publicité sur la toile
en associant des serveurs de publicité bien connus à l'adresse 127.0.0.1 ou
0.0.0.0 par le biais du fichier
.B /etc/hosts
ou d'un fichier d'hôte additionnel. Cette liste peut-être très longue, Dnsmasq
ou d'un fichier d'hôte additionnel. Cette liste peut être très longue, Dnsmasq
ayant été testé avec succès avec un million de noms. Cette taille de fichier
nécessite un processeur à 1 Ghz et environ 60 Mo de RAM.

1018
po/de.po

File diff suppressed because it is too large Load Diff

1042
po/es.po

File diff suppressed because it is too large Load Diff

1050
po/fi.po

File diff suppressed because it is too large Load Diff

1014
po/fr.po

File diff suppressed because it is too large Load Diff

1079
po/id.po

File diff suppressed because it is too large Load Diff

1050
po/it.po

File diff suppressed because it is too large Load Diff

1072
po/no.po

File diff suppressed because it is too large Load Diff

1018
po/pl.po

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1072
po/ro.po

File diff suppressed because it is too large Load Diff

View File

@@ -125,7 +125,7 @@ address of its ethernet card. For the former to work, a machine needs to know it
requests a DHCP lease. For dhcpcd, the -h option specifies this. The
names may be anything as far as DHCP is concerned, but dnsmasq adds
some limitations. By default the names must no have a domain part, ie
they must just be a alphanumeric name, without any dots. This is a
they must just be alphanumeric names, without any dots. This is a
security feature to stop a machine on your network telling DHCP that
its name is "www.microsoft.com" and thereby grabbing traffic which
shouldn't go to it. A domain part is only allowed by dnsmasq in DHCP machine names

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2017 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2018 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-2017 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2018 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
@@ -103,7 +103,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
{
char *name = daemon->namebuff;
unsigned char *p, *ansp;
int qtype, qclass;
int qtype, qclass, rc;
int nameoffset, axfroffset = 0;
int q, anscount = 0, authcount = 0;
struct crec *crecp;
@@ -119,11 +119,6 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
struct cname *a, *candidate;
unsigned int wclen;
/* Clear buffer beyond request to avoid risk of
information disclosure. */
memset(((char *)header) + qlen, 0,
(limit - ((char *)header)) - qlen);
if (ntohs(header->qdcount) == 0 || OPCODE(header) != QUERY )
return 0;
@@ -288,11 +283,11 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
}
for (rec = daemon->mxnames; rec; rec = rec->next)
if (!rec->issrv && hostname_isequal(name, rec->name))
if (!rec->issrv && (rc = hostname_issubdomain(name, rec->name)))
{
nxdomain = 0;
if (qtype == T_MX)
if (rc == 2 && qtype == T_MX)
{
found = 1;
log_query(F_CONFIG | F_RRNAME, name, NULL, "<MX>");
@@ -303,11 +298,11 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
}
for (move = NULL, up = &daemon->mxnames, rec = daemon->mxnames; rec; rec = rec->next)
if (rec->issrv && hostname_isequal(name, rec->name))
if (rec->issrv && (rc = hostname_issubdomain(name, rec->name)))
{
nxdomain = 0;
if (qtype == T_SRV)
if (rc == 2 && qtype == T_SRV)
{
found = 1;
log_query(F_CONFIG | F_RRNAME, name, NULL, "<SRV>");
@@ -338,13 +333,13 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
}
for (txt = daemon->rr; txt; txt = txt->next)
if (hostname_isequal(name, txt->name))
if ((rc = hostname_issubdomain(name, txt->name)))
{
nxdomain = 0;
if (txt->class == qtype)
if (rc == 2 && txt->class == qtype)
{
found = 1;
log_query(F_CONFIG | F_RRNAME, name, NULL, "<RR>");
log_query(F_CONFIG | F_RRNAME, name, NULL, querystr(NULL, txt->class));
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
NULL, txt->class, C_IN, "t", txt->len, txt->txt))
anscount++;
@@ -352,10 +347,10 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
}
for (txt = daemon->txt; txt; txt = txt->next)
if (txt->class == C_IN && hostname_isequal(name, txt->name))
if (txt->class == C_IN && (rc = hostname_issubdomain(name, txt->name)))
{
nxdomain = 0;
if (qtype == T_TXT)
if (rc == 2 && qtype == T_TXT)
{
found = 1;
log_query(F_CONFIG | F_RRNAME, name, NULL, "<TXT>");
@@ -366,10 +361,10 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
}
for (na = daemon->naptr; na; na = na->next)
if (hostname_isequal(name, na->name))
if ((rc = hostname_issubdomain(name, na->name)))
{
nxdomain = 0;
if (qtype == T_NAPTR)
if (rc == 2 && qtype == T_NAPTR)
{
found = 1;
log_query(F_CONFIG | F_RRNAME, name, NULL, "<NAPTR>");
@@ -389,13 +384,13 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
#endif
for (intr = daemon->int_names; intr; intr = intr->next)
if (hostname_isequal(name, intr->name))
if ((rc = hostname_issubdomain(name, intr->name)))
{
struct addrlist *addrlist;
nxdomain = 0;
if (flag)
if (rc == 2 && flag)
for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
if (((addrlist->flags & ADDRLIST_IPV6) ? T_AAAA : T_A) == qtype &&
(local_query || filter_zone(zone, flag, &addrlist->addr)))
@@ -441,8 +436,9 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
if (sockaddr_isequal(peer_addr, &peers->addr))
break;
/* Refuse all AXFR unless --auth-sec-servers is set */
if ((!peers && daemon->auth_peers) || !daemon->secondary_forward_server)
/* Refuse all AXFR unless --auth-sec-servers or auth-peers is set */
if ((!daemon->secondary_forward_server && !daemon->auth_peers) ||
(daemon->auth_peers && !peers))
{
if (peer_addr->sa.sa_family == AF_INET)
inet_ntop(AF_INET, &peer_addr->in.sin_addr, daemon->addrbuff, ADDRSTRLEN);
@@ -571,6 +567,8 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
goto cname_restart;
}
else if (cache_find_non_terminal(name, now))
nxdomain = 0;
log_query(flag | F_NEG | (nxdomain ? F_NXDOMAIN : 0) | F_FORWARD | F_AUTH, name, NULL, NULL);
}
@@ -597,12 +595,12 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
char *p = name;
if (subnet->prefixlen >= 24)
p += sprintf(p, "%d.", a & 0xff);
p += sprintf(p, "%u.", a & 0xff);
a = a >> 8;
if (subnet->prefixlen >= 16 )
p += sprintf(p, "%d.", a & 0xff);
p += sprintf(p, "%u.", a & 0xff);
a = a >> 8;
p += sprintf(p, "%d.in-addr.arpa", a & 0xff);
p += sprintf(p, "%u.in-addr.arpa", a & 0xff);
}
#ifdef HAVE_IPV6

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2017 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2018 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,7 +25,7 @@ static void blockdata_expand(int n)
{
struct blockdata *new = whine_malloc(n * sizeof(struct blockdata));
if (n > 0 && new)
if (new)
{
int i;
@@ -49,7 +49,7 @@ void blockdata_init(void)
/* Note that daemon->cachesize is enforced to have non-zero size if OPT_DNSSEC_VALID is set */
if (option_bool(OPT_DNSSEC_VALID))
blockdata_expand((daemon->cachesize * 100) / sizeof(struct blockdata));
blockdata_expand(daemon->cachesize);
}
void blockdata_report(void)
@@ -100,6 +100,7 @@ struct blockdata *blockdata_alloc(char *data, size_t len)
return ret;
}
void blockdata_free(struct blockdata *blocks)
{
struct blockdata *tmp;

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2017 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2018 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
@@ -169,7 +169,7 @@ int iface_enumerate(int family, void *parm, int (*callback)())
struct in6_ifreq ifr6;
memset(&ifr6, 0, sizeof(ifr6));
strncpy(ifr6.ifr_name, addrs->ifa_name, sizeof(ifr6.ifr_name));
safe_strncpy(ifr6.ifr_name, addrs->ifa_name, sizeof(ifr6.ifr_name));
ifr6.ifr_addr = *((struct sockaddr_in6 *) addrs->ifa_addr);
if (fd != -1 && ioctl(fd, SIOCGIFAFLAG_IN6, &ifr6) != -1)

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2017 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2018 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
@@ -21,10 +21,12 @@ static struct crec *cache_head = NULL, *cache_tail = NULL, **hash_table = NULL;
static struct crec *dhcp_spare = NULL;
#endif
static struct crec *new_chain = NULL;
static int cache_inserted = 0, cache_live_freed = 0, insert_error;
static int insert_error;
static union bigname *big_free = NULL;
static int bignames_left, hash_size;
static void make_non_terminals(struct crec *source);
/* type->string mapping: this is also used by the name-hash function as a mixing table. */
static const struct {
unsigned int type;
@@ -45,6 +47,7 @@ static const struct {
{ 24, "SIG" },
{ 25, "KEY" },
{ 28, "AAAA" },
{ 29, "LOC" },
{ 33, "SRV" },
{ 35, "NAPTR" },
{ 36, "KX" },
@@ -57,13 +60,18 @@ static const struct {
{ 47, "NSEC" },
{ 48, "DNSKEY" },
{ 50, "NSEC3" },
{ 51, "NSEC3PARAM" },
{ 52, "TLSA" },
{ 53, "SMIMEA" },
{ 55, "HIP" },
{ 249, "TKEY" },
{ 250, "TSIG" },
{ 251, "IXFR" },
{ 252, "AXFR" },
{ 253, "MAILB" },
{ 254, "MAILA" },
{ 255, "ANY" }
{ 255, "ANY" },
{ 257, "CAA" }
};
static void cache_free(struct crec *crecp);
@@ -72,17 +80,20 @@ static void cache_link(struct crec *crecp);
static void rehash(int size);
static void cache_hash(struct crec *crecp);
static unsigned int next_uid(void)
void next_uid(struct crec *crecp)
{
static unsigned int uid = 0;
uid++;
if (crecp->uid == UID_NONE)
{
uid++;
/* uid == 0 used to indicate CNAME to interface name. */
if (uid == SRC_INTERFACE)
uid++;
return uid;
/* uid == 0 used to indicate CNAME to interface name. */
if (uid == UID_NONE)
uid++;
crecp->uid = uid;
}
}
void cache_init(void)
@@ -100,7 +111,7 @@ void cache_init(void)
{
cache_link(crecp);
crecp->flags = 0;
crecp->uid = next_uid();
crecp->uid = UID_NONE;
}
}
@@ -199,7 +210,7 @@ static void cache_free(struct crec *crecp)
{
crecp->flags &= ~F_FORWARD;
crecp->flags &= ~F_REVERSE;
crecp->uid = next_uid(); /* invalidate CNAMES pointing to this. */
crecp->uid = UID_NONE; /* invalidate CNAMES pointing to this. */
if (cache_tail)
cache_tail->next = crecp;
@@ -317,7 +328,8 @@ static int is_expired(time_t now, struct crec *crecp)
return 1;
}
static struct crec *cache_scan_free(char *name, struct all_addr *addr, time_t now, unsigned short flags)
static struct crec *cache_scan_free(char *name, struct all_addr *addr, time_t now, unsigned short flags,
struct crec **target_crec, unsigned int *target_uid)
{
/* Scan and remove old entries.
If (flags & F_FORWARD) then remove any forward entries for name and any expired
@@ -330,7 +342,10 @@ static struct crec *cache_scan_free(char *name, struct all_addr *addr, time_t no
to a cache entry if the name exists in the cache as a HOSTS or DHCP entry (these are never deleted)
We take advantage of the fact that hash chains have stuff in the order <reverse>,<other>,<immortal>
so that when we hit an entry which isn't reverse and is immortal, we're done. */
so that when we hit an entry which isn't reverse and is immortal, we're done.
If we free a crec which is a CNAME target, return the entry and uid in target_crec and target_uid.
This entry will get re-used with the same name, to preserve CNAMEs. */
struct crec *crecp, **up;
@@ -338,17 +353,6 @@ static struct crec *cache_scan_free(char *name, struct all_addr *addr, time_t no
{
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);
}
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 */
@@ -358,6 +362,16 @@ static struct crec *cache_scan_free(char *name, struct all_addr *addr, time_t no
if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))
return crecp;
*up = crecp->hash_next;
/* If this record is for the name we're inserting and is the target
of a CNAME record. Make the new record for the same name, in the same
crec, with the same uid to avoid breaking the existing CNAME. */
if (crecp->uid != UID_NONE)
{
if (target_crec)
*target_crec = crecp;
if (target_uid)
*target_uid = crecp->uid;
}
cache_unlink(crecp);
cache_free(crecp);
continue;
@@ -376,6 +390,18 @@ static struct crec *cache_scan_free(char *name, struct all_addr *addr, time_t no
}
#endif
}
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;
}
up = &crecp->hash_next;
}
}
@@ -442,11 +468,12 @@ void cache_start_insert(void)
struct crec *cache_insert(char *name, struct all_addr *addr,
time_t now, unsigned long ttl, unsigned short flags)
{
struct crec *new;
struct crec *new, *target_crec = NULL;
union bigname *big_name = NULL;
int freed_all = flags & F_REVERSE;
int free_avail = 0;
unsigned int target_uid;
/* Don't log DNSSEC records here, done elsewhere */
if (flags & (F_IPV4 | F_IPV6 | F_CNAME))
{
@@ -464,7 +491,7 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
/* First remove any expired entries and entries for the name/address we
are currently inserting. */
if ((new = cache_scan_free(name, addr, now, flags)))
if ((new = cache_scan_free(name, addr, now, flags, &target_crec, &target_uid)))
{
/* We're trying to insert a record over one from
/etc/hosts or DHCP, or other config. If the
@@ -488,81 +515,89 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
}
/* Now get a cache entry from the end of the LRU list */
while (1) {
if (!(new = cache_tail)) /* no entries left - cache is too small, bail */
{
insert_error = 1;
return NULL;
}
/* End of LRU list is still in use: if we didn't scan all the hash
chains for expired entries do that now. If we already tried that
then it's time to start spilling things. */
if (new->flags & (F_FORWARD | F_REVERSE))
{
/* If free_avail set, we believe that an entry has been freed.
Bugs have been known to make this not true, resulting in
a tight loop here. If that happens, abandon the
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;;
if (!target_crec)
while (1) {
if (!(new = cache_tail)) /* no entries left - cache is too small, bail */
{
insert_error = 1;
return NULL;
}
/* Free entry at end of LRU list, use it. */
if (!(new->flags & (F_FORWARD | F_REVERSE)))
break;
/* End of LRU list is still in use: if we didn't scan all the hash
chains for expired entries do that now. If we already tried that
then it's time to start spilling things. */
/* If free_avail set, we believe that an entry has been freed.
Bugs have been known to make this not true, resulting in
a tight loop here. If that happens, abandon the
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. */
if (new->flags & (F_DS | F_DNSKEY))
free_addr.addr.dnssec.class = new->uid;
/* For DNSSEC records, addr holds class. */
if (new->flags & (F_DS | F_DNSKEY))
free_addr.addr.dnssec.class = new->uid;
#endif
free_avail = 1; /* Must be free space now. */
cache_scan_free(cache_get_name(new), &free_addr, now, new->flags);
cache_live_freed++;
}
else
{
cache_scan_free(NULL, NULL, now, 0);
freed_all = 1;
}
continue;
}
/* Check if we need to and can allocate extra memory for a long name.
If that fails, give up now, always succeed for DNSSEC records. */
if (name && (strlen(name) > SMALLDNAME-1))
{
if (big_free)
{
big_name = big_free;
big_free = big_free->next;
}
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 if (bignames_left != 0)
bignames_left--;
}
free_avail = 1; /* Must be free space now. */
cache_scan_free(cache_get_name(new), &free_addr, now, new->flags, NULL, NULL);
daemon->metrics[METRIC_DNS_CACHE_LIVE_FREED]++;
}
else
{
cache_scan_free(NULL, NULL, now, 0, NULL, NULL);
freed_all = 1;
}
}
/* Check if we need to and can allocate extra memory for a long name.
If that fails, give up now, always succeed for DNSSEC records. */
if (name && (strlen(name) > SMALLDNAME-1))
{
if (big_free)
{
big_name = big_free;
big_free = big_free->next;
}
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 if (bignames_left != 0)
bignames_left--;
}
/* Got the rest: finally grab entry. */
cache_unlink(new);
break;
}
/* If we freed a cache entry for our name which was a CNAME target, use that.
and preserve the uid, so that existing CNAMES are not broken. */
if (target_crec)
{
new = target_crec;
new->uid = target_uid;
}
/* Got the rest: finally grab entry. */
cache_unlink(new);
new->flags = flags;
if (big_name)
@@ -609,13 +644,27 @@ void cache_end_insert(void)
{
cache_hash(new_chain);
cache_link(new_chain);
cache_inserted++;
daemon->metrics[METRIC_DNS_CACHE_INSERTED]++;
}
new_chain = tmp;
}
new_chain = NULL;
}
int cache_find_non_terminal(char *name, time_t now)
{
struct crec *crecp;
for (crecp = *hash_bucket(name); crecp; crecp = crecp->hash_next)
if (!is_outdated_cname_pointer(crecp) &&
!is_expired(now, crecp) &&
(crecp->flags & F_FORWARD) &&
hostname_isequal(name, cache_get_name(crecp)))
return 1;
return 0;
}
struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsigned int prot)
{
struct crec *ans;
@@ -776,15 +825,18 @@ static void add_hosts_cname(struct crec *target)
for (a = daemon->cnames; a; a = a->next)
if (a->alias[1] != '*' &&
hostname_isequal(cache_get_name(target), a->target) &&
(crec = whine_malloc(sizeof(struct crec))))
(crec = whine_malloc(SIZEOF_POINTER_CREC)))
{
crec->flags = F_FORWARD | F_IMMORTAL | F_NAMEP | F_CONFIG | F_CNAME;
crec->ttd = a->ttl;
crec->name.namep = a->alias;
crec->addr.cname.target.cache = target;
next_uid(target);
crec->addr.cname.uid = target->uid;
crec->uid = next_uid();
crec->uid = UID_NONE;
cache_hash(crec);
make_non_terminals(crec);
add_hosts_cname(crec); /* handle chains */
}
}
@@ -856,6 +908,7 @@ static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrl
cache->uid = index;
memcpy(&cache->addr.addr, addr, addrlen);
cache_hash(cache);
make_non_terminals(cache);
/* don't need to do alias stuff for second and subsequent addresses. */
if (!nameexists)
@@ -976,8 +1029,7 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
{
/* If set, add a version of the name with a default domain appended */
if (option_bool(OPT_EXPAND) && domain_suffix && !fqdn &&
(cache = whine_malloc(sizeof(struct crec) +
strlen(canon)+2+strlen(domain_suffix)-SMALLDNAME)))
(cache = whine_malloc(SIZEOF_BARE_CREC + strlen(canon) + 2 + strlen(domain_suffix))))
{
strcpy(cache->name.sname, canon);
strcat(cache->name.sname, ".");
@@ -987,7 +1039,7 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz);
name_count++;
}
if ((cache = whine_malloc(sizeof(struct crec) + strlen(canon)+1-SMALLDNAME)))
if ((cache = whine_malloc(SIZEOF_BARE_CREC + strlen(canon) + 1)))
{
strcpy(cache->name.sname, canon);
cache->flags = flags;
@@ -1026,7 +1078,8 @@ void cache_reload(void)
struct ds_config *ds;
#endif
cache_inserted = cache_live_freed = 0;
daemon->metrics[METRIC_DNS_CACHE_INSERTED] = 0;
daemon->metrics[METRIC_DNS_CACHE_LIVE_FREED] = 0;
for (i=0; i<hash_size; i++)
for (cache = hash_table[i], up = &hash_table[i]; cache; cache = tmp)
@@ -1059,21 +1112,22 @@ void cache_reload(void)
for (intr = daemon->int_names; intr; intr = intr->next)
if (a->alias[1] != '*' &&
hostname_isequal(a->target, intr->name) &&
((cache = whine_malloc(sizeof(struct crec)))))
((cache = whine_malloc(SIZEOF_POINTER_CREC))))
{
cache->flags = F_FORWARD | F_NAMEP | F_CNAME | F_IMMORTAL | F_CONFIG;
cache->ttd = a->ttl;
cache->name.namep = a->alias;
cache->addr.cname.target.int_name = intr;
cache->addr.cname.uid = SRC_INTERFACE;
cache->uid = next_uid();
cache->uid = UID_NONE;
cache_hash(cache);
make_non_terminals(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))) &&
if ((cache = whine_malloc(SIZEOF_POINTER_CREC)) &&
(cache->addr.ds.keydata = blockdata_alloc(ds->digest, ds->digestlen)))
{
cache->flags = F_FORWARD | F_IMMORTAL | F_DS | F_CONFIG | F_NAMEP;
@@ -1085,6 +1139,7 @@ void cache_reload(void)
cache->addr.ds.digest = ds->digest_type;
cache->uid = ds->class;
cache_hash(cache);
make_non_terminals(cache);
}
#endif
@@ -1099,7 +1154,7 @@ void cache_reload(void)
for (nl = hr->names; nl; nl = nl->next)
{
if (hr->addr.s_addr != 0 &&
(cache = whine_malloc(sizeof(struct crec))))
(cache = whine_malloc(SIZEOF_POINTER_CREC)))
{
cache->name.namep = nl->name;
cache->ttd = hr->ttl;
@@ -1108,7 +1163,7 @@ void cache_reload(void)
}
#ifdef HAVE_IPV6
if (!IN6_IS_ADDR_UNSPECIFIED(&hr->addr6) &&
(cache = whine_malloc(sizeof(struct crec))))
(cache = whine_malloc(SIZEOF_POINTER_CREC)))
{
cache->name.namep = nl->name;
cache->ttd = hr->ttl;
@@ -1185,7 +1240,7 @@ static void add_dhcp_cname(struct crec *target, time_t ttd)
if ((aliasc = dhcp_spare))
dhcp_spare = dhcp_spare->next;
else /* need new one */
aliasc = whine_malloc(sizeof(struct crec));
aliasc = whine_malloc(SIZEOF_POINTER_CREC);
if (aliasc)
{
@@ -1196,9 +1251,11 @@ static void add_dhcp_cname(struct crec *target, time_t ttd)
aliasc->ttd = ttd;
aliasc->name.namep = a->alias;
aliasc->addr.cname.target.cache = target;
next_uid(target);
aliasc->addr.cname.uid = target->uid;
aliasc->uid = next_uid();
aliasc->uid = UID_NONE;
cache_hash(aliasc);
make_non_terminals(aliasc);
add_dhcp_cname(aliasc, ttd);
}
}
@@ -1238,7 +1295,7 @@ void cache_add_dhcp_entry(char *host_name, int prot,
}
else if (!(crec->flags & F_DHCP))
{
cache_scan_free(host_name, NULL, 0, crec->flags & (flags | F_CNAME | F_FORWARD));
cache_scan_free(host_name, NULL, 0, crec->flags & (flags | F_CNAME | F_FORWARD), NULL, NULL);
/* scan_free deletes all addresses associated with name */
break;
}
@@ -1265,7 +1322,7 @@ void cache_add_dhcp_entry(char *host_name, int prot,
if (crec->flags & F_NEG)
{
flags |= F_REVERSE;
cache_scan_free(NULL, (struct all_addr *)host_address, 0, flags);
cache_scan_free(NULL, (struct all_addr *)host_address, 0, flags, NULL, NULL);
}
}
else
@@ -1274,7 +1331,7 @@ void cache_add_dhcp_entry(char *host_name, int prot,
if ((crec = dhcp_spare))
dhcp_spare = dhcp_spare->next;
else /* need new one */
crec = whine_malloc(sizeof(struct crec));
crec = whine_malloc(SIZEOF_POINTER_CREC);
if (crec) /* malloc may fail */
{
@@ -1285,14 +1342,108 @@ void cache_add_dhcp_entry(char *host_name, int prot,
crec->ttd = ttd;
crec->addr.addr = *host_address;
crec->name.namep = host_name;
crec->uid = next_uid();
crec->uid = UID_NONE;
cache_hash(crec);
make_non_terminals(crec);
add_dhcp_cname(crec, ttd);
}
}
#endif
/* Called when we put a local or DHCP name into the cache.
Creates empty cache entries for subnames (ie,
for three.two.one, for two.one and one), without
F_IPV4 or F_IPV6 or F_CNAME set. These convert
NXDOMAIN answers to NoData ones. */
static void make_non_terminals(struct crec *source)
{
char *name = cache_get_name(source);
struct crec *crecp, *tmp, **up;
int type = F_HOSTS | F_CONFIG;
#ifdef HAVE_DHCP
if (source->flags & F_DHCP)
type = F_DHCP;
#endif
/* First delete any empty entries for our new real name. Note that
we only delete empty entries deriving from DHCP for a new DHCP-derived
entry and vice-versa for HOSTS and CONFIG. This ensures that
non-terminals from DHCP go when we reload DHCP and
for HOSTS/CONFIG when we re-read. */
for (up = hash_bucket(name), crecp = *up; crecp; crecp = tmp)
{
tmp = crecp->hash_next;
if (!is_outdated_cname_pointer(crecp) &&
(crecp->flags & F_FORWARD) &&
(crecp->flags & type) &&
!(crecp->flags & (F_IPV4 | F_IPV6 | F_CNAME | F_DNSKEY | F_DS)) &&
hostname_isequal(name, cache_get_name(crecp)))
{
*up = crecp->hash_next;
#ifdef HAVE_DHCP
if (type & F_DHCP)
{
crecp->next = dhcp_spare;
dhcp_spare = crecp;
}
else
#endif
free(crecp);
break;
}
else
up = &crecp->hash_next;
}
while ((name = strchr(name, '.')))
{
name++;
/* Look for one existing, don't need another */
for (crecp = *hash_bucket(name); crecp; crecp = crecp->hash_next)
if (!is_outdated_cname_pointer(crecp) &&
(crecp->flags & F_FORWARD) &&
(crecp->flags & type) &&
hostname_isequal(name, cache_get_name(crecp)))
break;
if (crecp)
{
/* If the new name expires later, transfer that time to
empty non-terminal entry. */
if (!(crecp->flags & F_IMMORTAL))
{
if (source->flags & F_IMMORTAL)
crecp->flags |= F_IMMORTAL;
else if (difftime(crecp->ttd, source->ttd) < 0)
crecp->ttd = source->ttd;
}
continue;
}
#ifdef HAVE_DHCP
if ((source->flags & F_DHCP) && dhcp_spare)
{
crecp = dhcp_spare;
dhcp_spare = dhcp_spare->next;
}
else
#endif
crecp = whine_malloc(SIZEOF_POINTER_CREC);
if (crecp)
{
crecp->flags = (source->flags | F_NAMEP) & ~(F_IPV4 | F_IPV6 | F_CNAME | F_DNSKEY | F_DS | F_REVERSE);
crecp->ttd = source->ttd;
crecp->name.namep = name;
cache_hash(crecp);
}
}
}
#ifndef NO_ID
int cache_make_stat(struct txt_record *t)
{
@@ -1314,24 +1465,24 @@ int cache_make_stat(struct txt_record *t)
break;
case TXT_STAT_INSERTS:
sprintf(buff+1, "%d", cache_inserted);
sprintf(buff+1, "%d", daemon->metrics[METRIC_DNS_CACHE_INSERTED]);
break;
case TXT_STAT_EVICTIONS:
sprintf(buff+1, "%d", cache_live_freed);
sprintf(buff+1, "%d", daemon->metrics[METRIC_DNS_CACHE_LIVE_FREED]);
break;
case TXT_STAT_MISSES:
sprintf(buff+1, "%u", daemon->queries_forwarded);
sprintf(buff+1, "%u", daemon->metrics[METRIC_DNS_QUERIES_FORWARDED]);
break;
case TXT_STAT_HITS:
sprintf(buff+1, "%u", daemon->local_answer);
sprintf(buff+1, "%u", daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]);
break;
#ifdef HAVE_AUTH
case TXT_STAT_AUTH:
sprintf(buff+1, "%u", daemon->auth_answer);
sprintf(buff+1, "%u", daemon->metrics[METRIC_DNS_AUTH_ANSWERED]);
break;
#endif
@@ -1408,15 +1559,14 @@ static char *sanitise(char *name)
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."),
daemon->cachesize, cache_live_freed, cache_inserted);
daemon->cachesize, daemon->metrics[METRIC_DNS_CACHE_LIVE_FREED], daemon->metrics[METRIC_DNS_CACHE_INSERTED]);
my_syslog(LOG_INFO, _("queries forwarded %u, queries answered locally %u"),
daemon->queries_forwarded, daemon->local_answer);
daemon->metrics[METRIC_DNS_QUERIES_FORWARDED], daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]);
#ifdef HAVE_AUTH
my_syslog(LOG_INFO, _("queries for authoritative zones %u"), daemon->auth_answer);
my_syslog(LOG_INFO, _("queries for authoritative zones %u"), daemon->metrics[METRIC_DNS_AUTH_ANSWERED]);
#endif
#ifdef HAVE_DNSSEC
blockdata_report();
@@ -1454,6 +1604,7 @@ void dump_cache(time_t now)
for (i=0; i<hash_size; i++)
for (cache = hash_table[i]; cache; cache = cache->hash_next)
{
char *t = " ";
char *a = daemon->addrbuff, *p = daemon->namebuff, *n = cache_get_name(cache);
*a = 0;
if (strlen(n) == 0 && !(cache->flags & F_REVERSE))
@@ -1511,7 +1662,7 @@ void dump_cache(time_t now)
/* ctime includes trailing \n - eat it */
*(p-1) = 0;
#endif
my_syslog(LOG_INFO, daemon->namebuff);
my_syslog(LOG_INFO, "%s", daemon->namebuff);
}
}
}
@@ -1554,9 +1705,13 @@ char *querystr(char *desc, unsigned short type)
break;
}
len += 3; /* braces, terminator */
len += strlen(desc);
if (desc)
{
len += 2; /* braces */
len += strlen(desc);
}
len++; /* terminator */
if (!buff || bufflen < len)
{
if (buff)
@@ -1570,12 +1725,22 @@ char *querystr(char *desc, unsigned short type)
if (buff)
{
if (types)
sprintf(buff, "%s[%s]", desc, types);
if (desc)
{
if (types)
sprintf(buff, "%s[%s]", desc, types);
else
sprintf(buff, "%s[type=%d]", desc, type);
}
else
sprintf(buff, "%s[type=%d]", desc, type);
{
if (types)
sprintf(buff, "<%s>", types);
else
sprintf(buff, "type=%d", type);
}
}
return buff ? buff : "";
}
@@ -1593,6 +1758,19 @@ void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
{
if (flags & F_KEYTAG)
sprintf(daemon->addrbuff, arg, addr->addr.log.keytag, addr->addr.log.algo, addr->addr.log.digest);
else if (flags & F_RCODE)
{
unsigned int rcode = addr->addr.rcode.rcode;
if (rcode == SERVFAIL)
dest = "SERVFAIL";
else if (rcode == REFUSED)
dest = "REFUSED";
else if (rcode == NOTIMP)
dest = "not implemented";
else
sprintf(daemon->addrbuff, "%u", rcode);
}
else
{
#ifdef HAVE_IPV6

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2017 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2018 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
@@ -26,6 +26,7 @@
#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 */
#define UDP_TEST_TIME 60 /* How often to reset our idea of max packet size. */
#define SERVERS_LOGGED 30 /* Only log this many servers when logging state */
#define LOCALS_LOGGED 8 /* Only log this many local addresses when logging state */
#define RANDOM_SOCKS 64 /* max simultaneous random ports */
@@ -93,6 +94,9 @@ HAVE_DBUS
support some methods to allow (re)configuration of the upstream DNS
servers via DBus.
HAVE_UBUS
define this if you want to link against libubus
HAVE_IDN
define this if you want international domain name 2003 support.
@@ -116,6 +120,9 @@ HAVE_AUTH
HAVE_DNSSEC
include DNSSEC validator.
HAVE_DUMPFILE
include code to dump packets to a libpcap-format file for debugging.
HAVE_LOOP
include functionality to probe for and remove DNS forwarding loops.
@@ -131,14 +138,12 @@ NO_DHCP6
NO_SCRIPT
NO_LARGEFILE
NO_AUTH
NO_DUMPFILE
NO_INOTIFY
these are available to explicitly disable compile time options which would
otherwise be enabled automatically (HAVE_IPV6, >2Gb file sizes) or
which are enabled by default in the distributed source tree. Building dnsmasq
with something like "make COPTS=-DNO_SCRIPT" will do the trick.
NO_NETTLE_ECC
Don't include the ECDSA cypher in DNSSEC validation. Needed for older Nettle versions.
NO_GMP
Don't use and link against libgmp, Useful if nettle is built with --enable-mini-gmp.
@@ -166,6 +171,7 @@ RESOLVFILE
#define HAVE_AUTH
#define HAVE_IPSET
#define HAVE_LOOP
#define HAVE_DUMPFILE
/* Build options which require external libraries.
@@ -365,6 +371,10 @@ HAVE_SOCKADDR_SA_LEN
#undef HAVE_LOOP
#endif
#ifdef NO_DUMPFILE
#undef HAVE_DUMPFILE
#endif
#if defined (HAVE_LINUX_NETWORK) && !defined(NO_INOTIFY)
#define HAVE_INOTIFY
#endif
@@ -453,8 +463,11 @@ static char *compile_opts =
#ifndef HAVE_INOTIFY
"no-"
#endif
"inotify";
"inotify "
#ifndef HAVE_DUMPFILE
"no-"
#endif
"dumpfile";
#endif

View File

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

460
src/crypto.c Normal file
View File

@@ -0,0 +1,460 @@
/* dnsmasq is Copyright (c) 2000-2018 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
#include <nettle/rsa.h>
#include <nettle/dsa.h>
#include <nettle/ecdsa.h>
#include <nettle/ecc-curve.h>
#include <nettle/eddsa.h>
#include <nettle/nettle-meta.h>
#include <nettle/bignum.h>
/* Implement a "hash-function" to the nettle API, which simply returns
the input data, concatenated into a single, statically maintained, buffer.
Used for the EdDSA sigs, which operate on the whole message, rather
than a digest. */
struct null_hash_digest
{
uint8_t *buff;
size_t len;
};
struct null_hash_ctx
{
size_t len;
};
static size_t null_hash_buff_sz = 0;
static uint8_t *null_hash_buff = NULL;
#define BUFF_INCR 128
static void null_hash_init(void *ctx)
{
((struct null_hash_ctx *)ctx)->len = 0;
}
static void null_hash_update(void *ctxv, size_t length, const uint8_t *src)
{
struct null_hash_ctx *ctx = ctxv;
size_t new_len = ctx->len + length;
if (new_len > null_hash_buff_sz)
{
uint8_t *new;
if (!(new = whine_malloc(new_len + BUFF_INCR)))
return;
if (null_hash_buff)
{
if (ctx->len != 0)
memcpy(new, null_hash_buff, ctx->len);
free(null_hash_buff);
}
null_hash_buff_sz = new_len + BUFF_INCR;
null_hash_buff = new;
}
memcpy(null_hash_buff + ctx->len, src, length);
ctx->len += length;
}
static void null_hash_digest(void *ctx, size_t length, uint8_t *dst)
{
(void)length;
((struct null_hash_digest *)dst)->buff = null_hash_buff;
((struct null_hash_digest *)dst)->len = ((struct null_hash_ctx *)ctx)->len;
}
static struct nettle_hash null_hash = {
"null_hash",
sizeof(struct null_hash_ctx),
sizeof(struct null_hash_digest),
0,
(nettle_hash_init_func *) null_hash_init,
(nettle_hash_update_func *) null_hash_update,
(nettle_hash_digest_func *) null_hash_digest
};
/* Find pointer to correct hash function in nettle library */
const struct nettle_hash *hash_find(char *name)
{
if (!name)
return NULL;
/* We provide a "null" hash which returns the input data as digest. */
if (strcmp(null_hash.name, name) == 0)
return &null_hash;
/* libnettle >= 3.4 provides nettle_lookup_hash() which avoids nasty ABI
incompatibilities if sizeof(nettle_hashes) changes between library
versions. It also #defines nettle_hashes, so use that to tell
if we have the new facilities. */
#ifdef nettle_hashes
return nettle_lookup_hash(name);
#else
{
int i;
for (i = 0; nettle_hashes[i]; i++)
if (strcmp(nettle_hashes[i]->name, name) == 0)
return nettle_hashes[i];
}
return NULL;
#endif
}
/* expand ctx and digest memory allocations if necessary and init hash function */
int hash_init(const struct nettle_hash *hash, void **ctxp, unsigned char **digestp)
{
static void *ctx = NULL;
static unsigned char *digest = NULL;
static unsigned int ctx_sz = 0;
static unsigned int digest_sz = 0;
void *new;
if (ctx_sz < hash->context_size)
{
if (!(new = whine_malloc(hash->context_size)))
return 0;
if (ctx)
free(ctx);
ctx = new;
ctx_sz = hash->context_size;
}
if (digest_sz < hash->digest_size)
{
if (!(new = whine_malloc(hash->digest_size)))
return 0;
if (digest)
free(digest);
digest = new;
digest_sz = hash->digest_size;
}
*ctxp = ctx;
*digestp = digest;
hash->init(ctx);
return 1;
}
static int dnsmasq_rsa_verify(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len,
unsigned char *digest, size_t digest_len, int algo)
{
unsigned char *p;
size_t exp_len;
static struct rsa_public_key *key = NULL;
static mpz_t sig_mpz;
(void)digest_len;
if (key == NULL)
{
if (!(key = whine_malloc(sizeof(struct rsa_public_key))))
return 0;
nettle_rsa_public_key_init(key);
mpz_init(sig_mpz);
}
if ((key_len < 3) || !(p = blockdata_retrieve(key_data, key_len, NULL)))
return 0;
key_len--;
if ((exp_len = *p++) == 0)
{
GETSHORT(exp_len, p);
key_len -= 2;
}
if (exp_len >= key_len)
return 0;
key->size = key_len - exp_len;
mpz_import(key->e, exp_len, 1, 1, 0, 0, p);
mpz_import(key->n, key->size, 1, 1, 0, 0, p + exp_len);
mpz_import(sig_mpz, sig_len, 1, 1, 0, 0, sig);
switch (algo)
{
case 1:
return nettle_rsa_md5_verify_digest(key, digest, sig_mpz);
case 5: case 7:
return nettle_rsa_sha1_verify_digest(key, digest, sig_mpz);
case 8:
return nettle_rsa_sha256_verify_digest(key, digest, sig_mpz);
case 10:
return nettle_rsa_sha512_verify_digest(key, digest, sig_mpz);
}
return 0;
}
static int dnsmasq_dsa_verify(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len,
unsigned char *digest, size_t digest_len, int algo)
{
unsigned char *p;
unsigned int t;
static mpz_t y;
static struct dsa_params *params = NULL;
static struct dsa_signature *sig_struct;
(void)digest_len;
if (params == NULL)
{
if (!(sig_struct = whine_malloc(sizeof(struct dsa_signature))) ||
!(params = whine_malloc(sizeof(struct dsa_params))))
return 0;
mpz_init(y);
nettle_dsa_params_init(params);
nettle_dsa_signature_init(sig_struct);
}
if ((sig_len < 41) || !(p = blockdata_retrieve(key_data, key_len, NULL)))
return 0;
t = *p++;
if (key_len < (213 + (t * 24)))
return 0;
mpz_import(params->q, 20, 1, 1, 0, 0, p); p += 20;
mpz_import(params->p, 64 + (t*8), 1, 1, 0, 0, p); p += 64 + (t*8);
mpz_import(params->g, 64 + (t*8), 1, 1, 0, 0, p); p += 64 + (t*8);
mpz_import(y, 64 + (t*8), 1, 1, 0, 0, p); p += 64 + (t*8);
mpz_import(sig_struct->r, 20, 1, 1, 0, 0, sig+1);
mpz_import(sig_struct->s, 20, 1, 1, 0, 0, sig+21);
(void)algo;
return nettle_dsa_verify(params, y, digest_len, digest, sig_struct);
}
static int dnsmasq_ecdsa_verify(struct blockdata *key_data, unsigned int key_len,
unsigned char *sig, size_t sig_len,
unsigned char *digest, size_t digest_len, int algo)
{
unsigned char *p;
unsigned int t;
struct ecc_point *key;
static struct ecc_point *key_256 = NULL, *key_384 = NULL;
static mpz_t x, y;
static struct dsa_signature *sig_struct;
if (!sig_struct)
{
if (!(sig_struct = whine_malloc(sizeof(struct dsa_signature))))
return 0;
nettle_dsa_signature_init(sig_struct);
mpz_init(x);
mpz_init(y);
}
switch (algo)
{
case 13:
if (!key_256)
{
if (!(key_256 = whine_malloc(sizeof(struct ecc_point))))
return 0;
nettle_ecc_point_init(key_256, &nettle_secp_256r1);
}
key = key_256;
t = 32;
break;
case 14:
if (!key_384)
{
if (!(key_384 = whine_malloc(sizeof(struct ecc_point))))
return 0;
nettle_ecc_point_init(key_384, &nettle_secp_384r1);
}
key = key_384;
t = 48;
break;
default:
return 0;
}
if (sig_len != 2*t || key_len != 2*t ||
!(p = blockdata_retrieve(key_data, key_len, NULL)))
return 0;
mpz_import(x, t , 1, 1, 0, 0, p);
mpz_import(y, t , 1, 1, 0, 0, p + t);
if (!ecc_point_set(key, x, y))
return 0;
mpz_import(sig_struct->r, t, 1, 1, 0, 0, sig);
mpz_import(sig_struct->s, t, 1, 1, 0, 0, sig + t);
return nettle_ecdsa_verify(key, digest_len, digest, sig_struct);
}
static int dnsmasq_eddsa_verify(struct blockdata *key_data, unsigned int key_len,
unsigned char *sig, size_t sig_len,
unsigned char *digest, size_t digest_len, int algo)
{
unsigned char *p;
if (key_len != ED25519_KEY_SIZE ||
sig_len != ED25519_SIGNATURE_SIZE ||
digest_len != sizeof(struct null_hash_digest) ||
!(p = blockdata_retrieve(key_data, key_len, NULL)))
return 0;
/* The "digest" returned by the null_hash function is simply a struct null_hash_digest
which has a pointer to the actual data and a length, because the buffer
may need to be extended during "hashing". */
switch (algo)
{
case 15:
return ed25519_sha512_verify(p,
((struct null_hash_digest *)digest)->len,
((struct null_hash_digest *)digest)->buff,
sig);
case 16:
/* Ed448 when available */
return 0;
}
return 0;
}
static int (*verify_func(int algo))(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len,
unsigned char *digest, size_t digest_len, int algo)
{
/* Enure at runtime that we have support for this digest */
if (!hash_find(algo_digest_name(algo)))
return NULL;
/* This switch defines which sig algorithms we support, can't introspect Nettle for that. */
switch (algo)
{
case 1: case 5: case 7: case 8: case 10:
return dnsmasq_rsa_verify;
case 3: case 6:
return dnsmasq_dsa_verify;
case 13: case 14:
return dnsmasq_ecdsa_verify;
case 15: case 16:
return dnsmasq_eddsa_verify;
}
return NULL;
}
int verify(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len,
unsigned char *digest, size_t digest_len, int algo)
{
int (*func)(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len,
unsigned char *digest, size_t digest_len, int algo);
func = verify_func(algo);
if (!func)
return 0;
return (*func)(key_data, key_len, sig, sig_len, digest, digest_len, algo);
}
/* Note the ds_digest_name(), algo_digest_name() and nsec3_digest_name()
define which algo numbers we support. If algo_digest_name() returns
non-NULL for an algorithm number, we assume that algorithm is
supported by verify(). */
/* http://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml */
char *ds_digest_name(int digest)
{
switch (digest)
{
case 1: return "sha1";
case 2: return "sha256";
case 3: return "gosthash94";
case 4: return "sha384";
default: return NULL;
}
}
/* http://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml */
char *algo_digest_name(int algo)
{
switch (algo)
{
case 1: return NULL; /* RSA/MD5 - Must Not Implement. RFC 6944 para 2.3. */
case 2: return NULL; /* Diffie-Hellman */
case 3: return "sha1"; /* DSA/SHA1 */
case 5: return "sha1"; /* RSA/SHA1 */
case 6: return "sha1"; /* DSA-NSEC3-SHA1 */
case 7: return "sha1"; /* RSASHA1-NSEC3-SHA1 */
case 8: return "sha256"; /* RSA/SHA-256 */
case 10: return "sha512"; /* RSA/SHA-512 */
case 12: return NULL; /* ECC-GOST */
case 13: return "sha256"; /* ECDSAP256SHA256 */
case 14: return "sha384"; /* ECDSAP384SHA384 */
case 15: return "null_hash"; /* ED25519 */
case 16: return NULL; /* ED448 */
default: return NULL;
}
}
/* http://www.iana.org/assignments/dnssec-nsec3-parameters/dnssec-nsec3-parameters.xhtml */
char *nsec3_digest_name(int digest)
{
switch (digest)
{
case 1: return "sha1";
default: return NULL;
}
}
#endif

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2017 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2018 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
@@ -85,6 +85,9 @@ const char* introspection_xml_template =
" <arg name=\"success\" type=\"b\" direction=\"out\"/>\n"
" </method>\n"
#endif
" <method name=\"GetMetrics\">\n"
" <arg name=\"metrics\" direction=\"out\" type=\"a{su}\"/>\n"
" </method>\n"
" </interface>\n"
"</node>\n";
@@ -613,6 +616,30 @@ static DBusMessage *dbus_del_lease(DBusMessage* message)
}
#endif
static DBusMessage *dbus_get_metrics(DBusMessage* message)
{
DBusMessage *reply = dbus_message_new_method_return(message);
DBusMessageIter array, dict, iter;
int i;
dbus_message_iter_init_append(reply, &iter);
dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{su}", &array);
for (i = 0; i < __METRIC_MAX; i++) {
const char *key = get_metric_name(i);
dbus_uint32_t value = daemon->metrics[i];
dbus_message_iter_open_container(&array, DBUS_TYPE_DICT_ENTRY, NULL, &dict);
dbus_message_iter_append_basic(&dict, DBUS_TYPE_STRING, &key);
dbus_message_iter_append_basic(&dict, DBUS_TYPE_UINT32, &value);
dbus_message_iter_close_container(&array, &dict);
}
dbus_message_iter_close_container(&iter, &array);
return reply;
}
DBusHandlerResult message_handler(DBusConnection *connection,
DBusMessage *message,
void *user_data)
@@ -680,6 +707,10 @@ DBusHandlerResult message_handler(DBusConnection *connection,
reply = dbus_del_lease(message);
}
#endif
else if (strcmp(method, "GetMetrics") == 0)
{
reply = dbus_get_metrics(message);
}
else if (strcmp(method, "ClearCache") == 0)
clear_cache = 1;
else

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2017 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2018 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
@@ -485,11 +485,11 @@ char *whichdevice(void)
void bindtodevice(char *device, int fd)
{
struct ifreq ifr;
strcpy(ifr.ifr_name, device);
size_t len = strlen(device)+1;
if (len > IFNAMSIZ)
len = IFNAMSIZ;
/* only allowed by root. */
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr)) == -1 &&
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, device, len) == -1 &&
errno != EPERM)
die(_("failed to set SO_BINDTODEVICE on DHCP socket: %s"), NULL, EC_BADNET);
}
@@ -559,6 +559,7 @@ static const struct opttab_t {
{ "nntp-server", 71, OT_ADDR_LIST },
{ "irc-server", 74, OT_ADDR_LIST },
{ "user-class", 77, 0 },
{ "rapid-commit", 80, 0 },
{ "FQDN", 81, OT_INTERNAL },
{ "agent-id", 82, OT_INTERNAL },
{ "client-arch", 93, 2 | OT_DEC },

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2017 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2018 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
@@ -54,6 +54,7 @@
#define OPTION_SNAME 66
#define OPTION_FILENAME 67
#define OPTION_USER_CLASS 77
#define OPTION_RAPID_COMMIT 80
#define OPTION_CLIENT_FQDN 81
#define OPTION_AGENT_ID 82
#define OPTION_ARCH 93

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2017 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2018 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
@@ -232,7 +232,7 @@ void dhcp_packet(time_t now, int pxe_fd)
#ifdef HAVE_LINUX_NETWORK
/* ARP fiddling uses original interface even if we pretend to use a different one. */
strncpy(arp_req.arp_dev, ifr.ifr_name, 16);
safe_strncpy(arp_req.arp_dev, ifr.ifr_name, sizeof(arp_req.arp_dev));
#endif
/* If the interface on which the DHCP request was received is an
@@ -255,7 +255,7 @@ void dhcp_packet(time_t now, int pxe_fd)
}
else
{
strncpy(ifr.ifr_name, bridge->iface, IF_NAMESIZE);
safe_strncpy(ifr.ifr_name, bridge->iface, sizeof(ifr.ifr_name));
break;
}
}
@@ -279,7 +279,7 @@ void dhcp_packet(time_t now, int pxe_fd)
is_relay_reply = 1;
iov.iov_len = sz;
#ifdef HAVE_LINUX_NETWORK
strncpy(arp_req.arp_dev, ifr.ifr_name, 16);
safe_strncpy(arp_req.arp_dev, ifr.ifr_name, sizeof(arp_req.arp_dev));
#endif
}
else
@@ -678,7 +678,7 @@ struct ping_result *do_icmp_ping(time_t now, struct in_addr addr, unsigned int h
if ((count >= max) || option_bool(OPT_NO_PING) || loopback)
{
/* overloaded, or configured not to check, loopback interface, return "not in use" */
dummy.hash = 0;
dummy.hash = hash;
return &dummy;
}
else if (icmp_ping(addr))
@@ -988,8 +988,7 @@ char *host_from_dns(struct in_addr addr)
if (!legal_hostname(hostname))
return NULL;
strncpy(daemon->dhcp_buff, hostname, 256);
daemon->dhcp_buff[255] = 0;
safe_strncpy(daemon->dhcp_buff, hostname, 256);
strip_hostname(daemon->dhcp_buff);
return daemon->dhcp_buff;

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2017 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2018 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-2017 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2018 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
@@ -640,13 +640,20 @@ static int construct_worker(struct in6_addr *local, int prefix,
return 0;
for (template = daemon->dhcp6; template; template = template->next)
if (!(template->flags & CONTEXT_TEMPLATE))
if (!(template->flags & (CONTEXT_TEMPLATE | CONTEXT_CONSTRUCTED)))
{
/* non-template entries, just fill in interface and local addresses */
if (prefix <= template->prefix &&
is_same_net6(local, &template->start6, template->prefix) &&
is_same_net6(local, &template->end6, template->prefix))
{
/* First time found, do fast RA. */
if (template->if_index != if_index || !IN6_ARE_ADDR_EQUAL(&template->local6, local))
{
ra_start_unsolicited(param->now, template);
param->newone = 1;
}
template->if_index = if_index;
template->local6 = *local;
}
@@ -661,24 +668,33 @@ static int construct_worker(struct in6_addr *local, int prefix,
setaddr6part(&end6, addr6part(&template->end6));
for (context = daemon->dhcp6; context; context = context->next)
if ((context->flags & CONTEXT_CONSTRUCTED) &&
if (!(context->flags & CONTEXT_TEMPLATE) &&
IN6_ARE_ADDR_EQUAL(&start6, &context->start6) &&
IN6_ARE_ADDR_EQUAL(&end6, &context->end6))
{
int flags = context->flags;
context->flags &= ~(CONTEXT_GC | CONTEXT_OLD);
if (flags & CONTEXT_OLD)
/* If there's an absolute address context covering this address
then don't construct one as well. */
if (!(context->flags & CONTEXT_CONSTRUCTED))
break;
if (context->if_index == if_index)
{
/* address went, now it's back */
log_context(AF_INET6, context);
/* fast RAs for a while */
ra_start_unsolicited(param->now, context);
param->newone = 1;
/* Add address to name again */
if (context->flags & CONTEXT_RA_NAME)
param->newname = 1;
int cflags = context->flags;
context->flags &= ~(CONTEXT_GC | CONTEXT_OLD);
if (cflags & CONTEXT_OLD)
{
/* address went, now it's back, and on the same interface */
log_context(AF_INET6, context);
/* fast RAs for a while */
ra_start_unsolicited(param->now, context);
param->newone = 1;
/* Add address to name again */
if (context->flags & CONTEXT_RA_NAME)
param->newname = 1;
}
break;
}
break;
}
if (!context && (context = whine_malloc(sizeof (struct dhcp_context))))

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2017 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2018 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,6 +16,7 @@
#define NAMESERVER_PORT 53
#define TFTP_PORT 69
#define MIN_PORT 1024 /* first non-reserved port */
#define MAX_PORT 65535u
#define IN6ADDRSZ 16
@@ -75,6 +76,7 @@
#define T_AXFR 252
#define T_MAILB 253
#define T_ANY 255
#define T_CAA 257
#define EDNS0_OPTION_MAC 65001 /* dyndns.org temporary assignment */
#define EDNS0_OPTION_CLIENT_SUBNET 8 /* IANA */

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2017 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2018 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
@@ -48,6 +48,7 @@ int main (int argc, char **argv)
long i, max_fd = sysconf(_SC_OPEN_MAX);
char *baduser = NULL;
int log_err;
int chown_warn = 0;
#if defined(HAVE_LINUX_NETWORK)
cap_user_header_t hdr = NULL;
cap_user_data_t data = NULL;
@@ -77,7 +78,8 @@ int main (int argc, char **argv)
sigaction(SIGTERM, &sigact, NULL);
sigaction(SIGALRM, &sigact, NULL);
sigaction(SIGCHLD, &sigact, NULL);
sigaction(SIGINT, &sigact, NULL);
/* ignore SIGPIPE */
sigact.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &sigact, NULL);
@@ -118,6 +120,9 @@ int main (int argc, char **argv)
daemon->namebuff = safe_malloc(MAXDNAME * 2);
daemon->keyname = safe_malloc(MAXDNAME * 2);
daemon->workspacename = safe_malloc(MAXDNAME * 2);
/* one char flag per possible RR in answer section (may get extended). */
daemon->rr_status_sz = 64;
daemon->rr_status = safe_malloc(daemon->rr_status_sz);
}
#endif
@@ -211,7 +216,7 @@ int main (int argc, char **argv)
#endif
#ifndef HAVE_AUTH
if (daemon->authserver)
if (daemon->auth_zones)
die(_("authoritative DNS not available: set HAVE_AUTH in src/config.h"), NULL, EC_BADCONF);
#endif
@@ -220,21 +225,30 @@ int main (int argc, char **argv)
die(_("loop detection not available: set HAVE_LOOP in src/config.h"), NULL, EC_BADCONF);
#endif
if (daemon->max_port != MAX_PORT && daemon->min_port == 0)
daemon->min_port = 1024u;
#ifndef HAVE_UBUS
if (option_bool(OPT_UBUS))
die(_("Ubus not available: set HAVE_UBUS in src/config.h"), NULL, EC_BADCONF);
#endif
if (daemon->max_port < daemon->min_port)
die(_("max_port cannot be smaller than min_port"), NULL, EC_BADCONF);
now = dnsmasq_time();
/* Create a serial at startup if not configured. */
if (daemon->authinterface && daemon->soa_sn == 0)
if (daemon->auth_zones)
{
if (!daemon->authserver)
die(_("--auth-server required when an auth zone is defined."), NULL, EC_BADCONF);
/* Create a serial at startup if not configured. */
#ifdef HAVE_BROKEN_RTC
die(_("zone serial must be configured in --auth-soa"), NULL, EC_BADCONF);
if (daemon_>soa_sn == 0)
die(_("zone serial must be configured in --auth-soa"), NULL, EC_BADCONF);
#else
daemon->soa_sn = now;
if (daemon->soa_sn == 0)
daemon->soa_sn = now;
#endif
}
#ifdef HAVE_DHCP6
if (daemon->dhcp6)
@@ -358,12 +372,22 @@ int main (int argc, char **argv)
}
#ifdef HAVE_INOTIFY
if (daemon->port != 0 || daemon->dhcp || daemon->doing_dhcp6)
if ((daemon->port != 0 || daemon->dhcp || daemon->doing_dhcp6)
&& (!option_bool(OPT_NO_RESOLV) || daemon->dynamic_dirs))
inotify_dnsmasq_init();
else
daemon->inotifyfd = -1;
#endif
if (daemon->dump_file)
#ifdef HAVE_DUMPFILE
dump_init();
else
daemon->dumpfd = -1;
#else
die(_("Packet dumps not available: set HAVE_DUMP in src/config.h"), NULL, EC_BADCONF);
#endif
if (option_bool(OPT_DBUS))
#ifdef HAVE_DBUS
{
@@ -386,10 +410,12 @@ int main (int argc, char **argv)
daemon->scriptuser &&
(daemon->lease_change_command || daemon->luascript))
{
if ((ent_pw = getpwnam(daemon->scriptuser)))
struct passwd *scr_pw;
if ((scr_pw = getpwnam(daemon->scriptuser)))
{
script_uid = ent_pw->pw_uid;
script_gid = ent_pw->pw_gid;
script_uid = scr_pw->pw_uid;
script_gid = scr_pw->pw_gid;
}
else
baduser = daemon->scriptuser;
@@ -537,9 +563,18 @@ int main (int argc, char **argv)
}
else
{
/* We're still running as root here. Change the ownership of the PID file
to the user we will be running as. Note that this is not to allow
us to delete the file, since that depends on the permissions
of the directory containing the file. That directory will
need to by owned by the dnsmasq user, and the ownership of the
file has to match, to keep systemd >273 happy. */
if (getuid() == 0 && ent_pw && ent_pw->pw_uid != 0 && fchown(fd, ent_pw->pw_uid, ent_pw->pw_gid) == -1)
chown_warn = errno;
if (!read_write(fd, (unsigned char *)daemon->namebuff, strlen(daemon->namebuff), 0))
err = 1;
else
else
{
while (retry_send(close(fd)));
if (errno != 0)
@@ -717,7 +752,11 @@ int main (int argc, char **argv)
else
{
if (daemon->cachesize != 0)
my_syslog(LOG_INFO, _("started, version %s cachesize %d"), VERSION, daemon->cachesize);
{
my_syslog(LOG_INFO, _("started, version %s cachesize %d"), VERSION, daemon->cachesize);
if (daemon->cachesize > 10000)
my_syslog(LOG_WARNING, _("cache size greater than 10000 may cause performance issues, and is unlikely to be useful."));
}
else
my_syslog(LOG_INFO, _("started, version %s cache disabled"), VERSION);
@@ -726,6 +765,9 @@ int main (int argc, char **argv)
}
my_syslog(LOG_INFO, _("compile time options: %s"), compile_opts);
if (chown_warn != 0)
my_syslog(LOG_WARNING, "chown of PID file %s failed: %s", daemon->runfile, strerror(chown_warn));
#ifdef HAVE_DBUS
if (option_bool(OPT_DBUS))
@@ -741,7 +783,8 @@ int main (int argc, char **argv)
if (option_bool(OPT_DNSSEC_VALID))
{
int rc;
struct ds_config *ds;
/* Delay creating the timestamp file until here, after we've changed user, so that
it has the correct owner to allow updating the mtime later.
This means we have to report fatal errors via the pipe. */
@@ -751,14 +794,21 @@ int main (int argc, char **argv)
_exit(0);
}
my_syslog(LOG_INFO, _("DNSSEC validation enabled"));
if (option_bool(OPT_DNSSEC_IGN_NS))
my_syslog(LOG_INFO, _("DNSSEC validation enabled but all unsigned answers are trusted"));
else
my_syslog(LOG_INFO, _("DNSSEC validation enabled"));
daemon->dnssec_no_time_check = option_bool(OPT_DNSSEC_TIME);
if (option_bool(OPT_DNSSEC_TIME) && !daemon->back_to_the_future)
my_syslog(LOG_INFO, _("DNSSEC signature timestamps not checked until first cache reload"));
my_syslog(LOG_INFO, _("DNSSEC signature timestamps not checked until receipt of SIGINT"));
if (rc == 1)
my_syslog(LOG_INFO, _("DNSSEC signature timestamps not checked until system time valid"));
for (ds = daemon->ds; ds; ds = ds->next)
my_syslog(LOG_INFO, _("configured with trust anchor for %s keytag %u"),
ds->name[0] == 0 ? "<root>" : ds->name, ds->keytag);
}
#endif
@@ -909,8 +959,13 @@ int main (int argc, char **argv)
#ifdef HAVE_DBUS
set_dbus_listeners();
#endif
#endif
#ifdef HAVE_UBUS
if (option_bool(OPT_UBUS))
set_ubus_listeners();
#endif
#ifdef HAVE_DHCP
if (daemon->dhcp || daemon->relay4)
{
@@ -1040,7 +1095,12 @@ int main (int argc, char **argv)
}
check_dbus_listeners();
#endif
#ifdef HAVE_UBUS
if (option_bool(OPT_UBUS))
check_ubus_listeners();
#endif
check_dns_listeners(now);
#ifdef HAVE_TFTP
@@ -1079,7 +1139,7 @@ static void sig_handler(int sig)
{
/* ignore anything other than TERM during startup
and in helper proc. (helper ignore TERM too) */
if (sig == SIGTERM)
if (sig == SIGTERM || sig == SIGINT)
exit(EC_MISC);
}
else if (pid != getpid())
@@ -1105,6 +1165,15 @@ static void sig_handler(int sig)
event = EVENT_DUMP;
else if (sig == SIGUSR2)
event = EVENT_REOPEN;
else if (sig == SIGINT)
{
/* Handle SIGINT normally in debug mode, so
ctrl-c continues to operate. */
if (option_bool(OPT_DEBUG))
exit(EC_MISC);
else
event = EVENT_TIME;
}
else
return;
@@ -1187,31 +1256,40 @@ static void fatal_event(struct event_desc *ev, char *msg)
case EVENT_FORK_ERR:
die(_("cannot fork into background: %s"), NULL, EC_MISC);
/* fall through */
case EVENT_PIPE_ERR:
die(_("failed to create helper: %s"), NULL, EC_MISC);
/* fall through */
case EVENT_CAP_ERR:
die(_("setting capabilities failed: %s"), NULL, EC_MISC);
/* fall through */
case EVENT_USER_ERR:
die(_("failed to change user-id to %s: %s"), msg, EC_MISC);
/* fall through */
case EVENT_GROUP_ERR:
die(_("failed to change group-id to %s: %s"), msg, EC_MISC);
/* fall through */
case EVENT_PIDFILE:
die(_("failed to open pidfile %s: %s"), msg, EC_FILE);
/* fall through */
case EVENT_LOG_ERR:
die(_("cannot open log %s: %s"), msg, EC_FILE);
/* fall through */
case EVENT_LUA_ERR:
die(_("failed to load Lua script: %s"), msg, EC_MISC);
/* fall through */
case EVENT_TFTP_ERR:
die(_("TFTP directory %s inaccessible: %s"), msg, EC_FILE);
/* fall through */
case EVENT_TIME_ERR:
die(_("cannot create timestamp file %s: %s" ), msg, EC_BADCONF);
}
@@ -1232,14 +1310,7 @@ static void async_event(int pipe, time_t now)
{
case EVENT_RELOAD:
daemon->soa_sn++; /* Bump zone serial, as it may have changed. */
#ifdef HAVE_DNSSEC
if (daemon->dnssec_no_time_check && option_bool(OPT_DNSSEC_VALID) && option_bool(OPT_DNSSEC_TIME))
{
my_syslog(LOG_INFO, _("now checking DNSSEC signature timestamps"));
daemon->dnssec_no_time_check = 0;
}
#endif
/* fall through */
case EVENT_INIT:
@@ -1348,6 +1419,17 @@ static void async_event(int pipe, time_t now)
poll_resolv(0, 1, now);
break;
case EVENT_TIME:
#ifdef HAVE_DNSSEC
if (daemon->dnssec_no_time_check && option_bool(OPT_DNSSEC_VALID) && option_bool(OPT_DNSSEC_TIME))
{
my_syslog(LOG_INFO, _("now checking DNSSEC signature timestamps"));
daemon->dnssec_no_time_check = 0;
clear_cache_and_reload(now);
}
#endif
break;
case EVENT_TERM:
/* Knock all our children on the head. */
for (i = 0; i < MAX_PROCS; i++)
@@ -1382,6 +1464,11 @@ static void async_event(int pipe, time_t now)
if (daemon->runfile)
unlink(daemon->runfile);
#ifdef HAVE_DUMPFILE
if (daemon->dumpfd != -1)
close(daemon->dumpfd);
#endif
my_syslog(LOG_INFO, _("exiting on receipt of SIGTERM"));
flush_log();
@@ -1472,9 +1559,6 @@ void clear_cache_and_reload(time_t now)
if (option_bool(OPT_ETHERS))
dhcp_read_ethers();
reread_dhcp();
#ifdef HAVE_INOTIFY
set_dynamic_inotify(AH_DHCP_HST | AH_DHCP_OPT, 0, NULL, 0);
#endif
dhcp_update_configs(daemon->dhcp_conf);
lease_update_from_configs();
lease_update_file(now);

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2017 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2018 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,13 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define COPYRIGHT "Copyright (c) 2000-2017 Simon Kelley"
#define COPYRIGHT "Copyright (c) 2000-2018 Simon Kelley"
/* We do defines that influence behavior of stdio.h, so complain
if included too early. */
#ifdef _STDIO_H
# error "Header file stdio.h included too early!"
#endif
#ifndef NO_LARGEFILE
/* Ensure we can use files >2GB (log files may grow this big) */
@@ -36,6 +42,12 @@
# define __EXTENSIONS__
#endif
#if (defined(__GNUC__) && __GNUC__ >= 3) || defined(__clang__)
#define ATTRIBUTE_NORETURN __attribute__ ((noreturn))
#else
#define ATTRIBUTE_NORETURN
#endif
/* get these before config.h for IPv6 stuff... */
#include <sys/types.h>
#include <sys/socket.h>
@@ -51,6 +63,7 @@
#include "config.h"
#include "ip6addr.h"
#include "metrics.h"
typedef unsigned char u8;
typedef unsigned short u16;
@@ -113,6 +126,9 @@ typedef unsigned long long u64;
#include <net/if_arp.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#ifdef HAVE_IPV6
#include <netinet/ip6.h>
#endif
#include <netinet/ip_icmp.h>
#include <sys/uio.h>
#include <syslog.h>
@@ -136,6 +152,10 @@ extern int capget(cap_user_header_t header, cap_user_data_t data);
#include <priv.h>
#endif
#ifdef HAVE_DNSSEC
# include <nettle/nettle-meta.h>
#endif
/* daemon is function in the C library.... */
#define daemon dnsmasq_daemon
@@ -169,6 +189,7 @@ struct event_desc {
#define EVENT_NEWROUTE 23
#define EVENT_TIME_ERR 24
#define EVENT_SCRIPT_LOG 25
#define EVENT_TIME 26
/* Exit codes. */
#define EC_GOOD 0
@@ -230,7 +251,7 @@ struct event_desc {
#define OPT_DNSSEC_VALID 45
#define OPT_DNSSEC_TIME 46
#define OPT_DNSSEC_DEBUG 47
#define OPT_DNSSEC_NO_SIGN 48
#define OPT_DNSSEC_IGN_NS 48
#define OPT_LOCAL_SERVICE 49
#define OPT_LOOP_DETECT 50
#define OPT_EXTRALOG 51
@@ -239,7 +260,9 @@ struct event_desc {
#define OPT_MAC_B64 54
#define OPT_MAC_HEX 55
#define OPT_TFTP_APREF_MAC 56
#define OPT_LAST 57
#define OPT_RAPID_COMMIT 57
#define OPT_UBUS 58
#define OPT_LAST 59
/* 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. */
@@ -256,7 +279,11 @@ struct all_addr {
/* for log_query */
struct {
unsigned short keytag, algo, digest;
} log;
} log;
/* for log_query */
struct {
unsigned int rcode;
} rcode;
/* for cache_insert of DNSKEY, DS */
struct {
unsigned short class, type;
@@ -415,6 +442,9 @@ struct crec {
} name;
};
#define SIZEOF_BARE_CREC (sizeof(struct crec) - SMALLDNAME)
#define SIZEOF_POINTER_CREC (sizeof(struct crec) + sizeof(char *) - SMALLDNAME)
#define F_IMMORTAL (1u<<0)
#define F_NAMEP (1u<<1)
#define F_REVERSE (1u<<2)
@@ -446,9 +476,15 @@ struct crec {
#define F_NO_RR (1u<<25)
#define F_IPSET (1u<<26)
#define F_NOEXTRA (1u<<27)
#define F_SERVFAIL (1u<<28)
#define F_RCODE (1u<<29)
#define UID_NONE 0
/* Values of uid in crecs with F_CONFIG bit set. */
#define SRC_INTERFACE 0
/* cname to uid SRC_INTERFACE are to interface names,
so use UID_NONE for that to eliminate clashes with
any other uid */
#define SRC_INTERFACE UID_NONE
#define SRC_CONFIG 1
#define SRC_HOSTS 2
#define SRC_AH 3
@@ -493,7 +529,7 @@ struct serverfd {
int fd;
union mysockaddr source_addr;
char interface[IF_NAMESIZE+1];
unsigned int ifindex, used;
unsigned int ifindex, used, preallocated;
struct serverfd *next;
};
@@ -508,6 +544,7 @@ struct server {
struct serverfd *sfd;
char *domain; /* set if this server only handles a domain. */
int flags, tcpfd, edns_pktsz;
time_t pktsz_reduced;
unsigned int queries, failed_queries;
#ifdef HAVE_LOOP
u32 uid;
@@ -579,6 +616,16 @@ struct hostsfile {
unsigned int index; /* matches to cache entries for logging */
};
/* packet-dump flags */
#define DUMP_QUERY 0x0001
#define DUMP_REPLY 0x0002
#define DUMP_UP_QUERY 0x0004
#define DUMP_UP_REPLY 0x0008
#define DUMP_SEC_QUERY 0x0010
#define DUMP_SEC_REPLY 0x0020
#define DUMP_BOGUS 0x0040
#define DUMP_SEC_BOGUS 0x0080
/* DNSSEC status values. */
#define STAT_SECURE 1
@@ -784,6 +831,13 @@ struct dhcp_boot {
struct dhcp_boot *next;
};
struct dhcp_match_name {
char *name;
int wildcard;
struct dhcp_netid *netid;
struct dhcp_match_name *next;
};
struct pxe_service {
unsigned short CSA, type;
char *menu, *basename, *sname;
@@ -826,7 +880,7 @@ struct cond_domain {
#ifdef HAVE_IPV6
struct in6_addr start6, end6;
#endif
int is6;
int is6, indexed;
struct cond_domain *next;
};
@@ -976,6 +1030,7 @@ extern struct daemon {
struct ra_interface *ra_interfaces;
struct dhcp_config *dhcp_conf;
struct dhcp_opt *dhcp_opts, *dhcp_match, *dhcp_opts6, *dhcp_match6;
struct dhcp_match_name *dhcp_name_match;
struct dhcp_vendor *dhcp_vendors;
struct dhcp_mac *dhcp_macs;
struct dhcp_boot *boot_config;
@@ -1001,14 +1056,15 @@ extern struct daemon {
unsigned int duid_enterprise, duid_config_len;
unsigned char *duid_config;
char *dbus_name;
char *dump_file;
int dump_mask;
unsigned long soa_sn, soa_refresh, soa_retry, soa_expiry;
u32 metrics[__METRIC_MAX];
#ifdef OPTION6_PREFIX_CLASS
struct prefix_class *prefix_classes;
#endif
#ifdef HAVE_DNSSEC
struct ds_config *ds;
int dnssec_no_time_check;
int back_to_the_future;
char *timestamp_file;
#endif
@@ -1019,8 +1075,11 @@ extern struct daemon {
#ifdef HAVE_DNSSEC
char *keyname; /* MAXDNAME size buffer */
char *workspacename; /* ditto */
char *rr_status; /* flags for individual RRs */
int rr_status_sz;
int dnssec_no_time_check;
int back_to_the_future;
#endif
unsigned int local_answer, queries_forwarded, auth_answer;
struct frec *frec_list;
struct serverfd *sfds;
struct irec *interfaces;
@@ -1073,13 +1132,19 @@ extern struct daemon {
char *addrbuff;
char *addrbuff2; /* only allocated when OPT_EXTRALOG */
#ifdef HAVE_DUMPFILE
/* file for packet dumps. */
int dumpfd;
#endif
} *daemon;
/* cache.c */
void cache_init(void);
void next_uid(struct crec *crecp);
void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg);
char *record_source(unsigned int index);
char *querystr(char *desc, unsigned short type);
int cache_find_non_terminal(char *name, time_t now);
struct crec *cache_find_by_addr(struct crec *crecp,
struct all_addr *addr, time_t now,
unsigned int prot);
@@ -1130,18 +1195,18 @@ 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,
struct all_addr *addrp, unsigned int flags,
unsigned long local_ttl);
int extract_addresses(struct dns_header *header, size_t qlen, char *namebuff,
time_t now, char **ipsets, int is_sign, int checkrebind,
int no_cache, int secure, int *doctored);
unsigned long ttl);
int extract_addresses(struct dns_header *header, size_t qlen, char *name,
time_t now, char **ipsets, int is_sign, int check_rebind,
int no_cache_dnssec, int secure, 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, int ad_reqd, int do_bit, int have_pseudoheader);
int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
struct bogus_addr *addr, time_t now);
struct bogus_addr *baddr, time_t now);
int check_for_ignored_address(struct dns_header *header, size_t qlen, struct bogus_addr *baddr);
int check_for_local_domain(char *name, time_t now);
unsigned int questions_crc(struct dns_header *header, size_t plen, char *buff);
unsigned int questions_crc(struct dns_header *header, size_t plen, char *name);
size_t resize_packet(struct dns_header *header, size_t plen,
unsigned char *pheader, size_t hlen);
int add_resource_record(struct dns_header *header, char *limit, int *truncp,
@@ -1162,30 +1227,41 @@ int in_zone(struct auth_zone *zone, char *name, char **cut);
#endif
/* dnssec.c */
size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char *name, int class, int type, union mysockaddr *addr, int edns_pktsz);
int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t n, char *name, char *keyname, int class);
size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char *name, int class, int type, int edns_pktsz);
int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, 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 check_unsigned, int *neganswer, int *nons);
int dnskey_keytag(int alg, int flags, unsigned char *rdata, int rdlen);
int dnskey_keytag(int alg, int flags, unsigned char *key, int keylen);
size_t filter_rrsigs(struct dns_header *header, size_t plen);
unsigned char* hash_questions(struct dns_header *header, size_t plen, char *name);
int setup_timestamp(void);
/* crypto.c */
const struct nettle_hash *hash_find(char *name);
int hash_init(const struct nettle_hash *hash, void **ctxp, unsigned char **digestp);
int verify(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len,
unsigned char *digest, size_t digest_len, int algo);
char *ds_digest_name(int digest);
char *algo_digest_name(int algo);
char *nsec3_digest_name(int digest);
/* util.c */
void rand_init(void);
unsigned short rand16(void);
u32 rand32(void);
u64 rand64(void);
int legal_hostname(char *c);
char *canonicalise(char *s, int *nomem);
unsigned char *do_rfc1035_name(unsigned char *p, char *sval);
int legal_hostname(char *name);
char *canonicalise(char *in, int *nomem);
unsigned char *do_rfc1035_name(unsigned char *p, char *sval, char *limit);
void *safe_malloc(size_t size);
void safe_strncpy(char *dest, const char *src, size_t size);
void safe_pipe(int *fd, int read_noblock);
void *whine_malloc(size_t size);
int sa_len(union mysockaddr *addr);
int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2);
int hostname_isequal(const char *a, const char *b);
int hostname_issubdomain(char *a, char *b);
time_t dnsmasq_time(void);
int netmask_length(struct in_addr mask);
int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask);
@@ -1209,10 +1285,12 @@ int wildcard_match(const char* wildcard, const char* match);
int wildcard_matchn(const char* wildcard, const char* match, int num);
/* log.c */
void die(char *message, char *arg1, int exit_code);
void die(char *message, char *arg1, int exit_code) ATTRIBUTE_NORETURN;
int log_start(struct passwd *ent_pw, int errfd);
int log_reopen(char *log_file);
void my_syslog(int priority, const char *format, ...);
void set_log_writer(void);
void check_log_writer(int force);
void flush_log(void);
@@ -1240,13 +1318,13 @@ 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);
void resend_query();
void resend_query(void);
struct randfd *allocate_rfd(int family);
void free_rfd(struct randfd *rfd);
/* network.c */
int indextoname(int fd, int index, char *name);
int local_bind(int fd, union mysockaddr *addr, char *intname, int is_tcp);
int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifindex, int is_tcp);
int random_sock(int family);
void pre_allocate_sfds(void);
int reload_servers(char *fname);
@@ -1260,12 +1338,12 @@ void add_update_server(int flags,
void check_servers(void);
int enumerate_interfaces(int reset);
void create_wildcard_listeners(void);
void create_bound_listeners(int die);
void create_bound_listeners(int dienow);
void warn_bound_listeners(void);
void warn_wild_labels(void);
void warn_int_names(void);
int is_dad_listeners(void);
int iface_check(int family, struct all_addr *addr, char *name, int *auth_dns);
int iface_check(int family, struct all_addr *addr, char *name, int *auth);
int loopback_exception(int fd, int family, struct all_addr *addr, char *name);
int label_exception(int index, int family, struct all_addr *addr);
int fix_fd(int fd);
@@ -1286,7 +1364,7 @@ void newaddress(time_t now);
void dhcp_init(void);
void dhcp_packet(time_t now, int pxe_fd);
struct dhcp_context *address_available(struct dhcp_context *context,
struct in_addr addr,
struct in_addr taddr,
struct dhcp_netid *netids);
struct dhcp_context *narrow_context(struct dhcp_context *context,
struct in_addr taddr,
@@ -1345,7 +1423,7 @@ void lease_add_extradata(struct dhcp_lease *lease, unsigned char *data,
#ifdef HAVE_DHCP
size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
size_t sz, time_t now, int unicast_dest, int loopback,
int *is_inform, int pxe_fd, struct in_addr fallback, time_t recvtime);
int *is_inform, int pxe, struct in_addr fallback, time_t recvtime);
unsigned char *extended_hwaddr(int hwtype, int hwlen, unsigned char *hwaddr,
int clid_len, unsigned char *clid, int *len_out);
#endif
@@ -1389,6 +1467,13 @@ void emit_dbus_signal(int action, struct dhcp_lease *lease, char *hostname);
# endif
#endif
/* ubus.c */
#ifdef HAVE_UBUS
void set_ubus_listeners(void);
void check_ubus_listeners(void);
void ubus_event_bcast(const char *type, const char *mac, const char *ip, const char *name, const char *interface);
#endif
/* ipset.c */
#ifdef HAVE_IPSET
void ipset_init(void);
@@ -1460,10 +1545,10 @@ unsigned short relay_reply6( struct sockaddr_in6 *peer, ssize_t sz, char *arriva
#ifdef HAVE_DHCP
void dhcp_common_init(void);
ssize_t recv_dhcp_packet(int fd, struct msghdr *msg);
struct dhcp_netid *run_tag_if(struct dhcp_netid *input);
struct dhcp_netid *run_tag_if(struct dhcp_netid *tags);
struct dhcp_netid *option_filter(struct dhcp_netid *tags, struct dhcp_netid *context_tags,
struct dhcp_opt *opts);
int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int negonly);
int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int tagnotneeded);
char *strip_hostname(char *hostname);
void log_tags(struct dhcp_netid *netid, u32 xid);
int match_bytes(struct dhcp_opt *o, unsigned char *p, int len);
@@ -1521,13 +1606,13 @@ void slaac_ping_reply(struct in6_addr *sender, unsigned char *packet, char *inte
/* loop.c */
#ifdef HAVE_LOOP
void loop_send_probes();
void loop_send_probes(void);
int detect_loop(char *query, int type);
#endif
/* inotify.c */
#ifdef HAVE_INOTIFY
void inotify_dnsmasq_init();
void inotify_dnsmasq_init(void);
int inotify_check(time_t now);
void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revhashsz);
#endif
@@ -1556,3 +1641,9 @@ int check_source(struct dns_header *header, size_t plen, unsigned char *pseudohe
/* arp.c */
int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy, time_t now);
int do_arp_script_run(void);
/* dump.c */
#ifdef HAVE_DUMPFILE
void dump_init(void);
void dump_packet(int mask, void *packet, size_t len, union mysockaddr *src, union mysockaddr *dst);
#endif

View File

@@ -1,5 +1,5 @@
/* dnssec.c is Copyright (c) 2012 Giovanni Bajo <rasky@develer.com>
and Copyright (c) 2012-2017 Simon Kelley
and Copyright (c) 2012-2018 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,332 +19,11 @@
#ifdef HAVE_DNSSEC
#include <nettle/rsa.h>
#include <nettle/dsa.h>
#ifndef NO_NETTLE_ECC
# include <nettle/ecdsa.h>
# include <nettle/ecc-curve.h>
#endif
#include <nettle/nettle-meta.h>
#include <nettle/bignum.h>
/* Nettle-3.0 moved to a new API for DSA. We use a name that's defined in the new API
to detect Nettle-3, and invoke the backwards compatibility mode. */
#ifdef dsa_params_init
#include <nettle/dsa-compat.h>
#endif
#define SERIAL_UNDEF -100
#define SERIAL_EQ 0
#define SERIAL_LT -1
#define SERIAL_GT 1
/* http://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml */
static char *ds_digest_name(int digest)
{
switch (digest)
{
case 1: return "sha1";
case 2: return "sha256";
case 3: return "gosthash94";
case 4: return "sha384";
default: return NULL;
}
}
/* http://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml */
static char *algo_digest_name(int algo)
{
switch (algo)
{
case 1: return "md5";
case 3: return "sha1";
case 5: return "sha1";
case 6: return "sha1";
case 7: return "sha1";
case 8: return "sha256";
case 10: return "sha512";
case 12: return "gosthash94";
case 13: return "sha256";
case 14: return "sha384";
default: return NULL;
}
}
/* http://www.iana.org/assignments/dnssec-nsec3-parameters/dnssec-nsec3-parameters.xhtml */
static char *nsec3_digest_name(int digest)
{
switch (digest)
{
case 1: return "sha1";
default: return NULL;
}
}
/* Find pointer to correct hash function in nettle library */
static const struct nettle_hash *hash_find(char *name)
{
int i;
if (!name)
return NULL;
for (i = 0; nettle_hashes[i]; i++)
{
if (strcmp(nettle_hashes[i]->name, name) == 0)
return nettle_hashes[i];
}
return NULL;
}
/* expand ctx and digest memory allocations if necessary and init hash function */
static int hash_init(const struct nettle_hash *hash, void **ctxp, unsigned char **digestp)
{
static void *ctx = NULL;
static unsigned char *digest = NULL;
static unsigned int ctx_sz = 0;
static unsigned int digest_sz = 0;
void *new;
if (ctx_sz < hash->context_size)
{
if (!(new = whine_malloc(hash->context_size)))
return 0;
if (ctx)
free(ctx);
ctx = new;
ctx_sz = hash->context_size;
}
if (digest_sz < hash->digest_size)
{
if (!(new = whine_malloc(hash->digest_size)))
return 0;
if (digest)
free(digest);
digest = new;
digest_sz = hash->digest_size;
}
*ctxp = ctx;
*digestp = digest;
hash->init(ctx);
return 1;
}
static int dnsmasq_rsa_verify(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len,
unsigned char *digest, size_t digest_len, int algo)
{
unsigned char *p;
size_t exp_len;
static struct rsa_public_key *key = NULL;
static mpz_t sig_mpz;
(void)digest_len;
if (key == NULL)
{
if (!(key = whine_malloc(sizeof(struct rsa_public_key))))
return 0;
nettle_rsa_public_key_init(key);
mpz_init(sig_mpz);
}
if ((key_len < 3) || !(p = blockdata_retrieve(key_data, key_len, NULL)))
return 0;
key_len--;
if ((exp_len = *p++) == 0)
{
GETSHORT(exp_len, p);
key_len -= 2;
}
if (exp_len >= key_len)
return 0;
key->size = key_len - exp_len;
mpz_import(key->e, exp_len, 1, 1, 0, 0, p);
mpz_import(key->n, key->size, 1, 1, 0, 0, p + exp_len);
mpz_import(sig_mpz, sig_len, 1, 1, 0, 0, sig);
switch (algo)
{
case 1:
return nettle_rsa_md5_verify_digest(key, digest, sig_mpz);
case 5: case 7:
return nettle_rsa_sha1_verify_digest(key, digest, sig_mpz);
case 8:
return nettle_rsa_sha256_verify_digest(key, digest, sig_mpz);
case 10:
return nettle_rsa_sha512_verify_digest(key, digest, sig_mpz);
}
return 0;
}
static int dnsmasq_dsa_verify(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len,
unsigned char *digest, size_t digest_len, int algo)
{
unsigned char *p;
unsigned int t;
static struct dsa_public_key *key = NULL;
static struct dsa_signature *sig_struct;
(void)digest_len;
if (key == NULL)
{
if (!(sig_struct = whine_malloc(sizeof(struct dsa_signature))) ||
!(key = whine_malloc(sizeof(struct dsa_public_key))))
return 0;
nettle_dsa_public_key_init(key);
nettle_dsa_signature_init(sig_struct);
}
if ((sig_len < 41) || !(p = blockdata_retrieve(key_data, key_len, NULL)))
return 0;
t = *p++;
if (key_len < (213 + (t * 24)))
return 0;
mpz_import(key->q, 20, 1, 1, 0, 0, p); p += 20;
mpz_import(key->p, 64 + (t*8), 1, 1, 0, 0, p); p += 64 + (t*8);
mpz_import(key->g, 64 + (t*8), 1, 1, 0, 0, p); p += 64 + (t*8);
mpz_import(key->y, 64 + (t*8), 1, 1, 0, 0, p); p += 64 + (t*8);
mpz_import(sig_struct->r, 20, 1, 1, 0, 0, sig+1);
mpz_import(sig_struct->s, 20, 1, 1, 0, 0, sig+21);
(void)algo;
return nettle_dsa_sha1_verify_digest(key, digest, sig_struct);
}
#ifndef NO_NETTLE_ECC
static int dnsmasq_ecdsa_verify(struct blockdata *key_data, unsigned int key_len,
unsigned char *sig, size_t sig_len,
unsigned char *digest, size_t digest_len, int algo)
{
unsigned char *p;
unsigned int t;
struct ecc_point *key;
static struct ecc_point *key_256 = NULL, *key_384 = NULL;
static mpz_t x, y;
static struct dsa_signature *sig_struct;
if (!sig_struct)
{
if (!(sig_struct = whine_malloc(sizeof(struct dsa_signature))))
return 0;
nettle_dsa_signature_init(sig_struct);
mpz_init(x);
mpz_init(y);
}
switch (algo)
{
case 13:
if (!key_256)
{
if (!(key_256 = whine_malloc(sizeof(struct ecc_point))))
return 0;
nettle_ecc_point_init(key_256, &nettle_secp_256r1);
}
key = key_256;
t = 32;
break;
case 14:
if (!key_384)
{
if (!(key_384 = whine_malloc(sizeof(struct ecc_point))))
return 0;
nettle_ecc_point_init(key_384, &nettle_secp_384r1);
}
key = key_384;
t = 48;
break;
default:
return 0;
}
if (sig_len != 2*t || key_len != 2*t ||
!(p = blockdata_retrieve(key_data, key_len, NULL)))
return 0;
mpz_import(x, t , 1, 1, 0, 0, p);
mpz_import(y, t , 1, 1, 0, 0, p + t);
if (!ecc_point_set(key, x, y))
return 0;
mpz_import(sig_struct->r, t, 1, 1, 0, 0, sig);
mpz_import(sig_struct->s, t, 1, 1, 0, 0, sig + t);
return nettle_ecdsa_verify(key, digest_len, digest, sig_struct);
}
#endif
static int (*verify_func(int algo))(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len,
unsigned char *digest, size_t digest_len, int algo)
{
/* Enure at runtime that we have support for this digest */
if (!hash_find(algo_digest_name(algo)))
return NULL;
/* This switch defines which sig algorithms we support, can't introspect Nettle for that. */
switch (algo)
{
case 1: case 5: case 7: case 8: case 10:
return dnsmasq_rsa_verify;
case 3: case 6:
return dnsmasq_dsa_verify;
#ifndef NO_NETTLE_ECC
case 13: case 14:
return dnsmasq_ecdsa_verify;
#endif
}
return NULL;
}
static int verify(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len,
unsigned char *digest, size_t digest_len, int algo)
{
int (*func)(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len,
unsigned char *digest, size_t digest_len, int algo);
func = verify_func(algo);
if (!func)
return 0;
return (*func)(key_data, key_len, sig, sig_len, digest, digest_len, algo);
}
/* Convert from presentation format to wire format, in place.
Also map UC -> LC.
Note that using extract_name to get presentation format
@@ -424,15 +103,17 @@ static void from_wire(char *name)
static int count_labels(char *name)
{
int i;
char *p;
if (*name == 0)
return 0;
for (i = 0; *name; name++)
if (*name == '.')
for (p = name, i = 0; *p; p++)
if (*p == '.')
i++;
return i+1;
/* Don't count empty first label. */
return *name == '.' ? i : i+1;
}
/* Implement RFC1982 wrapped compare for 32-bit numbers */
@@ -598,10 +279,10 @@ static int get_rdata(struct dns_header *header, size_t plen, unsigned char *end,
leaving the following bytes as deciding the order. Hence the nasty left1 and left2 variables.
*/
static void sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int rrsetidx,
unsigned char **rrset, char *buff1, char *buff2)
static int sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int rrsetidx,
unsigned char **rrset, char *buff1, char *buff2)
{
int swap, quit, i;
int swap, quit, i, j;
do
{
@@ -663,11 +344,21 @@ static void sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int
rrset[i] = tmp;
swap = quit = 1;
}
else if (rc == 0 && quit && len1 == len2)
{
/* Two RRs are equal, remove one copy. RFC 4034, para 6.3 */
for (j = i+1; j < rrsetidx-1; j++)
rrset[j] = rrset[j+1];
rrsetidx--;
i--;
}
else if (rc < 0)
quit = 1;
}
}
} while (swap);
return rrsetidx;
}
static unsigned char **rrset = NULL, **sigs = NULL;
@@ -812,7 +503,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
/* Sort RRset records into canonical order.
Note that at this point keyname and daemon->workspacename buffs are
unused, and used as workspace by the sort. */
sort_rrset(header, plen, rr_desc, rrsetidx, rrset, daemon->workspacename, keyname);
rrsetidx = sort_rrset(header, plen, rr_desc, rrsetidx, rrset, daemon->workspacename, keyname);
/* Now try all the sigs to try and find one which validates */
for (j = 0; j <sigidx; j++)
@@ -866,6 +557,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
u16 len, *dp;
p = rrset[i];
if (!extract_name(header, plen, &p, name, 1, 10))
return STAT_BOGUS;
@@ -1120,7 +812,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
{
a.addr.log.keytag = keytag;
a.addr.log.algo = algo;
if (verify_func(algo))
if (algo_digest_name(algo))
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %hu, algo %hu");
else
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %hu, algo %hu (not supported)");
@@ -1181,11 +873,13 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
rc = STAT_BOGUS;
else
rc = dnssec_validate_reply(now, header, plen, name, keyname, NULL, 0, &neganswer, &nons);
/* Note dnssec_validate_reply() will have cached positive answers */
if (rc == STAT_INSECURE)
rc = STAT_BOGUS;
{
my_syslog(LOG_WARNING, _("Insecure DS reply received, do upstream DNS servers support DNSSEC?"));
rc = STAT_BOGUS;
}
p = (unsigned char *)(header+1);
extract_name(header, plen, &p, name, 1, 4);
p += 4; /* qtype, qclass */
@@ -1248,7 +942,7 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
a.addr.log.keytag = keytag;
a.addr.log.algo = algo;
a.addr.log.digest = digest;
if (hash_find(ds_digest_name(digest)) && verify_func(algo))
if (ds_digest_name(digest) && algo_digest_name(algo))
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %hu, algo %hu, digest %hu");
else
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %hu, algo %hu, digest %hu (not supported)");
@@ -1405,8 +1099,8 @@ static int hostname_cmp(const char *a, const char *b)
}
}
static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsigned char **nsecs, int nsec_count,
char *workspace1, char *workspace2, char *name, int type, int *nons)
static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsigned char **nsecs, unsigned char **labels, int nsec_count,
char *workspace1_in, char *workspace2, char *name, int type, int *nons)
{
int i, rc, rdlen;
unsigned char *p, *psave;
@@ -1419,6 +1113,9 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
/* Find NSEC record that proves name doesn't exist */
for (i = 0; i < nsec_count; i++)
{
char *workspace1 = workspace1_in;
int sig_labels, name_labels;
p = nsecs[i];
if (!extract_name(header, plen, &p, workspace1, 1, 10))
return 0;
@@ -1427,7 +1124,27 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
psave = p;
if (!extract_name(header, plen, &p, workspace2, 1, 10))
return 0;
/* If NSEC comes from wildcard expansion, use original wildcard
as name for computation. */
sig_labels = *labels[i];
name_labels = count_labels(workspace1);
if (sig_labels < name_labels)
{
int k;
for (k = name_labels - sig_labels; k != 0; k--)
{
while (*workspace1 != '.' && *workspace1 != 0)
workspace1++;
if (k != 1 && *workspace1 == '.')
workspace1++;
}
workspace1--;
*workspace1 = '*';
}
rc = hostname_cmp(workspace1, name);
if (rc == 0)
@@ -1453,8 +1170,9 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
return 0;
/* If the SOA bit is set for a DS record, then we have the
DS from the wrong side of the delegation. */
if (type == T_DS && (p[2] & (0x80 >> T_SOA)) != 0)
DS from the wrong side of the delegation. For the root DS,
this is expected. */
if (name_labels != 0 && type == T_DS && (p[2] & (0x80 >> T_SOA)) != 0)
return 0;
}
@@ -1559,7 +1277,7 @@ static int base32_decode(char *in, unsigned char *out)
}
static int check_nsec3_coverage(struct dns_header *header, size_t plen, int digest_len, unsigned char *digest, int type,
char *workspace1, char *workspace2, unsigned char **nsecs, int nsec_count, int *nons)
char *workspace1, char *workspace2, unsigned char **nsecs, int nsec_count, int *nons, int name_labels)
{
int i, hash_len, salt_len, base32_len, rdlen, flags;
unsigned char *p, *psave;
@@ -1614,8 +1332,9 @@ static int check_nsec3_coverage(struct dns_header *header, size_t plen, int dige
return 0;
/* If the SOA bit is set for a DS record, then we have the
DS from the wrong side of the delegation. */
if (type == T_DS && (p[2] & (0x80 >> T_SOA)) != 0)
DS from the wrong side of the delegation. For the root DS,
this is expected. */
if (name_labels != 0 && type == T_DS && (p[2] & (0x80 >> T_SOA)) != 0)
return 0;
}
@@ -1756,7 +1475,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
if ((digest_len = hash_name(name, &digest, hash, salt, salt_len, iterations)) == 0)
return 0;
if (check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count, nons))
if (check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count, nons, count_labels(name)))
return 1;
/* Can't find an NSEC3 which covers the name directly, we need the "closest encloser NSEC3"
@@ -1801,7 +1520,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
if ((digest_len = hash_name(next_closest, &digest, hash, salt, salt_len, iterations)) == 0)
return 0;
if (!check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count, NULL))
if (!check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count, NULL, 1))
return 0;
/* Finally, check that there's no seat of wildcard synthesis */
@@ -1816,7 +1535,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
if ((digest_len = hash_name(wildcard, &digest, hash, salt, salt_len, iterations)) == 0)
return 0;
if (!check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count, NULL))
if (!check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count, NULL, 1))
return 0;
}
@@ -1825,24 +1544,26 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
static int prove_non_existence(struct dns_header *header, size_t plen, char *keyname, char *name, int qtype, int qclass, char *wildname, int *nons)
{
static unsigned char **nsecset = NULL;
static int nsecset_sz = 0;
static unsigned char **nsecset = NULL, **rrsig_labels = NULL;
static int nsecset_sz = 0, rrsig_labels_sz = 0;
int type_found = 0;
unsigned char *p = skip_questions(header, plen);
unsigned char *auth_start, *p = skip_questions(header, plen);
int type, class, rdlen, i, nsecs_found;
/* Move to NS section */
if (!p || !(p = skip_section(p, ntohs(header->ancount), header, plen)))
return 0;
auth_start = p;
for (nsecs_found = 0, i = ntohs(header->nscount); i != 0; i--)
{
unsigned char *pstart = p;
if (!(p = skip_name(p, header, plen, 10)))
if (!extract_name(header, plen, &p, daemon->workspacename, 1, 10))
return 0;
GETSHORT(type, p);
GETSHORT(class, p);
p += 4; /* TTL */
@@ -1859,7 +1580,69 @@ static int prove_non_existence(struct dns_header *header, size_t plen, char *key
if (!expand_workspace(&nsecset, &nsecset_sz, nsecs_found))
return 0;
nsecset[nsecs_found++] = pstart;
if (type == T_NSEC)
{
/* If we're looking for NSECs, find the corresponding SIGs, to
extract the labels value, which we need in case the NSECs
are the result of wildcard expansion.
Note that the NSEC may not have been validated yet
so if there are multiple SIGs, make sure the label value
is the same in all, to avoid be duped by a rogue one.
If there are no SIGs, that's an error */
unsigned char *p1 = auth_start;
int res, j, rdlen1, type1, class1;
if (!expand_workspace(&rrsig_labels, &rrsig_labels_sz, nsecs_found))
return 0;
rrsig_labels[nsecs_found] = NULL;
for (j = ntohs(header->nscount); j != 0; j--)
{
if (!(res = extract_name(header, plen, &p1, daemon->workspacename, 0, 10)))
return 0;
GETSHORT(type1, p1);
GETSHORT(class1, p1);
p1 += 4; /* TTL */
GETSHORT(rdlen1, p1);
if (!CHECK_LEN(header, p1, plen, rdlen1))
return 0;
if (res == 1 && class1 == qclass && type1 == T_RRSIG)
{
int type_covered;
unsigned char *psav = p1;
if (rdlen1 < 18)
return 0; /* bad packet */
GETSHORT(type_covered, p1);
if (type_covered == T_NSEC)
{
p1++; /* algo */
/* labels field must be the same in every SIG we find. */
if (!rrsig_labels[nsecs_found])
rrsig_labels[nsecs_found] = p1;
else if (*rrsig_labels[nsecs_found] != *p1) /* algo */
return 0;
}
p1 = psav;
}
if (!ADD_RDLEN(header, p1, plen, rdlen1))
return 0;
}
/* Must have found at least one sig. */
if (!rrsig_labels[nsecs_found])
return 0;
}
nsecset[nsecs_found++] = pstart;
}
if (!ADD_RDLEN(header, p, plen, rdlen))
@@ -1867,7 +1650,7 @@ static int prove_non_existence(struct dns_header *header, size_t plen, char *key
}
if (type_found == T_NSEC)
return prove_non_existence_nsec(header, plen, nsecset, nsecs_found, daemon->workspacename, keyname, name, qtype, nons);
return prove_non_existence_nsec(header, plen, nsecset, rrsig_labels, nsecs_found, daemon->workspacename, keyname, name, qtype, nons);
else if (type_found == T_NSEC3)
return prove_non_existence_nsec3(header, plen, nsecset, nsecs_found, daemon->workspacename, keyname, name, qtype, wildname, nons);
else
@@ -1935,8 +1718,8 @@ static int zone_status(char *name, int class, char *keyname, time_t now)
do
{
if (crecp->uid == (unsigned int)class &&
hash_find(ds_digest_name(crecp->addr.ds.digest)) &&
verify_func(crecp->addr.ds.algo))
ds_digest_name(crecp->addr.ds.digest) &&
algo_digest_name(crecp->addr.ds.algo))
break;
}
while ((crecp = cache_find_by_name(crecp, keyname, now, F_DS)));
@@ -1965,7 +1748,10 @@ static int zone_status(char *name, int class, char *keyname, time_t now)
STAT_INSECURE at least one RRset not validated, because in unsigned zone.
STAT_BOGUS signature is wrong, bad packet, no validation where there should be.
STAT_NEED_KEY need DNSKEY to complete validation (name is returned in keyname, class in *class)
STAT_NEED_DS need DS to complete validation (name is returned in keyname)
STAT_NEED_DS need DS to complete validation (name is returned in keyname)
daemon->rr_status points to a char array which corressponds to the RRs in the
answer section (only). This is set to 1 for each RR which is validated, and 0 for any which aren't.
*/
int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname,
int *class, int check_unsigned, int *neganswer, int *nons)
@@ -1974,9 +1760,25 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
static int target_sz = 0;
unsigned char *ans_start, *p1, *p2;
int type1, class1, rdlen1, type2, class2, rdlen2, qclass, qtype, targetidx;
int type1, class1, rdlen1 = 0, type2, class2, rdlen2, qclass, qtype, targetidx;
int i, j, rc;
int secure = STAT_SECURE;
/* extend rr_status if necessary */
if (daemon->rr_status_sz < ntohs(header->ancount))
{
char *new = whine_malloc(ntohs(header->ancount) + 64);
if (!new)
return STAT_BOGUS;
free(daemon->rr_status);
daemon->rr_status = new;
daemon->rr_status_sz = ntohs(header->ancount) + 64;
}
memset(daemon->rr_status, 0, ntohs(header->ancount));
if (neganswer)
*neganswer = 0;
@@ -2033,7 +1835,10 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
for (p1 = ans_start, i = 0; i < ntohs(header->ancount) + ntohs(header->nscount); i++)
{
if (!extract_name(header, plen, &p1, name, 1, 10))
if (i != 0 && !ADD_RDLEN(header, p1, plen, rdlen1))
return STAT_BOGUS;
if (!extract_name(header, plen, &p1, name, 1, 10))
return STAT_BOGUS; /* bad packet */
GETSHORT(type1, p1);
@@ -2042,140 +1847,159 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
GETSHORT(rdlen1, p1);
/* Don't try and validate RRSIGs! */
if (type1 != T_RRSIG)
if (type1 == T_RRSIG)
continue;
/* Check if we've done this RRset already */
for (p2 = ans_start, j = 0; j < i; j++)
{
/* Check if we've done this RRset already */
for (p2 = ans_start, j = 0; j < i; j++)
{
if (!(rc = extract_name(header, plen, &p2, name, 0, 10)))
return STAT_BOGUS; /* bad packet */
GETSHORT(type2, p2);
GETSHORT(class2, p2);
p2 += 4; /* TTL */
GETSHORT(rdlen2, p2);
if (type2 == type1 && class2 == class1 && rc == 1)
break; /* Done it before: name, type, class all match. */
if (!ADD_RDLEN(header, p2, plen, rdlen2))
return STAT_BOGUS;
}
if (!(rc = extract_name(header, plen, &p2, name, 0, 10)))
return STAT_BOGUS; /* bad packet */
GETSHORT(type2, p2);
GETSHORT(class2, p2);
p2 += 4; /* TTL */
GETSHORT(rdlen2, p2);
if (type2 == type1 && class2 == class1 && rc == 1)
break; /* Done it before: name, type, class all match. */
if (!ADD_RDLEN(header, p2, plen, rdlen2))
return STAT_BOGUS;
}
if (j != i)
{
/* Done already: copy the validation status */
if (i < ntohs(header->ancount))
daemon->rr_status[i] = daemon->rr_status[j];
}
else
{
/* Not done, validate now */
if (j == i)
int sigcnt, rrcnt;
char *wildname;
if (!explore_rrset(header, plen, class1, type1, name, keyname, &sigcnt, &rrcnt))
return STAT_BOGUS;
/* No signatures for RRset. We can be configured to assume this is OK and return an INSECURE result. */
if (sigcnt == 0)
{
int sigcnt, rrcnt;
char *wildname;
if (!explore_rrset(header, plen, class1, type1, name, keyname, &sigcnt, &rrcnt))
return STAT_BOGUS;
/* No signatures for RRset. We can be configured to assume this is OK and return a INSECURE result. */
if (sigcnt == 0)
if (check_unsigned)
{
if (check_unsigned)
{
rc = zone_status(name, class1, keyname, now);
if (rc == STAT_SECURE)
rc = STAT_BOGUS;
if (class)
*class = class1; /* Class for NEED_DS or NEED_KEY */
}
else
rc = STAT_INSECURE;
return rc;
rc = zone_status(name, class1, keyname, now);
if (rc == STAT_SECURE)
rc = STAT_BOGUS;
if (class)
*class = class1; /* Class for NEED_DS or NEED_KEY */
}
else
rc = STAT_INSECURE;
if (rc != STAT_INSECURE)
return rc;
}
else
{
/* explore_rrset() gives us key name from sigs in keyname.
Can't overwrite name here. */
strcpy(daemon->workspacename, keyname);
rc = zone_status(daemon->workspacename, class1, keyname, now);
if (rc != STAT_SECURE)
{
/* Zone is insecure, don't need to validate RRset */
if (class)
*class = class1; /* Class for NEED_DS or NEED_KEY */
return rc;
}
rc = validate_rrset(now, header, plen, class1, type1, sigcnt, rrcnt, name, keyname, &wildname, NULL, 0, 0, 0);
if (rc == STAT_BOGUS || rc == STAT_NEED_KEY || rc == STAT_NEED_DS)
{
if (class)
*class = class1; /* Class for DS or DNSKEY */
*class = class1; /* Class for NEED_DS or NEED_KEY */
return rc;
}
else
}
/* Zone is insecure, don't need to validate RRset */
if (rc == STAT_SECURE)
{
rc = validate_rrset(now, header, plen, class1, type1, sigcnt,
rrcnt, name, keyname, &wildname, NULL, 0, 0, 0);
if (rc == STAT_BOGUS || rc == STAT_NEED_KEY || rc == STAT_NEED_DS)
{
if (class)
*class = class1; /* Class for DS or DNSKEY */
return rc;
}
/* rc is now STAT_SECURE or STAT_SECURE_WILDCARD */
/* Note that RR is validated */
if (i < ntohs(header->ancount))
daemon->rr_status[i] = 1;
/* Note if we've validated either the answer to the question
or the target of a CNAME. Any not noted will need NSEC or
to be in unsigned space. */
for (j = 0; j <targetidx; j++)
if ((p2 = targets[j]))
{
if (!(rc = extract_name(header, plen, &p2, name, 0, 10)))
int rc1;
if (!(rc1 = extract_name(header, plen, &p2, name, 0, 10)))
return STAT_BOGUS; /* bad packet */
if (class1 == qclass && rc == 1 && (type1 == T_CNAME || type1 == qtype || qtype == T_ANY ))
if (class1 == qclass && rc1 == 1 && (type1 == T_CNAME || type1 == qtype || qtype == T_ANY ))
targets[j] = NULL;
}
/* An attacker replay a wildcard answer with a different
answer and overlay a genuine RR. To prove this
hasn't happened, the answer must prove that
the genuine record doesn't exist. Check that here.
Note that we may not yet have validated the NSEC/NSEC3 RRsets.
That's not a problem since if the RRsets later fail
we'll return BOGUS then. */
if (rc == STAT_SECURE_WILDCARD && !prove_non_existence(header, plen, keyname, name, type1, class1, wildname, NULL))
/* An attacker replay a wildcard answer with a different
answer and overlay a genuine RR. To prove this
hasn't happened, the answer must prove that
the genuine record doesn't exist. Check that here.
Note that we may not yet have validated the NSEC/NSEC3 RRsets.
That's not a problem since if the RRsets later fail
we'll return BOGUS then. */
if (rc == STAT_SECURE_WILDCARD &&
!prove_non_existence(header, plen, keyname, name, type1, class1, wildname, NULL))
return STAT_BOGUS;
rc = STAT_SECURE;
}
}
}
if (!ADD_RDLEN(header, p1, plen, rdlen1))
return STAT_BOGUS;
if (rc == STAT_INSECURE)
secure = STAT_INSECURE;
}
/* OK, all the RRsets validate, now see if we have a missing answer or CNAME target. */
for (j = 0; j <targetidx; j++)
if ((p2 = targets[j]))
{
if (neganswer)
*neganswer = 1;
if (!extract_name(header, plen, &p2, name, 1, 10))
return STAT_BOGUS; /* bad packet */
/* NXDOMAIN or NODATA reply, unanswered question is (name, qclass, qtype) */
/* For anything other than a DS record, this situation is OK if either
the answer is in an unsigned zone, or there's a NSEC records. */
if (!prove_non_existence(header, plen, keyname, name, qtype, qclass, NULL, nons))
{
/* Empty DS without NSECS */
if (qtype == T_DS)
return STAT_BOGUS;
if ((rc = zone_status(name, qclass, keyname, now)) != STAT_SECURE)
{
if (class)
*class = qclass; /* Class for NEED_DS or NEED_KEY */
return rc;
}
return STAT_BOGUS; /* signed zone, no NSECs */
}
}
if (secure == STAT_SECURE)
for (j = 0; j <targetidx; j++)
if ((p2 = targets[j]))
{
if (neganswer)
*neganswer = 1;
if (!extract_name(header, plen, &p2, name, 1, 10))
return STAT_BOGUS; /* bad packet */
/* NXDOMAIN or NODATA reply, unanswered question is (name, qclass, qtype) */
/* For anything other than a DS record, this situation is OK if either
the answer is in an unsigned zone, or there's a NSEC records. */
if (!prove_non_existence(header, plen, keyname, name, qtype, qclass, NULL, nons))
{
/* Empty DS without NSECS */
if (qtype == T_DS)
return STAT_BOGUS;
if ((rc = zone_status(name, qclass, keyname, now)) != STAT_SECURE)
{
if (class)
*class = qclass; /* Class for NEED_DS or NEED_KEY */
return rc;
}
return STAT_BOGUS; /* signed zone, no NSECs */
}
}
return STAT_SECURE;
return secure;
}
@@ -2202,19 +2026,11 @@ int dnskey_keytag(int alg, int flags, unsigned char *key, int keylen)
}
size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char *name, int class,
int type, union mysockaddr *addr, int edns_pktsz)
int type, int edns_pktsz)
{
unsigned char *p;
char *types = querystr("dnssec-query", type);
size_t ret;
if (addr->sa.sa_family == AF_INET)
log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, name, (struct all_addr *)&addr->in.sin_addr, types);
#ifdef HAVE_IPV6
else
log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, name, (struct all_addr *)&addr->in6.sin6_addr, types);
#endif
header->qdcount = htons(1);
header->ancount = htons(0);
header->nscount = htons(0);
@@ -2230,7 +2046,7 @@ size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char
p = (unsigned char *)(header+1);
p = do_rfc1035_name(p, name);
p = do_rfc1035_name(p, name, NULL);
*p++ = 0;
PUTSHORT(type, p);
PUTSHORT(class, p);

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2017 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2018 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
@@ -55,83 +55,133 @@ int is_name_synthetic(int flags, char *name, struct all_addr *addr)
if (pref && *pref != 0)
continue; /* prefix match fail */
/* NB, must not alter name if we return zero */
for (p = tail; *p; p++)
if (c->indexed)
{
char c = *p;
for (p = tail; *p; p++)
{
char c = *p;
if (c < '0' || c > '9')
break;
}
if ((c >='0' && c <= '9') || c == '-')
if (*p != '.')
continue;
#ifdef HAVE_IPV6
if (prot == AF_INET6 && ((c >='A' && c <= 'F') || (c >='a' && c <= 'f')))
continue;
*p = 0;
if (hostname_isequal(c->domain, p+1))
{
if (prot == AF_INET)
{
unsigned int index = atoi(tail);
if (!c->is6 &&
index <= ntohl(c->end.s_addr) - ntohl(c->start.s_addr))
{
addr->addr.addr4.s_addr = htonl(ntohl(c->start.s_addr) + index);
found = 1;
}
}
#ifdef HAVE_IPV6
else
{
u64 index = atoll(tail);
if (c->is6 &&
index <= addr6part(&c->end6) - addr6part(&c->start6))
{
u64 start = addr6part(&c->start6);
addr->addr.addr6 = c->start6;
setaddr6part(&addr->addr.addr6, start + index);
found = 1;
}
}
#endif
break;
}
if (*p != '.')
continue;
*p = 0;
#ifdef HAVE_IPV6
if (prot == AF_INET6 && strstr(tail, "--ffff-") == tail)
{
/* special hack for v4-mapped. */
memcpy(tail, "::ffff:", 7);
for (p = tail + 7; *p; p++)
if (*p == '-')
*p = '.';
}
}
else
#endif
{
/* swap . or : for - */
/* NB, must not alter name if we return zero */
for (p = tail; *p; p++)
if (*p == '-')
{
if (prot == AF_INET)
*p = '.';
{
char c = *p;
if ((c >='0' && c <= '9') || c == '-')
continue;
#ifdef HAVE_IPV6
else
*p = ':';
if (prot == AF_INET6 && ((c >='A' && c <= 'F') || (c >='a' && c <= 'f')))
continue;
#endif
}
break;
}
if (*p != '.')
continue;
*p = 0;
#ifdef HAVE_IPV6
if (prot == AF_INET6 && strstr(tail, "--ffff-") == tail)
{
/* special hack for v4-mapped. */
memcpy(tail, "::ffff:", 7);
for (p = tail + 7; *p; p++)
if (*p == '-')
*p = '.';
}
else
#endif
{
/* swap . or : for - */
for (p = tail; *p; p++)
if (*p == '-')
{
if (prot == AF_INET)
*p = '.';
#ifdef HAVE_IPV6
else
*p = ':';
#endif
}
}
if (hostname_isequal(c->domain, p+1) && inet_pton(prot, tail, addr))
{
if (prot == AF_INET)
{
if (!c->is6 &&
ntohl(addr->addr.addr4.s_addr) >= ntohl(c->start.s_addr) &&
ntohl(addr->addr.addr4.s_addr) <= ntohl(c->end.s_addr))
found = 1;
}
#ifdef HAVE_IPV6
else
{
u64 addrpart = addr6part(&addr->addr.addr6);
if (c->is6 &&
is_same_net6(&addr->addr.addr6, &c->start6, 64) &&
addrpart >= addr6part(&c->start6) &&
addrpart <= addr6part(&c->end6))
found = 1;
}
#endif
}
}
if (hostname_isequal(c->domain, p+1) && inet_pton(prot, tail, addr))
{
if (prot == AF_INET)
{
if (!c->is6 &&
ntohl(addr->addr.addr4.s_addr) >= ntohl(c->start.s_addr) &&
ntohl(addr->addr.addr4.s_addr) <= ntohl(c->end.s_addr))
found = 1;
}
#ifdef HAVE_IPV6
else
{
u64 addrpart = addr6part(&addr->addr.addr6);
if (c->is6 &&
is_same_net6(&addr->addr.addr6, &c->start6, 64) &&
addrpart >= addr6part(&c->start6) &&
addrpart <= addr6part(&c->end6))
found = 1;
}
#endif
}
/* restore name */
for (p = tail; *p; p++)
if (*p == '.' || *p == ':')
*p = '-';
*p = '.';
if (found)
return 1;
}
@@ -149,14 +199,22 @@ int is_rev_synth(int flag, struct all_addr *addr, char *name)
char *p;
*name = 0;
if (c->prefix)
strncpy(name, c->prefix, MAXDNAME - ADDRSTRLEN);
if (c->indexed)
{
unsigned int index = ntohl(addr->addr.addr4.s_addr) - ntohl(c->start.s_addr);
snprintf(name, MAXDNAME, "%s%u", c->prefix ? c->prefix : "", index);
}
else
{
if (c->prefix)
strncpy(name, c->prefix, MAXDNAME - ADDRSTRLEN);
inet_ntop(AF_INET, &addr->addr.addr4, name + strlen(name), ADDRSTRLEN);
for (p = name; *p; p++)
if (*p == '.')
*p = '-';
}
inet_ntop(AF_INET, &addr->addr.addr4, name + strlen(name), ADDRSTRLEN);
for (p = name; *p; p++)
if (*p == '.')
*p = '-';
strncat(name, ".", MAXDNAME);
strncat(name, c->domain, MAXDNAME);
@@ -169,23 +227,32 @@ int is_rev_synth(int flag, struct all_addr *addr, char *name)
char *p;
*name = 0;
if (c->prefix)
strncpy(name, c->prefix, MAXDNAME - ADDRSTRLEN);
inet_ntop(AF_INET6, &addr->addr.addr6, name + strlen(name), ADDRSTRLEN);
/* IPv6 presentation address can start with ":", but valid domain names
cannot start with "-" so prepend a zero in that case. */
if (!c->prefix && *name == ':')
if (c->indexed)
{
*name = '0';
inet_ntop(AF_INET6, &addr->addr.addr6, name+1, ADDRSTRLEN);
u64 index = addr6part(&addr->addr.addr6) - addr6part(&c->start6);
snprintf(name, MAXDNAME, "%s%llu", c->prefix ? c->prefix : "", index);
}
else
{
if (c->prefix)
strncpy(name, c->prefix, MAXDNAME - ADDRSTRLEN);
inet_ntop(AF_INET6, &addr->addr.addr6, name + strlen(name), ADDRSTRLEN);
/* V4-mapped have periods.... */
for (p = name; *p; p++)
if (*p == ':' || *p == '.')
*p = '-';
/* IPv6 presentation address can start with ":", but valid domain names
cannot start with "-" so prepend a zero in that case. */
if (!c->prefix && *name == ':')
{
*name = '0';
inet_ntop(AF_INET6, &addr->addr.addr6, name+1, ADDRSTRLEN);
}
/* V4-mapped have periods.... */
for (p = name; *p; p++)
if (*p == ':' || *p == '.')
*p = '-';
}
strncat(name, ".", MAXDNAME);
strncat(name, c->domain, MAXDNAME);

211
src/dump.c Normal file
View File

@@ -0,0 +1,211 @@
/* dnsmasq is Copyright (c) 2000-2018 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_DUMPFILE
static u32 packet_count;
/* https://wiki.wireshark.org/Development/LibpcapFileFormat */
struct pcap_hdr_s {
u32 magic_number; /* magic number */
u16 version_major; /* major version number */
u16 version_minor; /* minor version number */
u32 thiszone; /* GMT to local correction */
u32 sigfigs; /* accuracy of timestamps */
u32 snaplen; /* max length of captured packets, in octets */
u32 network; /* data link type */
};
struct pcaprec_hdr_s {
u32 ts_sec; /* timestamp seconds */
u32 ts_usec; /* timestamp microseconds */
u32 incl_len; /* number of octets of packet saved in file */
u32 orig_len; /* actual length of packet */
};
void dump_init(void)
{
struct stat buf;
struct pcap_hdr_s header;
struct pcaprec_hdr_s pcap_header;
packet_count = 0;
if (stat(daemon->dump_file, &buf) == -1)
{
/* doesn't exist, create and add header */
header.magic_number = 0xa1b2c3d4;
header.version_major = 2;
header.version_minor = 4;
header.thiszone = 0;
header.sigfigs = 0;
header.snaplen = daemon->edns_pktsz + 200; /* slop for IP/UDP headers */
header.network = 101; /* DLT_RAW http://www.tcpdump.org/linktypes.html */
if (errno != ENOENT ||
(daemon->dumpfd = creat(daemon->dump_file, S_IRUSR | S_IWUSR)) == -1 ||
!read_write(daemon->dumpfd, (void *)&header, sizeof(header), 0))
die(_("cannot create %s: %s"), daemon->dump_file, EC_FILE);
}
else if ((daemon->dumpfd = open(daemon->dump_file, O_APPEND | O_RDWR)) == -1 ||
!read_write(daemon->dumpfd, (void *)&header, sizeof(header), 1))
die(_("cannot access %s: %s"), daemon->dump_file, EC_FILE);
else if (header.magic_number != 0xa1b2c3d4)
die(_("bad header in %s"), daemon->dump_file, EC_FILE);
else
{
/* count existing records */
while (read_write(daemon->dumpfd, (void *)&pcap_header, sizeof(pcap_header), 1))
{
lseek(daemon->dumpfd, pcap_header.incl_len, SEEK_CUR);
packet_count++;
}
}
}
void dump_packet(int mask, void *packet, size_t len, union mysockaddr *src, union mysockaddr *dst)
{
struct ip ip;
#ifdef HAVE_IPV6
struct ip6_hdr ip6;
int family;
#endif
struct udphdr {
u16 uh_sport; /* source port */
u16 uh_dport; /* destination port */
u16 uh_ulen; /* udp length */
u16 uh_sum; /* udp checksum */
} udp;
struct pcaprec_hdr_s pcap_header;
struct timeval time;
u32 i, sum;
void *iphdr;
size_t ipsz;
int rc;
if (daemon->dumpfd == -1 || !(mask & daemon->dump_mask))
return;
/* So wireshark can Id the packet. */
udp.uh_sport = udp.uh_dport = htons(NAMESERVER_PORT);
#ifdef HAVE_IPV6
if (src)
family = src->sa.sa_family;
else
family = dst->sa.sa_family;
if (family == AF_INET6)
{
iphdr = &ip6;
ipsz = sizeof(ip6);
memset(&ip6, 0, sizeof(ip6));
ip6.ip6_vfc = 6 << 4;
ip6.ip6_plen = htons(sizeof(struct udphdr) + len);
ip6.ip6_nxt = IPPROTO_UDP;
ip6.ip6_hops = 64;
if (src)
{
memcpy(&ip6.ip6_src, &src->in6.sin6_addr, IN6ADDRSZ);
udp.uh_sport = src->in6.sin6_port;
}
if (dst)
{
memcpy(&ip6.ip6_dst, &dst->in6.sin6_addr, IN6ADDRSZ);
udp.uh_dport = dst->in6.sin6_port;
}
/* start UDP checksum */
for (sum = 0, i = 0; i < IN6ADDRSZ; i++)
sum += ((u16 *)&ip6.ip6_src)[i];
}
else
#endif
{
iphdr = &ip;
ipsz = sizeof(ip);
memset(&ip, 0, sizeof(ip));
ip.ip_v = IPVERSION;
ip.ip_hl = sizeof(struct ip) / 4;
ip.ip_len = htons(sizeof(struct ip) + sizeof(struct udphdr) + len);
ip.ip_ttl = IPDEFTTL;
ip.ip_p = IPPROTO_UDP;
if (src)
{
ip.ip_src = src->in.sin_addr;
udp.uh_sport = src->in.sin_port;
}
if (dst)
{
ip.ip_dst = dst->in.sin_addr;
udp.uh_dport = dst->in.sin_port;
}
ip.ip_sum = 0;
for (sum = 0, i = 0; i < sizeof(struct ip) / 2; i++)
sum += ((u16 *)&ip)[i];
while (sum >> 16)
sum = (sum & 0xffff) + (sum >> 16);
ip.ip_sum = (sum == 0xffff) ? sum : ~sum;
/* start UDP checksum */
sum = ip.ip_src.s_addr & 0xffff;
sum += (ip.ip_src.s_addr >> 16) & 0xffff;
sum += ip.ip_dst.s_addr & 0xffff;
sum += (ip.ip_dst.s_addr >> 16) & 0xffff;
}
if (len & 1)
((unsigned char *)packet)[len] = 0; /* for checksum, in case length is odd. */
udp.uh_sum = 0;
udp.uh_ulen = htons(sizeof(struct udphdr) + len);
sum += htons(IPPROTO_UDP);
sum += htons(sizeof(struct udphdr) + len);
for (i = 0; i < sizeof(struct udphdr)/2; i++)
sum += ((u16 *)&udp)[i];
for (i = 0; i < (len + 1) / 2; i++)
sum += ((u16 *)packet)[i];
while (sum >> 16)
sum = (sum & 0xffff) + (sum >> 16);
udp.uh_sum = (sum == 0xffff) ? sum : ~sum;
rc = gettimeofday(&time, NULL);
pcap_header.ts_sec = time.tv_sec;
pcap_header.ts_usec = time.tv_usec;
pcap_header.incl_len = pcap_header.orig_len = ipsz + sizeof(udp) + len;
if (rc == -1 ||
!read_write(daemon->dumpfd, (void *)&pcap_header, sizeof(pcap_header), 0) ||
!read_write(daemon->dumpfd, iphdr, ipsz, 0) ||
!read_write(daemon->dumpfd, (void *)&udp, sizeof(udp), 0) ||
!read_write(daemon->dumpfd, (void *)packet, len, 0))
my_syslog(LOG_ERR, _("failed to write packet dump"));
else
my_syslog(LOG_INFO, _("dumping UDP packet %u mask 0x%04x"), ++packet_count, mask);
}
#endif

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2017 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2018 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
@@ -144,7 +144,7 @@ size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *l
GETSHORT(len, p);
/* malformed option, delete the whole OPT RR and start again. */
if (i + len > rdlen)
if (i + 4 + len > rdlen)
{
rdlen = 0;
is_last = 0;
@@ -159,7 +159,7 @@ size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *l
/* delete option if we're to replace it. */
p -= 4;
rdlen -= len + 4;
memcpy(p, p+len+4, rdlen - i);
memmove(p, p+len+4, rdlen - i);
PUTSHORT(rdlen, lenp);
lenp -= 2;
}
@@ -192,7 +192,15 @@ size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *l
!(p = skip_section(p,
ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount),
header, plen)))
{
free(buff);
return plen;
}
if (p + 11 > limit)
{
free(buff);
return plen; /* Too big */
}
*p++ = 0; /* empty name */
PUTSHORT(T_OPT, p);
PUTSHORT(udp_sz, p); /* max packet length, 512 if not given in EDNS0 header */
@@ -204,11 +212,19 @@ size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *l
/* Copy back any options */
if (buff)
{
if (p + rdlen > limit)
{
free(buff);
return plen; /* Too big */
}
memcpy(p, buff, rdlen);
free(buff);
p += rdlen;
}
header->arcount = htons(ntohs(header->arcount) + 1);
/* Only bump arcount if RR is going to fit */
if (((ssize_t)optlen) <= (limit - (p + 4)))
header->arcount = htons(ntohs(header->arcount) + 1);
}
if (((ssize_t)optlen) > (limit - (p + 4)))
@@ -217,8 +233,12 @@ size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *l
/* Add new option */
if (optno != 0 && replace != 2)
{
if (p + 4 > limit)
return plen; /* Too big */
PUTSHORT(optno, p);
PUTSHORT(optlen, p);
if (p + optlen > limit)
return plen; /* Too big */
memcpy(p, opt, optlen);
p += optlen;
PUTSHORT(p - datap, lenp);

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2017 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2018 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
@@ -118,6 +118,7 @@ static unsigned int search_servers(time_t now, struct all_addr **addrpp, unsigne
unsigned int matchlen = 0;
struct server *serv;
unsigned int flags = 0;
static struct all_addr zero;
for (serv = daemon->servers; serv; serv=serv->next)
if (qtype == F_DNSSECOK && !(serv->flags & SERV_DO_DNSSEC))
@@ -129,9 +130,16 @@ static unsigned int search_servers(time_t now, struct all_addr **addrpp, unsigne
*type = SERV_FOR_NODOTS;
if (serv->flags & SERV_NO_ADDR)
flags = F_NXDOMAIN;
else if (serv->flags & SERV_LITERAL_ADDRESS)
else if (serv->flags & SERV_LITERAL_ADDRESS)
{
if (sflag & qtype)
/* literal address = '#' -> return all-zero address for IPv4 and IPv6 */
if ((serv->flags & SERV_USE_RESOLV) && (qtype & (F_IPV6 | F_IPV4)))
{
memset(&zero, 0, sizeof(zero));
flags = qtype;
*addrpp = &zero;
}
else if (sflag & qtype)
{
flags = sflag;
if (serv->addr.sa.sa_family == AF_INET)
@@ -184,7 +192,14 @@ static unsigned int search_servers(time_t now, struct all_addr **addrpp, unsigne
flags = F_NXDOMAIN;
else if (serv->flags & SERV_LITERAL_ADDRESS)
{
if (sflag & qtype)
/* literal address = '#' -> return all-zero address for IPv4 and IPv6 */
if ((serv->flags & SERV_USE_RESOLV) && (qtype & (F_IPV6 | F_IPV4)))
{
memset(&zero, 0, sizeof(zero));
flags = qtype;
*addrpp = &zero;
}
else if (sflag & qtype)
{
flags = sflag;
if (serv->addr.sa.sa_family == AF_INET)
@@ -214,12 +229,18 @@ static unsigned int search_servers(time_t now, struct all_addr **addrpp, unsigne
if (flags)
{
int logflags = 0;
if (flags == F_NXDOMAIN || flags == F_NOERR)
logflags = F_NEG | qtype;
log_query(logflags | flags | F_CONFIG | F_FORWARD, qdomain, *addrpp, NULL);
if (flags == F_NXDOMAIN || flags == F_NOERR)
log_query(flags | qtype | F_NEG | F_CONFIG | F_FORWARD, qdomain, NULL, NULL);
else
{
/* handle F_IPV4 and F_IPV6 set on ANY query to 0.0.0.0/:: domain. */
if (flags & F_IPV4)
log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV6, qdomain, *addrpp, NULL);
#ifdef HAVE_IPV6
if (flags & F_IPV6)
log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV4, qdomain, *addrpp, NULL);
#endif
}
}
else if ((*type) & SERV_USE_RESOLV)
{
@@ -246,9 +267,9 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
unsigned int crc = questions_crc(header, plen, daemon->namebuff);
void *hash = &crc;
#endif
unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL);
(void)do_bit;
unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL);
unsigned char *oph = find_pseudoheader(header, plen, NULL, NULL, NULL, NULL);
(void)do_bit;
/* may be no servers available. */
if (forward || (hash && (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, hash))))
@@ -272,14 +293,13 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
while (forward->blocking_query)
forward = forward->blocking_query;
forward->flags |= FREC_TEST_PKTSZ;
blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
plen = forward->stash_len;
forward->flags |= FREC_TEST_PKTSZ;
if (find_pseudoheader(header, plen, NULL, &pheader, &is_sign, NULL) && !is_sign)
PUTSHORT(SAFE_PKTSZ, pheader);
if (forward->sentto->addr.sa.sa_family == AF_INET)
log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, "retry", (struct all_addr *)&forward->sentto->addr.in.sin_addr, "dnssec");
#ifdef HAVE_IPV6
@@ -299,9 +319,9 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
fd = forward->rfd4->fd;
}
while (retry_send( sendto(fd, (char *)header, plen, 0,
&forward->sentto->addr.sa,
sa_len(&forward->sentto->addr))));
while (retry_send(sendto(fd, (char *)header, plen, 0,
&forward->sentto->addr.sa,
sa_len(&forward->sentto->addr))));
return 1;
}
@@ -400,31 +420,21 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
struct server *firstsentto = start;
int subnet, forwarded = 0;
size_t edns0_len;
unsigned char *pheader;
/* If a query is retried, use the log_id for the retry when logging the answer. */
forward->log_id = daemon->log_id;
edns0_len = add_edns0_config(header, plen, ((unsigned char *)header) + PACKETSZ, &forward->source, now, &subnet);
plen = add_edns0_config(header, plen, ((unsigned char *)header) + PACKETSZ, &forward->source, now, &subnet);
if (edns0_len != plen)
{
plen = edns0_len;
forward->flags |= FREC_ADDED_PHEADER;
if (subnet)
forward->flags |= FREC_HAS_SUBNET;
}
if (subnet)
forward->flags |= FREC_HAS_SUBNET;
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && do_dnssec)
{
size_t new = add_do_bit(header, plen, ((unsigned char *) header) + PACKETSZ);
if (new != plen)
forward->flags |= FREC_ADDED_PHEADER;
plen = new;
plen = add_do_bit(header, plen, ((unsigned char *) header) + PACKETSZ);
/* 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))
@@ -433,9 +443,20 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
}
#endif
/* If we're sending an EDNS0 with any options, we can't recreate the query from a reply. */
if (find_pseudoheader(header, plen, &edns0_len, NULL, NULL, NULL) && edns0_len > 11)
forward->flags |= FREC_HAS_EXTRADATA;
if (find_pseudoheader(header, plen, &edns0_len, &pheader, NULL, NULL))
{
/* If there wasn't a PH before, and there is now, we added it. */
if (!oph)
forward->flags |= FREC_ADDED_PHEADER;
/* If we're sending an EDNS0 with any options, we can't recreate the query from a reply. */
if (edns0_len > 11)
forward->flags |= FREC_HAS_EXTRADATA;
/* Reduce udp size on retransmits. */
if (forward->flags & FREC_TEST_PKTSZ)
PUTSHORT(SAFE_PKTSZ, pheader);
}
while (1)
{
@@ -507,6 +528,10 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
if (errno == 0)
{
#ifdef HAVE_DUMPFILE
dump_packet(DUMP_UP_QUERY, (void *)header, plen, NULL, &start->addr);
#endif
/* Keep info in case we want to re-send this packet */
daemon->srv_save = start;
daemon->packet_len = plen;
@@ -549,6 +574,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
if (udpfd != -1)
{
plen = setup_reply(header, plen, addrp, flags, daemon->local_ttl);
if (oph)
plen = add_pseudoheader(header, plen, ((unsigned char *) header) + PACKETSZ, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0);
send_from(udpfd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND), (char *)header, plen, udpaddr, dst_addr, dst_iface);
}
@@ -562,8 +589,9 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
unsigned char *pheader, *sizep;
char **sets = 0;
int munged = 0, is_sign;
unsigned int rcode = RCODE(header);
size_t plen;
(void)ad_reqd;
(void)do_bit;
(void)bogusanswer;
@@ -592,6 +620,9 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
if ((pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign, NULL)))
{
/* Get extended RCODE. */
rcode |= sizep[2] << 4;
if (check_subnet && !check_source(header, plen, pheader, query_source))
{
my_syslog(LOG_WARNING, _("discarding DNS reply: subnet option mismatch"));
@@ -640,11 +671,20 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
if (!is_sign && !option_bool(OPT_DNSSEC_PROXY))
header->hb4 &= ~HB4_AD;
if (OPCODE(header) != QUERY || (RCODE(header) != NOERROR && RCODE(header) != NXDOMAIN))
if (OPCODE(header) != QUERY)
return resize_packet(header, n, pheader, plen);
if (rcode != NOERROR && rcode != NXDOMAIN)
{
struct all_addr a;
a.addr.rcode.rcode = rcode;
log_query(F_UPSTREAM | F_RCODE, "error", &a, NULL);
return resize_packet(header, n, pheader, plen);
}
/* Complain loudly if the upstream server is non-recursive. */
if (!(header->hb4 & HB4_RA) && RCODE(header) == NOERROR &&
if (!(header->hb4 & HB4_RA) && rcode == NOERROR &&
server && !(server->flags & SERV_WARNED_RECURSIVE))
{
prettyprint_addr(&server->addr, daemon->namebuff);
@@ -653,7 +693,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
server->flags |= SERV_WARNED_RECURSIVE;
}
if (daemon->bogus_addr && RCODE(header) != NXDOMAIN &&
if (daemon->bogus_addr && rcode != NXDOMAIN &&
check_for_bogus_wildcard(header, n, daemon->namebuff, daemon->bogus_addr, now))
{
munged = 1;
@@ -665,7 +705,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
{
int doctored = 0;
if (RCODE(header) == NXDOMAIN &&
if (rcode == NXDOMAIN &&
extract_request(header, n, daemon->namebuff, NULL) &&
check_for_local_domain(daemon->namebuff, now))
{
@@ -755,7 +795,7 @@ void reply_query(int fd, int family, time_t now)
#endif
header = (struct dns_header *)daemon->packet;
if (n < (int)sizeof(struct dns_header) || !(header->hb3 & HB3_QR))
return;
@@ -767,7 +807,11 @@ void reply_query(int fd, int family, time_t now)
if (!server)
return;
/* If sufficient time has elapsed, try and expand UDP buffer size again. */
if (difftime(now, server->pktsz_reduced) > UDP_TEST_TIME)
server->edns_pktsz = daemon->edns_pktsz;
#ifdef HAVE_DNSSEC
hash = hash_questions(header, n, daemon->namebuff);
#else
@@ -778,6 +822,11 @@ void reply_query(int fd, int family, time_t now)
if (!(forward = lookup_frec(ntohs(header->id), hash)))
return;
#ifdef HAVE_DUMPFILE
dump_packet((forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY)) ? DUMP_SEC_REPLY : DUMP_UP_REPLY,
(void *)header, n, &serveraddr, NULL);
#endif
/* log_query gets called indirectly all over the place, so
pass these in global variables - sorry. */
daemon->log_display_id = forward->log_id;
@@ -789,8 +838,7 @@ void reply_query(int fd, int family, time_t now)
/* Note: if we send extra options in the EDNS0 header, we can't recreate
the query from the reply. */
if (RCODE(header) == REFUSED &&
!option_bool(OPT_ORDER) &&
if ((RCODE(header) == REFUSED || RCODE(header) == SERVFAIL) &&
forward->forwardall == 0 &&
!(forward->flags & FREC_HAS_EXTRADATA))
/* for broken servers, attempt to send to another one. */
@@ -798,10 +846,84 @@ void reply_query(int fd, int family, time_t now)
unsigned char *pheader;
size_t plen;
int is_sign;
#ifdef HAVE_DNSSEC
/* For DNSSEC originated queries, just retry the query to the same server. */
if (forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY))
{
struct server *start;
blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
plen = forward->stash_len;
forward->forwardall = 2; /* only retry once */
start = forward->sentto;
/* for non-domain specific servers, see if we can find another to try. */
if ((forward->sentto->flags & SERV_TYPE) == 0)
while (1)
{
if (!(start = start->next))
start = daemon->servers;
if (start == forward->sentto)
break;
if ((start->flags & SERV_TYPE) == 0 &&
(start->flags & SERV_DO_DNSSEC))
break;
}
if (start->sfd)
fd = start->sfd->fd;
else
{
#ifdef HAVE_IPV6
if (start->addr.sa.sa_family == AF_INET6)
{
/* may have changed family */
if (!forward->rfd6)
forward->rfd6 = allocate_rfd(AF_INET6);
fd = forward->rfd6->fd;
}
else
#endif
{
/* may have changed family */
if (!forward->rfd4)
forward->rfd4 = allocate_rfd(AF_INET);
fd = forward->rfd4->fd;
}
}
while (retry_send(sendto(fd, (char *)header, plen, 0,
&start->addr.sa,
sa_len(&start->addr))));
if (start->addr.sa.sa_family == AF_INET)
log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, "retry", (struct all_addr *)&start->addr.in.sin_addr, "dnssec");
#ifdef HAVE_IPV6
else
log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, "retry", (struct all_addr *)&start->addr.in6.sin6_addr, "dnssec");
#endif
return;
}
#endif
/* In strict order mode, there must be a server later in the chain
left to send to, otherwise without the forwardall mechanism,
code further on will cycle around the list forwever if they
all return REFUSED. Note that server is always non-NULL before
this executes. */
if (option_bool(OPT_ORDER))
for (server = forward->sentto->next; server; server = server->next)
if (!(server->flags & (SERV_LITERAL_ADDRESS | SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_NO_ADDR | SERV_LOOP)))
break;
/* recreate query from reply */
pheader = find_pseudoheader(header, (size_t)n, &plen, NULL, &is_sign, NULL);
if (!is_sign)
if (!is_sign && server)
{
header->ancount = htons(0);
header->nscount = htons(0);
@@ -845,12 +967,18 @@ void reply_query(int fd, int family, time_t now)
}
/* We tried resending to this server with a smaller maximum size and got an answer.
Make that permanent. To avoid reduxing the packet size for an single dropped packet,
Make that permanent. To avoid reduxing the packet size for a single dropped packet,
only do this when we get a truncated answer, or one larger than the safe size. */
if (server && (forward->flags & FREC_TEST_PKTSZ) &&
if (server && server->edns_pktsz > SAFE_PKTSZ && (forward->flags & FREC_TEST_PKTSZ) &&
((header->hb3 & HB3_TC) || n >= SAFE_PKTSZ))
server->edns_pktsz = SAFE_PKTSZ;
{
server->edns_pktsz = SAFE_PKTSZ;
server->pktsz_reduced = now;
prettyprint_addr(&server->addr, daemon->addrbuff);
my_syslog(LOG_WARNING, _("reducing DNS packet size for nameserver %s to %d"), daemon->addrbuff, SAFE_PKTSZ);
}
/* 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
@@ -859,7 +987,7 @@ void reply_query(int fd, int family, time_t now)
(RCODE(header) != REFUSED && RCODE(header) != SERVFAIL))
{
int check_rebind = 0, no_cache_dnssec = 0, cache_secure = 0, bogusanswer = 0;
if (option_bool(OPT_NO_REBIND))
check_rebind = !(forward->flags & FREC_NOREBIND);
@@ -899,7 +1027,13 @@ void reply_query(int fd, int family, time_t now)
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,
option_bool(OPT_DNSSEC_NO_SIGN) && (server->flags & SERV_DO_DNSSEC), NULL, NULL);
!option_bool(OPT_DNSSEC_IGN_NS) && (server->flags & SERV_DO_DNSSEC),
NULL, NULL);
#ifdef HAVE_DUMPFILE
if (status == STAT_BOGUS)
dump_packet((forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY)) ? DUMP_SEC_BOGUS : DUMP_BOGUS,
header, (size_t)n, &serveraddr, NULL);
#endif
}
/* Can't validate, as we're missing key data. Put this
@@ -924,7 +1058,7 @@ void reply_query(int fd, int family, time_t now)
status = STAT_ABANDONED;
else
{
int fd, type = SERV_DO_DNSSEC;
int querytype, fd, type = SERV_DO_DNSSEC;
struct frec *next = new->next;
char *domain;
@@ -942,7 +1076,7 @@ void reply_query(int fd, int family, time_t now)
while (1)
{
if (type == (start->flags & (SERV_TYPE | SERV_DO_DNSSEC)) &&
(type != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)) &&
((type & SERV_TYPE) != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)) &&
!(start->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP)))
{
new_server = start;
@@ -968,7 +1102,8 @@ void reply_query(int fd, int family, time_t now)
#ifdef HAVE_IPV6
new->rfd6 = NULL;
#endif
new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY);
new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY | FREC_HAS_EXTRADATA);
new->forwardall = 0;
new->dependent = forward; /* to find query awaiting new one. */
forward->blocking_query = new; /* for garbage cleaning */
@@ -976,15 +1111,26 @@ void reply_query(int fd, int family, time_t now)
if (status == STAT_NEED_KEY)
{
new->flags |= FREC_DNSKEY_QUERY;
nn = dnssec_generate_query(header, ((unsigned char *) header) + server->edns_pktsz,
daemon->keyname, forward->class, T_DNSKEY, &server->addr, server->edns_pktsz);
querytype = T_DNSKEY;
}
else
{
new->flags |= FREC_DS_QUERY;
nn = dnssec_generate_query(header,((unsigned char *) header) + server->edns_pktsz,
daemon->keyname, forward->class, T_DS, &server->addr, server->edns_pktsz);
querytype = T_DS;
}
nn = dnssec_generate_query(header,((unsigned char *) header) + server->edns_pktsz,
daemon->keyname, forward->class, querytype, server->edns_pktsz);
if (server->addr.sa.sa_family == AF_INET)
log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, daemon->keyname, (struct all_addr *)&(server->addr.in.sin_addr),
querystr("dnssec-query", querytype));
#ifdef HAVE_IPV6
else
log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, daemon->keyname, (struct all_addr *)&(server->addr.in6.sin6_addr),
querystr("dnssec-query", querytype));
#endif
if ((hash = hash_questions(header, nn, daemon->namebuff)))
memcpy(new->hash, hash, HASH_SIZE);
new->new_id = get_id();
@@ -1026,6 +1172,11 @@ void reply_query(int fd, int family, time_t now)
setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
}
#endif
#ifdef HAVE_DUMPFILE
dump_packet(DUMP_SEC_QUERY, (void *)header, (size_t)nn, NULL, &server->addr);
#endif
while (retry_send(sendto(fd, (char *)header, nn, 0,
&server->addr.sa,
sa_len(&server->addr))));
@@ -1069,7 +1220,7 @@ void reply_query(int fd, int family, time_t now)
if (status == STAT_BOGUS && extract_request(header, n, daemon->namebuff, NULL))
domain = daemon->namebuff;
log_query(F_KEYTAG | F_SECSTAT, domain, NULL, result);
log_query(F_SECSTAT, domain, NULL, result);
}
if (status == STAT_SECURE)
@@ -1080,8 +1231,8 @@ void reply_query(int fd, int family, time_t now)
bogusanswer = 1;
}
}
#endif
#endif
/* restore CD bit to the value in the query */
if (forward->flags & FREC_CHECKING_DISABLED)
header->hb4 |= HB4_CD;
@@ -1096,7 +1247,7 @@ void reply_query(int fd, int family, time_t now)
header->hb4 |= HB4_RA; /* recursion if available */
#ifdef HAVE_DNSSEC
/* We added an EDNSO header for the purpose of getting DNSSEC RRs, and set the value of the UDP payload size
greater than the no-EDNS0-implied 512 to have if space for the RRSIGS. If, having stripped them and the EDNS0
greater than the no-EDNS0-implied 512 to have space for the RRSIGS. If, having stripped them and the EDNS0
header, the answer is still bigger than 512, truncate it and mark it so. The client then retries with TCP. */
if (option_bool(OPT_DNSSEC_VALID) && (forward->flags & FREC_ADDED_PHEADER) && (nn > PACKETSZ))
{
@@ -1107,6 +1258,11 @@ void reply_query(int fd, int family, time_t now)
nn = resize_packet(header, nn, NULL, 0);
}
#endif
#ifdef HAVE_DUMPFILE
dump_packet(DUMP_REPLY, daemon->packet, (size_t)nn, NULL, &forward->source);
#endif
send_from(forward->fd, option_bool(OPT_NOWILD) || option_bool (OPT_CLEVERBIND), daemon->packet, nn,
&forward->source, &forward->dest, forward->iface);
}
@@ -1189,6 +1345,10 @@ void receive_query(struct listener *listen, time_t now)
(msg.msg_flags & MSG_TRUNC) ||
(header->hb3 & HB3_QR))
return;
/* Clear buffer beyond request to avoid risk of
information disclosure. */
memset(daemon->packet + n, 0, daemon->edns_pktsz - n);
source_addr.sa.sa_family = listen->family;
@@ -1356,7 +1516,11 @@ void receive_query(struct listener *listen, time_t now)
pass these in global variables - sorry. */
daemon->log_display_id = ++daemon->log_id;
daemon->log_source_addr = &source_addr;
#ifdef HAVE_DUMPFILE
dump_packet(DUMP_QUERY, daemon->packet, (size_t)n, &source_addr, NULL);
#endif
if (extract_request(header, (size_t)n, daemon->namebuff, &type))
{
#ifdef HAVE_AUTH
@@ -1409,6 +1573,8 @@ void receive_query(struct listener *listen, time_t now)
defaults to 512 */
if (udp_size > daemon->edns_pktsz)
udp_size = daemon->edns_pktsz;
else if (udp_size < PACKETSZ)
udp_size = PACKETSZ; /* Sanity check - can't reduce below default. RFC 6891 6.2.3 */
}
#ifdef HAVE_AUTH
@@ -1420,7 +1586,7 @@ void receive_query(struct listener *listen, time_t now)
{
send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND),
(char *)header, m, &source_addr, &dst_addr, if_index);
daemon->auth_answer++;
daemon->metrics[METRIC_DNS_AUTH_ANSWERED]++;
}
}
else
@@ -1438,13 +1604,13 @@ void receive_query(struct listener *listen, time_t now)
{
send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND),
(char *)header, m, &source_addr, &dst_addr, if_index);
daemon->local_answer++;
daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]++;
}
else if (forward_query(listen->fd, &source_addr, &dst_addr, if_index,
header, (size_t)n, now, NULL, ad_reqd, do_bit))
daemon->queries_forwarded++;
daemon->metrics[METRIC_DNS_QUERIES_FORWARDED]++;
else
daemon->local_answer++;
daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]++;
}
}
@@ -1477,7 +1643,8 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
new_status = dnssec_validate_ds(now, header, n, name, keyname, class);
else
new_status = dnssec_validate_reply(now, header, n, name, keyname, &class,
option_bool(OPT_DNSSEC_NO_SIGN) && (server->flags & SERV_DO_DNSSEC), NULL, NULL);
!option_bool(OPT_DNSSEC_IGN_NS) && (server->flags & SERV_DO_DNSSEC),
NULL, NULL);
if (new_status != STAT_NEED_DS && new_status != STAT_NEED_KEY)
break;
@@ -1497,9 +1664,9 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
new_status = STAT_ABANDONED;
break;
}
m = dnssec_generate_query(new_header, ((unsigned char *) new_header) + 65536, keyname, class,
new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS, &server->addr, server->edns_pktsz);
new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS, server->edns_pktsz);
*length = htons(m);
@@ -1532,30 +1699,30 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
(type == SERV_HAS_DOMAIN && !hostname_isequal(domain, server->domain)) ||
(server->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP)))
continue;
retry:
/* may need to make new connection. */
if (server->tcpfd == -1)
{
if ((server->tcpfd = socket(server->addr.sa.sa_family, SOCK_STREAM, 0)) == -1)
continue; /* No good, next server */
retry:
/* may need to make new connection. */
if (server->tcpfd == -1)
{
if ((server->tcpfd = socket(server->addr.sa.sa_family, SOCK_STREAM, 0)) == -1)
continue; /* No good, next server */
#ifdef HAVE_CONNTRACK
/* Copy connection mark of incoming query to outgoing connection. */
if (have_mark)
setsockopt(server->tcpfd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
/* Copy connection mark of incoming query to outgoing connection. */
if (have_mark)
setsockopt(server->tcpfd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
#endif
if (!local_bind(server->tcpfd, &server->source_addr, server->interface, 1) ||
connect(server->tcpfd, &server->addr.sa, sa_len(&server->addr)) == -1)
{
close(server->tcpfd);
server->tcpfd = -1;
continue; /* No good, next server */
}
server->flags &= ~SERV_GOT_TCP;
}
if (!local_bind(server->tcpfd, &server->source_addr, server->interface, 0, 1) ||
connect(server->tcpfd, &server->addr.sa, sa_len(&server->addr)) == -1)
{
close(server->tcpfd);
server->tcpfd = -1;
continue; /* No good, next server */
}
server->flags &= ~SERV_GOT_TCP;
}
if (!read_write(server->tcpfd, packet, m + sizeof(u16), 0) ||
!read_write(server->tcpfd, &c1, 1, 1) ||
@@ -1572,6 +1739,16 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
else
continue;
}
if (server->addr.sa.sa_family == AF_INET)
log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, keyname, (struct all_addr *)&(server->addr.in.sin_addr),
querystr("dnssec-query", new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS));
#ifdef HAVE_IPV6
else
log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, keyname, (struct all_addr *)&(server->addr.in6.sin6_addr),
querystr("dnssec-query", new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS));
#endif
server->flags |= SERV_GOT_TCP;
@@ -1689,6 +1866,10 @@ unsigned char *tcp_request(int confd, time_t now,
if (size < (int)sizeof(struct dns_header))
continue;
/* Clear buffer beyond request to avoid risk of
information disclosure. */
memset(payload + size, 0, 65536 - size);
query_count++;
@@ -1774,17 +1955,30 @@ unsigned char *tcp_request(int confd, time_t now,
struct all_addr *addrp = NULL;
int type = SERV_DO_DNSSEC;
char *domain = NULL;
size_t new_size = add_edns0_config(header, size, ((unsigned char *) header) + 65536, &peer_addr, now, &check_subnet);
unsigned char *oph = find_pseudoheader(header, size, NULL, NULL, NULL, NULL);
size = add_edns0_config(header, size, ((unsigned char *) header) + 65536, &peer_addr, now, &check_subnet);
if (size != new_size)
{
added_pheader = 1;
size = new_size;
}
if (gotname)
flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && (type & SERV_DO_DNSSEC))
{
size = add_do_bit(header, size, ((unsigned char *) header) + 65536);
/* 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
/* Check if we added a pheader on forwarding - may need to
strip it from the reply. */
if (!oph && find_pseudoheader(header, size, NULL, NULL, NULL, NULL))
added_pheader = 1;
type &= ~SERV_DO_DNSSEC;
if (type != 0 || option_bool(OPT_ORDER) || !daemon->last_server)
@@ -1838,7 +2032,7 @@ unsigned char *tcp_request(int confd, time_t now,
setsockopt(last_server->tcpfd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
#endif
if ((!local_bind(last_server->tcpfd, &last_server->source_addr, last_server->interface, 1) ||
if ((!local_bind(last_server->tcpfd, &last_server->source_addr, last_server->interface, 0, 1) ||
connect(last_server->tcpfd, &last_server->addr.sa, sa_len(&last_server->addr)) == -1))
{
close(last_server->tcpfd);
@@ -1849,24 +2043,6 @@ unsigned char *tcp_request(int confd, time_t now,
last_server->flags &= ~SERV_GOT_TCP;
}
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && (last_server->flags & SERV_DO_DNSSEC))
{
new_size = add_do_bit(header, size, ((unsigned char *) header) + 65536);
if (size != new_size)
{
added_pheader = 1;
size = new_size;
}
/* 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
*length = htons(size);
/* get query name again for logging - may have been overwritten */
@@ -1921,7 +2097,7 @@ unsigned char *tcp_request(int confd, time_t now,
if (status == STAT_BOGUS && extract_request(header, m, daemon->namebuff, NULL))
domain = daemon->namebuff;
log_query(F_KEYTAG | F_SECSTAT, domain, NULL, result);
log_query(F_SECSTAT, domain, NULL, result);
if (status == STAT_BOGUS)
{
@@ -1971,7 +2147,11 @@ unsigned char *tcp_request(int confd, time_t now,
/* In case of local answer or no connections made. */
if (m == 0)
m = setup_reply(header, (unsigned int)size, addrp, flags, daemon->local_ttl);
{
m = setup_reply(header, (unsigned int)size, addrp, flags, daemon->local_ttl);
if (have_pseudoheader)
m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0);
}
}
}

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2017 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2018 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,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include "dnsmasq.h"
#ifdef HAVE_SCRIPT
@@ -98,13 +97,14 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
return pipefd[1];
}
/* ignore SIGTERM, so that we can clean up when the main process gets hit
/* ignore SIGTERM and SIGINT, so that we can clean up when the main process gets hit
and SIGALRM so that we can use sleep() */
sigact.sa_handler = SIG_IGN;
sigact.sa_flags = 0;
sigemptyset(&sigact.sa_mask);
sigaction(SIGTERM, &sigact, NULL);
sigaction(SIGALRM, &sigact, NULL);
sigaction(SIGINT, &sigact, NULL);
if (!option_bool(OPT_DEBUG) && uid != 0)
{

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2017 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2018 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
@@ -20,7 +20,7 @@
#include <sys/inotify.h>
#include <sys/param.h> /* For MAXSYMLINKS */
/* the strategy is to set a inotify on the directories containing
/* the strategy is to set an inotify on the directories containing
resolv files, for any files in the directory which are close-write
or moved into the directory.
@@ -227,19 +227,21 @@ int inotify_check(time_t now)
for (p = inotify_buffer; rc - (p - inotify_buffer) >= (int)sizeof(struct inotify_event); p += sizeof(struct inotify_event) + in->len)
{
size_t namelen;
in = (struct inotify_event*)p;
for (res = daemon->resolv_files; res; res = res->next)
if (res->wd == in->wd && in->len != 0 && strcmp(res->file, in->name) == 0)
hit = 1;
/* ignore emacs backups and dotfiles */
if (in->len == 0 ||
in->name[in->len - 1] == '~' ||
(in->name[0] == '#' && in->name[in->len - 1] == '#') ||
if (in->len == 0 || (namelen = strlen(in->name)) == 0 ||
in->name[namelen - 1] == '~' ||
(in->name[0] == '#' && in->name[namelen - 1] == '#') ||
in->name[0] == '.')
continue;
for (res = daemon->resolv_files; res; res = res->next)
if (res->wd == in->wd && strcmp(res->file, in->name) == 0)
hit = 1;
for (ah = daemon->dynamic_dirs; ah; ah = ah->next)
if (ah->wd == in->wd)
{

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2017 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2018 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-2017 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2018 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
@@ -87,7 +87,7 @@ static int read_leases(time_t now, FILE *leasestream)
if ((lease = lease6_allocate(&addr.addr.addr6, lease_type)))
{
lease_set_iaid(lease, strtoul(s, NULL, 10));
domain = get_domain6((struct in6_addr *)lease->hwaddr);
domain = get_domain6(&lease->addr6);
}
}
#endif
@@ -229,7 +229,7 @@ void lease_update_from_configs(void)
else if ((name = host_from_dns(lease->addr)))
lease_set_hostname(lease, name, 1, get_domain(lease->addr), NULL); /* updates auth flag only */
}
static void ourprintf(int *errp, char *format, ...)
{
va_list ap;
@@ -555,7 +555,9 @@ void lease_prune(struct dhcp_lease *target, time_t now)
file_dirty = 1;
if (lease->hostname)
dns_dirty = 1;
daemon->metrics[lease->addr.s_addr ? METRIC_LEASES_PRUNED_4 : METRIC_LEASES_PRUNED_6]++;
*up = lease->next; /* unlink */
/* Put on old_leases list 'till we
@@ -773,7 +775,10 @@ struct dhcp_lease *lease4_allocate(struct in_addr addr)
{
struct dhcp_lease *lease = lease_allocate();
if (lease)
lease->addr = addr;
{
lease->addr = addr;
daemon->metrics[METRIC_LEASES_ALLOCATED_4]++;
}
return lease;
}
@@ -788,6 +793,8 @@ struct dhcp_lease *lease6_allocate(struct in6_addr *addrp, int lease_type)
lease->addr6 = *addrp;
lease->flags |= lease_type;
lease->iaid = 0;
daemon->metrics[METRIC_LEASES_ALLOCATED_6]++;
}
return lease;

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2017 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2018 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
@@ -232,7 +232,7 @@ static void log_write(void)
logaddr.sun_len = sizeof(logaddr) - sizeof(logaddr.sun_path) + strlen(_PATH_LOG) + 1;
#endif
logaddr.sun_family = AF_UNIX;
strncpy(logaddr.sun_path, _PATH_LOG, sizeof(logaddr.sun_path));
safe_strncpy(logaddr.sun_path, _PATH_LOG, sizeof(logaddr.sun_path));
/* Got connection back? try again. */
if (connect(log_fd, (struct sockaddr *)&logaddr, sizeof(logaddr)) != -1)

View File

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

44
src/metrics.c Normal file
View File

@@ -0,0 +1,44 @@
/* dnsmasq is Copyright (c) 2000-2018 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"
const char * metric_names[] = {
"dns_cache_inserted",
"dns_cache_live_freed",
"dns_queries_forwarded",
"dns_auth_answered",
"dns_local_answered",
"bootp",
"pxe",
"dhcp_ack",
"dhcp_decline",
"dhcp_discover",
"dhcp_inform",
"dhcp_nak",
"dhcp_offer",
"dhcp_release",
"dhcp_request",
"noanswer",
"leases_allocated_4",
"leases_pruned_4",
"leases_allocated_6",
"leases_pruned_6",
};
const char* get_metric_name(int i) {
return metric_names[i];
}

43
src/metrics.h Normal file
View File

@@ -0,0 +1,43 @@
/* dnsmasq is Copyright (c) 2000-2018 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/>.
*/
/* If you modify this list, please keep the labels in metrics.c in sync. */
enum {
METRIC_DNS_CACHE_INSERTED,
METRIC_DNS_CACHE_LIVE_FREED,
METRIC_DNS_QUERIES_FORWARDED,
METRIC_DNS_AUTH_ANSWERED,
METRIC_DNS_LOCAL_ANSWERED,
METRIC_BOOTP,
METRIC_PXE,
METRIC_DHCPACK,
METRIC_DHCPDECLINE,
METRIC_DHCPDISCOVER,
METRIC_DHCPINFORM,
METRIC_DHCPNAK,
METRIC_DHCPOFFER,
METRIC_DHCPRELEASE,
METRIC_DHCPREQUEST,
METRIC_NOANSWER,
METRIC_LEASES_ALLOCATED_4,
METRIC_LEASES_PRUNED_4,
METRIC_LEASES_ALLOCATED_6,
METRIC_LEASES_PRUNED_6,
__METRIC_MAX,
};
const char* get_metric_name(int);

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2017 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2018 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
@@ -149,10 +149,10 @@ int iface_enumerate(int family, void *parm, int (*callback)())
struct rtgenmsg g;
} req;
memset(&req, 0, sizeof(req));
memset(&addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
addr.nl_pad = 0;
addr.nl_groups = 0;
addr.nl_pid = 0; /* address to kernel */
again:
if (family == AF_UNSPEC)

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2017 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2018 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
@@ -29,7 +29,7 @@ int indextoname(int fd, int index, char *name)
if (ioctl(fd, SIOCGIFNAME, &ifr) == -1)
return 0;
strncpy(name, ifr.ifr_name, IF_NAMESIZE);
safe_strncpy(name, ifr.ifr_name, IF_NAMESIZE);
return 1;
}
@@ -82,12 +82,12 @@ int indextoname(int fd, int index, char *name)
for (i = lifc.lifc_len / sizeof(struct lifreq); i; i--, lifrp++)
{
struct lifreq lifr;
strncpy(lifr.lifr_name, lifrp->lifr_name, IF_NAMESIZE);
safe_strncpy(lifr.lifr_name, lifrp->lifr_name, IF_NAMESIZE);
if (ioctl(fd, SIOCGLIFINDEX, &lifr) < 0)
return 0;
if (lifr.lifr_index == index) {
strncpy(name, lifr.lifr_name, IF_NAMESIZE);
safe_strncpy(name, lifr.lifr_name, IF_NAMESIZE);
return 1;
}
}
@@ -188,7 +188,7 @@ int loopback_exception(int fd, int family, struct all_addr *addr, char *name)
struct ifreq ifr;
struct irec *iface;
strncpy(ifr.ifr_name, name, IF_NAMESIZE);
safe_strncpy(ifr.ifr_name, name, IF_NAMESIZE);
if (ioctl(fd, SIOCGIFFLAGS, &ifr) != -1 &&
ifr.ifr_flags & IFF_LOOPBACK)
{
@@ -1149,10 +1149,7 @@ int random_sock(int family)
if (fix_fd(fd))
while(tries--)
{
unsigned short port = rand16();
if (daemon->min_port != 0 || daemon->max_port != MAX_PORT)
port = htons(daemon->min_port + (port % ((unsigned short)ports_avail)));
unsigned short port = htons(daemon->min_port + (rand16() % ((unsigned short)ports_avail)));
if (family == AF_INET)
{
@@ -1187,7 +1184,7 @@ int random_sock(int family)
}
int local_bind(int fd, union mysockaddr *addr, char *intname, int is_tcp)
int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifindex, int is_tcp)
{
union mysockaddr addr_copy = *addr;
@@ -1204,7 +1201,25 @@ int local_bind(int fd, union mysockaddr *addr, char *intname, int is_tcp)
if (bind(fd, (struct sockaddr *)&addr_copy, sa_len(&addr_copy)) == -1)
return 0;
if (!is_tcp && ifindex > 0)
{
#if defined(IP_UNICAST_IF)
if (addr_copy.sa.sa_family == AF_INET)
{
uint32_t ifindex_opt = htonl(ifindex);
return setsockopt(fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex_opt, sizeof(ifindex_opt)) == 0;
}
#endif
#if defined(HAVE_IPV6) && defined (IPV6_UNICAST_IF)
if (addr_copy.sa.sa_family == AF_INET6)
{
uint32_t ifindex_opt = htonl(ifindex);
return setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex_opt, sizeof(ifindex_opt)) == 0;
}
#endif
}
#if defined(SO_BINDTODEVICE)
if (intname[0] != 0 &&
setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, intname, IF_NAMESIZE) == -1)
@@ -1219,7 +1234,8 @@ static struct serverfd *allocate_sfd(union mysockaddr *addr, char *intname)
struct serverfd *sfd;
unsigned int ifindex = 0;
int errsave;
int opt = 1;
/* when using random ports, servers which would otherwise use
the INADDR_ANY/port0 socket have sfd set to NULL */
if (!daemon->osport && intname[0] == 0)
@@ -1259,20 +1275,22 @@ static struct serverfd *allocate_sfd(union mysockaddr *addr, char *intname)
free(sfd);
return NULL;
}
if (!local_bind(sfd->fd, addr, intname, 0) || !fix_fd(sfd->fd))
if ((addr->sa.sa_family == AF_INET6 && setsockopt(sfd->fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)) == -1) ||
!local_bind(sfd->fd, addr, intname, ifindex, 0) || !fix_fd(sfd->fd))
{
errsave = errno; /* save error from bind. */
errsave = errno; /* save error from bind/setsockopt. */
close(sfd->fd);
free(sfd);
errno = errsave;
return NULL;
}
strcpy(sfd->interface, intname);
safe_strncpy(sfd->interface, intname, sizeof(sfd->interface));
sfd->source_addr = *addr;
sfd->next = daemon->sfds;
sfd->ifindex = ifindex;
sfd->preallocated = 0;
daemon->sfds = sfd;
return sfd;
@@ -1283,6 +1301,7 @@ static struct serverfd *allocate_sfd(union mysockaddr *addr, char *intname)
void pre_allocate_sfds(void)
{
struct server *srv;
struct serverfd *sfd;
if (daemon->query_port != 0)
{
@@ -1294,7 +1313,8 @@ void pre_allocate_sfds(void)
#ifdef HAVE_SOCKADDR_SA_LEN
addr.in.sin_len = sizeof(struct sockaddr_in);
#endif
allocate_sfd(&addr, "");
if ((sfd = allocate_sfd(&addr, "")))
sfd->preallocated = 1;
#ifdef HAVE_IPV6
memset(&addr, 0, sizeof(addr));
addr.in6.sin6_family = AF_INET6;
@@ -1303,7 +1323,8 @@ void pre_allocate_sfds(void)
#ifdef HAVE_SOCKADDR_SA_LEN
addr.in6.sin6_len = sizeof(struct sockaddr_in6);
#endif
allocate_sfd(&addr, "");
if ((sfd = allocate_sfd(&addr, "")))
sfd->preallocated = 1;
#endif
}
@@ -1437,7 +1458,7 @@ void add_update_server(int flags,
serv->flags |= SERV_HAS_DOMAIN;
if (interface)
strcpy(serv->interface, interface);
safe_strncpy(serv->interface, interface, sizeof(serv->interface));
if (addr)
serv->addr = *addr;
if (source_addr)
@@ -1456,16 +1477,10 @@ void check_servers(void)
/* interface may be new since startup */
if (!option_bool(OPT_NOWILD))
enumerate_interfaces(0);
for (sfd = daemon->sfds; sfd; sfd = sfd->next)
sfd->used = 0;
#ifdef HAVE_DNSSEC
/* Disable DNSSEC validation when using server=/domain/.... servers
unless there's a configured trust anchor. */
for (serv = daemon->servers; serv; serv = serv->next)
serv->flags |= SERV_DO_DNSSEC;
#endif
/* don't garbage collect pre-allocated sfds. */
for (sfd = daemon->sfds; sfd; sfd = sfd->next)
sfd->used = sfd->preallocated;
for (count = 0, serv = daemon->servers; serv; serv = serv->next)
{
@@ -1478,6 +1493,11 @@ void check_servers(void)
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID))
{
if (!(serv->flags & SERV_FOR_NODOTS))
serv->flags |= SERV_DO_DNSSEC;
/* Disable DNSSEC validation when using server=/domain/.... servers
unless there's a configured trust anchor. */
if (serv->flags & SERV_HAS_DOMAIN)
{
struct ds_config *ds;
@@ -1494,8 +1514,6 @@ void check_servers(void)
if (!ds)
serv->flags &= ~SERV_DO_DNSSEC;
}
else if (serv->flags & SERV_FOR_NODOTS)
serv->flags &= ~SERV_DO_DNSSEC;
}
#endif

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2017 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2018 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
@@ -160,6 +160,12 @@ struct myoption {
#define LOPT_DHCPTTL 348
#define LOPT_TFTP_MTU 349
#define LOPT_REPLY_DELAY 350
#define LOPT_RAPID_COMMIT 351
#define LOPT_DUMPFILE 352
#define LOPT_DUMPMASK 353
#define LOPT_UBUS 354
#define LOPT_NAME_MATCH 355
#define LOPT_CAA 356
#ifdef HAVE_GETOPT_LONG
static const struct option opts[] =
@@ -228,8 +234,10 @@ static const struct myoption opts[] =
{ "srv-host", 1, 0, 'W' },
{ "localise-queries", 0, 0, 'y' },
{ "txt-record", 1, 0, 'Y' },
{ "caa-record", 1, 0 , LOPT_CAA },
{ "dns-rr", 1, 0, LOPT_RR },
{ "enable-dbus", 2, 0, '1' },
{ "enable-ubus", 0, 0, LOPT_UBUS },
{ "bootp-dynamic", 2, 0, '3' },
{ "dhcp-mac", 1, 0, '4' },
{ "no-ping", 0, 0, '5' },
@@ -268,7 +276,8 @@ static const struct myoption opts[] =
{ "stop-dns-rebind", 0, 0, LOPT_REBIND },
{ "rebind-domain-ok", 1, 0, LOPT_NO_REBIND },
{ "all-servers", 0, 0, LOPT_NOLAST },
{ "dhcp-match", 1, 0, LOPT_MATCH },
{ "dhcp-match", 1, 0, LOPT_MATCH },
{ "dhcp-name-match", 1, 0, LOPT_NAME_MATCH },
{ "dhcp-broadcast", 2, 0, LOPT_BROADCAST },
{ "neg-ttl", 1, 0, LOPT_NEGTTL },
{ "max-ttl", 1, 0, LOPT_MAXTTL },
@@ -310,7 +319,7 @@ static const struct myoption opts[] =
{ "dnssec", 0, 0, LOPT_SEC_VALID },
{ "trust-anchor", 1, 0, LOPT_TRUST_ANCHOR },
{ "dnssec-debug", 0, 0, LOPT_DNSSEC_DEBUG },
{ "dnssec-check-unsigned", 0, 0, LOPT_DNSSEC_CHECK },
{ "dnssec-check-unsigned", 2, 0, LOPT_DNSSEC_CHECK },
{ "dnssec-no-timecheck", 0, 0, LOPT_DNSSEC_TIME },
{ "dnssec-timestamp", 1, 0, LOPT_DNSSEC_STAMP },
#ifdef OPTION6_PREFIX_CLASS
@@ -325,6 +334,9 @@ static const struct myoption opts[] =
{ "script-arp", 0, 0, LOPT_SCRIPT_ARP },
{ "dhcp-ttl", 1, 0 , LOPT_DHCPTTL },
{ "dhcp-reply-delay", 1, 0, LOPT_REPLY_DELAY },
{ "dhcp-rapid-commit", 0, 0, LOPT_RAPID_COMMIT },
{ "dumpfile", 1, 0, LOPT_DUMPFILE },
{ "dumpmask", 1, 0, LOPT_DUMPMASK },
{ NULL, 0, 0, 0 }
};
@@ -414,6 +426,7 @@ static struct {
{ 'z', OPT_NOWILD, NULL, gettext_noop("Bind only to interfaces in use."), NULL },
{ 'Z', OPT_ETHERS, NULL, gettext_noop("Read DHCP static host information from %s."), ETHERSFILE },
{ '1', ARG_ONE, "[=<busname>]", gettext_noop("Enable the DBus interface for setting upstream servers, etc."), NULL },
{ LOPT_UBUS, OPT_UBUS, NULL, gettext_noop("Enable the UBus interface."), NULL },
{ '2', ARG_DUP, "<interface>", gettext_noop("Do not provide DHCP on this interface, only provide DNS."), NULL },
{ '3', ARG_DUP, "[=tag:<tag>]...", gettext_noop("Enable dynamic address allocation for bootp."), NULL },
{ '4', ARG_DUP, "set:<tag>,<mac address>", gettext_noop("Map MAC address (with wildcards) to option set."), NULL },
@@ -447,6 +460,7 @@ static struct {
{ LOPT_NO_REBIND, ARG_DUP, "/<domain>/", gettext_noop("Inhibit DNS-rebind protection on this domain."), NULL },
{ LOPT_NOLAST, OPT_ALL_SERVERS, NULL, gettext_noop("Always perform DNS queries to all servers."), NULL },
{ LOPT_MATCH, ARG_DUP, "set:<tag>,<optspec>", gettext_noop("Set tag if client includes matching option in request."), NULL },
{ LOPT_NAME_MATCH, ARG_DUP, "set:<tag>,<string>[*]", gettext_noop("Set tag if client provides given name."), NULL },
{ LOPT_ALTPORT, ARG_ONE, "[=<ports>]", gettext_noop("Use alternative ports for DHCP."), NULL },
{ LOPT_NAPTR, ARG_DUP, "<name>,<naptr>", gettext_noop("Specify NAPTR DNS record."), NULL },
{ LOPT_MINPORT, ARG_ONE, "<port>", gettext_noop("Specify lowest port available for DNS query transmission."), NULL },
@@ -469,6 +483,7 @@ static struct {
{ LOPT_RA, OPT_RA, NULL, gettext_noop("Send router-advertisements for interfaces doing DHCPv6"), NULL },
{ LOPT_DUID, ARG_ONE, "<enterprise>,<duid>", gettext_noop("Specify DUID_EN-type DHCPv6 server DUID"), NULL },
{ LOPT_HOST_REC, ARG_DUP, "<name>,<address>[,<ttl>]", gettext_noop("Specify host (A/AAAA and PTR) records"), NULL },
{ LOPT_CAA, ARG_DUP, "<name>,<flags>,<tag>,<value>", gettext_noop("Specify certification authority authorization record"), NULL },
{ LOPT_RR, ARG_DUP, "<name>,<RR-number>,[<data>]", gettext_noop("Specify arbitrary DNS resource record"), NULL },
{ LOPT_CLVERBIND, OPT_CLEVERBIND, NULL, gettext_noop("Bind to interfaces in use - check for new interfaces"), NULL },
{ LOPT_AUTHSERV, ARG_ONE, "<NS>,<interface>", gettext_noop("Export local names to global DNS"), NULL },
@@ -482,7 +497,7 @@ static struct {
{ 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 },
{ LOPT_DNSSEC_CHECK, OPT_DNSSEC_NO_SIGN, NULL, gettext_noop("Ensure answers without DNSSEC are in unsigned zones."), NULL },
{ LOPT_DNSSEC_CHECK, ARG_DUP, NULL, gettext_noop("Ensure answers without DNSSEC are in unsigned zones."), NULL },
{ LOPT_DNSSEC_TIME, OPT_DNSSEC_TIME, NULL, gettext_noop("Don't check DNSSEC signature timestamps until first cache-reload"), NULL },
{ LOPT_DNSSEC_STAMP, ARG_ONE, "<path>", gettext_noop("Timestamp file to verify system clock for DNSSEC"), NULL },
#ifdef OPTION6_PREFIX_CLASS
@@ -497,6 +512,9 @@ static struct {
{ LOPT_IGNORE_ADDR, ARG_DUP, "<ipaddr>", gettext_noop("Ignore DNS responses containing ipaddr."), NULL },
{ LOPT_DHCPTTL, ARG_ONE, "<ttl>", gettext_noop("Set TTL in DNS responses with DHCP-derived addresses."), NULL },
{ LOPT_REPLY_DELAY, ARG_ONE, "<integer>", gettext_noop("Delay DHCP replies for at least number of seconds."), NULL },
{ LOPT_RAPID_COMMIT, OPT_RAPID_COMMIT, NULL, gettext_noop("Enables DHCPv4 Rapid Commit option."), NULL },
{ LOPT_DUMPFILE, ARG_ONE, "<path>", gettext_noop("Path to debug packet dump file"), NULL },
{ LOPT_DUMPMASK, ARG_ONE, "<hex>", gettext_noop("Mask which packets to dump"), NULL },
{ 0, 0, NULL, NULL, NULL }
};
@@ -792,7 +810,7 @@ char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_a
if (interface_opt)
{
#if defined(SO_BINDTODEVICE)
strncpy(interface, interface_opt, IF_NAMESIZE - 1);
safe_strncpy(interface, interface_opt, IF_NAMESIZE);
#else
return _("interface binding not supported");
#endif
@@ -821,7 +839,7 @@ char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_a
return _("interface can only be specified once");
source_addr->in.sin_addr.s_addr = INADDR_ANY;
strncpy(interface, source, IF_NAMESIZE - 1);
safe_strncpy(interface, source, IF_NAMESIZE);
#else
return _("interface binding not supported");
#endif
@@ -856,7 +874,7 @@ char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_a
return _("interface can only be specified once");
source_addr->in6.sin6_addr = in6addr_any;
strncpy(interface, source, IF_NAMESIZE - 1);
safe_strncpy(interface, source, IF_NAMESIZE);
#else
return _("interface binding not supported");
#endif
@@ -882,7 +900,7 @@ static struct server *add_rev4(struct in_addr addr, int msize)
switch (msize)
{
case 32:
p += sprintf(p, "%d.", a & 0xff);
p += sprintf(p, "%u.", a & 0xff);
/* fall through */
case 24:
p += sprintf(p, "%d.", (a >> 8) & 0xff);
@@ -1163,7 +1181,7 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags)
case 'd':
case 'D':
fac *= 24;
/* fall though */
/* fall through */
case 'h':
case 'H':
fac *= 60;
@@ -1415,7 +1433,7 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags)
}
p = newp;
end = do_rfc1035_name(p + len, dom);
end = do_rfc1035_name(p + len, dom, NULL);
*end++ = 0;
len = end - p;
free(dom);
@@ -1737,7 +1755,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
#endif
}
break;
case 'x': /* --pid-file */
daemon->runfile = opt_string_alloc(arg);
break;
@@ -1808,6 +1826,14 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
ret_err(_("bad MX target"));
break;
case LOPT_DUMPFILE: /* --dumpfile */
daemon->dump_file = opt_string_alloc(arg);
break;
case LOPT_DUMPMASK: /* --dumpmask */
daemon->dump_mask = strtol(arg, NULL, 0);
break;
#ifdef HAVE_DHCP
case 'l': /* --dhcp-leasefile */
daemon->lease_file = opt_string_alloc(arg);
@@ -1875,47 +1901,43 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
break;
}
#ifdef HAVE_AUTH
case LOPT_AUTHSERV: /* --auth-server */
if (!(comma = split(arg)))
ret_err(gen_err);
comma = split(arg);
daemon->authserver = opt_string_alloc(arg);
arg = comma;
do {
struct iname *new = opt_malloc(sizeof(struct iname));
comma = split(arg);
new->name = NULL;
unhide_metas(arg);
if (inet_pton(AF_INET, arg, &new->addr.in.sin_addr) > 0)
new->addr.sa.sa_family = AF_INET;
while ((arg = comma))
{
struct iname *new = opt_malloc(sizeof(struct iname));
comma = split(arg);
new->name = NULL;
unhide_metas(arg);
if (inet_pton(AF_INET, arg, &new->addr.in.sin_addr) > 0)
new->addr.sa.sa_family = AF_INET;
#ifdef HAVE_IPV6
else if (inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
new->addr.sa.sa_family = AF_INET6;
else if (inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
new->addr.sa.sa_family = AF_INET6;
#endif
else
{
char *fam = split_chr(arg, '/');
new->name = opt_string_alloc(arg);
new->addr.sa.sa_family = 0;
if (fam)
{
if (strcmp(fam, "4") == 0)
new->addr.sa.sa_family = AF_INET;
else
{
char *fam = split_chr(arg, '/');
new->name = opt_string_alloc(arg);
new->addr.sa.sa_family = 0;
if (fam)
{
if (strcmp(fam, "4") == 0)
new->addr.sa.sa_family = AF_INET;
#ifdef HAVE_IPV6
else if (strcmp(fam, "6") == 0)
new->addr.sa.sa_family = AF_INET6;
else if (strcmp(fam, "6") == 0)
new->addr.sa.sa_family = AF_INET6;
#endif
else
ret_err(gen_err);
}
}
new->next = daemon->authinterface;
daemon->authinterface = new;
arg = comma;
} while (arg);
else
ret_err(gen_err);
}
}
new->next = daemon->authinterface;
daemon->authinterface = new;
};
break;
@@ -2051,7 +2073,6 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
}
break;
#endif
case 's': /* --domain */
case LOPT_SYNTH: /* --synth-domain */
@@ -2071,7 +2092,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
char *netpart;
new->prefix = NULL;
new->indexed = 0;
unhide_metas(comma);
if ((netpart = split_chr(comma, '/')))
{
@@ -2208,8 +2230,16 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
}
else
{
char *star;
new->next = daemon->synth_domains;
daemon->synth_domains = new;
if (new->prefix &&
(star = strrchr(new->prefix, '*'))
&& *(star+1) == 0)
{
*star = 0;
new->indexed = 1;
}
}
}
else if (option == 's')
@@ -2393,7 +2423,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
if (strcmp(arg, "#") == 0)
domain = "";
else if (strlen (arg) != 0 && !(domain = canonicalise_opt(arg)))
option = '?';
ret_err(gen_err);
serv = opt_malloc(sizeof(struct server));
memset(serv, 0, sizeof(struct server));
serv->next = newlist;
@@ -2435,11 +2465,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
}
else if (strcmp(arg, "#") == 0)
{
newlist->flags |= SERV_USE_RESOLV; /* treat in ordinary way */
if (newlist->flags & SERV_LITERAL_ADDRESS)
ret_err(gen_err);
}
newlist->flags |= SERV_USE_RESOLV; /* treat in ordinary way */
else
{
char *err = parse_server(arg, &newlist->addr, &newlist->source_addr, newlist->interface, &newlist->flags);
@@ -2525,7 +2551,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
if (strcmp(arg, "#") == 0 || !*arg)
domain = "";
else if (strlen(arg) != 0 && !(domain = canonicalise_opt(arg)))
option = '?';
ret_err(gen_err);
ipsets->next = opt_malloc(sizeof(struct ipsets));
ipsets = ipsets->next;
memset(ipsets, 0, sizeof(struct ipsets));
@@ -2540,13 +2566,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
memset(ipsets, 0, sizeof(struct ipsets));
ipsets->domain = "";
}
if (!arg || !*arg)
{
option = '?';
break;
}
size = 2;
for (end = arg; *end; ++end)
ret_err(gen_err);
for (size = 2, end = arg; *end; ++end)
if (*end == ',')
++size;
@@ -2579,8 +2603,6 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
if (size < 0)
size = 0;
else if (size > 10000)
size = 10000;
daemon->cachesize = size;
}
@@ -2733,15 +2755,24 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
case LOPT_BRIDGE: /* --bridge-interface */
{
struct dhcp_bridge *new = opt_malloc(sizeof(struct dhcp_bridge));
struct dhcp_bridge *new;
if (!(comma = split(arg)) || strlen(arg) > IF_NAMESIZE - 1 )
ret_err(_("bad bridge-interface"));
strcpy(new->iface, arg);
new->alias = NULL;
new->next = daemon->bridges;
daemon->bridges = new;
for (new = daemon->bridges; new; new = new->next)
if (strcmp(new->iface, arg) == 0)
break;
if (!new)
{
new = opt_malloc(sizeof(struct dhcp_bridge));
strcpy(new->iface, arg);
new->alias = NULL;
new->next = daemon->bridges;
daemon->bridges = new;
}
do {
arg = comma;
comma = split(arg);
@@ -2767,12 +2798,6 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
memset (new, 0, sizeof(*new));
new->lease_time = DEFLEASE;
if (!arg)
{
option = '?';
break;
}
while(1)
{
for (cp = arg; *cp; cp++)
@@ -2961,7 +2986,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
case 'd':
case 'D':
fac *= 24;
/* fall though */
/* fall through */
case 'h':
case 'H':
fac *= 60;
@@ -3261,7 +3286,33 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
option == LOPT_FORCE ? DHOPT_FORCE :
(option == LOPT_MATCH ? DHOPT_MATCH :
(option == LOPT_OPTS ? DHOPT_BANK : 0)));
case LOPT_NAME_MATCH: /* --dhcp-name-match */
{
struct dhcp_match_name *new = opt_malloc(sizeof(struct dhcp_match_name));
struct dhcp_netid *id = opt_malloc(sizeof(struct dhcp_netid));
ssize_t len;
if (!(comma = split(arg)) || (len = strlen(comma)) == 0)
ret_err(gen_err);
new->wildcard = 0;
new->netid = id;
id->net = opt_string_alloc(set_prefix(arg));
if (comma[len-1] == '*')
{
comma[len-1] = 0;
new->wildcard = 1;
}
new->name = opt_string_alloc(comma);
new->next = daemon->dhcp_name_match;
daemon->dhcp_name_match = new;
break;
}
case 'M': /* --dhcp-boot */
{
struct dhcp_netid *id = NULL;
@@ -3774,11 +3825,9 @@ err:
if ((k < 2) ||
(!(inet_pton(AF_INET, a[0], &new->in) > 0)) ||
(!(inet_pton(AF_INET, a[1], &new->out) > 0)))
option = '?';
if (k == 3 && !inet_pton(AF_INET, a[2], &new->mask))
option = '?';
(!(inet_pton(AF_INET, a[1], &new->out) > 0)) ||
(k == 3 && !inet_pton(AF_INET, a[2], &new->mask)))
ret_err(_("missing address in alias"));
if (dash &&
(!(inet_pton(AF_INET, dash, &new->end) > 0) ||
@@ -3847,6 +3896,7 @@ err:
while (arg != last)
{
int arglen = strlen(arg);
alias = canonicalise_opt(arg);
if (!alias || !target)
@@ -3862,7 +3912,7 @@ err:
new->target = target;
new->ttl = ttl;
arg += strlen(arg)+1;
for (arg += arglen+1; *arg && isspace(*arg); arg++);
}
break;
@@ -3949,7 +3999,7 @@ err:
if (data)
{
new->txt=opt_malloc(len);
new->txt = opt_malloc(len);
new->len = len;
memcpy(new->txt, data, len);
}
@@ -3957,6 +4007,37 @@ err:
break;
}
case LOPT_CAA: /* --caa-record */
{
struct txt_record *new;
char *tag, *value;
int flags;
comma = split(arg);
tag = split(comma);
value = split(tag);
new = opt_malloc(sizeof(struct txt_record));
new->next = daemon->rr;
daemon->rr = new;
if (!atoi_check(comma, &flags) || !tag || !value || !(new->name = canonicalise_opt(arg)))
ret_err(_("bad CAA record"));
unhide_metas(tag);
unhide_metas(value);
new->len = strlen(tag) + strlen(value) + 2;
new->txt = opt_malloc(new->len);
new->txt[0] = flags;
new->txt[1] = strlen(tag);
memcpy(&new->txt[2], tag, strlen(tag));
memcpy(&new->txt[2 + strlen(tag)], value, strlen(value));
new->class = T_CAA;
break;
}
case 'Y': /* --txt-record */
{
struct txt_record *new;
@@ -4115,11 +4196,21 @@ err:
}
#ifdef HAVE_DNSSEC
case LOPT_DNSSEC_STAMP:
case LOPT_DNSSEC_STAMP: /* --dnssec-timestamp */
daemon->timestamp_file = opt_string_alloc(arg);
break;
case LOPT_TRUST_ANCHOR:
case LOPT_DNSSEC_CHECK: /* --dnssec-check-unsigned */
if (arg)
{
if (strcmp(arg, "no") == 0)
set_option_bool(OPT_DNSSEC_IGN_NS);
else
ret_err(_("bad value for dnssec-check-unsigned"));
}
break;
case LOPT_TRUST_ANCHOR: /* --trust-anchor */
{
struct ds_config *new = opt_malloc(sizeof(struct ds_config));
char *cp, *cp1, *keyhex, *digest, *algo = NULL;
@@ -4304,7 +4395,7 @@ static void read_file(char *file, FILE *f, int hard_opt)
if (errmess)
strcpy(daemon->namebuff, errmess);
if (errmess || !one_opt(option, arg, buff, _("error"), 0, hard_opt == LOPT_REV_SERV))
if (errmess || !one_opt(option, arg, daemon->namebuff, _("error"), 0, hard_opt == LOPT_REV_SERV))
{
sprintf(daemon->namebuff + strlen(daemon->namebuff), _(" at line %d of %s"), lineno, file);
if (hard_opt != 0)
@@ -4318,7 +4409,7 @@ static void read_file(char *file, FILE *f, int hard_opt)
fclose(f);
}
#ifdef HAVE_DHCP
#if defined(HAVE_DHCP) && defined(HAVE_INOTIFY)
int option_read_dynfile(char *file, int flags)
{
my_syslog(MS_DHCP | LOG_INFO, _("read %s"), file);
@@ -4519,86 +4610,99 @@ void read_servers_file(void)
#ifdef HAVE_DHCP
static void clear_dynamic_conf(void)
{
struct dhcp_config *configs, *cp, **up;
/* remove existing... */
for (up = &daemon->dhcp_conf, configs = daemon->dhcp_conf; configs; configs = cp)
{
cp = configs->next;
if (configs->flags & CONFIG_BANK)
{
struct hwaddr_config *mac, *tmp;
struct dhcp_netid_list *list, *tmplist;
for (mac = configs->hwaddr; mac; mac = tmp)
{
tmp = mac->next;
free(mac);
}
if (configs->flags & CONFIG_CLID)
free(configs->clid);
for (list = configs->netid; list; list = tmplist)
{
free(list->list);
tmplist = list->next;
free(list);
}
if (configs->flags & CONFIG_NAME)
free(configs->hostname);
*up = configs->next;
free(configs);
}
else
up = &configs->next;
}
}
static void clear_dynamic_opt(void)
{
struct dhcp_opt *opts, *cp, **up;
struct dhcp_netid *id, *next;
for (up = &daemon->dhcp_opts, opts = daemon->dhcp_opts; opts; opts = cp)
{
cp = opts->next;
if (opts->flags & DHOPT_BANK)
{
if ((opts->flags & DHOPT_VENDOR))
free(opts->u.vendor_class);
free(opts->val);
for (id = opts->netid; id; id = next)
{
next = id->next;
free(id->net);
free(id);
}
*up = opts->next;
free(opts);
}
else
up = &opts->next;
}
}
void reread_dhcp(void)
{
struct hostsfile *hf;
struct hostsfile *hf;
if (daemon->dhcp_hosts_file)
/* Do these even if there is no daemon->dhcp_hosts_file or
daemon->dhcp_opts_file since entries may have been created by the
inotify dynamic file reading system. */
clear_dynamic_conf();
clear_dynamic_opt();
if (daemon->dhcp_hosts_file)
{
struct dhcp_config *configs, *cp, **up;
/* remove existing... */
for (up = &daemon->dhcp_conf, configs = daemon->dhcp_conf; configs; configs = cp)
{
cp = configs->next;
if (configs->flags & CONFIG_BANK)
{
struct hwaddr_config *mac, *tmp;
struct dhcp_netid_list *list, *tmplist;
for (mac = configs->hwaddr; mac; mac = tmp)
{
tmp = mac->next;
free(mac);
}
if (configs->flags & CONFIG_CLID)
free(configs->clid);
for (list = configs->netid; list; list = tmplist)
{
free(list->list);
tmplist = list->next;
free(list);
}
if (configs->flags & CONFIG_NAME)
free(configs->hostname);
*up = configs->next;
free(configs);
}
else
up = &configs->next;
}
daemon->dhcp_hosts_file = expand_filelist(daemon->dhcp_hosts_file);
for (hf = daemon->dhcp_hosts_file; hf; hf = hf->next)
if (!(hf->flags & AH_INACTIVE))
{
if (one_file(hf->fname, LOPT_BANK))
my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
}
if (!(hf->flags & AH_INACTIVE))
{
if (one_file(hf->fname, LOPT_BANK))
my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
}
}
if (daemon->dhcp_opts_file)
{
struct dhcp_opt *opts, *cp, **up;
struct dhcp_netid *id, *next;
for (up = &daemon->dhcp_opts, opts = daemon->dhcp_opts; opts; opts = cp)
{
cp = opts->next;
if (opts->flags & DHOPT_BANK)
{
if ((opts->flags & DHOPT_VENDOR))
free(opts->u.vendor_class);
free(opts->val);
for (id = opts->netid; id; id = next)
{
next = id->next;
free(id->net);
free(id);
}
*up = opts->next;
free(opts);
}
else
up = &opts->next;
}
daemon->dhcp_opts_file = expand_filelist(daemon->dhcp_opts_file);
for (hf = daemon->dhcp_opts_file; hf; hf = hf->next)
if (!(hf->flags & AH_INACTIVE))
@@ -4607,11 +4711,18 @@ void reread_dhcp(void)
my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
}
}
# ifdef HAVE_INOTIFY
/* Setup notify and read pre-existing files. */
set_dynamic_inotify(AH_DHCP_HST | AH_DHCP_OPT, 0, NULL, 0);
# endif
}
#endif
void read_opts(int argc, char **argv, char *compile_opts)
{
size_t argbuf_size = MAXDNAME;
char *argbuf = opt_malloc(argbuf_size);
char *buff = opt_malloc(MAXDNAME);
int option, conffile_opt = '7', testmode = 0;
char *arg, *conffile = CONFFILE;
@@ -4642,6 +4753,7 @@ void read_opts(int argc, char **argv, char *compile_opts)
daemon->soa_retry = SOA_RETRY;
daemon->soa_expiry = SOA_EXPIRY;
daemon->max_port = MAX_PORT;
daemon->min_port = MIN_PORT;
#ifndef NO_ID
add_txt("version.bind", "dnsmasq-" VERSION, 0 );
@@ -4681,9 +4793,14 @@ void read_opts(int argc, char **argv, char *compile_opts)
/* Copy optarg so that argv doesn't get changed */
if (optarg)
{
strncpy(buff, optarg, MAXDNAME);
buff[MAXDNAME-1] = 0;
arg = buff;
if (strlen(optarg) >= argbuf_size)
{
free(argbuf);
argbuf_size = strlen(optarg) + 1;
argbuf = opt_malloc(argbuf_size);
}
safe_strncpy(argbuf, optarg, argbuf_size);
arg = argbuf;
}
else
arg = NULL;
@@ -4731,6 +4848,8 @@ void read_opts(int argc, char **argv, char *compile_opts)
}
}
free(argbuf);
if (conffile)
{
one_file(conffile, conffile_opt);

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2017 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2018 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-2017 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2018 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-2017 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2018 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-2017 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2018 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
@@ -198,6 +198,9 @@ void icmp6_packet(time_t now)
/* look for link-layer address option for logging */
if (sz >= 16 && packet[8] == ICMP6_OPT_SOURCE_MAC && (packet[9] * 8) + 8 <= sz)
{
if ((packet[9] * 8 - 2) * 3 - 1 >= MAXDNAME) {
return;
}
print_mac(daemon->namebuff, &packet[10], (packet[9] * 8) - 2);
mac = daemon->namebuff;
}
@@ -404,7 +407,7 @@ static void send_ra_alias(time_t now, int iface, char *iface_name, struct in6_ad
if (ra_param)
mtu = ra_param->mtu;
#ifdef HAVE_LINUX_NETWORK
/* Note that IPv6 MTU is not neccessarily the same as the IPv4 MTU
/* Note that IPv6 MTU is not necessarily the same as the IPv4 MTU
available from SIOCGIFMTU */
if (mtu == 0)
{

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2017 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2018 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,8 +36,8 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
if ((l = *p++) == 0)
/* end marker */
{
/* check that there are the correct no of bytes after the name */
if (!CHECK_LEN(header, p, plen, extrabytes))
/* check that there are the correct no. of bytes after the name */
if (!CHECK_LEN(header, p1 ? p1 : p, plen, extrabytes))
return 0;
if (isExtract)
@@ -156,7 +156,7 @@ int in_arpa_name_2_addr(char *namein, struct all_addr *addrp)
memset(addrp, 0, sizeof(struct all_addr));
/* turn name into a series of asciiz strings */
/* j counts no of labels */
/* j counts no. of labels */
for(j = 1,cp1 = name; *namein; cp1++, namein++)
if (*namein == '.')
{
@@ -498,6 +498,8 @@ static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *
{
unsigned int i, len = *p1;
unsigned char *p2 = p1;
if ((p1 + len - p) >= rdlen)
return 0; /* bad packet */
/* make counted string zero-term and sanitise */
for (i = 0; i < len; i++)
{
@@ -582,7 +584,8 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *doc
expired and cleaned out that way.
Return 1 if we reject an address because it look like part of dns-rebinding attack. */
int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t now,
char **ipsets, int is_sign, int check_rebind, int no_cache_dnssec, int secure, int *doctored)
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;
@@ -593,6 +596,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
#else
(void)ipsets; /* unused */
#endif
cache_start_insert();
@@ -601,10 +605,18 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
{
searched_soa = 1;
ttl = find_soa(header, qlen, name, doctored);
if (*doctored)
{
if (secure)
return 0;
#ifdef HAVE_DNSSEC
if (*doctored && secure)
return 0;
if (option_bool(OPT_DNSSEC_VALID))
for (i = 0; i < ntohs(header->ancount); i++)
if (daemon->rr_status[i])
return 0;
#endif
}
}
/* go through the questions. */
@@ -615,7 +627,9 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
int found = 0, cname_count = CNAME_CHAIN;
struct crec *cpp = NULL;
int flags = RCODE(header) == NXDOMAIN ? F_NXDOMAIN : 0;
int secflag = secure ? F_DNSSECOK : 0;
#ifdef HAVE_DNSSEC
int cname_short = 0;
#endif
unsigned long cttl = ULONG_MAX, attl;
namep = p;
@@ -643,8 +657,9 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
if (!(p1 = skip_questions(header, qlen)))
return 0;
for (j = ntohs(header->ancount); j != 0; j--)
for (j = 0; j < ntohs(header->ancount); j++)
{
int secflag = 0;
unsigned char *tmp = namep;
/* the loop body overwrites the original name, so get it back here. */
if (!extract_name(header, qlen, &tmp, name, 1, 0) ||
@@ -670,11 +685,24 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
{
if (!extract_name(header, qlen, &p1, name, 1, 0))
return 0;
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && daemon->rr_status[j])
{
/* validated RR anywhere in CNAME chain, don't cache. */
if (cname_short || aqtype == T_CNAME)
return 0;
secflag = F_DNSSECOK;
}
#endif
if (aqtype == T_CNAME)
{
if (!cname_count-- || secure)
return 0; /* looped CNAMES, or DNSSEC, which we can't cache. */
if (!cname_count--)
return 0; /* looped CNAMES, we can't cache. */
#ifdef HAVE_DNSSEC
cname_short = 1;
#endif
goto cname_loop;
}
@@ -696,7 +724,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
ttl = find_soa(header, qlen, NULL, doctored);
}
if (ttl)
cache_insert(NULL, &addr, now, ttl, name_encoding | F_REVERSE | F_NEG | flags | secflag);
cache_insert(NULL, &addr, now, ttl, name_encoding | F_REVERSE | F_NEG | flags | (secure ? F_DNSSECOK : 0));
}
}
else
@@ -724,8 +752,10 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
if (!(p1 = skip_questions(header, qlen)))
return 0;
for (j = ntohs(header->ancount); j != 0; j--)
for (j = 0; j < ntohs(header->ancount); j++)
{
int secflag = 0;
if (!(res = extract_name(header, qlen, &p1, name, 0, 10)))
return 0; /* bad packet */
@@ -742,6 +772,10 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == qtype))
{
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && daemon->rr_status[j])
secflag = F_DNSSECOK;
#endif
if (aqtype == T_CNAME)
{
if (!cname_count--)
@@ -754,6 +788,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
newc->addr.cname.uid = 1;
if (cpp)
{
next_uid(newc);
cpp->addr.cname.target.cache = newc;
cpp->addr.cname.uid = newc->uid;
}
@@ -810,6 +845,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
newc = cache_insert(name, &addr, now, attl, flags | F_FORWARD | secflag);
if (newc && cpp)
{
next_uid(newc);
cpp->addr.cname.target.cache = newc;
cpp->addr.cname.uid = newc->uid;
}
@@ -833,9 +869,10 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
pointing at this, inherit its TTL */
if (ttl || cpp)
{
newc = cache_insert(name, NULL, now, ttl ? ttl : cttl, F_FORWARD | F_NEG | flags | secflag);
newc = cache_insert(name, NULL, now, ttl ? ttl : cttl, F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0));
if (newc && cpp)
{
next_uid(newc);
cpp->addr.cname.target.cache = newc;
cpp->addr.cname.uid = newc->uid;
}
@@ -892,12 +929,11 @@ unsigned int extract_request(struct dns_header *header, size_t qlen, char *name,
return F_QUERY;
}
size_t setup_reply(struct dns_header *header, size_t qlen,
struct all_addr *addrp, unsigned int flags, unsigned long ttl)
{
unsigned char *p;
if (!(p = skip_questions(header, qlen)))
return 0;
@@ -913,65 +949,76 @@ size_t setup_reply(struct dns_header *header, size_t qlen,
SET_RCODE(header, NOERROR); /* empty domain */
else if (flags == F_NXDOMAIN)
SET_RCODE(header, NXDOMAIN);
else if (flags == F_IPV4)
{ /* we know the address */
SET_RCODE(header, NOERROR);
header->ancount = htons(1);
header->hb3 |= HB3_AA;
add_resource_record(header, NULL, NULL, sizeof(struct dns_header), &p, ttl, NULL, T_A, C_IN, "4", addrp);
}
#ifdef HAVE_IPV6
else if (flags == F_IPV6)
else if (flags == F_SERVFAIL)
{
SET_RCODE(header, NOERROR);
header->ancount = htons(1);
header->hb3 |= HB3_AA;
add_resource_record(header, NULL, NULL, sizeof(struct dns_header), &p, ttl, NULL, T_AAAA, C_IN, "6", addrp);
struct all_addr a;
a.addr.rcode.rcode = SERVFAIL;
log_query(F_CONFIG | F_RCODE, "error", &a, NULL);
SET_RCODE(header, SERVFAIL);
}
else if (flags & ( F_IPV4 | F_IPV6))
{
if (flags & F_IPV4)
{ /* we know the address */
SET_RCODE(header, NOERROR);
header->ancount = htons(1);
header->hb3 |= HB3_AA;
add_resource_record(header, NULL, NULL, sizeof(struct dns_header), &p, ttl, NULL, T_A, C_IN, "4", addrp);
}
#ifdef HAVE_IPV6
if (flags & F_IPV6)
{
SET_RCODE(header, NOERROR);
header->ancount = htons(ntohs(header->ancount) + 1);
header->hb3 |= HB3_AA;
add_resource_record(header, NULL, NULL, sizeof(struct dns_header), &p, ttl, NULL, T_AAAA, C_IN, "6", addrp);
}
#endif
}
else /* nowhere to forward to */
SET_RCODE(header, REFUSED);
{
struct all_addr a;
a.addr.rcode.rcode = REFUSED;
log_query(F_CONFIG | F_RCODE, "error", &a, NULL);
SET_RCODE(header, REFUSED);
}
return p - (unsigned char *)header;
}
/* check if name matches local names ie from /etc/hosts or DHCP or local mx names. */
int check_for_local_domain(char *name, time_t now)
{
struct crec *crecp;
struct mx_srv_record *mx;
struct txt_record *txt;
struct interface_name *intr;
struct ptr_record *ptr;
struct naptr *naptr;
/* Note: the call to cache_find_by_name is intended to find any record which matches
ie A, AAAA, 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;
for (naptr = daemon->naptr; naptr; naptr = naptr->next)
if (hostname_isequal(name, naptr->name))
if (hostname_issubdomain(name, naptr->name))
return 1;
for (mx = daemon->mxnames; mx; mx = mx->next)
if (hostname_isequal(name, mx->name))
if (hostname_issubdomain(name, mx->name))
return 1;
for (txt = daemon->txt; txt; txt = txt->next)
if (hostname_isequal(name, txt->name))
if (hostname_issubdomain(name, txt->name))
return 1;
for (intr = daemon->int_names; intr; intr = intr->next)
if (hostname_isequal(name, intr->name))
if (hostname_issubdomain(name, intr->name))
return 1;
for (ptr = daemon->ptr; ptr; ptr = ptr->next)
if (hostname_isequal(name, ptr->name))
if (hostname_issubdomain(name, ptr->name))
return 1;
if (cache_find_non_terminal(name, now))
return 1;
return 0;
}
@@ -1062,6 +1109,7 @@ int check_for_ignored_address(struct dns_header *header, size_t qlen, struct bog
return 0;
}
int add_resource_record(struct dns_header *header, char *limit, int *truncp, int nameoffset, unsigned char **pp,
unsigned long ttl, int *offset, unsigned short type, unsigned short class, char *format, ...)
{
@@ -1071,29 +1119,41 @@ int add_resource_record(struct dns_header *header, char *limit, int *truncp, int
unsigned short usval;
long lval;
char *sval;
#define CHECK_LIMIT(size) \
if (limit && p + (size) > (unsigned char*)limit) goto truncated;
if (truncp && *truncp)
return 0;
va_start(ap, format); /* make ap point to 1st unamed argument */
if (truncp && *truncp)
goto truncated;
if (nameoffset > 0)
{
CHECK_LIMIT(2);
PUTSHORT(nameoffset | 0xc000, p);
}
else
{
char *name = va_arg(ap, char *);
if (name)
p = do_rfc1035_name(p, name);
if (name && !(p = do_rfc1035_name(p, name, limit)))
goto truncated;
if (nameoffset < 0)
{
CHECK_LIMIT(2);
PUTSHORT(-nameoffset | 0xc000, p);
}
else
*p++ = 0;
{
CHECK_LIMIT(1);
*p++ = 0;
}
}
/* type (2) + class (2) + ttl (4) + rdlen (2) */
CHECK_LIMIT(10);
PUTSHORT(type, p);
PUTSHORT(class, p);
PUTLONG(ttl, p); /* TTL */
@@ -1106,6 +1166,7 @@ int add_resource_record(struct dns_header *header, char *limit, int *truncp, int
{
#ifdef HAVE_IPV6
case '6':
CHECK_LIMIT(IN6ADDRSZ);
sval = va_arg(ap, char *);
memcpy(p, sval, IN6ADDRSZ);
p += IN6ADDRSZ;
@@ -1113,36 +1174,43 @@ int add_resource_record(struct dns_header *header, char *limit, int *truncp, int
#endif
case '4':
CHECK_LIMIT(INADDRSZ);
sval = va_arg(ap, char *);
memcpy(p, sval, INADDRSZ);
p += INADDRSZ;
break;
case 'b':
CHECK_LIMIT(1);
usval = va_arg(ap, int);
*p++ = usval;
break;
case 's':
CHECK_LIMIT(2);
usval = va_arg(ap, int);
PUTSHORT(usval, p);
break;
case 'l':
CHECK_LIMIT(4);
lval = va_arg(ap, long);
PUTLONG(lval, p);
break;
case 'd':
/* get domain-name answer arg and store it in RDATA field */
if (offset)
*offset = p - (unsigned char *)header;
p = do_rfc1035_name(p, va_arg(ap, char *));
*p++ = 0;
/* get domain-name answer arg and store it in RDATA field */
if (offset)
*offset = p - (unsigned char *)header;
if (!(p = do_rfc1035_name(p, va_arg(ap, char *), limit)))
goto truncated;
CHECK_LIMIT(1);
*p++ = 0;
break;
case 't':
usval = va_arg(ap, int);
CHECK_LIMIT(usval);
sval = va_arg(ap, char *);
if (usval != 0)
memcpy(p, sval, usval);
@@ -1154,6 +1222,7 @@ int add_resource_record(struct dns_header *header, char *limit, int *truncp, int
usval = sval ? strlen(sval) : 0;
if (usval > 255)
usval = 255;
CHECK_LIMIT(usval + 1);
*p++ = (unsigned char)usval;
memcpy(p, sval, usval);
p += usval;
@@ -1162,19 +1231,20 @@ int add_resource_record(struct dns_header *header, char *limit, int *truncp, int
va_end(ap); /* clean up variable argument pointer */
/* Now, store real RDLength. sav already checked against limit. */
j = p - sav - 2;
PUTSHORT(j, sav); /* Now, store real RDLength */
/* check for overflow of buffer */
if (limit && ((unsigned char *)limit - p) < 0)
{
if (truncp)
*truncp = 1;
return 0;
}
PUTSHORT(j, sav);
*pp = p;
return 1;
truncated:
va_end(ap);
if (truncp)
*truncp = 1;
return 0;
#undef CHECK_LIMIT
}
static unsigned long crec_ttl(struct crec *crecp, time_t now)
@@ -1223,17 +1293,14 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
struct mx_srv_record *rec;
size_t len;
/* Clear buffer beyond request to avoid risk of
information disclosure. */
memset(((char *)header) + qlen, 0,
(limit - ((char *)header)) - qlen);
if (ntohs(header->ancount) != 0 ||
/* never answer queries with RD unset, to avoid cache snooping. */
if (!(header->hb3 & HB3_RD) ||
ntohs(header->ancount) != 0 ||
ntohs(header->nscount) != 0 ||
ntohs(header->qdcount) == 0 ||
OPCODE(header) != QUERY )
return 0;
/* Don't return AD set if checking disabled. */
if (header->hb4 & HB4_CD)
sec_data = 0;
@@ -1312,11 +1379,11 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
sec_data = 0;
if (!dryrun)
{
log_query(F_CONFIG | F_RRNAME, name, NULL, "<RR>");
log_query(F_CONFIG | F_RRNAME, name, NULL, querystr(NULL, t->class));
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->local_ttl, NULL,
t->class, C_IN, "t", t->len, t->txt))
anscount ++;
anscount++;
}
}
@@ -1510,44 +1577,6 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (qtype != type && qtype != T_ANY)
continue;
/* Check for "A for A" queries; be rather conservative
about what looks like dotted-quad. */
if (qtype == T_A)
{
char *cp;
unsigned int i, a;
int x;
for (cp = name, i = 0, a = 0; *cp; i++)
{
if (!isdigit((unsigned char)*cp) || (x = strtol(cp, &cp, 10)) > 255)
{
i = 5;
break;
}
a = (a << 8) + x;
if (*cp == '.')
cp++;
}
if (i == 4)
{
ans = 1;
sec_data = 0;
if (!dryrun)
{
addr.addr.addr4.s_addr = htonl(a);
log_query(F_FORWARD | F_CONFIG | F_IPV4, name, &addr, NULL);
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->local_ttl, NULL, type, C_IN, "4", &addr))
anscount++;
}
continue;
}
}
/* interface name stuff */
intname_restart:
for (intr = daemon->int_names; intr; intr = intr->next)
@@ -1634,7 +1663,9 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
}
/* If the client asked for DNSSEC don't use cached data. */
if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || !do_bit || !(crecp->flags & F_DNSSECOK))
if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) ||
!do_bit ||
(option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK)))
do
{
/* don't answer wildcard queries with data not from /etc/hosts
@@ -1716,8 +1747,9 @@ 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 | (dryrun ? F_NO_RR : 0)))))
if ((crecp = cache_find_by_name(NULL, name, now, F_CNAME | (dryrun ? F_NO_RR : 0))) &&
(qtype == T_CNAME || (crecp->flags & F_CONFIG)) &&
((crecp->flags & F_CONFIG) || !do_bit || (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK))))
{
if (!(crecp->flags & F_DNSSECOK))
sec_data = 0;

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2017 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2018 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
@@ -32,7 +32,7 @@ static void option_put(struct dhcp_packet *mess, unsigned char *end, int opt, in
static void option_put_string(struct dhcp_packet *mess, unsigned char *end,
int opt, char *string, int null_term);
static struct in_addr option_addr(unsigned char *opt);
static unsigned int option_uint(unsigned char *opt, int i, int size);
static unsigned int option_uint(unsigned char *opt, int offset, int size);
static void log_packet(char *type, void *addr, unsigned char *ext_mac,
int mac_len, char *interface, char *string, char *err, u32 xid);
static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize);
@@ -42,14 +42,14 @@ static void clear_packet(struct dhcp_packet *mess, unsigned char *end);
static int in_list(unsigned char *list, int opt);
static void do_options(struct dhcp_context *context,
struct dhcp_packet *mess,
unsigned char *real_end,
unsigned char *end,
unsigned char *req_options,
char *hostname,
char *config_domain,
char *domain,
struct dhcp_netid *netid,
struct in_addr subnet_addr,
unsigned char fqdn_flags,
int null_term, int pxearch,
int null_term, int pxe_arch,
unsigned char *uuid,
int vendor_class_len,
time_t now,
@@ -58,7 +58,7 @@ static void do_options(struct dhcp_context *context,
static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt);
static int do_encap_opts(struct dhcp_opt *opts, int encap, int flag, struct dhcp_packet *mess, unsigned char *end, int null_term);
static int do_encap_opts(struct dhcp_opt *opt, int encap, int flag, struct dhcp_packet *mess, unsigned char *end, int null_term);
static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char *uuid);
static int prune_vendor_opts(struct dhcp_netid *netid);
static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid, struct in_addr local, time_t now);
@@ -75,7 +75,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
struct dhcp_vendor *vendor;
struct dhcp_mac *mac;
struct dhcp_netid_list *id_list;
int clid_len = 0, ignore = 0, do_classes = 0, selecting = 0, pxearch = -1;
int clid_len = 0, ignore = 0, do_classes = 0, rapid_commit = 0, selecting = 0, pxearch = -1;
struct dhcp_packet *mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
unsigned char *end = (unsigned char *)(mess + 1);
unsigned char *real_end = (unsigned char *)(mess + 1);
@@ -157,7 +157,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
for (offset = 0; offset < (len - 5); offset += elen + 5)
{
elen = option_uint(opt, offset + 4 , 1);
if (option_uint(opt, offset, 4) == BRDBAND_FORUM_IANA)
if (option_uint(opt, offset, 4) == BRDBAND_FORUM_IANA && offset + elen + 5 <= len)
{
unsigned char *x = option_ptr(opt, offset + 5);
unsigned char *y = option_ptr(opt, offset + elen + 5);
@@ -626,6 +626,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
}
}
daemon->metrics[METRIC_BOOTP]++;
log_packet("BOOTP", logaddr, mess->chaddr, mess->hlen, iface_name, NULL, message, mess->xid);
return message ? 0 : dhcp_packet_size(mess, agent_id, real_end);
@@ -701,6 +702,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
if (client_hostname && option_bool(OPT_LOG_OPTS))
my_syslog(MS_DHCP | LOG_INFO, _("%u client provides name: %s"), ntohl(mess->xid), client_hostname);
if (have_config(config, CONFIG_NAME))
{
@@ -713,11 +715,15 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
}
else if (client_hostname)
{
struct dhcp_match_name *m;
size_t nl;
domain = strip_hostname(client_hostname);
if (strlen(client_hostname) != 0)
if ((nl = strlen(client_hostname)) != 0)
{
hostname = client_hostname;
if (!config)
{
/* Search again now we have a hostname.
@@ -735,6 +741,30 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
netid = &known_id;
}
}
for (m = daemon->dhcp_name_match; m; m = m->next)
{
size_t ml = strlen(m->name);
char save = 0;
if (nl < ml)
continue;
if (nl > ml)
{
save = client_hostname[ml];
client_hostname[ml] = 0;
}
if (hostname_isequal(client_hostname, m->name) &&
(save == 0 || m->wildcard))
{
m->netid->next = netid;
netid = m->netid;
}
if (save != 0)
client_hostname[ml] = save;
}
}
}
@@ -909,7 +939,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
if (!workaround && boot)
{
/* Provide the bootfile here, for gPXE, and in case we have no menu items
/* Provide the bootfile here, for iPXE, and in case we have no menu items
and set discovery_control = 8 */
if (boot->next_server.s_addr)
mess->siaddr = boot->next_server;
@@ -917,7 +947,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
if (boot->file)
strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);
safe_strncpy((char *)mess->file, boot->file, sizeof(mess->file));
}
option_put(mess, end, OPTION_MESSAGE_TYPE, 1,
@@ -928,6 +958,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
if ((pxe && !workaround) || !redirect4011)
do_encap_opts(pxe_opts(pxearch, tagif_netid, tmp->local, now), OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
daemon->metrics[METRIC_PXE]++;
log_packet("PXE", NULL, emac, emac_len, iface_name, ignore ? "proxy-ignored" : "proxy", NULL, mess->xid);
log_tags(tagif_netid, ntohl(mess->xid));
if (!ignore)
@@ -962,6 +993,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
return 0;
daemon->metrics[METRIC_DHCPDECLINE]++;
log_packet("DHCPDECLINE", option_ptr(opt, 0), emac, emac_len, iface_name, NULL, daemon->dhcp_buff, mess->xid);
if (lease && lease->addr.s_addr == option_addr(opt).s_addr)
@@ -994,6 +1026,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
else
message = _("unknown lease");
daemon->metrics[METRIC_DHCPRELEASE]++;
log_packet("DHCPRELEASE", &mess->ciaddr, emac, emac_len, iface_name, NULL, message, mess->xid);
return 0;
@@ -1040,8 +1073,6 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
else if (have_config(config, CONFIG_DECLINED) &&
difftime(now, config->decline_time) < (float)DECLINE_BACKOFF)
my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it was previously declined"), addrs);
else if ((!lease || lease->addr.s_addr != config->addr.s_addr) && !do_icmp_ping(now, config->addr, 0, loopback))
my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is in use by another host"), addrs);
else
conf = config->addr;
}
@@ -1062,6 +1093,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
message = _("no address available");
}
daemon->metrics[METRIC_DHCPDISCOVER]++;
log_packet("DHCPDISCOVER", opt ? option_ptr(opt, 0) : NULL, emac, emac_len, iface_name, NULL, message, mess->xid);
if (message || !(context = narrow_context(context, mess->yiaddr, tagif_netid)))
@@ -1075,6 +1107,14 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
log_tags(tagif_netid, ntohl(mess->xid));
apply_delay(mess->xid, recvtime, tagif_netid);
if (option_bool(OPT_RAPID_COMMIT) && option_find(mess, sz, OPTION_RAPID_COMMIT, 0))
{
rapid_commit = 1;
goto rapid_commit;
}
daemon->metrics[METRIC_DHCPOFFER]++;
log_packet("DHCPOFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL, NULL, mess->xid);
time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
@@ -1087,7 +1127,8 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz);
return dhcp_packet_size(mess, agent_id, real_end);
case DHCPREQUEST:
if (ignore || have_config(config, CONFIG_DISABLE))
return 0;
@@ -1185,9 +1226,11 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
fuzz = rand16();
mess->yiaddr = mess->ciaddr;
}
daemon->metrics[METRIC_DHCPREQUEST]++;
log_packet("DHCPREQUEST", &mess->yiaddr, emac, emac_len, iface_name, NULL, NULL, mess->xid);
rapid_commit:
if (!message)
{
struct dhcp_config *addr_config;
@@ -1258,7 +1301,12 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
if (message)
{
log_packet("DHCPNAK", &mess->yiaddr, emac, emac_len, iface_name, NULL, message, mess->xid);
daemon->metrics[rapid_commit ? METRIC_NOANSWER : METRIC_DHCPNAK]++;
log_packet(rapid_commit ? "NOANSWER" : "DHCPNAK", &mess->yiaddr, emac, emac_len, iface_name, NULL, message, mess->xid);
/* rapid commit case: lease allocate failed but don't send DHCPNAK */
if (rapid_commit)
return 0;
mess->yiaddr.s_addr = 0;
clear_packet(mess, end);
@@ -1415,13 +1463,16 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
else
override = lease->override;
daemon->metrics[METRIC_DHCPACK]++;
log_packet("DHCPACK", &mess->yiaddr, emac, emac_len, iface_name, hostname, NULL, mess->xid);
clear_packet(mess, end);
option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
option_put(mess, end, OPTION_LEASE_TIME, 4, time);
do_options(context, mess, end, req_options, hostname, get_domain(mess->yiaddr),
if (rapid_commit)
option_put(mess, end, OPTION_RAPID_COMMIT, 0, 0);
do_options(context, mess, end, req_options, hostname, get_domain(mess->yiaddr),
netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz);
}
@@ -1431,6 +1482,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
if (ignore || have_config(config, CONFIG_DISABLE))
message = _("ignored");
daemon->metrics[METRIC_DHCPINFORM]++;
log_packet("DHCPINFORM", &mess->ciaddr, emac, emac_len, iface_name, message, NULL, mess->xid);
if (message || mess->ciaddr.s_addr == 0)
@@ -1457,6 +1509,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
log_tags(tagif_netid, ntohl(mess->xid));
daemon->metrics[METRIC_DHCPACK]++;
log_packet("DHCPACK", &mess->ciaddr, emac, emac_len, iface_name, hostname, NULL, mess->xid);
if (lease)
@@ -1623,6 +1676,13 @@ static void log_packet(char *type, void *addr, unsigned char *ext_mac,
daemon->namebuff,
string ? string : "",
err ? err : "");
#ifdef HAVE_UBUS
if (!strcmp(type, "DHCPACK"))
ubus_event_bcast("dhcp.ack", daemon->namebuff, addr ? inet_ntoa(a) : NULL, string ? string : NULL, interface);
else if (!strcmp(type, "DHCPRELEASE"))
ubus_event_bcast("dhcp.release", daemon->namebuff, addr ? inet_ntoa(a) : NULL, string ? string : NULL, interface);
#endif
}
static void log_options(unsigned char *start, u32 xid)
@@ -2286,7 +2346,7 @@ static void do_options(struct dhcp_context *context,
/* See if we can send the boot stuff as options.
To do this we need a requested option list, BOOTP
and very old DHCP clients won't have this, we also
provide an manual option to disable it.
provide a manual option to disable it.
Some PXE ROMs have bugs (surprise!) and need zero-terminated
names, so we always send those. */
if ((boot = find_boot(tagif)))
@@ -2298,7 +2358,7 @@ static void do_options(struct dhcp_context *context,
in_list(req_options, OPTION_SNAME))
option_put_string(mess, end, OPTION_SNAME, boot->sname, 1);
else
strncpy((char *)mess->sname, boot->sname, sizeof(mess->sname)-1);
safe_strncpy((char *)mess->sname, boot->sname, sizeof(mess->sname));
}
if (boot->file)
@@ -2308,7 +2368,7 @@ static void do_options(struct dhcp_context *context,
in_list(req_options, OPTION_FILENAME))
option_put_string(mess, end, OPTION_FILENAME, boot->file, 1);
else
strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);
safe_strncpy((char *)mess->file, boot->file, sizeof(mess->file));
}
if (boot->next_server.s_addr)
@@ -2325,14 +2385,14 @@ static void do_options(struct dhcp_context *context,
if ((!req_options || !in_list(req_options, OPTION_FILENAME)) &&
(opt = option_find2(OPTION_FILENAME)) && !(opt->flags & DHOPT_FORCE))
{
strncpy((char *)mess->file, (char *)opt->val, sizeof(mess->file)-1);
safe_strncpy((char *)mess->file, (char *)opt->val, sizeof(mess->file));
done_file = 1;
}
if ((!req_options || !in_list(req_options, OPTION_SNAME)) &&
(opt = option_find2(OPTION_SNAME)) && !(opt->flags & DHOPT_FORCE))
{
strncpy((char *)mess->sname, (char *)opt->val, sizeof(mess->sname)-1);
safe_strncpy((char *)mess->sname, (char *)opt->val, sizeof(mess->sname));
done_server = 1;
}
@@ -2454,10 +2514,10 @@ static void do_options(struct dhcp_context *context,
if (fqdn_flags & 0x04)
{
p = do_rfc1035_name(p, hostname);
p = do_rfc1035_name(p, hostname, NULL);
if (domain)
{
p = do_rfc1035_name(p, domain);
p = do_rfc1035_name(p, domain, NULL);
*p++ = 0;
}
}

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2017 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2018 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
@@ -206,6 +206,9 @@ static int dhcp6_maybe_relay(struct state *state, void *inbuff, size_t sz,
/* RFC-6939 */
if ((opt = opt6_find(opts, end, OPTION6_CLIENT_MAC, 3)))
{
if (opt6_len(opt) - 2 > DHCP_CHADDR_MAX) {
return 0;
}
state->mac_type = opt6_uint(opt, 0, 2);
state->mac_len = opt6_len(opt) - 2;
memcpy(&state->mac[0], opt6_ptr(opt, 2), state->mac_len);
@@ -213,6 +216,9 @@ static int dhcp6_maybe_relay(struct state *state, void *inbuff, size_t sz,
for (opt = opts; opt; opt = opt6_next(opt, end))
{
if (opt6_ptr(opt, 0) + opt6_len(opt) > end)
return 0;
int o = new_opt6(opt6_type(opt));
if (opt6_type(opt) == OPTION6_RELAY_MSG)
{
@@ -490,11 +496,16 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
}
else if (state->client_hostname)
{
struct dhcp_match_name *m;
size_t nl;
state->domain = strip_hostname(state->client_hostname);
nl = strlen(state->client_hostname);
if (strlen(state->client_hostname) != 0)
{
state->hostname = state->client_hostname;
if (!config)
{
/* Search again now we have a hostname.
@@ -504,6 +515,30 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
if (new && !have_config(new, CONFIG_CLID) && !new->hwaddr)
config = new;
}
for (m = daemon->dhcp_name_match; m; m = m->next)
{
size_t ml = strlen(m->name);
char save = 0;
if (nl < ml)
continue;
if (nl > ml)
{
save = state->client_hostname[ml];
state->client_hostname[ml] = 0;
}
if (hostname_isequal(state->client_hostname, m->name) &&
(save == 0 || m->wildcard))
{
m->netid->next = state->tags;
state->tags = m->netid;
}
if (save != 0)
state->client_hostname[ml] = save;
}
}
}
}
@@ -633,9 +668,8 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
int plain_range = 1;
u32 lease_time;
struct dhcp_lease *ltmp;
struct in6_addr *req_addr;
struct in6_addr addr;
struct in6_addr req_addr, addr;
if (!check_ia(state, opt, &ia_end, &ia_option))
continue;
@@ -703,9 +737,10 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
for (ia_counter = 0; ia_option; ia_counter++, ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
{
req_addr = opt6_ptr(ia_option, 0);
/* worry about alignment here. */
memcpy(&req_addr, opt6_ptr(ia_option, 0), IN6ADDRSZ);
if ((c = address6_valid(state->context, req_addr, solicit_tags, plain_range)))
if ((c = address6_valid(state->context, &req_addr, solicit_tags, plain_range)))
{
lease_time = c->lease_time;
/* If the client asks for an address on the same network as a configured address,
@@ -713,14 +748,14 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
addresses automatic. */
if (!(c->flags & CONTEXT_CONF_USED) && config_valid(config, c, &addr) && check_address(state, &addr))
{
req_addr = &addr;
req_addr = addr;
mark_config_used(c, &addr);
if (have_config(config, CONFIG_TIME))
lease_time = config->lease_time;
}
else if (!(c = address6_available(state->context, req_addr, solicit_tags, plain_range)))
else if (!(c = address6_available(state->context, &req_addr, solicit_tags, plain_range)))
continue; /* not an address we're allowed */
else if (!check_address(state, req_addr))
else if (!check_address(state, &req_addr))
continue; /* address leased elsewhere */
/* add address to output packet */
@@ -728,8 +763,8 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
if (dump_all_prefix_classes && state->ia_type == OPTION6_IA_NA)
state->send_prefix_class = prefix_class_from_context(c);
#endif
add_address(state, c, lease_time, ia_option, &min_time, req_addr, now);
mark_context_used(state, req_addr);
add_address(state, c, lease_time, ia_option, &min_time, &req_addr, now);
mark_context_used(state, &req_addr);
get_context_tag(state, c);
address_assigned = 1;
}
@@ -762,15 +797,15 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
ltmp = NULL;
while ((ltmp = lease6_find_by_client(ltmp, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, state->clid, state->clid_len, state->iaid)))
{
req_addr = &ltmp->addr6;
if ((c = address6_available(state->context, req_addr, solicit_tags, plain_range)))
req_addr = ltmp->addr6;
if ((c = address6_available(state->context, &req_addr, solicit_tags, plain_range)))
{
#ifdef OPTION6_PREFIX_CLASS
if (dump_all_prefix_classes && state->ia_type == OPTION6_IA_NA)
state->send_prefix_class = prefix_class_from_context(c);
#endif
add_address(state, c, c->lease_time, NULL, &min_time, req_addr, now);
mark_context_used(state, req_addr);
add_address(state, c, c->lease_time, NULL, &min_time, &req_addr, now);
mark_context_used(state, &req_addr);
get_context_tag(state, c);
address_assigned = 1;
}
@@ -876,7 +911,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
if (!ia_option)
{
/* If we get a request with a IA_*A without addresses, treat it exactly like
/* If we get a request with an IA_*A without addresses, treat it exactly like
a SOLICT with rapid commit set. */
save_counter(start);
goto request_no_address;
@@ -886,16 +921,19 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
for (; ia_option; ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
{
struct in6_addr *req_addr = opt6_ptr(ia_option, 0);
struct in6_addr req_addr;
struct dhcp_context *dynamic, *c;
unsigned int lease_time;
struct in6_addr addr;
int config_ok = 0;
/* align. */
memcpy(&req_addr, opt6_ptr(ia_option, 0), IN6ADDRSZ);
if ((c = address6_valid(state->context, req_addr, tagif, 1)))
config_ok = config_valid(config, c, &addr) && IN6_ARE_ADDR_EQUAL(&addr, req_addr);
if ((c = address6_valid(state->context, &req_addr, tagif, 1)))
config_ok = config_valid(config, c, &addr) && IN6_ARE_ADDR_EQUAL(&addr, &req_addr);
if ((dynamic = address6_available(state->context, req_addr, tagif, 1)) || c)
if ((dynamic = address6_available(state->context, &req_addr, tagif, 1)) || c)
{
if (!dynamic && !config_ok)
{
@@ -905,7 +943,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
put_opt6_string(_("address unavailable"));
end_opt6(o1);
}
else if (!check_address(state, req_addr))
else if (!check_address(state, &req_addr))
{
/* Address leased to another DUID/IAID */
o1 = new_opt6(OPTION6_STATUS_CODE);
@@ -927,7 +965,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
if (dump_all_prefix_classes && state->ia_type == OPTION6_IA_NA)
state->send_prefix_class = prefix_class_from_context(c);
#endif
add_address(state, dynamic, lease_time, ia_option, &min_time, req_addr, now);
add_address(state, dynamic, lease_time, ia_option, &min_time, &req_addr, now);
get_context_tag(state, dynamic);
address_assigned = 1;
}
@@ -990,15 +1028,17 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
for (; ia_option; ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
{
struct dhcp_lease *lease = NULL;
struct in6_addr *req_addr = opt6_ptr(ia_option, 0);
struct in6_addr req_addr;
unsigned int preferred_time = opt6_uint(ia_option, 16, 4);
unsigned int valid_time = opt6_uint(ia_option, 20, 4);
char *message = NULL;
struct dhcp_context *this_context;
memcpy(&req_addr, opt6_ptr(ia_option, 0), IN6ADDRSZ);
if (!(lease = lease6_find(state->clid, state->clid_len,
state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA,
state->iaid, req_addr)))
state->iaid, &req_addr)))
{
/* If the server cannot find a client entry for the IA the server
returns the IA containing no addresses with a Status Code option set
@@ -1006,7 +1046,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
save_counter(iacntr);
t1cntr = 0;
log6_packet(state, "DHCPREPLY", req_addr, _("lease not found"));
log6_packet(state, "DHCPREPLY", &req_addr, _("lease not found"));
o1 = new_opt6(OPTION6_STATUS_CODE);
put_opt6_short(DHCP6NOBINDING);
@@ -1018,15 +1058,15 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
}
if ((this_context = address6_available(state->context, req_addr, tagif, 1)) ||
(this_context = address6_valid(state->context, req_addr, tagif, 1)))
if ((this_context = address6_available(state->context, &req_addr, tagif, 1)) ||
(this_context = address6_valid(state->context, &req_addr, tagif, 1)))
{
struct in6_addr addr;
unsigned int lease_time;
get_context_tag(state, this_context);
if (config_valid(config, this_context, &addr) && IN6_ARE_ADDR_EQUAL(&addr, req_addr) && have_config(config, CONFIG_TIME))
if (config_valid(config, this_context, &addr) && IN6_ARE_ADDR_EQUAL(&addr, &req_addr) && have_config(config, CONFIG_TIME))
lease_time = config->lease_time;
else
lease_time = this_context->lease_time;
@@ -1039,7 +1079,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
lease_set_hwaddr(lease, state->mac, state->clid, state->mac_len, state->mac_type, state->clid_len, now, 0);
if (state->ia_type == OPTION6_IA_NA && state->hostname)
{
char *addr_domain = get_domain6(req_addr);
char *addr_domain = get_domain6(&req_addr);
if (!state->send_domain)
state->send_domain = addr_domain;
lease_set_hostname(lease, state->hostname, state->hostname_auth, addr_domain, state->domain);
@@ -1057,12 +1097,12 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
}
if (message && (message != state->hostname))
log6_packet(state, "DHCPREPLY", req_addr, message);
log6_packet(state, "DHCPREPLY", &req_addr, message);
else
log6_quiet(state, "DHCPREPLY", req_addr, message);
log6_quiet(state, "DHCPREPLY", &req_addr, message);
o1 = new_opt6(OPTION6_IAADDR);
put_opt6(req_addr, sizeof(*req_addr));
put_opt6(&req_addr, sizeof(req_addr));
put_opt6_long(preferred_time);
put_opt6_long(valid_time);
end_opt6(o1);
@@ -1094,19 +1134,23 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
ia_option;
ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
{
struct in6_addr *req_addr = opt6_ptr(ia_option, 0);
struct in6_addr req_addr;
/* alignment */
memcpy(&req_addr, opt6_ptr(ia_option, 0), IN6ADDRSZ);
if (!address6_valid(state->context, req_addr, tagif, 1))
if (!address6_valid(state->context, &req_addr, tagif, 1))
{
o1 = new_opt6(OPTION6_STATUS_CODE);
put_opt6_short(DHCP6NOTONLINK);
put_opt6_string(_("confirm failed"));
end_opt6(o1);
log6_quiet(state, "DHCPREPLY", &req_addr, _("confirm failed"));
return 1;
}
good_addr = 1;
log6_quiet(state, "DHCPREPLY", req_addr, state->hostname);
log6_quiet(state, "DHCPREPLY", &req_addr, state->hostname);
}
}
@@ -1165,9 +1209,12 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
{
struct dhcp_lease *lease;
struct in6_addr addr;
/* align */
memcpy(&addr, opt6_ptr(ia_option, 0), IN6ADDRSZ);
if ((lease = lease6_find(state->clid, state->clid_len, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA,
state->iaid, opt6_ptr(ia_option, 0))))
state->iaid, &addr)))
lease_prune(lease, now);
else
{
@@ -1184,7 +1231,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
}
o1 = new_opt6(OPTION6_IAADDR);
put_opt6(opt6_ptr(ia_option, 0), IN6ADDRSZ);
put_opt6(&addr, IN6ADDRSZ);
put_opt6_long(0);
put_opt6_long(0);
end_opt6(o1);
@@ -1227,12 +1274,15 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
{
struct dhcp_lease *lease;
struct in6_addr *addrp = opt6_ptr(ia_option, 0);
struct in6_addr addr;
if (have_config(config, CONFIG_ADDR6) && IN6_ARE_ADDR_EQUAL(&config->addr6, addrp))
/* align */
memcpy(&addr, opt6_ptr(ia_option, 0), IN6ADDRSZ);
if (have_config(config, CONFIG_ADDR6) && IN6_ARE_ADDR_EQUAL(&config->addr6, &addr))
{
prettyprint_time(daemon->dhcp_buff3, DECLINE_BACKOFF);
inet_ntop(AF_INET6, addrp, daemon->addrbuff, ADDRSTRLEN);
inet_ntop(AF_INET6, &addr, daemon->addrbuff, ADDRSTRLEN);
my_syslog(MS_DHCP | LOG_WARNING, _("disabling DHCP static address %s for %s"),
daemon->addrbuff, daemon->dhcp_buff3);
config->flags |= CONFIG_DECLINED;
@@ -1244,7 +1294,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
context_tmp->addr_epoch++;
if ((lease = lease6_find(state->clid, state->clid_len, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA,
state->iaid, opt6_ptr(ia_option, 0))))
state->iaid, &addr)))
lease_prune(lease, now);
else
{
@@ -1261,7 +1311,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
}
o1 = new_opt6(OPTION6_IAADDR);
put_opt6(opt6_ptr(ia_option, 0), IN6ADDRSZ);
put_opt6(&addr, IN6ADDRSZ);
put_opt6_long(0);
put_opt6_long(0);
end_opt6(o1);
@@ -1479,10 +1529,10 @@ static struct dhcp_netid *add_options(struct state *state, int do_refresh)
if ((p = expand(len + 2)))
{
*(p++) = state->fqdn_flags;
p = do_rfc1035_name(p, state->hostname);
p = do_rfc1035_name(p, state->hostname, NULL);
if (state->send_domain)
{
p = do_rfc1035_name(p, state->send_domain);
p = do_rfc1035_name(p, state->send_domain, NULL);
*p = 0;
}
}
@@ -1619,7 +1669,7 @@ static void end_ia(int t1cntr, unsigned int min_time, int do_fuzz)
{
if (t1cntr != 0)
{
/* go back an fill in fields in IA_NA option */
/* go back and fill in fields in IA_NA option */
int sav = save_counter(t1cntr);
unsigned int t1, t2, fuzz = 0;
@@ -1929,7 +1979,11 @@ static void log6_opts(int nest, unsigned int xid, void *start_opts, void *end_op
}
else if (type == OPTION6_IAADDR)
{
inet_ntop(AF_INET6, opt6_ptr(opt, 0), daemon->addrbuff, ADDRSTRLEN);
struct in6_addr addr;
/* align */
memcpy(&addr, opt6_ptr(opt, 0), IN6ADDRSZ);
inet_ntop(AF_INET6, &addr, daemon->addrbuff, ADDRSTRLEN);
sprintf(daemon->namebuff, "%s PL=%u VL=%u",
daemon->addrbuff, opt6_uint(opt, 16, 4), opt6_uint(opt, 20, 4));
optname = "iaaddr";

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2017 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2018 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/>.
*/
/* Code to safely remove RRs from an DNS answer */
/* Code to safely remove RRs from a DNS answer */
#include "dnsmasq.h"
@@ -247,7 +247,7 @@ size_t rrfilter(struct dns_header *header, size_t plen, int mode)
check_rrs(p, header, plen, 1, rrs, rr_found);
/* Fouth pass, elide records */
/* Fourth pass, elide records */
for (p = rrs[0], i = 1; i < rr_found; i += 2)
{
unsigned char *start = rrs[i];
@@ -270,7 +270,7 @@ u16 *rrfilter_desc(int type)
{
/* List of RRtypes which include domains in the data.
0 -> domain
integer -> no of plain bytes
integer -> no. of plain bytes
-1 -> end
zero is not a valid RRtype, so the final entry is returned for

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2017 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2018 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
@@ -166,7 +166,8 @@ time_t periodic_slaac(time_t now, struct dhcp_lease *leases)
if (sendto(daemon->icmp6fd, daemon->outpacket.iov_base, save_counter(-1), 0,
(struct sockaddr *)&addr, sizeof(addr)) == -1 &&
errno == EHOSTUNREACH)
errno == EHOSTUNREACH &&
slaac->backoff == 12)
slaac->ping_time = 0; /* Give up */
else
{

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2017 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2018 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
@@ -20,7 +20,7 @@
static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix);
static void free_transfer(struct tftp_transfer *transfer);
static ssize_t tftp_err(int err, char *packet, char *mess, char *file);
static ssize_t tftp_err(int err, char *packet, char *message, char *file);
static ssize_t tftp_err_oops(char *packet, char *file);
static ssize_t get_block(char *packet, struct tftp_transfer *transfer);
static char *next(char **p, char *end);
@@ -234,7 +234,7 @@ void tftp_request(struct listener *listen, time_t now)
#endif
}
strncpy(ifr.ifr_name, name, IF_NAMESIZE);
safe_strncpy(ifr.ifr_name, name, IF_NAMESIZE);
if (ioctl(listen->tftpfd, SIOCGIFMTU, &ifr) != -1)
{
mtu = ifr.ifr_mtu;
@@ -734,7 +734,7 @@ static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
if (transfer->opt_blocksize)
{
p += (sprintf(p, "blksize") + 1);
p += (sprintf(p, "%d", transfer->blocksize) + 1);
p += (sprintf(p, "%u", transfer->blocksize) + 1);
}
if (transfer->opt_transize)
{

107
src/ubus.c Normal file
View File

@@ -0,0 +1,107 @@
/* dnsmasq is Copyright (c) 2000-2018 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_UBUS
#include <libubus.h>
static struct ubus_context *ubus = NULL;
static struct blob_buf b;
static int ubus_handle_metrics(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg);
static struct ubus_method ubus_object_methods[] = {
{.name = "metrics", .handler = ubus_handle_metrics},
};
static struct ubus_object_type ubus_object_type = UBUS_OBJECT_TYPE("dnsmasq", ubus_object_methods);
static struct ubus_object ubus_object = {
.name = "dnsmasq",
.type = &ubus_object_type,
.methods = ubus_object_methods,
.n_methods = ARRAY_SIZE(ubus_object_methods),
};
void set_ubus_listeners()
{
if (!ubus)
return;
poll_listen(ubus->sock.fd, POLLIN);
poll_listen(ubus->sock.fd, POLLERR);
poll_listen(ubus->sock.fd, POLLHUP);
}
void check_ubus_listeners()
{
if (!ubus)
{
ubus = ubus_connect(NULL);
if (!ubus)
return;
ubus_add_object(ubus, &ubus_object);
}
if (poll_check(ubus->sock.fd, POLLIN))
ubus_handle_event(ubus);
if (poll_check(ubus->sock.fd, POLLHUP))
{
ubus_free(ubus);
ubus = NULL;
}
}
static int ubus_handle_metrics(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
int i;
blob_buf_init(&b, 0);
for(i=0; i < __METRIC_MAX; i++)
blobmsg_add_u32(&b, get_metric_name(i), daemon->metrics[i]);
ubus_send_reply(ctx, req, b.head);
return 0;
}
void ubus_event_bcast(const char *type, const char *mac, const char *ip, const char *name, const char *interface)
{
if (!ubus || !ubus_object.has_subscribers)
return;
blob_buf_init(&b, 0);
if (mac)
blobmsg_add_string(&b, "mac", mac);
if (ip)
blobmsg_add_string(&b, "ip", ip);
if (name)
blobmsg_add_string(&b, "name", name);
if (interface)
blobmsg_add_string(&b, "interface", interface);
ubus_notify(ubus, &ubus_object, type, b.head, -1);
}
#endif /* HAVE_UBUS */

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2017 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2018 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
@@ -111,6 +111,7 @@ u64 rand64(void)
return (u64)out[outleft+1] + (((u64)out[outleft]) << 32);
}
/* returns 2 if names is OK but contains one or more underscores */
static int check_name(char *in)
{
/* remove trailing .
@@ -118,6 +119,7 @@ static int check_name(char *in)
size_t dotgap = 0, l = strlen(in);
char c;
int nowhite = 0;
int hasuscore = 0;
if (l == 0 || l > MAXDNAME) return 0;
@@ -141,13 +143,17 @@ static int check_name(char *in)
return 0;
#endif
else if (c != ' ')
nowhite = 1;
{
nowhite = 1;
if (c == '_')
hasuscore = 1;
}
}
if (!nowhite)
return 0;
return 1;
return hasuscore ? 2 : 1;
}
/* Hostnames have a more limited valid charset than domain names
@@ -186,56 +192,70 @@ int legal_hostname(char *name)
char *canonicalise(char *in, int *nomem)
{
char *ret = NULL;
#if defined(HAVE_IDN) || defined(HAVE_LIBIDN2)
int rc;
#endif
if (nomem)
*nomem = 0;
if (!check_name(in))
if (!(rc = check_name(in)))
return NULL;
#if defined(HAVE_IDN) || defined(HAVE_LIBIDN2)
#ifdef HAVE_LIBIDN2
rc = idn2_to_ascii_lz(in, &ret, IDN2_NONTRANSITIONAL);
if (rc == IDN2_DISALLOWED)
rc = idn2_to_ascii_lz(in, &ret, IDN2_TRANSITIONAL);
#else
rc = idna_to_ascii_lz(in, &ret, 0);
#if defined(HAVE_LIBIDN2) && (!defined(IDN2_VERSION_NUMBER) || IDN2_VERSION_NUMBER < 0x02000003)
/* older libidn2 strips underscores, so don't do IDN processing
if the name has an underscore (check_name() returned 2) */
if (rc != 2)
#endif
if (rc != IDNA_SUCCESS)
#if defined(HAVE_IDN) || defined(HAVE_LIBIDN2)
{
if (ret)
free(ret);
if (nomem && (rc == IDNA_MALLOC_ERROR || rc == IDNA_DLOPEN_ERROR))
# ifdef HAVE_LIBIDN2
rc = idn2_to_ascii_lz(in, &ret, IDN2_NONTRANSITIONAL);
if (rc == IDN2_DISALLOWED)
rc = idn2_to_ascii_lz(in, &ret, IDN2_TRANSITIONAL);
# else
rc = idna_to_ascii_lz(in, &ret, 0);
# endif
if (rc != IDNA_SUCCESS)
{
my_syslog(LOG_ERR, _("failed to allocate memory"));
*nomem = 1;
if (ret)
free(ret);
if (nomem && (rc == IDNA_MALLOC_ERROR || rc == IDNA_DLOPEN_ERROR))
{
my_syslog(LOG_ERR, _("failed to allocate memory"));
*nomem = 1;
}
return NULL;
}
return NULL;
return ret;
}
#else
#endif
if ((ret = whine_malloc(strlen(in)+1)))
strcpy(ret, in);
else if (nomem)
*nomem = 1;
#endif
return ret;
}
unsigned char *do_rfc1035_name(unsigned char *p, char *sval)
unsigned char *do_rfc1035_name(unsigned char *p, char *sval, char *limit)
{
int j;
while (sval && *sval)
{
unsigned char *cp = p++;
if (limit && p > (unsigned char*)limit)
return NULL;
for (j = 0; *sval && (*sval != '.'); sval++, j++)
{
if (limit && p + 1 > (unsigned char*)limit)
return NULL;
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && *sval == NAME_ESCAPE)
*p++ = (*(++sval))-1;
@@ -243,10 +263,12 @@ unsigned char *do_rfc1035_name(unsigned char *p, char *sval)
#endif
*p++ = *sval;
}
*cp = j;
if (*sval)
sval++;
}
return p;
}
@@ -259,7 +281,18 @@ void *safe_malloc(size_t size)
die(_("could not get memory"), NULL, EC_NOMEM);
return ret;
}
}
/* Ensure limited size string is always terminated.
* Can be replaced by (void)strlcpy() on some platforms */
void safe_strncpy(char *dest, const char *src, size_t size)
{
if (size != 0)
{
dest[size-1] = '\0';
strncpy(dest, src, size-1);
}
}
void safe_pipe(int *fd, int read_noblock)
{
@@ -333,6 +366,44 @@ int hostname_isequal(const char *a, const char *b)
return 1;
}
/* is b equal to or a subdomain of a return 2 for equal, 1 for subdomain */
int hostname_issubdomain(char *a, char *b)
{
char *ap, *bp;
unsigned int c1, c2;
/* move to the end */
for (ap = a; *ap; ap++);
for (bp = b; *bp; bp++);
/* a shorter than b or a empty. */
if ((bp - b) < (ap - a) || ap == a)
return 0;
do
{
c1 = (unsigned char) *(--ap);
c2 = (unsigned char) *(--bp);
if (c1 >= 'A' && c1 <= 'Z')
c1 += 'a' - 'A';
if (c2 >= 'A' && c2 <= 'Z')
c2 += 'a' - 'A';
if (c1 != c2)
return 0;
} while (ap != a);
if (bp == b)
return 2;
if (*(--bp) == '.')
return 1;
return 0;
}
time_t dnsmasq_time(void)
{
#ifdef HAVE_BROKEN_RTC
@@ -448,13 +519,13 @@ void prettyprint_time(char *buf, unsigned int t)
{
unsigned int x, p = 0;
if ((x = t/86400))
p += sprintf(&buf[p], "%dd", x);
p += sprintf(&buf[p], "%ud", x);
if ((x = (t/3600)%24))
p += sprintf(&buf[p], "%dh", x);
p += sprintf(&buf[p], "%uh", x);
if ((x = (t/60)%60))
p += sprintf(&buf[p], "%dm", x);
p += sprintf(&buf[p], "%um", x);
if ((x = t%60))
p += sprintf(&buf[p], "%ds", x);
p += sprintf(&buf[p], "%us", x);
}
}