Compare commits

...

178 Commits

Author SHA1 Message Date
Simon Kelley
f186bdcbc7 Fix debian changelog. 2016-05-18 15:51:54 +01:00
Jan Psota
ed1bd54b5c Update Polish translation.
Thanks to Jan Psota.
2016-05-18 15:16:29 +01:00
Simon Kelley
da2cad4b14 Doc about --pxe-service basename change into CHANGELOG. 2016-05-18 15:14:08 +01:00
Simon Kelley
0a4a04969d Final PXE tweak, and release-note to reflect changes. 2016-05-15 20:13:45 +01:00
Sergey Nechaev
45cb8dd9be Stricter command line args validation to dhcp_release6. 2016-05-14 21:36:15 +01:00
Simon Kelley
88b09aaddc Merge messages into translation files. 2016-05-14 20:56:02 +01:00
Simon Kelley
fe71bba356 Tweak UEFI workaround code. 2016-05-14 20:50:45 +01:00
Simon Kelley
cbc100fc81 Tweak CSAs affected by UEFI PXE workaround code. 2016-05-11 22:17:18 +01:00
Simon Kelley
68bea10bbf Fix error in PXE arch names and add ARM32 and ARM64. 2016-05-11 22:15:06 +01:00
Simon Kelley
8628cd603f Workaround for UEFI PXE boot problems. 2016-05-10 17:31:48 +01:00
Neil Jerram
ff325644c7 Fix for DHCP in transmission interface when --bridge-interface in use.
From f3d832b41f44c856003517c583fbd7af4dca722c Mon Sep 17 00:00:00 2001
From: Neil Jerram <Neil.Jerram@metaswitch.com>
Date: Fri, 8 Apr 2016 19:23:47 +0100
Subject: [PATCH] Fix DHCPv4 reply via --bridge-interface alias interface

Sending a DHCPv4 reply through a --bridge-interface alias interface
was inadvertently broken by

  commit 65c7212000
  Author: Lung-Pin Chang <changlp@cs.nctu.edu.tw>
  Date:   Thu Mar 19 23:22:21 2015 +0000

      dhcp: set outbound interface via cmsg in unicast reply

        If multiple routes to the same network exist, Linux blindly picks
        the first interface (route) based on destination address, which might not be
        the one we're actually offering leases. Rather than relying on this,
        always set the interface for outgoing unicast DHCP packets.

because in the aliasing case, iface_index is changed from the index of
the interface on which the packet was received, to be the interface
index of the 'bridge' interface (where the DHCP context is expected to
be defined, and so needs to be looked up).

For the cmsg code that the cited commit added, we need the original
iface_index; so this commit saves that off before the aliasing code
can change it, as rcvd_iface_index, and then uses rcvd_iface_index
instead of iface_index for the cmsg code.
2016-05-03 22:49:46 +01:00
Simon Kelley
b97026035e Limit number of upstream nameservers when logging configuration. 2016-05-03 22:34:06 +01:00
Simon Kelley
69cbf78bb6 Add contrib/lease-tools/dhcp_release6 2016-05-03 21:33:38 +01:00
Simon Kelley
c6cdf6bbee Move dhcp_release and dhcp_lease_time to contrib/lease_tools. 2016-05-03 21:14:14 +01:00
Simon Kelley
09217a1a87 Add --help to manpage . 2016-05-03 17:04:35 +01:00
Simon Kelley
332c41e2ff Debian init.d script fix. 2016-05-01 22:36:46 +01:00
Simon Kelley
2c0c36f54b Fix problem with IPv6 in new ARP-search code. 2016-05-01 20:57:08 +01:00
Simon Kelley
d6b749af91 Fix typo in SDBM hash function.
Thanks to Luis Carvalho for spotting the error.
2016-04-25 17:05:15 +01:00
Simon Kelley
14ffa0770b Fix init of per server EDNS UDP packet size. 2016-04-25 16:36:44 +01:00
Simon Kelley
87985855ad Remove pre-existing EDNS0_OPTION_NOMDEVICEID if MAC address unknown. 2016-04-25 15:33:30 +01:00
Simon Kelley
a2bc254bed Check return-code of inet_pton when parsing DHCPv4 options. 2016-04-21 22:41:31 +01:00
Simon Kelley
a7b27e84fa NULL pointer check. 2016-03-16 19:11:52 +00:00
Simon Kelley
529b030228 Tidy code. 2016-03-16 19:00:45 +00:00
Simon Kelley
4caa86dd7d Tidy. 2016-03-16 18:44:16 +00:00
Simon Kelley
e1abeeeec2 Fix memory leak in inotify code. 2016-03-16 18:12:35 +00:00
Simon Kelley
40205a053e Bound hash-iterations in DNSSEC NSEC3 checking. 2016-03-14 21:24:00 +00:00
Simon Kelley
b8ac466209 Tidy code. 2016-03-10 18:40:53 +00:00
Simon Kelley
d1377fa3c4 Account for TFTP packet headers in IPv6 correctly. 2016-03-04 21:32:21 +00:00
Simon Kelley
fa79466c2a Tighten syntax checking for dhcp-range and clarify man page. 2016-03-03 20:33:54 +00:00
Simon Kelley
a93bd4b016 Fix broken DNSMASQ_USER<x> envvars in script with more than one class. 2016-03-01 18:58:01 +00:00
Simon Kelley
407a1f3e95 Tidy parsing code. 2016-03-01 17:06:07 +00:00
Simon Kelley
4b6af5d53f Fix pointer declaration botch. 2016-03-01 17:00:26 +00:00
Simon Kelley
7aa3f9af66 format fix. 2016-03-01 16:32:30 +00:00
Simon Kelley
f7cf749943 Check return code from open() 2016-03-01 16:19:23 +00:00
Simon Kelley
aa300f7167 Fix typo in last commit. 2016-03-01 15:19:13 +00:00
Simon Kelley
c7f3bd2ac8 Replace incoming EDNS0_OPTION_NOMDEVICEID and EDNS0_OPTION_NOMCPEID options. 2016-02-28 21:48:34 +00:00
Simon Kelley
22fe2fd038 Fix --add-subnet when returning empty or default subnet. 2016-02-28 17:07:10 +00:00
Simon Kelley
7480aeffc8 Apply ceiling of lease length to TTL when --dhcp-ttl in use. 2016-02-26 21:58:20 +00:00
Simon Kelley
bec366b404 Add --tftp-mtu option. 2016-02-24 22:03:26 +00:00
Simon Kelley
e06e6e34bf Update CHANGELOG. 2016-02-24 21:26:16 +00:00
Simon Kelley
832e47beab Add --dhcp-ttl option. 2016-02-24 21:24:45 +00:00
Simon Kelley
df3d54f776 Add TTL parameter to --host-record and --cname. 2016-02-24 21:03:38 +00:00
Simon Kelley
22c0f4fe87 Fix previous commit. 2016-02-17 22:12:31 +00:00
Simon Kelley
9e4cf47ee8 Add --add-mac=text option. 2016-02-17 20:26:32 +00:00
Simon Kelley
fdc97e1383 Avoid divide-by-zero when dhcp-range is a whole /64 2016-02-13 17:47:17 +00:00
S L
a18bf3149a Avoid losing timer when deleting a RA context. 2016-02-12 17:36:20 +00:00
Simon Kelley
1566bacb2c Fix breakage in ARP code when IPV6 support not compiled in. 2016-02-05 14:48:25 +00:00
Simon Kelley
e6e751b066 Make names of ARP script actions consistent. 2016-02-01 17:59:07 +00:00
Andy Stormont
8de875f0fb Fix FTBFS on illumos 2016-02-01 12:07:57 +00:00
Chris Novakovic
4ace25c5d6 Treat REFUSED (not SERVFAIL) as an unsuccessful upstream response
Commit 51967f9807 began treating SERVFAIL
as a successful response from an upstream server (thus ignoring future
responses to the query from other upstream servers), but a typo in that
commit means that REFUSED responses are accidentally being treated as
successful instead of SERVFAIL responses.

This commit corrects this typo and provides the behaviour intended by
commit 51967f9: SERVFAIL responses are considered successful (and will
be sent back to the requester), while REFUSED responses are considered
unsuccessful (and dnsmasq will wait for responses from other upstream
servers that haven't responded yet).
2016-01-25 21:54:35 +00:00
Simon Kelley
1e5051228d Final form of configuration for EDNS0 MAC-address code. 2016-01-25 21:29:23 +00:00
Hans Dedecker
926332a764 Add --max-port config option. 2016-01-23 10:48:12 +00:00
Simon Kelley
d05dd58de1 Fix wrong reply to simple name when --domain-needed set and no servers configured.
Also return REFUSED and not SERVFAIL when out of memory.

Thanks to Allain Legacy for problem report.
2016-01-19 21:23:30 +00:00
Simon Kelley
f7443d76f7 Fix problems in last commit when DNSSEC not enabled. 2016-01-19 20:29:57 +00:00
Simon Kelley
f344dbc622 Complete DNSSEC server-selection code and set conntrack on DNSSEC queries. 2016-01-18 18:11:54 +00:00
Simon Kelley
f4d0c660ca Fix sporadic crash in find_mac() - hwlen must be zero for empty entries. 2016-01-18 12:51:08 +00:00
Simon Kelley
1801a29226 Fix botch in forward.c flags code.
Thanks to Matthias Anfree for spotting this.
2016-01-17 21:53:57 +00:00
Simon Kelley
92be34a407 Complete work to allow DNSSEC validation with private DNS servers. 2016-01-16 18:39:54 +00:00
Simon Kelley
bb58f63ce5 arp.c tidy up. 2016-01-14 19:23:10 +00:00
Simon Kelley
367341f745 Disable DNSSEC for server=/domain/.. servers unless trust-anchor provided. 2016-01-12 15:58:23 +00:00
André Glüpker
eddf365284 Fix bad cache-size calculation when hosts-file read fails. 2016-01-12 12:54:17 +00:00
Simon Kelley
a63b8b89e6 DNSSEC: Handle non-root trust anchors, and check we have a root trust anchor. 2016-01-12 11:28:58 +00:00
Simon Kelley
5757371d43 Inhibit DNSSEC validation when forwarding to private servers for a domain.
server=/example.com/<ip-of-server>

The rationale is that the chain-of-trust will not be complete to
private servers. If it was, it would not be necessary to access the
server direct.
2016-01-11 22:50:00 +00:00
Simon Kelley
b633de9413 Fix FTBFS when scripts excluded at compilation time. 2016-01-06 22:51:17 +00:00
Simon Kelley
c49778df4a Update copyright notices. Happy new year! 2016-01-06 18:52:33 +00:00
Simon Kelley
53a9173fc0 Handle building with script support enabled and DHCP disabled. 2016-01-06 17:59:13 +00:00
Simon Kelley
d917275e48 Fix botch in new arp-cache linked-list code resulting in 100% CPU spin. 2016-01-04 17:17:41 +00:00
Simon Kelley
cc7cb0b893 Fix datatype-sixe botch which broke DNSSEC sig timestamps when far in the future. 2016-01-04 16:04:51 +00:00
Simon Kelley
ec0628c4b2 Trivial code tweak. 2015-12-31 20:55:39 +00:00
Simon Kelley
97b1d25764 Correct logic for when to start helper. 2015-12-31 18:52:38 +00:00
Simon Kelley
33702ab1f8 First complete version of DNS-client-id EDNS0 and ARP tracking code. 2015-12-28 23:17:15 +00:00
Simon Kelley
11867dc28c Cache access to the kernel's ARP table. 2015-12-23 16:15:58 +00:00
Simon Kelley
d3a8b39c7d More EDNS0 packet-size tweaks. 2015-12-23 12:27:37 +00:00
Simon Kelley
15379ea1f2 Log signature algo with DNSKEY and DS, also digest with DS. 2015-12-21 18:31:55 +00:00
Simon Kelley
efef497b89 Fix build failure when DNSSEC code omitted. 2015-12-21 17:30:44 +00:00
Simon Kelley
5aa5f0ff2f Truncate DNS replies >512 bytes that the client isn't expecting. 2015-12-21 17:20:35 +00:00
Simon Kelley
5bb88f0963 Handle extending EDNS0 OPT RR. 2015-12-21 16:23:47 +00:00
Simon Kelley
1d03016bbc Split EDNS0 stuff into its own source file. 2015-12-21 14:17:06 +00:00
Simon Kelley
ce5732e84f NSEC3 check: RFC5155 para 8.2 2015-12-20 21:39:19 +00:00
Simon Kelley
a86fdf437e Minor tweak to previous commit. 2015-12-20 21:19:20 +00:00
Simon Kelley
3e86d316c4 Nasty, rare and obscure off-by-one in DNSSEC hostname_cmp(). 2015-12-20 20:50:05 +00:00
Simon Kelley
d67ecac59d More tweaks in handling unknown DNSSEC algorithms. 2015-12-20 20:44:23 +00:00
Simon Kelley
fa14bec83b Major tidy up of EDNS0 handling and computation/use of udp packet size. 2015-12-20 17:12:16 +00:00
Simon Kelley
14a4ae883d Do a better job of determining which DNSSEC sig algos are supported. 2015-12-17 17:29:57 +00:00
Simon Kelley
3b799c826d Fix brace botch in dnssec_validate_ds()
Thanks to Michał Kępień for spotting this.
2015-12-17 16:58:04 +00:00
Simon Kelley
b40f26c019 Tidy up DNSSEC non-existence code. Check zone status is NSEC proof bad. 2015-12-17 11:57:26 +00:00
Simon Kelley
dd4ad9ac7e Tweaks to EDNS0 handling in DNS replies. 2015-12-17 10:44:58 +00:00
Simon Kelley
2dbba34b2c DNSSEC validation tweak.
A zone which has at least one key with an algorithm we don't
support should be considered as insecure.
2015-12-16 13:41:58 +00:00
Simon Kelley
c2bcd1e183 Generalise RR-filtering code, for use with EDNS0. 2015-12-15 17:25:21 +00:00
Simon Kelley
d64c81fff7 Move code which caches DS records to a more logical place. 2015-12-15 16:11:06 +00:00
Simon Kelley
93be5b1e02 Abandon caching RRSIGs and returning them from cache.
The list of exceptions to being able to locally answer
cached data for validated records when DNSSEC data is requested
was getting too long, so don't ever do that. This means
that the cache no longer has to hold RRSIGS and allows
us to lose lots of code. Note that cached validated
answers are still returned as long as do=0
2015-12-15 12:04:40 +00:00
Simon Kelley
9a31b68b59 Major rationalisation of DNSSEC validation.
Much gnarly special-case code removed and replaced with correct
general implementaion. Checking of zone-status moved to DNSSEC code,
where it should be, vastly simplifying query-forwarding code.
2015-12-15 10:20:39 +00:00
Simon Kelley
0007ee9064 Fix crash at start up with conf-dir=/path,*
Thanks to Brian Carpenter and American Fuzzy Lop for finding the bug.
2015-11-21 21:47:41 +00:00
Simon Kelley
67ab3285b5 Handle unknown DS hash algos correctly.
When we can validate a DS RRset, but don't speak the hash algo it
contains, treat that the same as an NSEC/3 proving that the DS
doesn't exist. 4025 5.2
2015-11-20 23:20:47 +00:00
Edwin Török
41a8d9e99b Fix crash when empty address from DNS overlays A record from hosts. 2015-11-14 17:55:41 +00:00
Simon Kelley
90477fb794 Update list of subnet for --bogus-priv
RFC6303 specifies & recommends following zones not be forwarded
to globally facing servers.
+------------------------------+-----------------------+
| Zone                         | Description           |
+------------------------------+-----------------------+
| 0.IN-ADDR.ARPA               | IPv4 "THIS" NETWORK   |
| 127.IN-ADDR.ARPA             | IPv4 Loopback NETWORK |
| 254.169.IN-ADDR.ARPA         | IPv4 LINK LOCAL       |
| 2.0.192.IN-ADDR.ARPA         | IPv4 TEST-NET-1       |
| 100.51.198.IN-ADDR.ARPA      | IPv4 TEST-NET-2       |
| 113.0.203.IN-ADDR.ARPA       | IPv4 TEST-NET-3       |
| 255.255.255.255.IN-ADDR.ARPA | IPv4 BROADCAST        |
+------------------------------+-----------------------+

Signed-off-by: Kevin Darbyshire-Bryant <kevin@darbyshire-bryant.me.uk>
2015-10-20 21:21:32 +01:00
Simon Kelley
98079ea898 Catch errors from sendmsg in DHCP code.
Logs, eg,  iptables DROPS of dest 255.255.255.255
2015-10-13 20:32:21 +01:00
Simon Kelley
4790115455 Use /run/dnsmasq instead of /var/run/dnsmasq in Debian package. 2015-09-29 22:54:41 +01:00
Simon Kelley
27b78d990b Rationalise 5e3e464ac4 2015-09-26 21:40:45 +01:00
Simon Kelley
77607cbea0 Respect the --no-resolv flag in inotify code. 2015-09-10 23:08:43 +01:00
Simon Kelley
102208df69 DHCPv6 option 56 does not hold an address list. (RFC 5908). 2015-09-10 21:50:00 +01:00
Simon Kelley
6de81f1250 Handle signed dangling CNAME replies to DS queries. 2015-09-09 22:51:13 +01:00
Simon Kelley
20fd11e11a Clarify man page on RDNSS set in router advertisement. 2015-08-26 22:48:13 +01:00
Simon Kelley
9cdcfe9f19 Suggest solution to ENOMEM error with IPv6 multicast. 2015-08-26 22:38:08 +01:00
Simon Kelley
5e3e464ac4 Fix behaviour of empty dhcp-option=option6:dns-server, which should inhibit sending option. 2015-08-25 23:08:39 +01:00
Simon Kelley
3a3965ac21 Don't answer non-auth queries for auth zones locally when --localise-queries set. 2015-08-09 17:45:06 +01:00
Ed Bardsley
a7369bef8a Enhance --add-subnet to allow arbitary subnet addresses. 2015-08-05 21:17:18 +01:00
Simon Kelley
d2aa7dfbb6 Include 0.0.0.0/8 in DNS rebind checks. 2015-08-03 21:52:12 +01:00
Simon Kelley
63ec5d1264 Fix new poll() code for helper pipe. Removed CPU-spin. 2015-07-30 20:59:07 +01:00
Simon Kelley
f6381cf482 Declare utime(). 2015-07-27 19:48:43 +01:00
Kevin Darbyshire-Bryant
34b5d19488 Update DNSSEC timestamp file on process TERM. 2015-07-27 19:34:23 +01:00
Conrad Kostecki
d6cb7e4815 Update german translation 2015-07-27 19:22:49 +01:00
Simon Kelley
d389e0191b DNSSEC fix: correct logic for signed records in unsigned DNS space. 2015-07-27 18:56:43 +01:00
Simon Kelley
d3699bb6bc Small tweak to DNSSEC fix. 2015-07-16 22:37:37 +01:00
Simon Kelley
13480e8c2a DNSSEC fix, signed wildcard CNAME to unsigned domain. 2015-07-16 22:23:13 +01:00
Jan Psota
5b3b93f80a Update Polish translation. 2015-07-15 19:57:47 +01:00
Simon Kelley
b69e845b1c Close Lauchpad bug in Debian changelog. 2015-07-15 19:54:50 +01:00
Simon Kelley
90c3822bfa Grow pollfds array exponentially. 2015-07-13 12:47:52 +01:00
Simon Kelley
c895a0626d Merge messages for translations. 2015-07-12 21:27:40 +01:00
Simon Kelley
b842bc97bb Use poll() instead of select() to remove limits on open file descriptors. 2015-07-12 21:09:11 +01:00
Simon Kelley
0f38fa05a6 Log message typo. 2015-07-08 22:42:14 +01:00
Simon Kelley
45c5cb1f8f Fix compilation warning. 2015-07-08 22:40:57 +01:00
Simon Kelley
f6d6956261 Test for overflowing platform FD_SET size. 2015-07-08 22:38:13 +01:00
Simon Kelley
60176c7bf4 Bump version in Debian changelog. 2015-07-07 21:54:55 +01:00
Simon Kelley
362c9303da Fix inotify code to handle dangling symlinks better. 2015-07-06 21:48:49 +01:00
Simon Kelley
5e95a552ee Avoid hanngs in DHCP ping code when system time goes backwards. 2015-07-05 22:31:30 +01:00
Simon Kelley
90cb222551 --conf-file should read no file, not try and read the default file. 2015-07-05 21:59:10 +01:00
Ján Sáreník
850163288d Manpage typo fix. 2015-07-05 21:23:27 +01:00
Simon Kelley
e3ec6f0bd7 Handle CNAMEs to DS records when confirming absence of DS for DNSSEC. 2015-06-12 21:39:11 +01:00
Simon Kelley
f7bfbdc872 Merge messages and fix makefile process to do this. 2015-06-10 22:31:02 +01:00
Neil Jerram
4918bd5505 Documenation updates for --bridge-interface and "off-link". 2015-06-10 22:23:20 +01:00
Neil Jerram
9bad339af8 Apply --bridge-interfaces to unsolicited router advertisements. 2015-06-10 22:16:35 +01:00
Neil Jerram
ba4fc0f996 Upply --bridge-interface aliasing to solicited router advertisements. 2015-06-10 22:14:49 +01:00
Neil Jerram
2fd5bc952d Allow router advertisements to have the "off-link" bit set. 2015-06-10 22:13:06 +01:00
Neil Jerram
0ddb8769bb Extend --bridge-interface aliasing to DHCPv6. 2015-06-10 22:11:06 +01:00
Neil Jerram
654f59e762 Fix logging of unknown interface in --bridge-interface, DHPCv4. 2015-06-10 22:06:33 +01:00
Simon Kelley
d91b1fd402 Add a couple of missed logging strings to the catalogue. 2015-06-09 20:45:07 +01:00
Nicolas Cavallari
c6d82c9ba6 Add Dbus methods to create and delete DHCP leases. 2015-06-09 20:42:20 +01:00
Simon Kelley
4d25cf89d5 Handle corner cases in NSEC coverage checks. 2015-06-06 23:13:57 +01:00
Simon Kelley
24e9207e13 More reproducibility fixes for Debian package. 2015-06-04 22:32:43 +01:00
Simon Kelley
89130d91d6 DHCPv6: DHCPCONFIRM should be OK for any address on link, not just dynamic addresses. 2015-06-03 22:34:14 +01:00
Simon Kelley
d644b2a17d Close Debian bug for bug fixed upstream. 2015-06-01 21:00:16 +01:00
swigger
bd7bfa21c4 Correctly sanitise DNS header bits in answer when recreating query for retry. 2015-06-01 20:54:59 +01:00
Simon Kelley
403de05925 Merge branch 'master' of ssh://thekelleys.org.uk/var/cache/git/dnsmasq 2015-05-26 22:12:01 +01:00
John Hanks
46c89f2bd0 Add infiniband to example config file. 2015-05-26 22:07:57 +01:00
Christian Demsar
23facf0d77 Man page typo. 2015-05-20 20:26:23 +01:00
Simon Kelley
549b1a478c Tweak immediately previous patch. 2015-05-20 20:20:24 +01:00
Simon Kelley
7f8565b94c Select correct DHCP context when in PXE bootserver mode. 2015-05-19 23:01:27 +01:00
Simon Kelley
06568c6636 Remove support for DNS Extended Label Types.
The support was only partial, and the whole concept is
now deprecated in the standards.
2015-05-15 20:43:48 +01:00
Simon Kelley
5d07d77e75 Fix buffer overflow introduced in 2.73rc6.
Fix off-by-one in code which checks for over-long domain names
in received DNS packets. This enables buffer overflow attacks
which can certainly crash dnsmasq and may allow for arbitrary
code execution. The problem was introduced in commit b8f16556d,
release 2.73rc6, so has not escaped into any stable release.
Note that the off-by-one was in the label length determination,
so the buffer can be overflowed by as many bytes as there are
labels in the name - ie, many.

Thanks to Ron Bowes, who used lcmatuf's afl-fuzz tool to find
the problem.
2015-05-15 18:13:06 +01:00
Simon Kelley
62018e1f72 Use correct DHCP context for PXE-proxy server-id. 2015-05-14 21:30:00 +01:00
Simon Kelley
7c0f2543a7 Tweak last commit. 2015-05-14 21:16:18 +01:00
Simon Kelley
ca85a28241 Allow T1 and T2 DHCPv4 options to be set. 2015-05-13 22:33:04 +01:00
Simon Kelley
585840b033 Pointer to mail-archive mailing list mirror in doc.html. 2015-05-13 12:35:57 +01:00
Simon Kelley
dec180ac00 Tweak Debian systemd unit file. 2015-05-13 12:16:13 +01:00
Simon Kelley
86fa104692 Tweak EDNS timeout code. 2015-05-10 14:04:06 +01:00
Simon Kelley
b059c96dc6 Check IPv4-mapped IPv6 addresses with --stop-rebind. 2015-05-08 20:25:51 +01:00
Simon Kelley
a77cec8d58 Handle UDP packet loss when fragmentation of large packets is broken. 2015-05-08 16:25:38 +01:00
Nicolas Cavallari
64bcff1c7c Constify some DHCP lease management functions. 2015-04-28 21:55:18 +01:00
Simon Kelley
2ed162ac20 Don't remove RRSIG RR from answers to ANY queries when the do bit is not set. 2015-04-28 21:26:35 +01:00
Simon Kelley
e66b4dff3c Fix argument-order botch which broke DNSSEC for TCP queries. 2015-04-28 20:45:57 +01:00
Johnny S. Lee
8efd731cc4 Make get-version work when repo is a git submodule. 2015-04-26 22:23:57 +01:00
Simon Kelley
a5ae1f8587 Logs in DHCPv6 not suppressed by dhcp6-quiet. 2015-04-25 21:46:10 +01:00
Simon Kelley
b8f16556d3 Tweaks to previous, DNS label charset commit. 2015-04-22 21:14:31 +01:00
Simon Kelley
cbe379ad6b Handle domain names with '.' or /000 within labels.
Only in DNSSEC mode, where we might need to validate or store
such names. In none-DNSSEC mode, simply don't cache these, as before.
2015-04-21 22:57:06 +01:00
Simon Kelley
338b340be9 Revert 61b838dd57 and just quieten log instead. 2015-04-20 21:34:05 +01:00
Moshe Levi
a006eb7e14 Check IP address command line arg in dhcp_release.c 2015-04-19 22:10:40 +01:00
Simon Kelley
554b580e97 Log domain when reporting DNSSEC validation failure. 2015-04-17 22:50:20 +01:00
Simon Kelley
0df29f5e23 Note CVE-2015-3294 2015-04-16 15:24:52 +01:00
Stefan Tomanek
b4c0f092d8 Fix (srk induced) crash in new tftp_no_fail code. 2015-04-16 15:20:59 +01:00
Simon Kelley
78c6184752 Auth: correct replies to NS and SOA in .arpa zones. 2015-04-16 15:05:30 +01:00
Simon Kelley
38440b204d Fix crash in auth code with odd configuration. 2015-04-12 21:52:47 +01:00
Simon Kelley
ad4a8ff7d9 Fix crash on receipt of certain malformed DNS requests. 2015-04-09 21:48:00 +01:00
Simon Kelley
04b0ac0537 Fix crash caused by looking up servers.bind when many servers defined. 2015-04-06 17:19:13 +01:00
Simon Kelley
982faf4024 Fix compiler warning when not including DNSSEC. 2015-04-03 21:42:30 +01:00
Simon Kelley
fe3992f9fa Return INSECURE, rather than BOGUS when DS proved not to exist.
Return INSECURE when validating DNS replies which have RRSIGs, but
when a needed DS record in the trust chain is proved not to exist.
It's allowed for a zone to set up DNSKEY and RRSIG records first, then
add a DS later, completing the chain of trust.

Also, since we don't have the infrastructure to track that these
non-validated replies have RRSIGS, don't cache them, so we don't
provide answers with missing RRSIGS from the cache.
2015-04-03 21:25:05 +01:00
Stefan Tomanek
7aa970e2c7 Whitespace fixes. 2015-04-01 17:55:07 +01:00
Stefan Tomanek
30d0879ed5 add --tftp-no-fail to ignore missing tftp root 2015-03-31 22:32:11 +01:00
77 changed files with 10778 additions and 7786 deletions

210
CHANGELOG
View File

@@ -1,3 +1,141 @@
version 2.76
Include 0.0.0.0/8 in DNS rebind checks. This range
translates to hosts on the local network, or, at
least, 0.0.0.0 accesses the local host, so could
be targets for DNS rebinding. See RFC 5735 section 3
for details. Thanks to Stephen Röttger for the bug report.
Enhance --add-subnet to allow arbitrary subnet addresses.
Thanks to Ed Barsley for the patch.
Respect the --no-resolv flag in inotify code. Fixes bug
which caused dnsmasq to fail to start if a resolv-file
was a dangling symbolic link, even of --no-resolv set.
Thanks to Alexander Kurtz for spotting the problem.
Fix crash when an A or AAAA record is defined locally,
in a hosts file, and an upstream server sends a reply
that the same name is empty. Thanks to Edwin Török for
the patch.
Fix failure to correctly calculate cache-size when
reading a hosts-file fails. Thanks to André Glüpker
for the patch.
Fix wrong answer to simple name query when --domain-needed
set, but no upstream servers configured. Dnsmasq returned
REFUSED, in this case, when it should be the same as when
upstream servers are configured - NOERROR. Thanks to
Allain Legacy for spotting the problem.
Return REFUSED when running out of forwarding table slots,
not SERVFAIL.
Add --max-port configuration. Thanks to Hans Dedecker for
the patch.
Add --script-arp and two new functions for the dhcp-script.
These are "arp" and "arp-old" which announce the arrival and
removal of entries in the ARP or nieghbour tables.
Extend --add-mac to allow a new encoding of the MAC address
as base64, by configurting --add-mac=base64
Add --add-cpe-id option.
Don't crash with divide-by-zero if an IPv6 dhcp-range
is declared as a whole /64.
(ie xx::0 to xx::ffff:ffff:ffff:ffff)
Thanks to Laurent Bendel for spotting this problem.
Add support for a TTL parameter in --host-record and
--cname.
Add --dhcp-ttl option.
Add --tftp-mtu option. Thanks to Patrick McLean for the
initial patch.
Check return-code of inet_pton() when parsing dhcp-option.
Bad addresses could fail to generate errors and result in
garbage dhcp-options being sent. Thanks to Marc Branchaud
for spotting this.
Fix wrong value for EDNS UDP packet size when using
--servers-file to define upstream DNS servers. Thanks to
Scott Bonar for the bug report.
Move the dhcp_release and dhcp_lease_time tools from
contrib/wrt to contrib/lease-tools.
Add dhcp_release6 to contrib/lease-tools. Many thanks
to Sergey Nechaev for this code.
To avoid filling logs in configurations which define
many upstream nameservers, don't log more that 30 servers.
The number to be logged can be changed as SERVERS_LOGGED
in src/config.h.
Swap the values if BC_EFI and x86-64_EFI in --pxe-service.
These were previously wrong due to an error in RFC 4578.
If you're using BC_EFI to boot 64-bit EFI machines, you
will need to update your config.
Add ARM32_EFI and ARM64_EFI as valid architectures in
--pxe-service.
Fix PXE booting for UEFI architectures. Modify PXE boot
sequence in this case to force the client to talk to dnsmasq
over port 4011. This makes PXE and especially proxy-DHCP PXE
work with these archictectures.
Workaround problems with UEFI PXE clients. There exist
in the wild PXE clients which have problems with PXE
boot menus. To work around this, when there's a single
--pxe-service which applies to client, then that target
will be booted directly, rather then sending a
single-item boot menu.
Many thanks to Jarek Polok, Michael Kuron and Dreamcat4
for their work on the long-standing UEFI PXE problem.
Subtle change in the semantics of "basename" in
--pxe-service. The historical behaviour has always been
that the actual filename downloaded from the TFTP server
is <basename>.<layer> where <layer> is an integer which
corresponds to the layer parameter supplied by the client.
It's not clear what the function of the "layer"
actually is in the PXE protocol, and in practise layer
is always zero, so the filename is <basename>.0
The new behaviour is the same as the old, except when
<basename> includes a file suffix, in which case
the layer suffix is no longer added. This allows
sensible suffices to be used, rather then the
meaningless ".0". Only in the unlikely event that you
have a config with a basename which already has a
suffix, is this an incompatible change, since the file
downloaded will change from name.suffix.0 to just
name.suffix
version 2.75
Fix reversion on 2.74 which caused 100% CPU use when a
dhcp-script is configured. Thanks to Adrian Davey for
reporting the bug and testing the fix.
version 2.74
Fix reversion in 2.73 where --conf-file would attempt to
read the default file, rather than no file.
Fix inotify code to handle dangling symlinks better and
not SEGV in some circumstances.
DNSSEC fix. In the case of a signed CNAME generated by a
wildcard which pointed to an unsigned domain, the wrong
status would be logged, and some necessary checks omitted.
version 2.73
Fix crash at startup when an empty suffix is supplied to
--conf-dir, also trivial memory leak. Thanks to
@@ -68,10 +206,72 @@ version 2.73
Fix broken DNSSEC validation of ECDSA signatures.
Add --dnssec-timestamp option, which provides an automatic
way to detect when the system time becomes valid after boot
on systems without an RTC, whilst allowing DNS queries before the
clock is valid so that NTP can run. Thanks to
Kevin Darbyshire-Bryant for developing this idea.
way to detect when the system time becomes valid after
boot on systems without an RTC, whilst allowing DNS
queries before the clock is valid so that NTP can run.
Thanks to Kevin Darbyshire-Bryant for developing this idea.
Add --tftp-no-fail option. Thanks to Stefan Tomanek for
the patch.
Fix crash caused by looking up servers.bind, CHAOS text
record, when more than about five --servers= lines are
in the dnsmasq config. This causes memory corruption
which causes a crash later. Thanks to Matt Coddington for
sterling work chasing this down.
Fix crash on receipt of certain malformed DNS requests.
Thanks to Nick Sampanis for spotting the problem.
Note that this is could allow the dnsmasq process's
memory to be read by an attacker under certain
circumstances, so it has a CVE, CVE-2015-3294
Fix crash in authoritative DNS code, if a .arpa zone
is declared as authoritative, and then a PTR query which
is not to be treated as authoritative arrived. Normally,
directly declaring .arpa zone as authoritative is not
done, so this crash wouldn't be seen. Instead the
relevant .arpa zone should be specified as a subnet
in the auth-zone declaration. Thanks to Johnny S. Lee
for the bugreport and initial patch.
Fix authoritative DNS code to correctly reply to NS
and SOA queries for .arpa zones for which we are
declared authoritative by means of a subnet in auth-zone.
Previously we provided correct answers to PTR queries
in such zones (including NS and SOA) but not direct
NS and SOA queries. Thanks to Johnny S. Lee for
pointing out the problem.
Fix logging of DHCPREPLY which should be suppressed
by quiet-dhcp6. Thanks to J. Pablo Abonia for
spotting the problem.
Try and handle net connections with broken fragmentation
that lose large UDP packets. If a server times out,
reduce the maximum UDP packet size field in the EDNS0
header to 1280 bytes. If it then answers, make that
change permanent.
Check IPv4-mapped IPv6 addresses when --stop-rebind
is active. Thanks to Jordan Milne for spotting this.
Allow DHCPv4 options T1 and T2 to be set using --dhcp-option.
Thanks to Kevin Benton for patches and work on this.
Fix code for DHCPCONFIRM DHCPv6 messages to confirm addresses
in the correct subnet, even of not in dynamic address
allocation range. Thanks to Steve Hirsch for spotting
the problem.
Add AddDhcpLease and DeleteDhcpLease DBus methods. Thanks
to Nicolas Cavallari for the patch.
Allow configuration of router advertisements without the
"on-link" bit set. Thanks to Neil Jerram for the patch.
Extend --bridge-interface to DHCPv6 and router
advertisements. Thanks to Neil Jerram for the patch.
version 2.72
@@ -117,7 +317,7 @@ version 2.72
Fix problem with --local-service option on big-endian platforms
Thanks to Richard Genoud for the patch.
version 2.71
Subtle change to error handling to help DNSSEC validation
when servers fail to provide NODATA answers for

View File

@@ -1,4 +1,4 @@
# dnsmasq is Copyright (c) 2000-2015 Simon Kelley
# dnsmasq is Copyright (c) 2000-2016 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
@@ -73,7 +73,8 @@ objs = cache.o rfc1035.o util.o option.o forward.o network.o \
dnsmasq.o dhcp.o lease.o rfc2131.o netlink.o dbus.o bpf.o \
helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o \
dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o \
domain.o dnssec.o blockdata.o tables.o loop.o inotify.o
domain.o dnssec.o blockdata.o tables.o loop.o inotify.o \
poll.o rrfilter.o edns0.o arp.o
hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \
dns-protocol.h radv-protocol.h ip6addr.h
@@ -117,7 +118,7 @@ install-i18n : all-i18n install-common
cd $(MAN); ../bld/install-man $(DESTDIR)$(MANDIR) $(INSTALL)
merge :
@cd $(BUILDDIR) && $(MAKE) -f $(top)/Makefile dnsmasq.pot
@cd $(BUILDDIR) && $(MAKE) top="$(top)" -f $(top)/Makefile dnsmasq.pot
for f in `cd $(PO); echo *.po`; do \
echo -n msgmerge $(PO)/$$f && $(MSGMERGE) --no-wrap -U $(PO)/$$f $(BUILDDIR)/dnsmasq.pot; \
done

View File

@@ -10,7 +10,7 @@ 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
loop.c inotify.c poll.c rrfilter.c edns0.c arp.c
LOCAL_MODULE := dnsmasq

View File

@@ -11,8 +11,9 @@
# If there is more than one v[0-9].* tag, sort them and use the
# first. This favours, eg v2.63 over 2.63rc6.
if which git >/dev/null 2>&1 && [ -d $1/.git ]; then
cd $1; git describe | sed 's/^v//'
if which git >/dev/null 2>&1 && \
([ -d $1/.git ] || grep '^gitdir:' $1/.git >/dev/null 2>&1); then
cd $1; git describe | sed 's/^v//'
elif grep '\$Format:%d\$' $1/VERSION >/dev/null 2>&1; then
# unsubstituted VERSION, but no git available.
echo UNKNOWN

View File

@@ -0,0 +1,6 @@
CFLAGS?= -O2 -Wall -W
all: dhcp_release dhcp_release6 dhcp_lease_time
clean:
rm -f *~ *.o core dhcp_release dhcp_release6 dhcp_lease_time

View File

@@ -277,6 +277,11 @@ int main(int argc, char **argv)
exit(1);
}
if (inet_addr(argv[2]) == INADDR_NONE)
{
perror("invalid ip address");
exit(1);
}
lease.s_addr = inet_addr(argv[2]);
server = find_interface(lease, nl, if_nametoindex(argv[1]));

View File

@@ -0,0 +1,38 @@
.TH DHCP_RELEASE 1
.SH NAME
dhcp_release6 \- Release a DHCPv6 lease on a the local dnsmasq DHCP server.
.SH SYNOPSIS
.B dhcp_release6 --iface <interface> --client-id <client-id> --server-id
server-id --iaid <iaid> --ip <IP> [--dry-run] [--help]
.SH "DESCRIPTION"
A utility which forces the DHCP server running on this machine to release a
DHCPv6 lease.
.SS OPTIONS
.IP "-a, --ip"
IPv6 address to release.
.IP "-c, --client-id"
Colon-separated hex string representing DHCPv6 client id. Normally
it can be found in leases file both on client and server.
.IP "-d, --dry-run"
Print hexadecimal representation of generated DHCPv6 release packet to standard
output and exit.
.IP "-h, --help"
print usage information to standard output and exit.
.IP "-i, --iaid"
Decimal representation of DHCPv6 IAID. Normally it can be found in leases file
both on client and server.
.IP "-n, --iface"
Network interface to send a DHCPv6 release packet from.
.IP "-s, --server-id"
Colon-separated hex string representing DHCPv6 server id. Normally
it can be found in leases file both on client and server.
.SH NOTES
MUST be run as root - will fail otherwise.
.SH LIMITATIONS
Only usable on IPv6 DHCP leases.
.SH SEE ALSO
.BR dnsmasq (8)
.SH AUTHOR
This manual page was written by Simon Kelley <simon@thekelleys.org.uk>.

View File

@@ -0,0 +1,445 @@
/*
dhcp_release6 --iface <interface> --client-id <client-id> --server-id
server-id --iaid <iaid> --ip <IP> [--dry-run] [--help]
MUST be run as root - will fail othewise
*/
/* Send a DHCPRELEASE message to IPv6 multicast address via the specified interface
to tell the local DHCP server to delete a particular lease.
The interface argument is the interface in which a DHCP
request _would_ be received if it was coming from the client,
rather than being faked up here.
The client-id argument is colon-separated hex string and mandatory. Normally
it can be found in leases file both on client and server
The server-id argument is colon-separated hex string and mandatory. Normally
it can be found in leases file both on client and server.
The iaid argument is numeric string and mandatory. Normally
it can be found in leases file both on client and server.
IP is an IPv6 adress to release
If --dry-run is specified, dhcp_release6 just prints hexadecimal represantation of
packet to send to stdout and exits.
If --help is specified, dhcp_release6 print usage information to stdout and exits
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <getopt.h>
#include <errno.h>
#include <unistd.h>
#define NOT_REPLY_CODE 115
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
enum DHCP6_TYPES{
SOLICIT = 1,
ADVERTISE = 2,
REQUEST = 3,
CONFIRM = 4,
RENEW = 5,
REBIND = 6,
REPLY = 7,
RELEASE = 8,
DECLINE = 9,
RECONFIGURE = 10,
INFORMATION_REQUEST = 11,
RELAY_FORW = 12,
RELAY_REPL = 13
};
enum DHCP6_OPTIONS{
CLIENTID = 1,
SERVERID = 2,
IA_NA = 3,
IA_TA = 4,
IAADDR = 5,
ORO = 6,
PREFERENCE = 7,
ELAPSED_TIME = 8,
RELAY_MSG = 9,
AUTH = 11,
UNICAST = 12,
STATUS_CODE = 13,
RAPID_COMMIT = 14,
USER_CLASS = 15,
VENDOR_CLASS = 16,
VENDOR_OPTS = 17,
INTERFACE_ID = 18,
RECONF_MSG = 19,
RECONF_ACCEPT = 20,
};
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}
};
const short DHCP6_CLIENT_PORT = 546;
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_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_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;
}
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_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_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 -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;
}
}
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;
}
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;
}
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;
client_addr.sin6_port = htons(DHCP6_CLIENT_PORT);
client_addr.sin6_flowinfo = 0;
client_addr.sin6_scope_id =0;
inet_pton(AF_INET6, "::", &client_addr.sin6_addr);
bind(sock, (struct sockaddr*)&client_addr, sizeof(struct sockaddr_in6));
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");
exit(4);
}
recv_size = recvfrom(sock, response, sizeof(response), MSG_DONTWAIT, NULL, 0);
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){
sleep(1);
continue;
}
return result;
}
fprintf(stderr, "Response timed out\n");
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();
}
}
if (iaid == UNINITIALIZED){
fprintf(stderr, "Missing required iaid parameter\n");
usage(argv[0], stderr);
return -1;
}
if (server_id == UNINITIALIZED){
fprintf(stderr, "Missing required server-id parameter\n");
usage(argv[0], stderr);
return -1;
}
if (client_id == UNINITIALIZED){
fprintf(stderr, "Missing required client-id parameter\n");
usage(argv[0], stderr);
return -1;
}
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");
usage(argv[0], stderr);
return -1;
}
struct dhcp6_packet packet = create_release_packet(iaid, ip, client_id, server_id);
if (dry_run){
uint16_t 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

@@ -1,6 +0,0 @@
CFLAGS?= -O2 -Wall -W
all: dhcp_release dhcp_lease_time
clean:
rm -f *~ *.o core dhcp_release dhcp_lease_time

View File

@@ -169,6 +169,80 @@ Return an array of strings, each string is the IP address of an upstream
server which has been found to loop queries back to this dnsmasq instance, and
it therefore not being used.
AddDhcpLease
------------
Returns nothing. Adds or updates a DHCP or DHCPv6 lease to the internal lease
database, as if a client requested and obtained a lease.
If a lease for the IPv4 or IPv6 address already exist, it is overwritten.
Note that this function will trigger the DhcpLeaseAdded or DhcpLeaseUpdated
D-Bus signal and will run the configured DHCP lease script accordingly.
This function takes many arguments which are the lease parameters:
- A string with the textual representation of the IPv4 or IPv6 address of the
client.
Examples:
"192.168.1.115"
"1003:1234:abcd::1%eth0"
"2001:db8:abcd::1"
- A string representing the hardware address of the client, using the same
format as the one used in the lease database.
Examples:
"00:23:45:67:89:ab"
"06-00:20:e0:3b:13:af" (token ring)
- The hostname of the client, as an array of bytes (so there is no problem
with non-ASCII character encoding). May be empty.
Example (for "hostname.or.fqdn"):
[104, 111, 115, 116, 110, 97, 109, 101, 46, 111, 114, 46, 102, 113, 100, 110]
- The client identifier (IPv4) or DUID (IPv6) as an array of bytes. May be
empty.
Examples:
DHCPv6 DUID:
[0, 3, 0, 1, 0, 35, 69, 103, 137, 171]
DHCPv4 client identifier:
[255, 12, 34, 56, 78, 0, 1, 0, 1, 29, 9, 99, 190, 35, 69, 103, 137, 171]
- The duration of the lease, in seconds. If the lease is updated, then
the duration replaces the previous duration.
Example:
7200
- The IAID (Identity association identifier) of the DHCPv6 lease, as a network
byte-order unsigned integer. For DHCPv4 leases, this must be set to 0.
Example (for IPv6):
203569230
- A boolean which, if true, indicates that the DHCPv6 lease is for a temporary
address (IA_TA). If false, the DHCPv6 lease is for a non-temporary address
(IA_NA). For DHCPv4 leases, this must be set to false.
RemoveDhcpLease
---------------
Returns nothing. Removes a DHCP or DHCPv6 lease to the internal lease
database, as if a client sent a release message to abandon a lease.
This function takes only one parameter: the text representation of the
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.
2. SIGNALS

34
debian/changelog vendored
View File

@@ -1,12 +1,42 @@
dnsmasq (2.76-1) unstable; urgency=low
* New upstream. (closes: #798586)
* Use /run/dnsmasq directly, rather than relying on link from /var/run
to avoid problems before /var is mounted. (closes: #800351)
* Test for the existance of /usr/share/doc/dnsmasq rather then
/etc/dnsmasq.d/README in the daemon startup script. (closes: #819856)
* Add --help to manpage and mention dhcp6 in summary. (closes: #821226)
-- Simon Kelley <simon@thekelleys.org.uk> Thu, 10 Sep 2015 23:07:21 +0000
dnsmasq (2.75-1) unstable; urgency=low
* New upstream. (closes: #794095)
-- Simon Kelley <simon@thekelleys.org.uk> Thu, 30 Jul 2015 20:58:31 +0000
dnsmasq (2.74-1) unstable; urgency=low
* New upstream. (LP: #1468611)
-- Simon Kelley <simon@thekelleys.org.uk> Wed, 15 Jul 2015 21:54:11 +0000
dnsmasq (2.73-2) unstable; urgency=low
* Fix behaviour of empty --conf-file (closes: #790341)
-- Simon Kelley <simon@thekelleys.org.uk> Thu, 7 Jul 2015 21:46:42 +0000
dnsmasq (2.73-1) unstable; urgency=low
* New upstream.
* New upstream. (closes: #786996)
* Tweak field width in cache dump to avoid truncating IPv6
addresses. (closes: #771557)
* Add newline at the end of example config file. (LP: #1416895)
* Make Debian package build reproducible. (closes: #777323)
* Add Requires=network.target to systemd unit.
-- Simon Kelley <simon@thekelleys.org.uk> Wed, 11 Feb 2015 21:59:42 +0000
-- Simon Kelley <simon@thekelleys.org.uk> Thu, 4 Jun 2015 22:31:42 +0000
dnsmasq (2.72-3) unstable; urgency=medium

2
debian/copyright vendored
View File

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

View File

@@ -17,8 +17,8 @@ if [ "$1" = "configure" ]; then
# dnsmasq-base, but it's much easier to create it here so that
# we don't have synchronisation issues with the creation of the
# dnsmasq user.
if [ ! -d /var/run/dnsmasq ]; then
mkdir /var/run/dnsmasq
chown dnsmasq:nogroup /var/run/dnsmasq
if [ ! -d /run/dnsmasq ]; then
mkdir /run/dnsmasq
chown dnsmasq:nogroup /run/dnsmasq
fi
fi

View File

@@ -7,5 +7,5 @@ if [ purge = "$1" ]; then
else
echo >&2 "not removing dnsmasq system account because deluser command was not found"
fi
rm -rf /var/run/dnsmasq
rm -rf /run/dnsmasq
fi

52
debian/init vendored
View File

@@ -8,7 +8,8 @@
# Description: DHCP and DNS server
### END INIT INFO
set +e # Don't exit on error status
# Don't exit on error status
set +e
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DAEMON=/usr/sbin/dnsmasq
@@ -29,12 +30,11 @@ if [ -r /etc/default/locale ]; then
export LANG
fi
# /etc/dnsmasq.d/README is a non-conffile installed by the dnsmasq package.
# Should the dnsmasq package be removed, the following test ensures that
# the daemon is no longer started, even if the dnsmasq-base package is
# still in place.
test -e /etc/dnsmasq.d/README || exit 0
# The following test ensures the dnsmasq service is not started, when the
# package 'dnsmasq' is removed but not purged, even if the dnsmasq-base
# package is still in place.
test -d /usr/share/doc/dnsmasq || exit 0
test -x $DAEMON || exit 0
# Provide skeleton LSB log functions for backports which don't have LSB functions.
@@ -81,7 +81,7 @@ if [ ! "$RESOLV_CONF" ] &&
[ "$IGNORE_RESOLVCONF" != "yes" ] &&
[ -x /sbin/resolvconf ]
then
RESOLV_CONF=/var/run/dnsmasq/resolv.conf
RESOLV_CONF=/run/dnsmasq/resolv.conf
fi
for INTERFACE in $DNSMASQ_INTERFACE; do
@@ -121,16 +121,16 @@ start()
# 1 if daemon was already running
# 2 if daemon could not be started
# /var/run may be volatile, so we need to ensure that
# /var/run/dnsmasq exists here as well as in postinst
if [ ! -d /var/run/dnsmasq ]; then
mkdir /var/run/dnsmasq || return 2
chown dnsmasq:nogroup /var/run/dnsmasq || return 2
# /run may be volatile, so we need to ensure that
# /run/dnsmasq exists here as well as in postinst
if [ ! -d /run/dnsmasq ]; then
mkdir /run/dnsmasq || return 2
chown dnsmasq:nogroup /run/dnsmasq || return 2
fi
start-stop-daemon --start --quiet --pidfile /var/run/dnsmasq/$NAME.pid --exec $DAEMON --test > /dev/null || return 1
start-stop-daemon --start --quiet --pidfile /var/run/dnsmasq/$NAME.pid --exec $DAEMON -- \
-x /var/run/dnsmasq/$NAME.pid \
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 \
${MAILHOSTNAME:+ -m $MAILHOSTNAME} \
${MAILTARGET:+ -t $MAILTARGET} \
${DNSMASQ_USER:+ -u $DNSMASQ_USER} \
@@ -167,7 +167,7 @@ stop()
# 1 if daemon was already stopped
# 2 if daemon could not be stopped
# other if a failure occurred
start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile /var/run/dnsmasq/$NAME.pid --name $NAME
start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile /run/dnsmasq/$NAME.pid --name $NAME
}
stop_resolvconf()
@@ -185,9 +185,9 @@ status()
# 1 if daemon is dead and pid file exists
# 3 if daemon is not running
# 4 if daemon status is unknown
start-stop-daemon --start --quiet --pidfile /var/run/dnsmasq/$NAME.pid --exec $DAEMON --test > /dev/null
start-stop-daemon --start --quiet --pidfile /run/dnsmasq/$NAME.pid --exec $DAEMON --test > /dev/null
case "$?" in
0) [ -e "/var/run/dnsmasq/$NAME.pid" ] && return 1 ; return 3 ;;
0) [ -e "/run/dnsmasq/$NAME.pid" ] && return 1 ; return 3 ;;
1) return 0 ;;
*) return 4 ;;
esac
@@ -278,7 +278,7 @@ case "$1" in
esac
;;
dump-stats)
kill -s USR1 `cat /var/run/dnsmasq/$NAME.pid`
kill -s USR1 `cat /run/dnsmasq/$NAME.pid`
;;
systemd-start-resolvconf)
start_resolvconf
@@ -287,13 +287,13 @@ case "$1" in
stop_resolvconf
;;
systemd-exec)
# /var/run may be volatile, so we need to ensure that
# /var/run/dnsmasq exists here as well as in postinst
if [ ! -d /var/run/dnsmasq ]; then
mkdir /var/run/dnsmasq || return 2
chown dnsmasq:nogroup /var/run/dnsmasq || return 2
# /run may be volatile, so we need to ensure that
# /run/dnsmasq exists here as well as in postinst
if [ ! -d /run/dnsmasq ]; then
mkdir /run/dnsmasq || return 2
chown dnsmasq:nogroup /run/dnsmasq || return 2
fi
exec $DAEMON -x /var/run/dnsmasq/$NAME.pid \
exec $DAEMON -x /run/dnsmasq/$NAME.pid \
${MAILHOSTNAME:+ -m $MAILHOSTNAME} \
${MAILTARGET:+ -t $MAILTARGET} \
${DNSMASQ_USER:+ -u $DNSMASQ_USER} \

2
debian/postinst vendored
View File

@@ -21,7 +21,7 @@ if [ -x /etc/init.d/dnsmasq ]; then
update-rc.d dnsmasq defaults 15 85 >/dev/null
if [ "$1" = "configure" ] || [ "$1" = "abort-upgrade" ]; then
if [ -e /var/run/dnsmasq/dnsmasq.pid ]; then
if [ -e /run/dnsmasq/dnsmasq.pid ]; then
ACTION=restart
else
ACTION=start

2
debian/resolvconf vendored
View File

@@ -13,7 +13,7 @@
set -e
RUN_DIR="/var/run/dnsmasq"
RUN_DIR="/run/dnsmasq"
RSLVRLIST_FILE="${RUN_DIR}/resolv.conf"
TMP_FILE="${RSLVRLIST_FILE}_new.$$"
MY_NAME_FOR_RESOLVCONF="dnsmasq"

26
debian/rules vendored
View File

@@ -24,6 +24,7 @@ DEB_COPTS = $(COPTS)
TARGET = install-i18n
DEB_HOST_ARCH_OS := $(shell dpkg-architecture -qDEB_HOST_ARCH_OS)
BUILD_DATE := $(shell dpkg-parsechangelog --show-field Date)
# Force package version based on git tags.
ifneq (,$(filter gitversion,$(DEB_BUILD_OPTIONS)))
@@ -92,7 +93,7 @@ clean:
$(checkdir)
rm -rf debian/daemon debian/base debian/utils debian/*~ debian/files debian/substvars debian/utils-substvars
make clean
make -C contrib/wrt clean
make -C contrib/lease-tools clean
binary-indep: checkroot
$(checkdir)
@@ -118,8 +119,9 @@ binary-indep: checkroot
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' | xargs -r0 md5sum > DEBIAN/md5sums
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 ..
@@ -132,7 +134,6 @@ binary-arch: checkroot
-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/var/run \
-d debian/base/usr/share/$(package) \
-d debian/base/var/lib/misc
make $(TARGET) PREFIX=/usr DESTDIR=`pwd`/debian/base CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" COPTS="$(DEB_COPTS)" CC=gcc
@@ -169,9 +170,10 @@ endif
ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
strip -R .note -R .comment debian/base/usr/sbin/dnsmasq
endif
cd debian/base && find . -type f ! -regex '.*DEBIAN/.*' -printf '%P\0' | xargs -r0 md5sum > DEBIAN/md5sums
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 ..
@@ -182,12 +184,15 @@ ifeq ($(DEB_HOST_ARCH_OS),linux)
-d debian/utils/usr/share/man/man1 \
-d debian/utils/usr/bin \
-d debian/utils/usr/share/doc/dnsmasq-utils
make -C contrib/wrt PREFIX=/usr DESTDIR=`pwd`/debian/utils CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" COPTS="$(DEB_COPTS)" CC=gcc
install -m 755 contrib/wrt/dhcp_release debian/utils/usr/bin/dhcp_release
install -m 644 contrib/wrt/dhcp_release.1 debian/utils/usr/share/man/man1/dhcp_release.1
make -C contrib/lease-tools PREFIX=/usr DESTDIR=`pwd`/debian/utils CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" COPTS="$(DEB_COPTS)" CC=gcc
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/wrt/dhcp_lease_time debian/utils/usr/bin/dhcp_lease_time
install -m 644 contrib/wrt/dhcp_lease_time.1 debian/utils/usr/share/man/man1/dhcp_lease_time.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
@@ -196,9 +201,10 @@ ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
strip -R .note -R .comment debian/utils/usr/bin/dhcp_release
strip -R .note -R .comment debian/utils/usr/bin/dhcp_lease_time
endif
cd debian/utils && find . -type f ! -regex '.*DEBIAN/.*' -printf '%P\0' | xargs -r0 md5sum > DEBIAN/md5sums
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 ..

View File

@@ -1,9 +1,10 @@
[Unit]
Description=dnsmasq - A lightweight DHCP and caching DNS server
Requires=network.target
[Service]
Type=forking
PIDFile=/var/run/dnsmasq/dnsmasq.pid
PIDFile=/run/dnsmasq/dnsmasq.pid
# Test the config file and refuse starting if it is not valid.
ExecStartPre=/usr/sbin/dnsmasq --test

View File

@@ -251,6 +251,13 @@
# the IP address 192.168.0.60
#dhcp-host=id:01:02:02:04,192.168.0.60
# Always give the Infiniband interface with hardware address
# 80:00:00:48:fe:80:00:00:00:00:00:00:f4:52:14:03:00:28:05:81 the
# ip address 192.168.0.61. The client id is derived from the prefix
# ff:00:00:00:00:00:02:00:00:02:c9:00 and the last 8 pairs of
# hex digits of the hardware address.
#dhcp-host=id:ff:00:00:00:00:00:02:00:00:02:c9:00:f4:52:14:03:00:28:05:81,192.168.0.61
# Always give the host with client identifier "marjorie"
# the IP address 192.168.0.60
#dhcp-host=id:marjorie,192.168.0.60
@@ -345,6 +352,14 @@
# Ask client to poll for option changes every six hours. (RFC4242)
#dhcp-option=option6:information-refresh-time,6h
# Set option 58 client renewal time (T1). Defaults to half of the
# lease time if not specified. (RFC2132)
#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
# Set the NTP time server address to be the same machine as
# is running dnsmasq
#dhcp-option=42,0.0.0.0
@@ -486,6 +501,9 @@
# Set the root directory for files available via FTP.
#tftp-root=/var/ftpd
# Do not abort if the tftp-root is unavailable
#tftp-no-fail
# Make the TFTP server more secure: with this set, only files owned by
# the user dnsmasq is running as will be send over the net.
#tftp-secure

View File

@@ -74,7 +74,9 @@ for details.
There is a dnsmasq mailing list at <A
HREF="http://lists.thekelleys.org.uk/mailman/listinfo/dnsmasq-discuss">
http://lists.thekelleys.org.uk/mailman/listinfo/dnsmasq-discuss</A> which should be the
first location for queries, bugreports, suggestions etc.
first location for queries, bugreports, suggestions etc. The list is mirrored, with a
search facility, at <A HREF="https://www.mail-archive.com/dnsmasq-discuss@lists.thekelleys.org.uk/">
https://www.mail-archive.com/dnsmasq-discuss@lists.thekelleys.org.uk/</A>.
You can contact me at <A
HREF="mailto:simon@thekelleys.org.uk">simon@thekelleys.org.uk</A>.

View File

@@ -42,6 +42,13 @@ the configuration file.
Read and syntax check configuration file(s). Exit with code 0 if all
is OK, or a non-zero code otherwise. Do not start up dnsmasq.
.TP
.B \-w, --help
Display all command-line options.
.B --help dhcp
will display known DHCPv4 configuration options, and
.B --help dhcp6
will display DHCPv6 options.
.TP
.B \-h, --no-hosts
Don't read the hostnames in /etc/hosts.
.TP
@@ -60,7 +67,7 @@ in the same way as for DHCP-derived names. Note that this does not
apply to domain names in cnames, PTR records, TXT records etc.
.TP
.B \-T, --local-ttl=<time>
When replying with information from /etc/hosts or the DHCP leases
When replying with information from /etc/hosts or configuration or the DHCP leases
file dnsmasq by default sets the time-to-live field to zero, meaning
that the requester should not itself cache the information. This is
the correct thing to do in almost all situations. This option allows a
@@ -68,6 +75,9 @@ time-to-live (in seconds) to be given for these replies. This will
reduce the load on the server at the expense of clients using stale
data under some circumstances.
.TP
.B --dhcp-ttl=<time>
As for --local-ttl, but affects only replies with information from DHCP leases. If both are given, --dhcp-ttl applies for DHCP information, and --local-ttl for others. Setting this to zero eliminates the effect of --local-ttl for DHCP.
.TP
.B --neg-ttl=<time>
Negative replies from upstream servers normally contain time-to-live
information in SOA records which dnsmasq uses for caching. If the
@@ -174,6 +184,13 @@ queries. Dnsmasq picks random ports as source for outbound queries:
when this option is given, the ports used will always to larger
than that specified. Useful for systems behind firewalls.
.TP
.B --max-port=<port>
Use ports lower than that given as source for outbound DNS queries.
Dnsmasq picks random ports as source for outbound queries:
when this option is given, the ports used will always be lower
than that specified. Useful for systems behind firewalls.
.TP
.B \-i, --interface=<interface name>
Listen only on the specified interface(s). Dnsmasq automatically adds
the loopback (local) interface to the list of interfaces to use when
@@ -307,7 +324,7 @@ instead of the correct NXDOMAIN response. This option tells dnsmasq to
fake the correct response when it sees this behaviour. As at Sept 2003
the IP address being returned by Verisign is 64.94.110.11
.TP
.B \-B, --ignore-address=<ipaddr>
.B --ignore-address=<ipaddr>
Ignore replies to A-record queries which include the specified address.
No error is generated, dnsmasq simply continues to listen for another reply.
This is useful to defeat blocking strategies which rely on quickly supplying a
@@ -405,7 +422,10 @@ xxx.internal.thekelleys.org.uk at 192.168.1.1 then giving the flag
.B -S /internal.thekelleys.org.uk/192.168.1.1
will send all queries for
internal machines to that nameserver, everything else will go to the
servers in /etc/resolv.conf. An empty domain specification,
servers in /etc/resolv.conf. DNSSEC validation is turned off for such
private nameservers, UNLESS a
.B --trust-anchor
is specified for the domain in question. An empty domain specification,
.B //
has the special meaning of "unqualified names only" ie names without any
dots in them. A non-standard port may be specified as
@@ -519,7 +539,7 @@ zone files: the port, weight and priority numbers are in a different
order. More than one SRV record for a given service/domain is allowed,
all that match are returned.
.TP
.B --host-record=<name>[,<name>....],[<IPv4-address>],[<IPv6-address>]
.B --host-record=<name>[,<name>....],[<IPv4-address>],[<IPv6-address>][,<TTL>]
Add A, AAAA and PTR records to the DNS. This adds one or more names to
the DNS with associated IPv4 (A) and IPv6 (AAAA) records. A name may
appear in more than one
@@ -536,6 +556,10 @@ is in effect. Short and long names may appear in the same
.B host-record,
eg.
.B --host-record=laptop,laptop.thekelleys.org,192.168.0.1,1234::100
If the time-to-live is given, it overrides the default, which is zero
or the value of --local-ttl. The value is a positive integer and gives
the time-to-live in seconds.
.TP
.B \-Y, --txt-record=<name>[[,<text>],<text>]
Return a TXT DNS record. The value of TXT record is a set of strings,
@@ -549,7 +573,7 @@ Return a PTR DNS record.
.B --naptr-record=<name>,<order>,<preference>,<flags>,<service>,<regexp>[,<replacement>]
Return an NAPTR DNS record, as specified in RFC3403.
.TP
.B --cname=<cname>,<target>
.B --cname=<cname>,<target>[,<TTL>]
Return a CNAME record which indicates that <cname> is really
<target>. There are significant limitations on the target; it must be a
DNS name which is known to dnsmasq from /etc/hosts (or additional
@@ -558,6 +582,10 @@ hosts files), from DHCP, from --interface-name or from another
If the target does not satisfy this
criteria, the whole cname is ignored. The cname must be unique, but it
is permissable to have more than one cname pointing to the same target.
If the time-to-live is given, it overrides the default, which is zero
or the value of -local-ttl. The value is a positive integer and gives
the time-to-live in seconds.
.TP
.B --dns-rr=<name>,<RR-number>,[<hex data>]
Return an arbitrary DNS Resource Record. The number is the type of the
@@ -594,7 +622,7 @@ configured a zero is added in front of the label. ::1 becomes 0--1.
The address range can be of the form
<ip address>,<ip address> or <ip address>/<netmask>
.TP
.B --add-mac
.B --add-mac[=base64|text]
Add the MAC address of the requestor to DNS queries which are
forwarded upstream. This may be used to DNS filtering by the upstream
server. The MAC address can only be added if the requestor is on the same
@@ -602,19 +630,34 @@ subnet as the dnsmasq server. Note that the mechanism used to achieve this (an E
is not yet standardised, so this should be considered
experimental. Also note that exposing MAC addresses in this way may
have security and privacy implications. The warning about caching
given for --add-subnet applies to --add-mac too.
given for --add-subnet applies to --add-mac too. An alternative encoding of the
MAC, as base64, is enabled by adding the "base64" parameter and a human-readable encoding of hex-and-colons is enabled by added the "text" parameter.
.TP
.B --add-cpe-id=<string>
Add a arbitrary identifying string to o DNS queries which are
forwarded upstream.
.TP
.B --add-subnet[[=<IPv4 prefix length>],<IPv6 prefix length>]
Add the subnet address of the requestor to the DNS queries which are
forwarded upstream. The amount of the address forwarded depends on the
prefix length parameter: 32 (128 for IPv6) forwards the whole address,
zero forwards none of it but still marks the request so that no
upstream nameserver will add client address information either. The
default is zero for both IPv4 and IPv6. Note that upstream nameservers
may be configured to return different results based on this
information, but the dnsmasq cache does not take account. If a dnsmasq
instance is configured such that different results may be encountered,
caching should be disabled.
.B --add-subnet[[=[<IPv4 address>/]<IPv4 prefix length>][,[<IPv6 address>/]<IPv6 prefix length>]]
Add a subnet address to the DNS queries which are forwarded
upstream. If an address is specified in the flag, it will be used,
otherwise, the address of the requestor will be used. The amount of
the address forwarded depends on the prefix length parameter: 32 (128
for IPv6) forwards the whole address, zero forwards none of it but
still marks the request so that no upstream nameserver will add client
address information either. The default is zero for both IPv4 and
IPv6. Note that upstream nameservers may be configured to return
different results based on this information, but the dnsmasq cache
does not take account. If a dnsmasq instance is configured such that
different results may be encountered, caching should be disabled.
For example,
.B --add-subnet=24,96
will add the /24 and /96 subnets of the requestor for IPv4 and IPv6 requestors, respectively.
.B --add-subnet=1.2.3.4/24
will add 1.2.3.0/24 for IPv4 requestors and ::/0 for IPv6 requestors.
.B --add-subnet=1.2.3.4/24,1.2.3.4/24
will add 1.2.3.0/24 for both IPv4 and IPv6 requestors.
.TP
.B \-c, --cache-size=<cachesize>
Set the size of dnsmasq's cache. The default is 150 names. Setting the cache size to zero disables caching.
@@ -654,7 +697,7 @@ Provide DS records to act a trust anchors for DNSSEC
validation. Typically these will be the DS record(s) for Zone Signing
key(s) of the root zone,
but trust anchors for limited domains are also possible. The current
root-zone trust anchors may be donwloaded from https://data.iana.org/root-anchors/root-anchors.xml
root-zone trust anchors may be downloaded from https://data.iana.org/root-anchors/root-anchors.xml
.TP
.B --dnssec-check-unsigned
As a default, dnsmasq does not check that unsigned DNS replies are
@@ -747,7 +790,7 @@ compiled in and the kernel must have conntrack support
included and configured. This option cannot be combined with
--query-port.
.TP
.B \-F, --dhcp-range=[tag:<tag>[,tag:<tag>],][set:<tag>,]<start-addr>[,<end-addr>][,<mode>][,<netmask>[,<broadcast>]][,<lease time>]
.B \-F, --dhcp-range=[tag:<tag>[,tag:<tag>],][set:<tag>,]<start-addr>[,<end-addr>|<mode>][,<netmask>[,<broadcast>]][,<lease time>]
.TP
.B \-F, --dhcp-range=[tag:<tag>[,tag:<tag>],][set:<tag>,]<start-IPv6addr>[,<end-IPv6addr>|constructor:<interface>][,<mode>][,<prefix-len>][,<lease time>]
@@ -833,7 +876,7 @@ and
for details.)
For IPv6, the mode may be some combination of
.B ra-only, slaac, ra-names, ra-stateless, ra-advrouter.
.B ra-only, slaac, ra-names, ra-stateless, ra-advrouter, off-link.
.B ra-only
tells dnsmasq to offer Router Advertisement only on this subnet,
@@ -873,6 +916,9 @@ enables a mode where router address(es) rather than prefix(es) are included in t
This is described in RFC-3775 section 7.2 and is used in mobile IPv6. In this mode the interval option
is also included, as described in RFC-3775 section 7.3.
.B off-link
tells dnsmasq to advertise the prefix without the on-link (aka L) bit set.
.TP
.B \-G, --dhcp-host=[<hwaddr>][,id:<client_id>|*][,set:<tag>][,<ipaddr>][,<hostname>][,<lease_time>][,ignore]
Specify per host parameters for the DHCP server. This allows a machine
@@ -1312,7 +1358,7 @@ functions when supported by a suitable DHCP server.
This specifies a boot option which may appear in a PXE boot menu. <CSA> is
client system type, only services of the correct type will appear in a
menu. The known types are x86PC, PC98, IA64_EFI, Alpha, Arc_x86,
Intel_Lean_Client, IA32_EFI, BC_EFI, Xscale_EFI and X86-64_EFI; an
Intel_Lean_Client, IA32_EFI, X86-64_EFI, Xscale_EFI, BC_EFI, ARM32_EFI and ARM64_EFI; an
integer may be used for other types. The
parameter after the menu text may be a file name, in which case dnsmasq acts as a
boot server and directs the PXE client to download the file by TFTP,
@@ -1321,8 +1367,9 @@ either from itself (
must be set for this to work) or another TFTP server if the final server
address/name is given.
Note that the "layer"
suffix (normally ".0") is supplied by PXE, and should not be added to
the basename. If an integer boot service type, rather than a basename
suffix (normally ".0") is supplied by PXE, and need not be added to
the basename. Alternatively, the basename may be a filename, complete with suffix, in which case
no layer suffix is added. If an integer boot service type, rather than a basename
is given, then the PXE client will search for a
suitable boot service for that type on the network. This search may be done
by broadcast, or direct to a server if its IP address/name is provided.
@@ -1388,7 +1435,7 @@ enables dynamic allocation. With tags, only when the tags are all
set. It may be repeated with different tag sets.
.TP
.B \-5, --no-ping
(IPv4 only) By default, the DHCP server will attempt to ensure that an address in
(IPv4 only) By default, the DHCP server will attempt to ensure that an address is
not in use before allocating it to a host. It does this by sending an
ICMP echo request (aka "ping") to the address in question. If it gets
a reply, then the address must already be in use, and another is
@@ -1520,11 +1567,11 @@ At dnsmasq startup, the script will be invoked for
all existing leases as they are read from the lease file. Expired
leases will be called with "del" and others with "old". When dnsmasq
receives a HUP signal, the script will be invoked for existing leases
with an "old " event.
with an "old" event.
There are two further actions which may appear as the first argument
to the script, "init" and "tftp". More may be added in the future, so
There are four further actions which may appear as the first argument
to the script, "init", "arp-add", "arp-del" and "tftp". More may be added in the future, so
scripts should be written to ignore unknown actions. "init" is
described below in
.B --leasefile-ro
@@ -1532,6 +1579,11 @@ The "tftp" action is invoked when a TFTP file transfer completes: the
arguments are the file size in bytes, the address to which the file
was sent, and the complete pathname of the file.
The "arp-add" and "arp-del" actions are only called if enabled with
.B --script-arp
They are are supplied with a MAC address and IP address as arguments. "arp-add" indicates
the arrival of a new entry in the ARP or neighbour table, and "arp-del" indicates the deletion of same.
.TP
.B --dhcp-luascript=<path>
Specify a script written in Lua, to be run when leases are created,
@@ -1578,10 +1630,24 @@ table holds the tags
.B file_name
and
.B file_size.
The
.B arp
and
.B arp-old
functions are called only when enabled with
.B --script-arp
and have a table which holds the tags
.B mac_addres
and
.B client_address.
.TP
.B --dhcp-scriptuser
Specify the user as which to run the lease-change script or Lua script. This defaults to root, but can be changed to another user using this flag.
.TP
.TP
.B --script-arp
Enable the "arp" and "arp-old" functions in the dhcp-script and dhcp-luascript.
.TP
.B \-9, --leasefile-ro
Completely suppress use of the lease database file. The file will not
be created, read, or written. Change the way the lease-change
@@ -1597,11 +1663,13 @@ option also forces the leasechange script to be called on changes
to the client-id and lease length and expiry time.
.TP
.B --bridge-interface=<interface>,<alias>[,<alias>]
Treat DHCP request packets arriving at any of the <alias> interfaces
as if they had arrived at <interface>. This option is necessary when
using "old style" bridging on BSD platforms, since
packets arrive at tap interfaces which don't have an IP address.
A trailing '*' wildcard can be used in each <alias>.
Treat DHCP (v4 and v6) request and IPv6 Router Solicit packets
arriving at any of the <alias> interfaces as if they had arrived at
<interface>. This option allows dnsmasq to provide DHCP and RA
service over unaddressed and unbridged Ethernet interfaces, e.g. on an
OpenStack compute host where each such interface is a TAP interface to
a VM, or as in "old style bridging" on BSD platforms. A trailing '*'
wildcard can be used in each <alias>.
.TP
.B \-s, --domain=<domain>[,<address range>[,local]]
Specifies DNS domains for the DHCP server. Domains may be be given
@@ -1672,15 +1740,15 @@ creation are handled by a different protocol. When DHCP is in use,
only a subset of this is needed, and dnsmasq can handle it, using
existing DHCP configuration to provide most data. When RA is enabled,
dnsmasq will advertise a prefix for each dhcp-range, with default
router and recursive DNS server as the relevant link-local address on
the machine running dnsmasq. By default, he "managed address" bits are set, and
router as the relevant link-local address on
the machine running dnsmasq. By default, the "managed address" bits are set, and
the "use SLAAC" bit is reset. This can be changed for individual
subnets with the mode keywords described in
.B --dhcp-range.
RFC6106 DNS parameters are included in the advertisements. By default,
the relevant link-local address of the machine running dnsmasq is sent
as recursive DNS server. If provided, the DHCPv6 options dns-server and
domain-search are used for RDNSS and DNSSL.
domain-search are used for the DNS server (RDNSS) and the domain serach list (DNSSL).
.TP
.B --ra-param=<interface>,[high|low],[[<ra-interval>],<router lifetime>]
Set non-default values for router advertisements sent via an
@@ -1711,6 +1779,9 @@ Absolute paths (starting with /) are allowed, but they must be within
the tftp-root. If the optional interface argument is given, the
directory is only used for TFTP requests via that interface.
.TP
.B --tftp-no-fail
Do not abort startup if specified tftp root directories are inaccessible.
.TP
.B --tftp-unique-root
Add the IP address of the TFTP client as a path component on the end
of the TFTP-root (in standard dotted-quad format). Only valid if a
@@ -1747,6 +1818,10 @@ require about (2*n) + 10 descriptors. If
.B --tftp-port-range
is given, that can affect the number of concurrent connections.
.TP
.B --tftp-mtu=<mtu size>
Use size as the ceiling of the MTU supported by the intervening network when
negotiating TFTP blocksize, overriding the MTU setting of the local interface if it is larger.
.TP
.B --tftp-no-blocksize
Stop the TFTP server from negotiating the "blocksize" option with a
client. Some buggy clients request this option but then behave badly

View File

@@ -1062,10 +1062,14 @@ esta opci
cuando hay cambios hechos a el client-id y tiempos de arriendo y vencimiento.
.TP
.B --bridge-interface=<nombre de interface>,<alias>[,<alias>]
Tratar paquetes de pedidos DHCP que llegan a cualquiera de las interfaces <alias>
como si hubieran llegado a la interface <nombre de interface>. Esta opción
es necesaria al usar bridging estilo viejo en plataformas BSD, dado a que
los paquetes llegan a interfaces tap que no tienen una dirección IP.
Tratar paquetes de pedidos DHCP (v4 y v6) y de IPv6 Router Solicit que
llegan a cualquiera de las interfaces <alias> como si hubieran llegado
a la interface <nombre de interface>. Esta opción permite que dnsmasq
puede proporcionar los servicios DHCP y RA a través de interfaces
ethernet sin dirección y sin puente; por ejemplo en un nodo de cálculo
de OpenStack, donde cada una de esas interfaces es una interfaz TAP
para una máquina virtual, o al usar bridging estilo viejo en
plataformas BSD.
.TP
.B \-s, --domain=<dominio>[,<rango de IPs>]
Especifica los dominios DNS para el servidor DHCP. Dominios pueden ser

View File

@@ -852,7 +852,7 @@ et
pour plus de détails).
Pour IPv6, le mode peut-être une combinaison des valeurs
.B ra-only, slaac, ra-names, ra-stateless.
.B ra-only, slaac, ra-names, ra-stateless, off-link.
.B ra-only
indique à dnsmasq de n'effectuer que des annonces de routeur (Router
@@ -888,6 +888,9 @@ peut-être combiné avec
et
.B slaac.
.B off-link
indique à dnsmasq d'annoncer le préfixe sans le bit L (sur lien).
.TP
.B \-G, --dhcp-host=[<adresse matérielle>][,id:<identifiant client>|*][,set:<label>][,<adresse IP>][,<nom d'hôte>][,<durée de bail>][,ignore]
Spécifie les paramètres DHCP relatifs à un hôte. Cela permet à une machine
@@ -1655,11 +1658,14 @@ changement d'état de bail à chaque changement de l'identifiant de client, de
longueur de bail ou de date d'expiration.
.TP
.B --bridge-interface=<interface>,<alias>[,<alias>]
Traiter les requêtes DHCP arrivant sur n'importe laquelle des interfaces <alias>
comme si elles arrivaient de l'interface <interface>. Cette option est
nécessaire lors de l'utilisation de pont ethernet "ancien mode" sur plate-forme
BSD, puisque dans ce cas les paquets arrivent sur des interfaces "tap" n'ont
pas d'adresse IP. Chaque <alias> peut finir avec un simple '*' joker.
Traiter les requêtes DHCP (v4 et v6) et IPv6 Router Solicit arrivant
sur n'importe laquelle des interfaces <alias> comme si elles
arrivaient de l'interface <interface>. Cette option permet à dnsmasq
de fournir les service DHCP et RA sur les interfaces ethernet non
adressés et non pontés; par exemple sur un hôte de calcul d'OpenStack
où chaque telle interface est une interface TAP à une machine
virtuelle, ou lors de l'utilisation de pont ethernet "ancien mode" sur
plate-forme BSD. Chaque <alias> peut finir avec un simple '*' joker.
.TP
.B \-s, --domain=<domaine>[,<gamme d'adresses>[,local]]
Spécifie le domaine du serveur DHCP. Le domaine peut être donné de manière

1036
po/de.po

File diff suppressed because it is too large Load Diff

980
po/es.po

File diff suppressed because it is too large Load Diff

1179
po/fi.po

File diff suppressed because it is too large Load Diff

969
po/fr.po

File diff suppressed because it is too large Load Diff

1089
po/id.po

File diff suppressed because it is too large Load Diff

1179
po/it.po

File diff suppressed because it is too large Load Diff

1060
po/no.po

File diff suppressed because it is too large Load Diff

1058
po/pl.po

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1060
po/ro.po

File diff suppressed because it is too large Load Diff

247
src/arp.c Normal file
View File

@@ -0,0 +1,247 @@
/* dnsmasq is Copyright (c) 2000-2016 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"
/* Time between forced re-loads from kernel. */
#define INTERVAL 90
#define ARP_MARK 0
#define ARP_FOUND 1 /* Confirmed */
#define ARP_NEW 2 /* Newly created */
#define ARP_EMPTY 3 /* No MAC addr */
struct arp_record {
unsigned short hwlen, status;
int family;
unsigned char hwaddr[DHCP_CHADDR_MAX];
struct all_addr addr;
struct arp_record *next;
};
static struct arp_record *arps = NULL, *old = NULL, *freelist = NULL;
static time_t last = 0;
static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv)
{
struct arp_record *arp;
(void)parmv;
if (maclen > DHCP_CHADDR_MAX)
return 1;
#ifndef HAVE_IPV6
if (family != AF_INET)
return 1;
#endif
/* Look for existing entry */
for (arp = arps; arp; arp = arp->next)
{
if (family != arp->family || arp->status == ARP_NEW)
continue;
if (family == AF_INET)
{
if (arp->addr.addr.addr4.s_addr != ((struct in_addr *)addrp)->s_addr)
continue;
}
#ifdef HAVE_IPV6
else
{
if (!IN6_ARE_ADDR_EQUAL(&arp->addr.addr.addr6, (struct in6_addr *)addrp))
continue;
}
#endif
if (arp->status == ARP_EMPTY)
{
/* existing address, was negative. */
arp->status = ARP_NEW;
arp->hwlen = maclen;
memcpy(arp->hwaddr, mac, maclen);
}
else if (arp->hwlen == maclen && memcmp(arp->hwaddr, mac, maclen) == 0)
/* Existing entry matches - confirm. */
arp->status = ARP_FOUND;
else
continue;
break;
}
if (!arp)
{
/* New entry */
if (freelist)
{
arp = freelist;
freelist = freelist->next;
}
else if (!(arp = whine_malloc(sizeof(struct arp_record))))
return 1;
arp->next = arps;
arps = arp;
arp->status = ARP_NEW;
arp->hwlen = maclen;
arp->family = family;
memcpy(arp->hwaddr, mac, maclen);
if (family == AF_INET)
arp->addr.addr.addr4.s_addr = ((struct in_addr *)addrp)->s_addr;
#ifdef HAVE_IPV6
else
memcpy(&arp->addr.addr.addr6, addrp, IN6ADDRSZ);
#endif
}
return 1;
}
/* If in lazy mode, we cache absence of ARP entries. */
int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy, time_t now)
{
struct arp_record *arp, *tmp, **up;
int updated = 0;
again:
/* If the database is less then INTERVAL old, look in there */
if (difftime(now, last) < INTERVAL)
{
/* addr == NULL -> just make cache up-to-date */
if (!addr)
return 0;
for (arp = arps; arp; arp = arp->next)
{
if (addr->sa.sa_family != arp->family)
continue;
if (arp->family == AF_INET &&
arp->addr.addr.addr4.s_addr != addr->in.sin_addr.s_addr)
continue;
#ifdef HAVE_IPV6
if (arp->family == AF_INET6 &&
!IN6_ARE_ADDR_EQUAL(&arp->addr.addr.addr6, &addr->in6.sin6_addr))
continue;
#endif
/* Only accept positive entries unless in lazy mode. */
if (arp->status != ARP_EMPTY || lazy || updated)
{
if (mac && arp->hwlen != 0)
memcpy(mac, arp->hwaddr, arp->hwlen);
return arp->hwlen;
}
}
}
/* Not found, try the kernel */
if (!updated)
{
updated = 1;
last = now;
/* Mark all non-negative entries */
for (arp = arps; arp; arp = arp->next)
if (arp->status != ARP_EMPTY)
arp->status = ARP_MARK;
iface_enumerate(AF_UNSPEC, NULL, filter_mac);
/* Remove all unconfirmed entries to old list. */
for (arp = arps, up = &arps; arp; arp = tmp)
{
tmp = arp->next;
if (arp->status == ARP_MARK)
{
*up = arp->next;
arp->next = old;
old = arp;
}
else
up = &arp->next;
}
goto again;
}
/* record failure, so we don't consult the kernel each time
we're asked for this address */
if (freelist)
{
arp = freelist;
freelist = freelist->next;
}
else
arp = whine_malloc(sizeof(struct arp_record));
if (arp)
{
arp->next = arps;
arps = arp;
arp->status = ARP_EMPTY;
arp->family = addr->sa.sa_family;
arp->hwlen = 0;
if (addr->sa.sa_family == AF_INET)
arp->addr.addr.addr4.s_addr = addr->in.sin_addr.s_addr;
#ifdef HAVE_IPV6
else
memcpy(&arp->addr.addr.addr6, &addr->in6.sin6_addr, IN6ADDRSZ);
#endif
}
return 0;
}
int do_arp_script_run(void)
{
struct arp_record *arp;
/* Notify any which went, then move to free list */
if (old)
{
#ifdef HAVE_SCRIPT
if (option_bool(OPT_SCRIPT_ARP))
queue_arp(ACTION_ARP_DEL, old->hwaddr, old->hwlen, old->family, &old->addr);
#endif
arp = old;
old = arp->next;
arp->next = freelist;
freelist = arp;
return 1;
}
for (arp = arps; arp; arp = arp->next)
if (arp->status == ARP_NEW)
{
#ifdef HAVE_SCRIPT
if (option_bool(OPT_SCRIPT_ARP))
queue_arp(ACTION_ARP, arp->hwaddr, arp->hwlen, arp->family, &arp->addr);
#endif
arp->status = ARP_FOUND;
return 1;
}
return 0;
}

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2016 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -81,7 +81,8 @@ int in_zone(struct auth_zone *zone, char *name, char **cut)
}
size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t now, union mysockaddr *peer_addr, int local_query)
size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t now, union mysockaddr *peer_addr,
int local_query, int do_bit, int have_pseudoheader)
{
char *name = daemon->namebuff;
unsigned char *p, *ansp;
@@ -131,24 +132,27 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
continue;
}
if (qtype == T_PTR)
if ((qtype == T_PTR || qtype == T_SOA || qtype == T_NS) &&
(flag = in_arpa_name_2_addr(name, &addr)) &&
!local_query)
{
if (!(flag = in_arpa_name_2_addr(name, &addr)))
continue;
if (!local_query)
for (zone = daemon->auth_zones; zone; zone = zone->next)
if ((subnet = find_subnet(zone, flag, &addr)))
break;
if (!zone)
{
for (zone = daemon->auth_zones; zone; zone = zone->next)
if ((subnet = find_subnet(zone, flag, &addr)))
break;
if (!zone)
{
auth = 0;
continue;
}
auth = 0;
continue;
}
else if (qtype == T_SOA)
soa = 1, found = 1;
else if (qtype == T_NS)
ns = 1, found = 1;
}
if (qtype == T_PTR && flag)
{
intr = NULL;
if (flag == F_IPV4)
@@ -186,7 +190,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
if (intr)
{
if (in_zone(zone, intr->name, NULL))
if (local_query || in_zone(zone, intr->name, NULL))
{
found = 1;
log_query(flag | F_REVERSE | F_CONFIG, intr->name, &addr, NULL);
@@ -208,8 +212,11 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
*p = 0; /* must be bare name */
/* add external domain */
strcat(name, ".");
strcat(name, zone->domain);
if (zone)
{
strcat(name, ".");
strcat(name, zone->domain);
}
log_query(flag | F_DHCP | F_REVERSE, name, &addr, record_source(crecp->uid));
found = 1;
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
@@ -217,7 +224,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
T_PTR, C_IN, "d", name))
anscount++;
}
else if (crecp->flags & (F_DHCP | F_HOSTS) && in_zone(zone, name, NULL))
else if (crecp->flags & (F_DHCP | F_HOSTS) && (local_query || in_zone(zone, name, NULL)))
{
log_query(crecp->flags & ~F_FORWARD, name, &addr, record_source(crecp->uid));
found = 1;
@@ -240,14 +247,20 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
}
cname_restart:
for (zone = daemon->auth_zones; zone; zone = zone->next)
if (in_zone(zone, name, &cut))
break;
if (!zone)
if (found)
/* NS and SOA .arpa requests have set found above. */
cut = NULL;
else
{
auth = 0;
continue;
for (zone = daemon->auth_zones; zone; zone = zone->next)
if (in_zone(zone, name, &cut))
break;
if (!zone)
{
auth = 0;
continue;
}
}
for (rec = daemon->mxnames; rec; rec = rec->next)
@@ -808,6 +821,11 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
header->ancount = htons(anscount);
header->nscount = htons(authcount);
header->arcount = htons(0);
/* Advertise our packet size limit in our reply */
if (have_pseudoheader)
return add_pseudoheader(header, ansp - (unsigned char *)header, (unsigned char *)limit, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0);
return ansp - (unsigned char *)header;
}

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2016 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-2015 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2016 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,9 @@
#include <ifaddrs.h>
#include <sys/param.h>
#if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
#include <sys/sysctl.h>
#endif
#include <net/if.h>
#include <net/route.h>
#include <net/if_dl.h>

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2016 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
@@ -189,12 +189,7 @@ static void cache_hash(struct crec *crecp)
static void cache_blockdata_free(struct crec *crecp)
{
if (crecp->flags & F_DNSKEY)
{
if (crecp->flags & F_DS)
blockdata_free(crecp->addr.sig.keydata);
else
blockdata_free(crecp->addr.key.keydata);
}
blockdata_free(crecp->addr.key.keydata);
else if ((crecp->flags & F_DS) && !(crecp->flags & F_NEG))
blockdata_free(crecp->addr.ds.keydata);
}
@@ -369,13 +364,8 @@ static struct crec *cache_scan_free(char *name, struct all_addr *addr, time_t no
}
#ifdef HAVE_DNSSEC
/* Deletion has to be class-sensitive for DS, DNSKEY, RRSIG, also
type-covered sensitive for RRSIG */
if ((flags & (F_DNSKEY | F_DS)) &&
(flags & (F_DNSKEY | F_DS)) == (crecp->flags & (F_DNSKEY | F_DS)) &&
crecp->uid == addr->addr.dnssec.class &&
(!((flags & (F_DS | F_DNSKEY)) == (F_DS | F_DNSKEY)) ||
crecp->addr.sig.type_covered == addr->addr.dnssec.type))
/* Deletion has to be class-sensitive for DS and DNSKEY */
if ((flags & crecp->flags & (F_DNSKEY | F_DS)) && crecp->uid == addr->addr.dnssec.class)
{
if (crecp->flags & F_CONFIG)
return crecp;
@@ -481,7 +471,7 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
existing record is for an A or AAAA and
the record we're trying to insert is the same,
just drop the insert, but don't error the whole process. */
if ((flags & (F_IPV4 | F_IPV6)) && (flags & F_FORWARD))
if ((flags & (F_IPV4 | F_IPV6)) && (flags & F_FORWARD) && addr)
{
if ((flags & F_IPV4) && (new->flags & F_IPV4) &&
new->addr.addr.addr.addr4.s_addr == addr->addr.addr4.s_addr)
@@ -532,13 +522,9 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
struct all_addr free_addr = new->addr.addr;;
#ifdef HAVE_DNSSEC
/* For DNSSEC records, addr holds class and type_covered for RRSIG */
/* For DNSSEC records, addr holds class. */
if (new->flags & (F_DS | F_DNSKEY))
{
free_addr.addr.dnssec.class = new->uid;
if ((new->flags & (F_DS | F_DNSKEY)) == (F_DS | F_DNSKEY))
free_addr.addr.dnssec.type = new->addr.sig.type_covered;
}
free_addr.addr.dnssec.class = new->uid;
#endif
free_avail = 1; /* Must be free space now. */
@@ -653,9 +639,6 @@ struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsi
if (!is_expired(now, crecp) && !is_outdated_cname_pointer(crecp))
{
if ((crecp->flags & F_FORWARD) &&
#ifdef HAVE_DNSSEC
(((crecp->flags & (F_DNSKEY | F_DS)) == (prot & (F_DNSKEY | F_DS))) || (prot & F_NSIGMATCH)) &&
#endif
(crecp->flags & prot) &&
hostname_isequal(cache_get_name(crecp), name))
{
@@ -713,9 +696,6 @@ struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsi
if (ans &&
(ans->flags & F_FORWARD) &&
#ifdef HAVE_DNSSEC
(((ans->flags & (F_DNSKEY | F_DS)) == (prot & (F_DNSKEY | F_DS))) || (prot & F_NSIGMATCH)) &&
#endif
(ans->flags & prot) &&
hostname_isequal(cache_get_name(ans), name))
return ans;
@@ -798,6 +778,7 @@ static void add_hosts_cname(struct crec *target)
(crec = whine_malloc(sizeof(struct 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;
crec->addr.cname.uid = target->uid;
@@ -939,7 +920,7 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
if (!f)
{
my_syslog(LOG_ERR, _("failed to load names from %s: %s"), filename, strerror(errno));
return 0;
return cache_size;
}
eatspace(f);
@@ -1001,6 +982,7 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
strcat(cache->name.sname, ".");
strcat(cache->name.sname, domain_suffix);
cache->flags = flags;
cache->ttd = daemon->local_ttl;
add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz);
name_count++;
}
@@ -1008,6 +990,7 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
{
strcpy(cache->name.sname, canon);
cache->flags = flags;
cache->ttd = daemon->local_ttl;
add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz);
name_count++;
}
@@ -1077,6 +1060,7 @@ void cache_reload(void)
((cache = whine_malloc(sizeof(struct 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;
@@ -1091,6 +1075,7 @@ void cache_reload(void)
(cache->addr.ds.keydata = blockdata_alloc(ds->digest, ds->digestlen)))
{
cache->flags = F_FORWARD | F_IMMORTAL | F_DS | F_CONFIG | F_NAMEP;
cache->ttd = daemon->local_ttl;
cache->name.namep = ds->name;
cache->addr.ds.keylen = ds->digestlen;
cache->addr.ds.algo = ds->algo;
@@ -1115,6 +1100,7 @@ void cache_reload(void)
(cache = whine_malloc(sizeof(struct crec))))
{
cache->name.namep = nl->name;
cache->ttd = hr->ttl;
cache->flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4 | F_NAMEP | F_CONFIG;
add_hosts_entry(cache, (struct all_addr *)&hr->addr, INADDRSZ, SRC_CONFIG, (struct crec **)daemon->packet, revhashsz);
}
@@ -1123,6 +1109,7 @@ void cache_reload(void)
(cache = whine_malloc(sizeof(struct crec))))
{
cache->name.namep = nl->name;
cache->ttd = hr->ttl;
cache->flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6 | F_NAMEP | F_CONFIG;
add_hosts_entry(cache, (struct all_addr *)&hr->addr6, IN6ADDRSZ, SRC_CONFIG, (struct crec **)daemon->packet, revhashsz);
}
@@ -1367,7 +1354,7 @@ int cache_make_stat(struct txt_record *t)
}
port = prettyprint_addr(&serv->addr, daemon->addrbuff);
lenp = p++; /* length */
bytes_avail = (p - buff) + bufflen;
bytes_avail = bufflen - (p - buff );
bytes_needed = snprintf(p, bytes_avail, "%s#%d %u %u", daemon->addrbuff, port, queries, failed_queries);
if (bytes_needed >= bytes_avail)
{
@@ -1381,7 +1368,7 @@ int cache_make_stat(struct txt_record *t)
lenp = p - 1;
buff = new;
bufflen = newlen;
bytes_avail = (p - buff) + bufflen;
bytes_avail = bufflen - (p - buff );
bytes_needed = snprintf(p, bytes_avail, "%s#%d %u %u", daemon->addrbuff, port, queries, failed_queries);
}
*lenp = bytes_needed;
@@ -1472,11 +1459,7 @@ void dump_cache(time_t now)
#ifdef HAVE_DNSSEC
else if (cache->flags & F_DS)
{
if (cache->flags & F_DNSKEY)
/* RRSIG */
sprintf(a, "%5u %3u %s", cache->addr.sig.keytag,
cache->addr.sig.algo, querystr("", cache->addr.sig.type_covered));
else if (!(cache->flags & F_NEG))
if (!(cache->flags & F_NEG))
sprintf(a, "%5u %3u %3u", cache->addr.ds.keytag,
cache->addr.ds.algo, cache->addr.ds.digest);
}
@@ -1502,8 +1485,6 @@ void dump_cache(time_t now)
else if (cache->flags & F_CNAME)
t = "C";
#ifdef HAVE_DNSSEC
else if ((cache->flags & (F_DS | F_DNSKEY)) == (F_DS | F_DNSKEY))
t = "G"; /* DNSKEY and DS set -> RRISG */
else if (cache->flags & F_DS)
t = "S";
else if (cache->flags & F_DNSKEY)
@@ -1606,7 +1587,7 @@ void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
if (addr)
{
if (flags & F_KEYTAG)
sprintf(daemon->addrbuff, arg, addr->addr.keytag);
sprintf(daemon->addrbuff, arg, addr->addr.log.keytag, addr->addr.log.algo, addr->addr.log.digest);
else
{
#ifdef HAVE_IPV6

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2016 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,11 +19,13 @@
#define CHILD_LIFETIME 150 /* secs 'till terminated (RFC1035 suggests > 120s) */
#define TCP_MAX_QUERIES 100 /* Maximum number of queries per incoming TCP connection */
#define EDNS_PKTSZ 4096 /* default max EDNS.0 UDP packet from RFC5625 */
#define SAFE_PKTSZ 1280 /* "go anywhere" UDP packet size */
#define KEYBLOCK_LEN 40 /* choose to mininise fragmentation when storing DNSSEC keys */
#define DNSSEC_WORK 50 /* Max number of queries to validate one question */
#define TIMEOUT 10 /* drop UDP queries after TIMEOUT seconds */
#define FORWARD_TEST 50 /* try all servers every 50 queries */
#define FORWARD_TIME 20 /* or 20 seconds */
#define SERVERS_LOGGED 30 /* Only log this many servers when logging state */
#define RANDOM_SOCKS 64 /* max simultaneous random ports */
#define LEASE_RETRY 60 /* on error, retry writing leasefile after LEASE_RETRY seconds */
#define CACHESIZ 150 /* default cache size */
@@ -336,7 +338,7 @@ HAVE_SOCKADDR_SA_LEN
#define HAVE_DHCP
#endif
#if defined(NO_SCRIPT) || !defined(HAVE_DHCP) || defined(NO_FORK)
#if defined(NO_SCRIPT) || defined(NO_FORK)
#undef HAVE_SCRIPT
#undef HAVE_LUASCRIPT
#endif

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2016 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-2015 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2016 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
@@ -70,6 +70,21 @@ const char* introspection_xml_template =
" <arg name=\"hwaddr\" type=\"s\"/>\n"
" <arg name=\"hostname\" type=\"s\"/>\n"
" </signal>\n"
#ifdef HAVE_DHCP
" <method name=\"AddDhcpLease\">\n"
" <arg name=\"ipaddr\" type=\"s\"/>\n"
" <arg name=\"hwaddr\" type=\"s\"/>\n"
" <arg name=\"hostname\" type=\"ay\"/>\n"
" <arg name=\"clid\" type=\"ay\"/>\n"
" <arg name=\"lease_duration\" type=\"u\"/>\n"
" <arg name=\"ia_id\" type=\"u\"/>\n"
" <arg name=\"is_temporary\" type=\"b\"/>\n"
" </method>\n"
" <method name=\"DeleteDhcpLease\">\n"
" <arg name=\"ipaddr\" type=\"s\"/>\n"
" <arg name=\"success\" type=\"b\" direction=\"out\"/>\n"
" </method>\n"
#endif
" </interface>\n"
"</node>\n";
@@ -421,18 +436,184 @@ static DBusMessage *dbus_set_bool(DBusMessage *message, int flag, char *name)
if (enabled)
{
my_syslog(LOG_INFO, "Enabling --%s option from D-Bus", name);
my_syslog(LOG_INFO, _("Enabling --%s option from D-Bus"), name);
set_option_bool(flag);
}
else
{
my_syslog(LOG_INFO, "Disabling --%s option from D-Bus", name);
my_syslog(LOG_INFO, _("Disabling --%s option from D-Bus"), name);
reset_option_bool(flag);
}
return NULL;
}
#ifdef HAVE_DHCP
static DBusMessage *dbus_add_lease(DBusMessage* message)
{
struct dhcp_lease *lease;
const char *ipaddr, *hwaddr, *hostname, *tmp;
const unsigned char* clid;
int clid_len, hostname_len, hw_len, hw_type;
dbus_uint32_t expires, ia_id;
dbus_bool_t is_temporary;
struct all_addr addr;
time_t now = dnsmasq_time();
unsigned char dhcp_chaddr[DHCP_CHADDR_MAX];
DBusMessageIter iter, array_iter;
if (!dbus_message_iter_init(message, &iter))
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"Failed to initialize dbus message iter");
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"Expected string as first argument");
dbus_message_iter_get_basic(&iter, &ipaddr);
dbus_message_iter_next(&iter);
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"Expected string as second argument");
dbus_message_iter_get_basic(&iter, &hwaddr);
dbus_message_iter_next(&iter);
if ((dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) ||
(dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_BYTE))
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"Expected byte array as third argument");
dbus_message_iter_recurse(&iter, &array_iter);
dbus_message_iter_get_fixed_array(&array_iter, &hostname, &hostname_len);
tmp = memchr(hostname, '\0', hostname_len);
if (tmp)
{
if (tmp == &hostname[hostname_len - 1])
hostname_len--;
else
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"Hostname contains an embedded NUL character");
}
dbus_message_iter_next(&iter);
if ((dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) ||
(dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_BYTE))
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"Expected byte array as fourth argument");
dbus_message_iter_recurse(&iter, &array_iter);
dbus_message_iter_get_fixed_array(&array_iter, &clid, &clid_len);
dbus_message_iter_next(&iter);
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"Expected uint32 as fifth argument");
dbus_message_iter_get_basic(&iter, &expires);
dbus_message_iter_next(&iter);
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"Expected uint32 as sixth argument");
dbus_message_iter_get_basic(&iter, &ia_id);
dbus_message_iter_next(&iter);
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN)
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"Expected uint32 as sixth argument");
dbus_message_iter_get_basic(&iter, &is_temporary);
if (inet_pton(AF_INET, ipaddr, &addr.addr.addr4))
{
if (ia_id != 0 || is_temporary)
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"ia_id and is_temporary must be zero for IPv4 lease");
if (!(lease = lease_find_by_addr(addr.addr.addr4)))
lease = lease4_allocate(addr.addr.addr4);
}
#ifdef HAVE_DHCP6
else if (inet_pton(AF_INET6, ipaddr, &addr.addr.addr6))
{
if (!(lease = lease6_find_by_addr(&addr.addr.addr6, 128, 0)))
lease = lease6_allocate(&addr.addr.addr6,
is_temporary ? LEASE_TA : LEASE_NA);
lease_set_iaid(lease, ia_id);
}
#endif
else
return dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
"Invalid IP address '%s'", ipaddr);
hw_len = parse_hex((char*)hwaddr, dhcp_chaddr, DHCP_CHADDR_MAX, NULL,
&hw_type);
if (hw_type == 0 && hw_len != 0)
hw_type = ARPHRD_ETHER;
lease_set_hwaddr(lease, dhcp_chaddr, clid, hw_len, hw_type,
clid_len, now, 0);
lease_set_expires(lease, expires, now);
if (hostname_len != 0)
lease_set_hostname(lease, hostname, 0, get_domain(lease->addr), NULL);
lease_update_file(now);
lease_update_dns(0);
return NULL;
}
static DBusMessage *dbus_del_lease(DBusMessage* message)
{
struct dhcp_lease *lease;
DBusMessageIter iter;
const char *ipaddr;
DBusMessage *reply;
struct all_addr addr;
dbus_bool_t ret = 1;
time_t now = dnsmasq_time();
if (!dbus_message_iter_init(message, &iter))
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"Failed to initialize dbus message iter");
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"Expected string as first argument");
dbus_message_iter_get_basic(&iter, &ipaddr);
if (inet_pton(AF_INET, ipaddr, &addr.addr.addr4))
lease = lease_find_by_addr(addr.addr.addr4);
#ifdef HAVE_DHCP6
else if (inet_pton(AF_INET6, ipaddr, &addr.addr.addr6))
lease = lease6_find_by_addr(&addr.addr.addr6, 128, 0);
#endif
else
return dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
"Invalid IP address '%s'", ipaddr);
if (lease)
{
lease_prune(lease, now);
lease_update_file(now);
lease_update_dns(0);
}
else
ret = 0;
if ((reply = dbus_message_new_method_return(message)))
dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &ret,
DBUS_TYPE_INVALID);
return reply;
}
#endif
DBusHandlerResult message_handler(DBusConnection *connection,
DBusMessage *message,
void *user_data)
@@ -490,6 +671,16 @@ DBusHandlerResult message_handler(DBusConnection *connection,
{
reply = dbus_set_bool(message, OPT_BOGUSPRIV, "bogus-priv");
}
#ifdef HAVE_DHCP
else if (strcmp(method, "AddDhcpLease") == 0)
{
reply = dbus_add_lease(message);
}
else if (strcmp(method, "DeleteDhcpLease") == 0)
{
reply = dbus_del_lease(message);
}
#endif
else if (strcmp(method, "ClearCache") == 0)
clear_cache = 1;
else
@@ -558,8 +749,7 @@ char *dbus_init(void)
}
void set_dbus_listeners(int *maxfdp,
fd_set *rset, fd_set *wset, fd_set *eset)
void set_dbus_listeners(void)
{
struct watch *w;
@@ -569,19 +759,17 @@ void set_dbus_listeners(int *maxfdp,
unsigned int flags = dbus_watch_get_flags(w->watch);
int fd = dbus_watch_get_unix_fd(w->watch);
bump_maxfd(fd, maxfdp);
if (flags & DBUS_WATCH_READABLE)
FD_SET(fd, rset);
poll_listen(fd, POLLIN);
if (flags & DBUS_WATCH_WRITABLE)
FD_SET(fd, wset);
poll_listen(fd, POLLOUT);
FD_SET(fd, eset);
poll_listen(fd, POLLERR);
}
}
void check_dbus_listeners(fd_set *rset, fd_set *wset, fd_set *eset)
void check_dbus_listeners()
{
DBusConnection *connection = (DBusConnection *)daemon->dbus;
struct watch *w;
@@ -592,13 +780,13 @@ void check_dbus_listeners(fd_set *rset, fd_set *wset, fd_set *eset)
unsigned int flags = 0;
int fd = dbus_watch_get_unix_fd(w->watch);
if (FD_ISSET(fd, rset))
if (poll_check(fd, POLLIN))
flags |= DBUS_WATCH_READABLE;
if (FD_ISSET(fd, wset))
if (poll_check(fd, POLLOUT))
flags |= DBUS_WATCH_WRITABLE;
if (FD_ISSET(fd, eset))
if (poll_check(fd, POLLERR))
flags |= DBUS_WATCH_ERROR;
if (flags != 0)

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2016 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
@@ -545,8 +545,8 @@ static const struct opttab_t {
{ "parameter-request", 55, OT_INTERNAL },
{ "message", 56, OT_INTERNAL },
{ "max-message-size", 57, OT_INTERNAL },
{ "T1", 58, OT_INTERNAL | OT_TIME},
{ "T2", 59, OT_INTERNAL | OT_TIME},
{ "T1", 58, OT_TIME},
{ "T2", 59, OT_TIME},
{ "vendor-class", 60, 0 },
{ "client-id", 61, OT_INTERNAL },
{ "nis+-domain", 64, OT_NAME },
@@ -599,7 +599,7 @@ static const struct opttab_t opttab6[] = {
{ "sntp-server", 31, OT_ADDR_LIST },
{ "information-refresh-time", 32, OT_TIME },
{ "FQDN", 39, OT_INTERNAL | OT_RFC1035_NAME },
{ "ntp-server", 56, OT_ADDR_LIST },
{ "ntp-server", 56, 0 },
{ "bootfile-url", 59, OT_NAME },
{ "bootfile-param", 60, OT_CSTRING },
{ NULL, 0, 0 }

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2016 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-2015 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2016 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -146,6 +146,7 @@ void dhcp_packet(time_t now, int pxe_fd)
struct iovec iov;
ssize_t sz;
int iface_index = 0, unicast_dest = 0, is_inform = 0;
int rcvd_iface_index;
struct in_addr iface_addr;
struct iface_param parm;
#ifdef HAVE_LINUX_NETWORK
@@ -225,10 +226,12 @@ void dhcp_packet(time_t now, int pxe_fd)
strncpy(arp_req.arp_dev, ifr.ifr_name, 16);
#endif
/* One form of bridging on BSD has the property that packets
can be recieved on bridge interfaces which do not have an IP address.
We allow these to be treated as aliases of another interface which does have
an IP address with --dhcp-bridge=interface,alias,alias */
/* If the interface on which the DHCP request was received is an
alias of some other interface (as specified by the
--bridge-interface option), change ifr.ifr_name so that we look
for DHCP contexts associated with the aliased interface instead
of with the aliasing one. */
rcvd_iface_index = iface_index;
for (bridge = daemon->bridges; bridge; bridge = bridge->next)
{
for (alias = bridge->alias; alias; alias = alias->next)
@@ -236,7 +239,9 @@ void dhcp_packet(time_t now, int pxe_fd)
{
if (!(iface_index = if_nametoindex(bridge->iface)))
{
my_syslog(LOG_WARNING, _("unknown interface %s in bridge-interface"), ifr.ifr_name);
my_syslog(MS_DHCP | LOG_WARNING,
_("unknown interface %s in bridge-interface"),
bridge->iface);
return;
}
else
@@ -384,7 +389,7 @@ void dhcp_packet(time_t now, int pxe_fd)
msg.msg_controllen = sizeof(control_u);
cmptr = CMSG_FIRSTHDR(&msg);
pkt = (struct in_pktinfo *)CMSG_DATA(cmptr);
pkt->ipi_ifindex = iface_index;
pkt->ipi_ifindex = rcvd_iface_index;
pkt->ipi_spec_dst.s_addr = 0;
msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
cmptr->cmsg_level = IPPROTO_IP;
@@ -449,8 +454,13 @@ void dhcp_packet(time_t now, int pxe_fd)
#endif
while(retry_send(sendmsg(fd, &msg, 0)));
/* This can fail when, eg, iptables DROPS destination 255.255.255.255 */
if (errno != 0)
my_syslog(MS_DHCP | LOG_WARNING, _("Error sending DHCP packet to %s: %s"),
inet_ntoa(dest.sin_addr), strerror(errno));
}
/* check against secondary interface addresses */
static int check_listen_addrs(struct in_addr local, int if_index, char *label,
struct in_addr netmask, struct in_addr broadcast, void *vparam)
@@ -643,7 +653,7 @@ int address_allocate(struct dhcp_context *context,
/* hash hwaddr: use the SDBM hashing algorithm. Seems to give good
dispersal even with similarly-valued "strings". */
for (j = 0, i = 0; i < hw_len; i++)
j += hwaddr[i] + (j << 6) + (j << 16) - j;
j = hwaddr[i] + (j << 6) + (j << 16) - j;
for (pass = 0; pass <= 1; pass++)
for (c = context; c; c = c->current)

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2016 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-2015 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2016 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
@@ -27,17 +27,10 @@ struct iface_param {
int ind, addr_match;
};
struct mac_param {
struct in6_addr *target;
unsigned char *mac;
unsigned int maclen;
};
static int complete_context6(struct in6_addr *local, int prefix,
int scope, int if_index, int flags,
unsigned int preferred, unsigned int valid, void *vparam);
static int find_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv);
static int make_duid1(int index, unsigned int type, char *mac, size_t maclen, void *parm);
void dhcp6_init(void)
@@ -144,6 +137,8 @@ void dhcp6_packet(time_t now)
if ((port = relay_reply6(&from, sz, ifr.ifr_name)) == 0)
{
struct dhcp_bridge *bridge, *alias;
for (tmp = daemon->if_except; tmp; tmp = tmp->next)
if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
return;
@@ -160,6 +155,30 @@ void dhcp6_packet(time_t now)
memset(&parm.fallback, 0, IN6ADDRSZ);
memset(&parm.ll_addr, 0, IN6ADDRSZ);
memset(&parm.ula_addr, 0, IN6ADDRSZ);
/* If the interface on which the DHCPv6 request was received is
an alias of some other interface (as specified by the
--bridge-interface option), change parm.ind so that we look
for DHCPv6 contexts associated with the aliased interface
instead of with the aliasing one. */
for (bridge = daemon->bridges; bridge; bridge = bridge->next)
{
for (alias = bridge->alias; alias; alias = alias->next)
if (wildcard_matchn(alias->iface, ifr.ifr_name, IF_NAMESIZE))
{
parm.ind = if_nametoindex(bridge->iface);
if (!parm.ind)
{
my_syslog(MS_DHCP | LOG_WARNING,
_("unknown interface %s in bridge-interface"),
bridge->iface);
return;
}
break;
}
if (alias)
break;
}
for (context = daemon->dhcp6; context; context = context->next)
if (IN6_IS_ADDR_UNSPECIFIED(&context->start6) && context->prefix == 0)
@@ -201,14 +220,14 @@ void dhcp6_packet(time_t now)
inet_pton(AF_INET6, ALL_SERVERS, &all_servers);
if (!IN6_ARE_ADDR_EQUAL(&dst_addr, &all_servers))
relay_upstream6(parm.relay, sz, &from.sin6_addr, from.sin6_scope_id);
relay_upstream6(parm.relay, sz, &from.sin6_addr, from.sin6_scope_id, now);
return;
}
/* May have configured relay, but not DHCP server */
if (!daemon->doing_dhcp6)
return;
lease_prune(NULL, now); /* lose any expired leases */
port = dhcp6_reply(parm.current, if_index, ifr.ifr_name, &parm.fallback,
@@ -231,16 +250,15 @@ void dhcp6_packet(time_t now)
}
}
void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsigned int *maclenp, unsigned int *mactypep)
void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsigned int *maclenp, unsigned int *mactypep, time_t now)
{
/* Recieving a packet from a host does not populate the neighbour
cache, so we send a neighbour discovery request if we can't
find the sender. Repeat a few times in case of packet loss. */
struct neigh_packet neigh;
struct sockaddr_in6 addr;
struct mac_param mac_param;
int i;
union mysockaddr addr;
int i, maclen;
neigh.type = ND_NEIGHBOR_SOLICIT;
neigh.code = 0;
@@ -251,55 +269,31 @@ void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsi
memset(&addr, 0, sizeof(addr));
#ifdef HAVE_SOCKADDR_SA_LEN
addr.sin6_len = sizeof(struct sockaddr_in6);
addr.in6.sin6_len = sizeof(struct sockaddr_in6);
#endif
addr.sin6_family = AF_INET6;
addr.sin6_port = htons(IPPROTO_ICMPV6);
addr.sin6_addr = *client;
addr.sin6_scope_id = iface;
mac_param.target = client;
mac_param.maclen = 0;
mac_param.mac = mac;
addr.in6.sin6_family = AF_INET6;
addr.in6.sin6_port = htons(IPPROTO_ICMPV6);
addr.in6.sin6_addr = *client;
addr.in6.sin6_scope_id = iface;
for (i = 0; i < 5; i++)
{
struct timespec ts;
iface_enumerate(AF_UNSPEC, &mac_param, find_mac);
if (mac_param.maclen != 0)
if ((maclen = find_mac(&addr, mac, 0, now)) != 0)
break;
sendto(daemon->icmp6fd, &neigh, sizeof(neigh), 0, (struct sockaddr *)&addr, sizeof(addr));
sendto(daemon->icmp6fd, &neigh, sizeof(neigh), 0, &addr.sa, sizeof(addr));
ts.tv_sec = 0;
ts.tv_nsec = 100000000; /* 100ms */
nanosleep(&ts, NULL);
}
*maclenp = mac_param.maclen;
*maclenp = maclen;
*mactypep = ARPHRD_ETHER;
}
static int find_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv)
{
struct mac_param *parm = parmv;
if (family == AF_INET6 && IN6_ARE_ADDR_EQUAL(parm->target, (struct in6_addr *)addrp))
{
if (maclen <= DHCP_CHADDR_MAX)
{
parm->maclen = maclen;
memcpy(parm->mac, mac, maclen);
}
return 0; /* found, abort */
}
return 1;
}
static int complete_context6(struct in6_addr *local, int prefix,
int scope, int if_index, int flags, unsigned int preferred,
unsigned int valid, void *vparam)
@@ -426,7 +420,7 @@ struct dhcp_context *address6_allocate(struct dhcp_context *context, unsigned c
j = rand64();
else
for (j = iaid, i = 0; i < clid_len; i++)
j += clid[i] + (j << 6) + (j << 16) - j;
j = clid[i] + (j << 6) + (j << 16) - j;
for (pass = 0; pass <= plain_range ? 1 : 0; pass++)
for (c = context; c; c = c->current)
@@ -440,7 +434,16 @@ struct dhcp_context *address6_allocate(struct dhcp_context *context, unsigned c
/* seed is largest extant lease addr in this context */
start = lease_find_max_addr6(c) + serial;
else
start = addr6part(&c->start6) + ((j + c->addr_epoch) % (1 + addr6part(&c->end6) - addr6part(&c->start6)));
{
u64 range = 1 + addr6part(&c->end6) - addr6part(&c->start6);
u64 offset = j + c->addr_epoch;
/* don't divide by zero if range is whole 2^64 */
if (range != 0)
offset = offset % range;
start = addr6part(&c->start6) + offset;
}
/* iterate until we find a free address. */
addr = start;

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2016 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 MAX_PORT 65535u
#define IN6ADDRSZ 16
#define INADDRSZ 4
@@ -77,6 +78,8 @@
#define EDNS0_OPTION_MAC 65001 /* dyndns.org temporary assignment */
#define EDNS0_OPTION_CLIENT_SUBNET 8 /* IANA */
#define EDNS0_OPTION_NOMDEVICEID 65073 /* Nominum temporary assignment */
#define EDNS0_OPTION_NOMCPEID 65074 /* Nominum temporary assignment */
struct dns_header {
u16 id;
@@ -84,15 +87,15 @@ struct dns_header {
u16 qdcount,ancount,nscount,arcount;
};
#define HB3_QR 0x80
#define HB3_QR 0x80 /* Query */
#define HB3_OPCODE 0x78
#define HB3_AA 0x04
#define HB3_TC 0x02
#define HB3_RD 0x01
#define HB3_AA 0x04 /* Authoritative Answer */
#define HB3_TC 0x02 /* TrunCated */
#define HB3_RD 0x01 /* Recursion Desired */
#define HB4_RA 0x80
#define HB4_AD 0x20
#define HB4_CD 0x10
#define HB4_RA 0x80 /* Recursion Available */
#define HB4_AD 0x20 /* Authenticated Data */
#define HB4_CD 0x10 /* Checking Disabled */
#define HB4_RCODE 0x0f
#define OPCODE(x) (((x)->hb3 & HB3_OPCODE) >> 3)
@@ -142,3 +145,11 @@ struct dns_header {
#define ADD_RDLEN(header, pp, plen, len) \
(!CHECK_LEN(header, pp, plen, len) ? 0 : (((pp) += (len)), 1))
/* Escape character in our presentation format for names.
Cannot be '.' or /000 and must be !isprint().
Note that escaped chars are stored as
<NAME_ESCAPE> <orig-char+1>
to ensure that the escaped form of /000 doesn't include /000
*/
#define NAME_ESCAPE 1

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2016 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -24,8 +24,8 @@ struct daemon *daemon;
static volatile pid_t pid = 0;
static volatile int pipewrite;
static int set_dns_listeners(time_t now, fd_set *set, int *maxfdp);
static void check_dns_listeners(fd_set *set, time_t now);
static int set_dns_listeners(time_t now);
static void check_dns_listeners(time_t now);
static void sig_handler(int sig);
static void async_event(int pipe, time_t now);
static void fatal_event(struct event_desc *ev, char *msg);
@@ -58,6 +58,9 @@ int main (int argc, char **argv)
struct dhcp_context *context;
struct dhcp_relay *relay;
#endif
#ifdef HAVE_TFTP
int tftp_prefix_missing = 0;
#endif
#ifdef LOCALEDIR
setlocale(LC_ALL, "");
@@ -88,8 +91,11 @@ int main (int argc, char **argv)
if (daemon->edns_pktsz < PACKETSZ)
daemon->edns_pktsz = PACKETSZ;
daemon->packet_buff_sz = daemon->edns_pktsz > DNSMASQ_PACKETSZ ?
daemon->edns_pktsz : DNSMASQ_PACKETSZ;
/* Min buffer size: we check after adding each record, so there must be
memory for the largest packet, and the largest record so the
min for DNS is PACKETSZ+MAXDNAME+RRFIXEDSZ which is < 1000.
This might be increased is EDNS packet size if greater than the minimum. */
daemon->packet_buff_sz = daemon->edns_pktsz + MAXDNAME + RRFIXEDSZ;
daemon->packet = safe_malloc(daemon->packet_buff_sz);
daemon->addrbuff = safe_malloc(ADDRSTRLEN);
@@ -99,8 +105,19 @@ int main (int argc, char **argv)
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID))
{
daemon->keyname = safe_malloc(MAXDNAME);
daemon->workspacename = safe_malloc(MAXDNAME);
/* Note that both /000 and '.' are allowed within labels. These get
represented in presentation format using NAME_ESCAPE as an escape
character when in DNSSEC mode.
In theory, if all the characters in a name were /000 or
'.' or NAME_ESCAPE then all would have to be escaped, so the
presentation format would be twice as long as the spec.
daemon->namebuff was previously allocated by the option-reading
code before we knew if we're in DNSSEC mode, so reallocate here. */
free(daemon->namebuff);
daemon->namebuff = safe_malloc(MAXDNAME * 2);
daemon->keyname = safe_malloc(MAXDNAME * 2);
daemon->workspacename = safe_malloc(MAXDNAME * 2);
}
#endif
@@ -152,8 +169,16 @@ int main (int argc, char **argv)
if (option_bool(OPT_DNSSEC_VALID))
{
#ifdef HAVE_DNSSEC
if (!daemon->ds)
die(_("no trust anchors provided for DNSSEC"), NULL, EC_BADCONF);
struct ds_config *ds;
/* Must have at least a root trust anchor, or the DNSSEC code
can loop forever. */
for (ds = daemon->ds; ds; ds = ds->next)
if (ds->name[0] == 0)
break;
if (!ds)
die(_("no root trust anchor provided for DNSSEC"), NULL, EC_BADCONF);
if (daemon->cachesize < CACHESIZ)
die(_("cannot reduce cache size from default when DNSSEC enabled"), NULL, EC_BADCONF);
@@ -194,7 +219,13 @@ int main (int argc, char **argv)
if (option_bool(OPT_LOOP_DETECT))
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;
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. */
@@ -228,8 +259,11 @@ int main (int argc, char **argv)
/* Note that order matters here, we must call lease_init before
creating any file descriptors which shouldn't be leaked
to the lease-script init process. We need to call common_init
before lease_init to allocate buffers it uses.*/
if (daemon->dhcp || daemon->doing_dhcp6 || daemon->relay4 || daemon->relay6)
before lease_init to allocate buffers it uses.
The script subsystem relies on DHCP buffers, hence the last two
conditions below. */
if (daemon->dhcp || daemon->doing_dhcp6 || daemon->relay4 ||
daemon->relay6 || option_bool(OPT_TFTP) || option_bool(OPT_SCRIPT_ARP))
{
dhcp_common_init();
if (daemon->dhcp || daemon->doing_dhcp6)
@@ -342,7 +376,7 @@ int main (int argc, char **argv)
#else
die(_("DBus not available: set HAVE_DBUS in src/config.h"), NULL, EC_BADCONF);
#endif
if (daemon->port != 0)
pre_allocate_sfds();
@@ -527,17 +561,21 @@ int main (int argc, char **argv)
{
/* open stdout etc to /dev/null */
int nullfd = open("/dev/null", O_RDWR);
dup2(nullfd, STDOUT_FILENO);
dup2(nullfd, STDERR_FILENO);
dup2(nullfd, STDIN_FILENO);
close(nullfd);
if (nullfd != -1)
{
dup2(nullfd, STDOUT_FILENO);
dup2(nullfd, STDERR_FILENO);
dup2(nullfd, STDIN_FILENO);
close(nullfd);
}
}
/* if we are to run scripts, we need to fork a helper before dropping root. */
daemon->helperfd = -1;
#ifdef HAVE_SCRIPT
if ((daemon->dhcp || daemon->dhcp6) && (daemon->lease_change_command || daemon->luascript))
daemon->helperfd = create_helper(pipewrite, err_pipe[1], script_uid, script_gid, max_fd);
if ((daemon->dhcp || daemon->dhcp6 || option_bool(OPT_TFTP) || option_bool(OPT_SCRIPT_ARP)) &&
(daemon->lease_change_command || daemon->luascript))
daemon->helperfd = create_helper(pipewrite, err_pipe[1], script_uid, script_gid, max_fd);
#endif
if (!option_bool(OPT_DEBUG) && getuid() == 0)
@@ -636,7 +674,7 @@ int main (int argc, char **argv)
#endif
#ifdef HAVE_TFTP
if (option_bool(OPT_TFTP))
if (option_bool(OPT_TFTP))
{
DIR *dir;
struct tftp_prefix *p;
@@ -645,20 +683,31 @@ int main (int argc, char **argv)
{
if (!((dir = opendir(daemon->tftp_prefix))))
{
send_event(err_pipe[1], EVENT_TFTP_ERR, errno, daemon->tftp_prefix);
_exit(0);
tftp_prefix_missing = 1;
if (!option_bool(OPT_TFTP_NO_FAIL))
{
send_event(err_pipe[1], EVENT_TFTP_ERR, errno, daemon->tftp_prefix);
_exit(0);
}
}
closedir(dir);
else
closedir(dir);
}
for (p = daemon->if_prefix; p; p = p->next)
{
p->missing = 0;
if (!((dir = opendir(p->prefix))))
{
send_event(err_pipe[1], EVENT_TFTP_ERR, errno, p->prefix);
_exit(0);
}
closedir(dir);
{
p->missing = 1;
if (!option_bool(OPT_TFTP_NO_FAIL))
{
send_event(err_pipe[1], EVENT_TFTP_ERR, errno, p->prefix);
_exit(0);
}
}
else
closedir(dir);
}
}
#endif
@@ -773,16 +822,20 @@ int main (int argc, char **argv)
#ifdef HAVE_TFTP
if (option_bool(OPT_TFTP))
{
#ifdef FD_SETSIZE
if (FD_SETSIZE < (unsigned)max_fd)
max_fd = FD_SETSIZE;
#endif
struct tftp_prefix *p;
my_syslog(MS_TFTP | LOG_INFO, "TFTP %s%s %s",
daemon->tftp_prefix ? _("root is ") : _("enabled"),
daemon->tftp_prefix ? daemon->tftp_prefix: "",
option_bool(OPT_TFTP_SECURE) ? _("secure mode") : "");
if (tftp_prefix_missing)
my_syslog(MS_TFTP | LOG_WARNING, _("warning: %s inaccessible"), daemon->tftp_prefix);
for (p = daemon->if_prefix; p; p = p->next)
if (p->missing)
my_syslog(MS_TFTP | LOG_WARNING, _("warning: TFTP directory %s inaccessible"), p->prefix);
/* This is a guess, it assumes that for small limits,
disjoint files might be served, but for large limits,
a single file will be sent to may clients (the file only needs
@@ -829,126 +882,100 @@ int main (int argc, char **argv)
while (1)
{
int maxfd = -1;
struct timeval t, *tp = NULL;
fd_set rset, wset, eset;
int t, timeout = -1;
FD_ZERO(&rset);
FD_ZERO(&wset);
FD_ZERO(&eset);
poll_reset();
/* if we are out of resources, find how long we have to wait
for some to come free, we'll loop around then and restart
listening for queries */
if ((t.tv_sec = set_dns_listeners(now, &rset, &maxfd)) != 0)
{
t.tv_usec = 0;
tp = &t;
}
if ((t = set_dns_listeners(now)) != 0)
timeout = t * 1000;
/* Whilst polling for the dbus, or doing a tftp transfer, wake every quarter second */
if (daemon->tftp_trans ||
(option_bool(OPT_DBUS) && !daemon->dbus))
{
t.tv_sec = 0;
t.tv_usec = 250000;
tp = &t;
}
timeout = 250;
/* Wake every second whilst waiting for DAD to complete */
else if (is_dad_listeners())
{
t.tv_sec = 1;
t.tv_usec = 0;
tp = &t;
}
timeout = 1000;
#ifdef HAVE_DBUS
set_dbus_listeners(&maxfd, &rset, &wset, &eset);
set_dbus_listeners();
#endif
#ifdef HAVE_DHCP
if (daemon->dhcp || daemon->relay4)
{
FD_SET(daemon->dhcpfd, &rset);
bump_maxfd(daemon->dhcpfd, &maxfd);
poll_listen(daemon->dhcpfd, POLLIN);
if (daemon->pxefd != -1)
{
FD_SET(daemon->pxefd, &rset);
bump_maxfd(daemon->pxefd, &maxfd);
}
poll_listen(daemon->pxefd, POLLIN);
}
#endif
#ifdef HAVE_DHCP6
if (daemon->doing_dhcp6 || daemon->relay6)
{
FD_SET(daemon->dhcp6fd, &rset);
bump_maxfd(daemon->dhcp6fd, &maxfd);
}
poll_listen(daemon->dhcp6fd, POLLIN);
if (daemon->doing_ra)
{
FD_SET(daemon->icmp6fd, &rset);
bump_maxfd(daemon->icmp6fd, &maxfd);
}
poll_listen(daemon->icmp6fd, POLLIN);
#endif
#ifdef HAVE_INOTIFY
if (daemon->inotifyfd != -1)
{
FD_SET(daemon->inotifyfd, &rset);
bump_maxfd(daemon->inotifyfd, &maxfd);
}
poll_listen(daemon->inotifyfd, POLLIN);
#endif
#if defined(HAVE_LINUX_NETWORK)
FD_SET(daemon->netlinkfd, &rset);
bump_maxfd(daemon->netlinkfd, &maxfd);
poll_listen(daemon->netlinkfd, POLLIN);
#elif defined(HAVE_BSD_NETWORK)
FD_SET(daemon->routefd, &rset);
bump_maxfd(daemon->routefd, &maxfd);
poll_listen(daemon->routefd, POLLIN);
#endif
FD_SET(piperead, &rset);
bump_maxfd(piperead, &maxfd);
poll_listen(piperead, POLLIN);
#ifdef HAVE_DHCP
# ifdef HAVE_SCRIPT
while (helper_buf_empty() && do_script_run(now));
#ifdef HAVE_SCRIPT
# ifdef HAVE_DHCP
while (helper_buf_empty() && do_script_run(now));
# endif
/* Refresh cache */
if (option_bool(OPT_SCRIPT_ARP))
find_mac(NULL, NULL, 0, now);
while (helper_buf_empty() && do_arp_script_run());
# ifdef HAVE_TFTP
while (helper_buf_empty() && do_tftp_script_run());
# endif
if (!helper_buf_empty())
{
FD_SET(daemon->helperfd, &wset);
bump_maxfd(daemon->helperfd, &maxfd);
}
# else
poll_listen(daemon->helperfd, POLLOUT);
#else
/* need this for other side-effects */
# ifdef HAVE_DHCP
while (do_script_run(now));
# endif
while (do_arp_script_run());
# ifdef HAVE_TFTP
while (do_tftp_script_run());
# endif
# endif
#endif
/* must do this just before select(), when we know no
more calls to my_syslog() can occur */
set_log_writer(&wset, &maxfd);
set_log_writer();
if (do_poll(timeout) < 0)
continue;
if (select(maxfd+1, &rset, &wset, &eset, tp) < 0)
{
/* otherwise undefined after error */
FD_ZERO(&rset); FD_ZERO(&wset); FD_ZERO(&eset);
}
now = dnsmasq_time();
check_log_writer(&wset);
check_log_writer(0);
/* prime. */
enumerate_interfaces(1);
@@ -964,15 +991,15 @@ int main (int argc, char **argv)
}
#if defined(HAVE_LINUX_NETWORK)
if (FD_ISSET(daemon->netlinkfd, &rset))
if (poll_check(daemon->netlinkfd, POLLIN))
netlink_multicast();
#elif defined(HAVE_BSD_NETWORK)
if (FD_ISSET(daemon->routefd, &rset))
if (poll_check(daemon->routefd, POLLIN))
route_sock();
#endif
#ifdef HAVE_INOTIFY
if (daemon->inotifyfd != -1 && FD_ISSET(daemon->inotifyfd, &rset) && inotify_check(now))
if (daemon->inotifyfd != -1 && poll_check(daemon->inotifyfd, POLLIN) && inotify_check(now))
{
if (daemon->port != 0 && !option_bool(OPT_NO_POLL))
poll_resolv(1, 1, now);
@@ -992,7 +1019,7 @@ int main (int argc, char **argv)
}
#endif
if (FD_ISSET(piperead, &rset))
if (poll_check(piperead, POLLIN))
async_event(piperead, now);
#ifdef HAVE_DBUS
@@ -1005,34 +1032,34 @@ int main (int argc, char **argv)
if (daemon->dbus)
my_syslog(LOG_INFO, _("connected to system DBus"));
}
check_dbus_listeners(&rset, &wset, &eset);
check_dbus_listeners();
#endif
check_dns_listeners(&rset, now);
check_dns_listeners(now);
#ifdef HAVE_TFTP
check_tftp_listeners(&rset, now);
check_tftp_listeners(now);
#endif
#ifdef HAVE_DHCP
if (daemon->dhcp || daemon->relay4)
{
if (FD_ISSET(daemon->dhcpfd, &rset))
if (poll_check(daemon->dhcpfd, POLLIN))
dhcp_packet(now, 0);
if (daemon->pxefd != -1 && FD_ISSET(daemon->pxefd, &rset))
if (daemon->pxefd != -1 && poll_check(daemon->pxefd, POLLIN))
dhcp_packet(now, 1);
}
#ifdef HAVE_DHCP6
if ((daemon->doing_dhcp6 || daemon->relay6) && FD_ISSET(daemon->dhcp6fd, &rset))
if ((daemon->doing_dhcp6 || daemon->relay6) && poll_check(daemon->dhcp6fd, POLLIN))
dhcp6_packet(now);
if (daemon->doing_ra && FD_ISSET(daemon->icmp6fd, &rset))
if (daemon->doing_ra && poll_check(daemon->icmp6fd, POLLIN))
icmp6_packet(now);
#endif
# ifdef HAVE_SCRIPT
if (daemon->helperfd != -1 && FD_ISSET(daemon->helperfd, &wset))
if (daemon->helperfd != -1 && poll_check(daemon->helperfd, POLLOUT))
helper_write();
# endif
#endif
@@ -1311,7 +1338,7 @@ static void async_event(int pipe, time_t now)
if (daemon->tcp_pids[i] != 0)
kill(daemon->tcp_pids[i], SIGALRM);
#if defined(HAVE_SCRIPT)
#if defined(HAVE_SCRIPT) && defined(HAVE_DHCP)
/* handle pending lease transitions */
if (daemon->helperfd != -1)
{
@@ -1328,6 +1355,15 @@ static void async_event(int pipe, time_t now)
if (daemon->lease_stream)
fclose(daemon->lease_stream);
#ifdef HAVE_DNSSEC
/* update timestamp file on TERM if time is considered valid */
if (daemon->back_to_the_future)
{
if (utime(daemon->timestamp_file, NULL) == -1)
my_syslog(LOG_ERR, _("failed to update mtime on %s: %s"), daemon->timestamp_file, strerror(errno));
}
#endif
if (daemon->runfile)
unlink(daemon->runfile);
@@ -1437,7 +1473,7 @@ void clear_cache_and_reload(time_t now)
#endif
}
static int set_dns_listeners(time_t now, fd_set *set, int *maxfdp)
static int set_dns_listeners(time_t now)
{
struct serverfd *serverfdp;
struct listener *listener;
@@ -1449,8 +1485,7 @@ static int set_dns_listeners(time_t now, fd_set *set, int *maxfdp)
for (transfer = daemon->tftp_trans; transfer; transfer = transfer->next)
{
tftp++;
FD_SET(transfer->sockfd, set);
bump_maxfd(transfer->sockfd, maxfdp);
poll_listen(transfer->sockfd, POLLIN);
}
#endif
@@ -1459,45 +1494,32 @@ static int set_dns_listeners(time_t now, fd_set *set, int *maxfdp)
get_new_frec(now, &wait, 0);
for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
{
FD_SET(serverfdp->fd, set);
bump_maxfd(serverfdp->fd, maxfdp);
}
poll_listen(serverfdp->fd, POLLIN);
if (daemon->port != 0 && !daemon->osport)
for (i = 0; i < RANDOM_SOCKS; i++)
if (daemon->randomsocks[i].refcount != 0)
{
FD_SET(daemon->randomsocks[i].fd, set);
bump_maxfd(daemon->randomsocks[i].fd, maxfdp);
}
poll_listen(daemon->randomsocks[i].fd, POLLIN);
for (listener = daemon->listeners; listener; listener = listener->next)
{
/* only listen for queries if we have resources */
if (listener->fd != -1 && wait == 0)
{
FD_SET(listener->fd, set);
bump_maxfd(listener->fd, maxfdp);
}
poll_listen(listener->fd, POLLIN);
/* death of a child goes through the select loop, so
we don't need to explicitly arrange to wake up here */
if (listener->tcpfd != -1)
for (i = 0; i < MAX_PROCS; i++)
if (daemon->tcp_pids[i] == 0)
{
FD_SET(listener->tcpfd, set);
bump_maxfd(listener->tcpfd, maxfdp);
poll_listen(listener->tcpfd, POLLIN);
break;
}
#ifdef HAVE_TFTP
if (tftp <= daemon->tftp_max && listener->tftpfd != -1)
{
FD_SET(listener->tftpfd, set);
bump_maxfd(listener->tftpfd, maxfdp);
}
poll_listen(listener->tftpfd, POLLIN);
#endif
}
@@ -1505,33 +1527,33 @@ static int set_dns_listeners(time_t now, fd_set *set, int *maxfdp)
return wait;
}
static void check_dns_listeners(fd_set *set, time_t now)
static void check_dns_listeners(time_t now)
{
struct serverfd *serverfdp;
struct listener *listener;
int i;
for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
if (FD_ISSET(serverfdp->fd, set))
if (poll_check(serverfdp->fd, POLLIN))
reply_query(serverfdp->fd, serverfdp->source_addr.sa.sa_family, now);
if (daemon->port != 0 && !daemon->osport)
for (i = 0; i < RANDOM_SOCKS; i++)
if (daemon->randomsocks[i].refcount != 0 &&
FD_ISSET(daemon->randomsocks[i].fd, set))
poll_check(daemon->randomsocks[i].fd, POLLIN))
reply_query(daemon->randomsocks[i].fd, daemon->randomsocks[i].family, now);
for (listener = daemon->listeners; listener; listener = listener->next)
{
if (listener->fd != -1 && FD_ISSET(listener->fd, set))
if (listener->fd != -1 && poll_check(listener->fd, POLLIN))
receive_query(listener, now);
#ifdef HAVE_TFTP
if (listener->tftpfd != -1 && FD_ISSET(listener->tftpfd, set))
if (listener->tftpfd != -1 && poll_check(listener->tftpfd, POLLIN))
tftp_request(listener, now);
#endif
if (listener->tcpfd != -1 && FD_ISSET(listener->tcpfd, set))
if (listener->tcpfd != -1 && poll_check(listener->tcpfd, POLLIN))
{
int confd, client_ok = 1;
struct irec *iface = NULL;
@@ -1722,14 +1744,22 @@ int icmp_ping(struct in_addr addr)
better not use any resources our caller has in use...)
but we remain deaf to signals or further DHCP packets. */
int fd;
/* There can be a problem using dnsmasq_time() to end the loop, since
it's not monotonic, and can go backwards if the system clock is
tweaked, leading to the code getting stuck in this loop and
ignoring DHCP requests. To fix this, we check to see if select returned
as a result of a timeout rather than a socket becoming available. We
only allow this to happen as many times as it takes to get to the wait time
in quarter-second chunks. This provides a fallback way to end loop. */
int fd, rc;
struct sockaddr_in saddr;
struct {
struct ip ip;
struct icmp icmp;
} packet;
unsigned short id = rand16();
unsigned int i, j;
unsigned int i, j, timeout_count;
int gotreply = 0;
time_t start, now;
@@ -1761,53 +1791,44 @@ int icmp_ping(struct in_addr addr)
while (retry_send(sendto(fd, (char *)&packet.icmp, sizeof(struct icmp), 0,
(struct sockaddr *)&saddr, sizeof(saddr))));
for (now = start = dnsmasq_time();
difftime(now, start) < (float)PING_WAIT;)
for (now = start = dnsmasq_time(), timeout_count = 0;
(difftime(now, start) < (float)PING_WAIT) && (timeout_count < PING_WAIT * 4);)
{
struct timeval tv;
fd_set rset, wset;
struct sockaddr_in faddr;
int maxfd = fd;
socklen_t len = sizeof(faddr);
tv.tv_usec = 250000;
tv.tv_sec = 0;
FD_ZERO(&rset);
FD_ZERO(&wset);
FD_SET(fd, &rset);
set_dns_listeners(now, &rset, &maxfd);
set_log_writer(&wset, &maxfd);
poll_reset();
poll_listen(fd, POLLIN);
set_dns_listeners(now);
set_log_writer();
#ifdef HAVE_DHCP6
if (daemon->doing_ra)
{
FD_SET(daemon->icmp6fd, &rset);
bump_maxfd(daemon->icmp6fd, &maxfd);
}
poll_listen(daemon->icmp6fd, POLLIN);
#endif
if (select(maxfd+1, &rset, &wset, NULL, &tv) < 0)
{
FD_ZERO(&rset);
FD_ZERO(&wset);
}
rc = do_poll(250);
if (rc < 0)
continue;
else if (rc == 0)
timeout_count++;
now = dnsmasq_time();
check_log_writer(&wset);
check_dns_listeners(&rset, now);
check_log_writer(0);
check_dns_listeners(now);
#ifdef HAVE_DHCP6
if (daemon->doing_ra && FD_ISSET(daemon->icmp6fd, &rset))
if (daemon->doing_ra && poll_check(daemon->icmp6fd, POLLIN))
icmp6_packet(now);
#endif
#ifdef HAVE_TFTP
check_tftp_listeners(&rset, now);
check_tftp_listeners(now);
#endif
if (FD_ISSET(fd, &rset) &&
if (poll_check(fd, POLLIN) &&
recvfrom(fd, &packet, sizeof(packet), 0,
(struct sockaddr *)&faddr, &len) == sizeof(packet) &&
saddr.sin_addr.s_addr == faddr.sin_addr.s_addr &&

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2016 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -14,7 +14,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define COPYRIGHT "Copyright (c) 2000-2015 Simon Kelley"
#define COPYRIGHT "Copyright (c) 2000-2016 Simon Kelley"
#ifndef NO_LARGEFILE
/* Ensure we can use files >2GB (log files may grow this big) */
@@ -82,7 +82,7 @@ typedef unsigned long long u64;
#if defined(HAVE_SOLARIS_NETWORK)
# include <sys/sockio.h>
#endif
#include <sys/select.h>
#include <sys/poll.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/un.h>
@@ -117,6 +117,7 @@ typedef unsigned long long u64;
#include <sys/uio.h>
#include <syslog.h>
#include <dirent.h>
#include <utime.h>
#ifndef HAVE_LINUX_NETWORK
# include <net/if_dl.h>
#endif
@@ -178,13 +179,6 @@ struct event_desc {
#define EC_MISC 5
#define EC_INIT_OFFSET 10
/* Min buffer size: we check after adding each record, so there must be
memory for the largest packet, and the largest record so the
min for DNS is PACKETSZ+MAXDNAME+RRFIXEDSZ which is < 1000.
This might be increased is EDNS packet size if greater than the minimum.
*/
#define DNSMASQ_PACKETSZ PACKETSZ+MAXDNAME+RRFIXEDSZ
/* Trust the compiler dead-code eliminator.... */
#define option_bool(x) (((x) < 32) ? daemon->options & (1u << (x)) : daemon->options2 & (1u << ((x) - 32)))
@@ -240,7 +234,11 @@ struct event_desc {
#define OPT_LOCAL_SERVICE 49
#define OPT_LOOP_DETECT 50
#define OPT_EXTRALOG 51
#define OPT_LAST 52
#define OPT_TFTP_NO_FAIL 52
#define OPT_SCRIPT_ARP 53
#define OPT_MAC_B64 54
#define OPT_MAC_HEX 55
#define OPT_LAST 56
/* 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. */
@@ -254,8 +252,10 @@ struct all_addr {
struct in6_addr addr6;
#endif
/* for log_query */
unsigned int keytag;
/* for cache_insert if RRSIG, DNSKEY, DS */
struct {
unsigned short keytag, algo, digest;
} log;
/* for cache_insert of DNSKEY, DS */
struct {
unsigned short class, type;
} dnssec;
@@ -308,6 +308,7 @@ struct ptr_record {
};
struct cname {
int ttl;
char *alias, *target;
struct cname *next;
};
@@ -344,6 +345,7 @@ struct auth_zone {
struct host_record {
int ttl;
struct name_list {
char *name;
struct name_list *next;
@@ -396,14 +398,9 @@ struct crec {
unsigned char algo;
unsigned char digest;
} ds;
struct {
struct blockdata *keydata;
unsigned short keylen, type_covered, keytag;
char algo;
} sig;
} addr;
time_t ttd; /* time to die */
/* used as class if DNSKEY/DS/RRSIG, index to source for F_HOSTS */
/* used as class if DNSKEY/DS, index to source for F_HOSTS */
unsigned int uid;
unsigned short flags;
union {
@@ -443,8 +440,7 @@ struct crec {
#define F_SECSTAT (1u<<24)
#define F_NO_RR (1u<<25)
#define F_IPSET (1u<<26)
#define F_NSIGMATCH (1u<<27)
#define F_NOEXTRA (1u<<28)
#define F_NOEXTRA (1u<<27)
/* Values of uid in crecs with F_CONFIG bit set. */
#define SRC_INTERFACE 0
@@ -485,6 +481,7 @@ union mysockaddr {
#define SERV_NO_REBIND 2048 /* inhibit dns-rebind protection */
#define SERV_FROM_FILE 4096 /* read from --servers-file */
#define SERV_LOOP 8192 /* server causes forwarding loop */
#define SERV_DO_DNSSEC 16384 /* Validate DNSSEC when using this server */
struct serverfd {
int fd;
@@ -503,7 +500,7 @@ struct server {
char interface[IF_NAMESIZE+1];
struct serverfd *sfd;
char *domain; /* set if this server only handles a domain. */
int flags, tcpfd;
int flags, tcpfd, edns_pktsz;
unsigned int queries, failed_queries;
#ifdef HAVE_LOOP
u32 uid;
@@ -539,6 +536,13 @@ struct iname {
struct iname *next;
};
/* subnet parameters from command line */
struct mysubnet {
union mysockaddr addr;
int addr_used;
int mask;
};
/* resolv-file parms from command-line */
struct resolvc {
struct resolvc *next;
@@ -577,11 +581,8 @@ struct hostsfile {
#define STAT_NEED_KEY 5
#define STAT_TRUNCATED 6
#define STAT_SECURE_WILDCARD 7
#define STAT_NO_SIG 8
#define STAT_NO_DS 9
#define STAT_NO_NS 10
#define STAT_NEED_DS_NEG 11
#define STAT_CHASE_CNAME 12
#define STAT_OK 8
#define STAT_ABANDONED 9
#define FREC_NOREBIND 1
#define FREC_CHECKING_DISABLED 2
@@ -591,7 +592,8 @@ struct hostsfile {
#define FREC_AD_QUESTION 32
#define FREC_DO_QUESTION 64
#define FREC_ADDED_PHEADER 128
#define FREC_CHECK_NOSIGN 256
#define FREC_TEST_PKTSZ 256
#define FREC_HAS_EXTRADATA 512
#ifdef HAVE_DNSSEC
#define HASH_SIZE 20 /* SHA-1 digest size */
@@ -615,9 +617,7 @@ struct frec {
#ifdef HAVE_DNSSEC
int class, work_counter;
struct blockdata *stash; /* Saved reply, whilst we validate */
struct blockdata *orig_domain; /* domain of original query, whilst
we're seeing is if in unsigned domain */
size_t stash_len, name_start, name_len;
size_t stash_len;
struct frec *dependent; /* Query awaiting internally-generated DNSKEY or DS query */
struct frec *blocking_query; /* Query which is blocking us. */
#endif
@@ -639,6 +639,8 @@ struct frec {
#define ACTION_OLD 3
#define ACTION_ADD 4
#define ACTION_TFTP 5
#define ACTION_ARP 6
#define ACTION_ARP_DEL 7
#define LEASE_NEW 1 /* newly created */
#define LEASE_CHANGED 2 /* modified */
@@ -865,6 +867,7 @@ struct dhcp_context {
#define CONTEXT_USED (1u<<15)
#define CONTEXT_OLD (1u<<16)
#define CONTEXT_V6 (1u<<17)
#define CONTEXT_RA_OFF_LINK (1u<<18)
struct ping_result {
struct in_addr addr;
@@ -901,6 +904,7 @@ struct addr_list {
struct tftp_prefix {
char *interface;
char *prefix;
int missing;
struct tftp_prefix *next;
};
@@ -929,9 +933,9 @@ extern struct daemon {
struct auth_zone *auth_zones;
struct interface_name *int_names;
char *mxtarget;
int addr4_netmask;
int addr6_netmask;
char *lease_file;
struct mysubnet *add_subnet4;
struct mysubnet *add_subnet6;
char *lease_file;
char *username, *groupname, *scriptuser;
char *luascript;
char *authserver, *hostmaster;
@@ -950,8 +954,9 @@ extern struct daemon {
char *log_file; /* optional log file */
int max_logs; /* queue limit */
int cachesize, ftabsize;
int port, query_port, min_port;
unsigned long local_ttl, neg_ttl, max_ttl, min_cache_ttl, max_cache_ttl, auth_ttl;
int port, query_port, min_port, max_port;
unsigned long local_ttl, neg_ttl, max_ttl, min_cache_ttl, max_cache_ttl, auth_ttl, dhcp_ttl, use_dhcp_ttl;
char *dns_client_id;
struct hostsfile *addn_hosts;
struct dhcp_context *dhcp, *dhcp6;
struct ra_interface *ra_interfaces;
@@ -970,7 +975,7 @@ extern struct daemon {
struct dhcp_netid_list *dhcp_ignore, *dhcp_ignore_names, *dhcp_gen_names;
struct dhcp_netid_list *force_broadcast, *bootp_dynamic;
struct hostsfile *dhcp_hosts_file, *dhcp_opts_file, *dynamic_dirs;
int dhcp_max, tftp_max;
int dhcp_max, tftp_max, tftp_mtu;
int dhcp_server_port, dhcp_client_port;
int start_tftp_port, end_tftp_port;
unsigned int min_leasetime;
@@ -987,6 +992,7 @@ extern struct daemon {
#endif
#ifdef HAVE_DNSSEC
struct ds_config *ds;
int back_to_the_future;
char *timestamp_file;
#endif
@@ -1112,22 +1118,14 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *namebuff,
int no_cache, int secure, int *doctored);
size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
struct in_addr local_addr, struct in_addr local_netmask,
time_t now, int *ad_reqd, int *do_bit);
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);
int check_for_ignored_address(struct dns_header *header, size_t qlen, struct bogus_addr *baddr);
unsigned char *find_pseudoheader(struct dns_header *header, size_t plen,
size_t *len, unsigned char **p, int *is_sign);
int check_for_local_domain(char *name, time_t now);
unsigned int questions_crc(struct dns_header *header, size_t plen, char *buff);
size_t resize_packet(struct dns_header *header, size_t plen,
unsigned char *pheader, size_t hlen);
size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysockaddr *l3);
size_t add_source_addr(struct dns_header *header, size_t plen, char *limit, union mysockaddr *source);
#ifdef HAVE_DNSSEC
size_t add_do_bit(struct dns_header *header, size_t plen, char *limit);
#endif
int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer);
int add_resource_record(struct dns_header *header, char *limit, int *truncp,
int nameoffset, unsigned char **pp, unsigned long ttl,
int *offset, unsigned short type, unsigned short class, char *format, ...);
@@ -1140,16 +1138,17 @@ int private_net(struct in_addr addr, int ban_localhost);
/* auth.c */
#ifdef HAVE_AUTH
size_t answer_auth(struct dns_header *header, char *limit, size_t qlen,
time_t now, union mysockaddr *peer_addr, int local_query);
time_t now, union mysockaddr *peer_addr, int local_query,
int do_bit, int have_pseudoheader);
int in_zone(struct auth_zone *zone, char *name, char **cut);
#endif
/* dnssec.c */
size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, int class, int type, union mysockaddr *addr);
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);
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 *neganswer, int *nons);
int dnssec_chase_cname(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname);
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);
size_t filter_rrsigs(struct dns_header *header, size_t plen);
unsigned char* hash_questions(struct dns_header *header, size_t plen, char *name);
@@ -1186,7 +1185,6 @@ int memcmp_masked(unsigned char *a, unsigned char *b, int len,
unsigned int mask);
int expand_buf(struct iovec *iov, size_t size);
char *print_mac(char *buff, unsigned char *mac, int len);
void bump_maxfd(int fd, int *max);
int read_write(int fd, unsigned char *packet, int size, int rw);
int wildcard_match(const char* wildcard, const char* match);
@@ -1197,8 +1195,8 @@ void die(char *message, char *arg1, int exit_code);
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(fd_set *set, int *maxfdp);
void check_log_writer(fd_set *set);
void set_log_writer(void);
void check_log_writer(int force);
void flush_log(void);
/* option.c */
@@ -1301,9 +1299,10 @@ void lease_update_slaac(time_t now);
void lease_set_iaid(struct dhcp_lease *lease, int iaid);
void lease_make_duid(time_t now);
#endif
void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr,
unsigned char *clid, int hw_len, int hw_type, int clid_len, time_t now, int force);
void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth, char *domain, char *config_domain);
void lease_set_hwaddr(struct dhcp_lease *lease, const unsigned char *hwaddr,
const unsigned char *clid, int hw_len, int hw_type,
int clid_len, time_t now, int force);
void lease_set_hostname(struct dhcp_lease *lease, const char *name, int auth, char *domain, char *config_domain);
void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now);
void lease_set_interface(struct dhcp_lease *lease, int interface, time_t now);
struct dhcp_lease *lease_find_by_client(unsigned char *hwaddr, int hw_len, int hw_type,
@@ -1360,8 +1359,8 @@ int iface_enumerate(int family, void *parm, int (callback)());
/* dbus.c */
#ifdef HAVE_DBUS
char *dbus_init(void);
void check_dbus_listeners(fd_set *rset, fd_set *wset, fd_set *eset);
void set_dbus_listeners(int *maxfdp, fd_set *rset, fd_set *wset, fd_set *eset);
void check_dbus_listeners(void);
void set_dbus_listeners(void);
# ifdef HAVE_DHCP
void emit_dbus_signal(int action, struct dhcp_lease *lease, char *hostname);
# endif
@@ -1382,13 +1381,15 @@ void queue_script(int action, struct dhcp_lease *lease,
#ifdef HAVE_TFTP
void queue_tftp(off_t file_len, char *filename, union mysockaddr *peer);
#endif
void queue_arp(int action, unsigned char *mac, int maclen,
int family, struct all_addr *addr);
int helper_buf_empty(void);
#endif
/* tftp.c */
#ifdef HAVE_TFTP
void tftp_request(struct listener *listen, time_t now);
void check_tftp_listeners(fd_set *rset, time_t now);
void check_tftp_listeners(time_t now);
int do_tftp_script_run(void);
#endif
@@ -1418,7 +1419,7 @@ struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct
void make_duid(time_t now);
void dhcp_construct_contexts(time_t now);
void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac,
unsigned int *maclenp, unsigned int *mactypep);
unsigned int *maclenp, unsigned int *mactypep, time_t now);
#endif
/* rfc3315.c */
@@ -1426,7 +1427,8 @@ void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac,
unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name,
struct in6_addr *fallback, struct in6_addr *ll_addr, struct in6_addr *ula_addr,
size_t sz, struct in6_addr *client_addr, time_t now);
void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, struct in6_addr *peer_address, u32 scope_id);
void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, struct in6_addr *peer_address,
u32 scope_id, time_t now);
unsigned short relay_reply6( struct sockaddr_in6 *peer, ssize_t sz, char *arrival_interface);
#endif
@@ -1505,3 +1507,28 @@ void inotify_dnsmasq_init();
int inotify_check(time_t now);
void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revhashsz);
#endif
/* poll.c */
void poll_reset(void);
int poll_check(int fd, short event);
void poll_listen(int fd, short event);
int do_poll(int timeout);
/* rrfilter.c */
size_t rrfilter(struct dns_header *header, size_t plen, int mode);
u16 *rrfilter_desc(int type);
int expand_workspace(unsigned char ***wkspc, int *szp, int new);
/* edns0.c */
unsigned char *find_pseudoheader(struct dns_header *header, size_t plen,
size_t *len, unsigned char **p, int *is_sign, int *is_last);
size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit,
unsigned short udp_sz, int optno, unsigned char *opt, size_t optlen, int set_do, int replace);
size_t add_do_bit(struct dns_header *header, size_t plen, unsigned char *limit);
size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *limit,
union mysockaddr *source, time_t now, int *check_subnet);
int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer);
/* arp.c */
int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy, time_t now);
int do_arp_script_run(void);

File diff suppressed because it is too large Load Diff

View File

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

429
src/edns0.c Normal file
View File

@@ -0,0 +1,429 @@
/* dnsmasq is Copyright (c) 2000-2016 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"
unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t *len, unsigned char **p, int *is_sign, int *is_last)
{
/* See if packet has an RFC2671 pseudoheader, and if so return a pointer to it.
also return length of pseudoheader in *len and pointer to the UDP size in *p
Finally, check to see if a packet is signed. If it is we cannot change a single bit before
forwarding. We look for TSIG in the addition section, and TKEY queries (for GSS-TSIG) */
int i, arcount = ntohs(header->arcount);
unsigned char *ansp = (unsigned char *)(header+1);
unsigned short rdlen, type, class;
unsigned char *ret = NULL;
if (is_sign)
{
*is_sign = 0;
if (OPCODE(header) == QUERY)
{
for (i = ntohs(header->qdcount); i != 0; i--)
{
if (!(ansp = skip_name(ansp, header, plen, 4)))
return NULL;
GETSHORT(type, ansp);
GETSHORT(class, ansp);
if (class == C_IN && type == T_TKEY)
*is_sign = 1;
}
}
}
else
{
if (!(ansp = skip_questions(header, plen)))
return NULL;
}
if (arcount == 0)
return NULL;
if (!(ansp = skip_section(ansp, ntohs(header->ancount) + ntohs(header->nscount), header, plen)))
return NULL;
for (i = 0; i < arcount; i++)
{
unsigned char *save, *start = ansp;
if (!(ansp = skip_name(ansp, header, plen, 10)))
return NULL;
GETSHORT(type, ansp);
save = ansp;
GETSHORT(class, ansp);
ansp += 4; /* TTL */
GETSHORT(rdlen, ansp);
if (!ADD_RDLEN(header, ansp, plen, rdlen))
return NULL;
if (type == T_OPT)
{
if (len)
*len = ansp - start;
if (p)
*p = save;
if (is_last)
*is_last = (i == arcount-1);
ret = start;
}
else if (is_sign &&
i == arcount - 1 &&
class == C_ANY &&
type == T_TSIG)
*is_sign = 1;
}
return ret;
}
/* replace == 2 ->delete existing option only. */
size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit,
unsigned short udp_sz, int optno, unsigned char *opt, size_t optlen, int set_do, int replace)
{
unsigned char *lenp, *datap, *p, *udp_len, *buff = NULL;
int rdlen = 0, is_sign, is_last;
unsigned short flags = set_do ? 0x8000 : 0, rcode = 0;
p = find_pseudoheader(header, plen, NULL, &udp_len, &is_sign, &is_last);
if (is_sign)
return plen;
if (p)
{
/* Existing header */
int i;
unsigned short code, len;
p = udp_len;
GETSHORT(udp_sz, p);
GETSHORT(rcode, p);
GETSHORT(flags, p);
if (set_do)
{
p -= 2;
flags |= 0x8000;
PUTSHORT(flags, p);
}
lenp = p;
GETSHORT(rdlen, p);
if (!CHECK_LEN(header, p, plen, rdlen))
return plen; /* bad packet */
datap = p;
/* no option to add */
if (optno == 0)
return plen;
/* check if option already there */
for (i = 0; i + 4 < rdlen;)
{
GETSHORT(code, p);
GETSHORT(len, p);
/* malformed option, delete the whole OPT RR and start again. */
if (i + len > rdlen)
{
rdlen = 0;
is_last = 0;
break;
}
if (code == optno)
{
if (replace == 0)
return plen;
/* delete option if we're to replace it. */
p -= 4;
rdlen -= len + 4;
memcpy(p, p+len+4, rdlen - i);
PUTSHORT(rdlen, lenp);
lenp -= 2;
}
else
{
p += len;
i += len + 4;
}
}
/* If we're going to extend the RR, it has to be the last RR in the packet */
if (!is_last)
{
/* First, take a copy of the options. */
if (rdlen != 0 && (buff = whine_malloc(rdlen)))
memcpy(buff, datap, rdlen);
/* now, delete OPT RR */
plen = rrfilter(header, plen, 0);
/* Now, force addition of a new one */
p = NULL;
}
}
if (!p)
{
/* We are (re)adding the pseudoheader */
if (!(p = skip_questions(header, plen)) ||
!(p = skip_section(p,
ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount),
header, plen)))
return plen;
*p++ = 0; /* empty name */
PUTSHORT(T_OPT, p);
PUTSHORT(udp_sz, p); /* max packet length, 512 if not given in EDNS0 header */
PUTSHORT(rcode, p); /* extended RCODE and version */
PUTSHORT(flags, p); /* DO flag */
lenp = p;
PUTSHORT(rdlen, p); /* RDLEN */
datap = p;
/* Copy back any options */
if (buff)
{
memcpy(p, buff, rdlen);
free(buff);
p += rdlen;
}
header->arcount = htons(ntohs(header->arcount) + 1);
}
if (((ssize_t)optlen) > (limit - (p + 4)))
return plen; /* Too big */
/* Add new option */
if (optno != 0 && replace != 2)
{
PUTSHORT(optno, p);
PUTSHORT(optlen, p);
memcpy(p, opt, optlen);
p += optlen;
PUTSHORT(p - datap, lenp);
}
return p - (unsigned char *)header;
}
size_t add_do_bit(struct dns_header *header, size_t plen, unsigned char *limit)
{
return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, 0, NULL, 0, 1, 0);
}
static unsigned char char64(unsigned char c)
{
return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[c & 0x3f];
}
static void encoder(unsigned char *in, char *out)
{
out[0] = char64(in[0]>>2);
out[1] = char64((in[0]<<4) | (in[1]>>4));
out[2] = char64((in[1]<<2) | (in[2]>>6));
out[3] = char64(in[2]);
}
static size_t add_dns_client(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *l3, time_t now)
{
int maclen, replace = 2; /* can't get mac address, just delete any incoming. */
unsigned char mac[DHCP_CHADDR_MAX];
char encode[18]; /* handle 6 byte MACs */
if ((maclen = find_mac(l3, mac, 1, now)) == 6)
{
replace = 1;
if (option_bool(OPT_MAC_HEX))
print_mac(encode, mac, maclen);
else
{
encoder(mac, encode);
encoder(mac+3, encode+4);
encode[8] = 0;
}
}
return add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMDEVICEID, (unsigned char *)encode, strlen(encode), 0, replace);
}
static size_t add_mac(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *l3, time_t now)
{
int maclen;
unsigned char mac[DHCP_CHADDR_MAX];
if ((maclen = find_mac(l3, mac, 1, now)) != 0)
plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_MAC, mac, maclen, 0, 0);
return plen;
}
struct subnet_opt {
u16 family;
u8 source_netmask, scope_netmask;
#ifdef HAVE_IPV6
u8 addr[IN6ADDRSZ];
#else
u8 addr[INADDRSZ];
#endif
};
static void *get_addrp(union mysockaddr *addr, const short family)
{
#ifdef HAVE_IPV6
if (family == AF_INET6)
return &addr->in6.sin6_addr;
#endif
return &addr->in.sin_addr;
}
static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source)
{
/* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */
int len;
void *addrp;
int sa_family = source->sa.sa_family;
opt->source_netmask = 0;
opt->scope_netmask = 0;
#ifdef HAVE_IPV6
if (source->sa.sa_family == AF_INET6 && daemon->add_subnet6)
{
opt->source_netmask = daemon->add_subnet6->mask;
if (daemon->add_subnet6->addr_used)
{
sa_family = daemon->add_subnet6->addr.sa.sa_family;
addrp = get_addrp(&daemon->add_subnet6->addr, sa_family);
}
else
addrp = &source->in6.sin6_addr;
}
#endif
if (source->sa.sa_family == AF_INET && daemon->add_subnet4)
{
opt->source_netmask = daemon->add_subnet4->mask;
if (daemon->add_subnet4->addr_used)
{
sa_family = daemon->add_subnet4->addr.sa.sa_family;
addrp = get_addrp(&daemon->add_subnet4->addr, sa_family);
}
else
addrp = &source->in.sin_addr;
}
#ifdef HAVE_IPV6
opt->family = htons(sa_family == AF_INET6 ? 2 : 1);
#else
opt->family = htons(1);
#endif
len = 0;
if (opt->source_netmask != 0)
{
len = ((opt->source_netmask - 1) >> 3) + 1;
memcpy(opt->addr, addrp, len);
if (opt->source_netmask & 7)
opt->addr[len-1] &= 0xff << (8 - (opt->source_netmask & 7));
}
return len + 4;
}
static size_t add_source_addr(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *source)
{
/* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */
int len;
struct subnet_opt opt;
len = calc_subnet_opt(&opt, source);
return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0, 0);
}
int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer)
{
/* Section 9.2, Check that subnet option in reply matches. */
int len, calc_len;
struct subnet_opt opt;
unsigned char *p;
int code, i, rdlen;
calc_len = calc_subnet_opt(&opt, peer);
if (!(p = skip_name(pseudoheader, header, plen, 10)))
return 1;
p += 8; /* skip UDP length and RCODE */
GETSHORT(rdlen, p);
if (!CHECK_LEN(header, p, plen, rdlen))
return 1; /* bad packet */
/* check if option there */
for (i = 0; i + 4 < rdlen; i += len + 4)
{
GETSHORT(code, p);
GETSHORT(len, p);
if (code == EDNS0_OPTION_CLIENT_SUBNET)
{
/* make sure this doesn't mismatch. */
opt.scope_netmask = p[3];
if (len != calc_len || memcmp(p, &opt, len) != 0)
return 0;
}
p += len;
}
return 1;
}
size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *limit,
union mysockaddr *source, time_t now, int *check_subnet)
{
*check_subnet = 0;
if (option_bool(OPT_ADD_MAC))
plen = add_mac(header, plen, limit, source, now);
if (option_bool(OPT_MAC_B64) || option_bool(OPT_MAC_HEX))
plen = add_dns_client(header, plen, limit, source, now);
if (daemon->dns_client_id)
plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMCPEID,
(unsigned char *)daemon->dns_client_id, strlen(daemon->dns_client_id), 0, 1);
if (option_bool(OPT_CLIENT_SUBNET))
{
plen = add_source_addr(header, plen, limit, source);
*check_subnet = 1;
}
return plen;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2016 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
@@ -219,7 +219,18 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
action_str = "tftp";
is6 = (data.flags != AF_INET);
}
else
else if (data.action == ACTION_ARP)
{
action_str = "arp-add";
is6 = (data.flags != AF_INET);
}
else if (data.action == ACTION_ARP_DEL)
{
action_str = "arp-del";
is6 = (data.flags != AF_INET);
data.action = ACTION_ARP;
}
else
continue;
@@ -289,7 +300,7 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
if (!is6)
inet_ntop(AF_INET, &data.addr, daemon->addrbuff, ADDRSTRLEN);
#ifdef HAVE_DHCP6
#ifdef HAVE_IPV6
else
inet_ntop(AF_INET6, &data.addr6, daemon->addrbuff, ADDRSTRLEN);
#endif
@@ -321,6 +332,22 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
lua_call(lua, 2, 0); /* pass 2 values, expect 0 */
}
}
else if (data.action == ACTION_ARP)
{
lua_getglobal(lua, "arp");
if (lua_type(lua, -1) != LUA_TFUNCTION)
lua_pop(lua, 1); /* arp function optional */
else
{
lua_pushstring(lua, action_str); /* arg1 - action */
lua_newtable(lua); /* arg2 - data table */
lua_pushstring(lua, daemon->addrbuff);
lua_setfield(lua, -2, "client_address");
lua_pushstring(lua, daemon->dhcp_buff);
lua_setfield(lua, -2, "mac_address");
lua_call(lua, 2, 0); /* pass 2 values, expect 0 */
}
}
else
{
lua_getglobal(lua, "lease"); /* function to call */
@@ -478,7 +505,7 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
continue;
}
if (data.action != ACTION_TFTP)
if (data.action != ACTION_TFTP && data.action != ACTION_ARP)
{
#ifdef HAVE_DHCP6
my_setenv("DNSMASQ_IAID", is6 ? daemon->dhcp_buff3 : NULL, &err);
@@ -550,10 +577,9 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
my_setenv("DNSMASQ_OLD_HOSTNAME", data.action == ACTION_OLD_HOSTNAME ? hostname : NULL, &err);
if (data.action == ACTION_OLD_HOSTNAME)
hostname = NULL;
}
my_setenv("DNSMASQ_LOG_DHCP", option_bool(OPT_LOG_OPTS) ? "1" : NULL, &err);
my_setenv("DNSMASQ_LOG_DHCP", option_bool(OPT_LOG_OPTS) ? "1" : NULL, &err);
}
/* we need to have the event_fd around if exec fails */
if ((i = fcntl(event_fd, F_GETFD)) != -1)
fcntl(event_fd, F_SETFD, i | FD_CLOEXEC);
@@ -563,8 +589,8 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
if (err == 0)
{
execl(daemon->lease_change_command,
p ? p+1 : daemon->lease_change_command,
action_str, is6 ? daemon->packet : daemon->dhcp_buff,
p ? p+1 : daemon->lease_change_command, action_str,
(is6 && data.action != ACTION_ARP) ? daemon->packet : daemon->dhcp_buff,
daemon->addrbuff, hostname, (char*)NULL);
err = errno;
}
@@ -760,6 +786,30 @@ void queue_tftp(off_t file_len, char *filename, union mysockaddr *peer)
}
#endif
void queue_arp(int action, unsigned char *mac, int maclen, int family, struct all_addr *addr)
{
/* no script */
if (daemon->helperfd == -1)
return;
buff_alloc(sizeof(struct script_data));
memset(buf, 0, sizeof(struct script_data));
buf->action = action;
buf->hwaddr_len = maclen;
buf->hwaddr_type = ARPHRD_ETHER;
if ((buf->flags = family) == AF_INET)
buf->addr = addr->addr.addr4;
#ifdef HAVE_IPV6
else
buf->addr6 = addr->addr.addr6;
#endif
memcpy(buf->hwaddr, mac, maclen);
bytes_in_buf = sizeof(struct script_data);
}
int helper_buf_empty(void)
{
return bytes_in_buf == 0;

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2016 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -18,6 +18,7 @@
#ifdef HAVE_INOTIFY
#include <sys/inotify.h>
#include <sys/param.h> /* For MAXSYMLINKS */
/* the strategy is to set a inotify on the directories containing
resolv files, for any files in the directory which are close-write
@@ -35,31 +36,85 @@
static char *inotify_buffer;
#define INOTIFY_SZ (sizeof(struct inotify_event) + NAME_MAX + 1)
/* If path is a symbolic link, return the path it
points to, made absolute if relative.
If path doesn't exist or is not a symlink, return NULL.
Return value is malloc'ed */
static char *my_readlink(char *path)
{
ssize_t rc, size = 64;
char *buf;
while (1)
{
buf = safe_malloc(size);
rc = readlink(path, buf, (size_t)size);
if (rc == -1)
{
/* Not link or doesn't exist. */
if (errno == EINVAL || errno == ENOENT)
{
free(buf);
return NULL;
}
else
die(_("cannot access path %s: %s"), path, EC_MISC);
}
else if (rc < size-1)
{
char *d;
buf[rc] = 0;
if (buf[0] != '/' && ((d = strrchr(path, '/'))))
{
/* Add path to relative link */
char *new_buf = safe_malloc((d - path) + strlen(buf) + 2);
*(d+1) = 0;
strcpy(new_buf, path);
strcat(new_buf, buf);
free(buf);
buf = new_buf;
}
return buf;
}
/* Buffer too small, increase and retry */
size += 64;
free(buf);
}
}
void inotify_dnsmasq_init()
{
struct resolvc *res;
inotify_buffer = safe_malloc(INOTIFY_SZ);
daemon->inotifyfd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
if (daemon->inotifyfd == -1)
die(_("failed to create inotify: %s"), NULL, EC_MISC);
if (option_bool(OPT_NO_RESOLV))
return;
for (res = daemon->resolv_files; res; res = res->next)
{
char *d = NULL, *path;
if (!(path = realpath(res->name, NULL)))
char *d, *new_path, *path = safe_malloc(strlen(res->name) + 1);
int links = MAXSYMLINKS;
strcpy(path, res->name);
/* Follow symlinks until we reach a non-symlink, or a non-existant file. */
while ((new_path = my_readlink(path)))
{
/* realpath will fail if the file doesn't exist, but
dnsmasq copes with missing files, so fall back
and assume that symlinks are not in use in that case. */
if (errno == ENOENT)
path = res->name;
else
die(_("cannot cannonicalise resolv-file %s: %s"), res->name, EC_MISC);
if (links-- == 0)
die(_("too many symlinks following %s"), res->name, EC_MISC);
free(path);
path = new_path;
}
res->wd = -1;
if ((d = strrchr(path, '/')))
{
*d = 0; /* make path just directory */
@@ -70,10 +125,11 @@ void inotify_dnsmasq_init()
if (res->wd == -1 && errno == ENOENT)
die(_("directory %s for resolv-file is missing, cannot poll"), res->name, EC_MISC);
if (res->wd == -1)
die(_("failed to create inotify for %s: %s"), res->name, EC_MISC);
}
}
if (res->wd == -1)
die(_("failed to create inotify for %s: %s"), res->name, EC_MISC);
}
}
@@ -147,6 +203,8 @@ void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revh
free(path);
}
}
closedir(dir_stream);
}
}

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2016 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-2015 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2016 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
@@ -813,9 +813,9 @@ void lease_set_iaid(struct dhcp_lease *lease, int iaid)
}
#endif
void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr,
unsigned char *clid, int hw_len, int hw_type, int clid_len,
time_t now, int force)
void lease_set_hwaddr(struct dhcp_lease *lease, const unsigned char *hwaddr,
const unsigned char *clid, int hw_len, int hw_type,
int clid_len, time_t now, int force)
{
#ifdef HAVE_DHCP6
int change = force;
@@ -897,7 +897,7 @@ static void kill_name(struct dhcp_lease *lease)
lease->hostname = lease->fqdn = NULL;
}
void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth, char *domain, char *config_domain)
void lease_set_hostname(struct dhcp_lease *lease, const char *name, int auth, char *domain, char *config_domain)
{
struct dhcp_lease *lease_tmp;
char *new_name = NULL, *new_fqdn = NULL;
@@ -1110,18 +1110,22 @@ int do_script_run(time_t now)
}
#ifdef HAVE_SCRIPT
/* delim == -1 -> delim = 0, but embeded 0s, creating extra records, are OK. */
void lease_add_extradata(struct dhcp_lease *lease, unsigned char *data, unsigned int len, int delim)
{
unsigned int i;
/* check for embeded NULLs */
for (i = 0; i < len; i++)
if (data[i] == 0)
{
len = i;
break;
}
if (delim == -1)
delim = 0;
else
/* check for embeded NULLs */
for (i = 0; i < len; i++)
if (data[i] == 0)
{
len = i;
break;
}
if ((lease->extradata_size - lease->extradata_len) < (len + 1))
{
size_t newsz = lease->extradata_len + len + 100;

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2016 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
@@ -421,18 +421,15 @@ void my_syslog(int priority, const char *format, ...)
}
}
void set_log_writer(fd_set *set, int *maxfdp)
void set_log_writer(void)
{
if (entries && log_fd != -1 && connection_good)
{
FD_SET(log_fd, set);
bump_maxfd(log_fd, maxfdp);
}
poll_listen(log_fd, POLLOUT);
}
void check_log_writer(fd_set *set)
void check_log_writer(int force)
{
if (log_fd != -1 && (!set || FD_ISSET(log_fd, set)))
if (log_fd != -1 && (force || poll_check(log_fd, POLLOUT)))
log_write();
}

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2016 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-2015 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2016 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
@@ -288,7 +288,8 @@ int iface_enumerate(int family, void *parm, int (*callback)())
rta = RTA_NEXT(rta, len1);
}
if (inaddr && mac && callback_ok)
if (!(neigh->ndm_state & (NUD_NOARP | NUD_INCOMPLETE | NUD_FAILED)) &&
inaddr && mac && callback_ok)
if (!((*callback)(neigh->ndm_family, inaddr, mac, maclen, parm)))
callback_ok = 0;
}

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2016 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
@@ -532,13 +532,14 @@ static int iface_allowed_v4(struct in_addr local, int if_index, char *label,
{
union mysockaddr addr;
int prefix, bit;
(void)broadcast; /* warning */
memset(&addr, 0, sizeof(addr));
#ifdef HAVE_SOCKADDR_SA_LEN
addr.in.sin_len = sizeof(addr.in);
#endif
addr.in.sin_family = AF_INET;
addr.in.sin_addr = broadcast; /* warning */
addr.in.sin_addr = local;
addr.in.sin_port = htons(daemon->port);
@@ -809,10 +810,11 @@ int tcp_interface(int fd, int af)
int opt = 1;
struct cmsghdr *cmptr;
struct msghdr msg;
socklen_t len;
/* use mshdr do that the CMSDG_* macros are available */
/* use mshdr so that the CMSDG_* macros are available */
msg.msg_control = daemon->packet;
msg.msg_controllen = daemon->packet_buff_sz;
msg.msg_controllen = len = daemon->packet_buff_sz;
/* we overwrote the buffer... */
daemon->srv_save = NULL;
@@ -820,18 +822,21 @@ int tcp_interface(int fd, int af)
if (af == AF_INET)
{
if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt)) != -1 &&
getsockopt(fd, IPPROTO_IP, IP_PKTOPTIONS, msg.msg_control, (socklen_t *)&msg.msg_controllen) != -1)
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO)
{
union {
unsigned char *c;
struct in_pktinfo *p;
} p;
p.c = CMSG_DATA(cmptr);
if_index = p.p->ipi_ifindex;
}
getsockopt(fd, IPPROTO_IP, IP_PKTOPTIONS, msg.msg_control, &len) != -1)
{
msg.msg_controllen = len;
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO)
{
union {
unsigned char *c;
struct in_pktinfo *p;
} p;
p.c = CMSG_DATA(cmptr);
if_index = p.p->ipi_ifindex;
}
}
}
#ifdef HAVE_IPV6
else
@@ -849,9 +854,10 @@ int tcp_interface(int fd, int af)
#endif
if (set_ipv6pktinfo(fd) &&
getsockopt(fd, IPPROTO_IPV6, PKTOPTIONS, msg.msg_control, (socklen_t *)&msg.msg_controllen) != -1)
getsockopt(fd, IPPROTO_IPV6, PKTOPTIONS, msg.msg_control, &len) != -1)
{
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
msg.msg_controllen = len;
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
{
union {
@@ -1076,23 +1082,30 @@ void join_multicast(int dienow)
if ((daemon->doing_dhcp6 || daemon->relay6) &&
setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
err = 1;
err = errno;
inet_pton(AF_INET6, ALL_SERVERS, &mreq.ipv6mr_multiaddr);
if (daemon->doing_dhcp6 &&
setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
err = 1;
err = errno;
inet_pton(AF_INET6, ALL_ROUTERS, &mreq.ipv6mr_multiaddr);
if (daemon->doing_ra &&
setsockopt(daemon->icmp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
err = 1;
err = errno;
if (err)
{
char *s = _("interface %s failed to join DHCPv6 multicast group: %s");
errno = err;
#ifdef HAVE_LINUX_NETWORK
if (errno == ENOMEM)
my_syslog(LOG_ERR, _("try increasing /proc/sys/net/core/optmem_max"));
#endif
if (dienow)
die(s, iface->name, EC_BADNET);
else
@@ -1112,7 +1125,7 @@ int random_sock(int family)
if ((fd = socket(family, SOCK_DGRAM, 0)) != -1)
{
union mysockaddr addr;
unsigned int ports_avail = 65536u - (unsigned short)daemon->min_port;
unsigned int ports_avail = ((unsigned short)daemon->max_port - (unsigned short)daemon->min_port) + 1;
int tries = ports_avail < 30 ? 3 * ports_avail : 100;
memset(&addr, 0, sizeof(addr));
@@ -1125,8 +1138,8 @@ int random_sock(int family)
{
unsigned short port = rand16();
if (daemon->min_port != 0)
port = htons(daemon->min_port + (port % ((unsigned short)ports_avail)));
if (daemon->min_port != 0 || daemon->max_port != MAX_PORT)
port = htons(daemon->min_port + (port % ((unsigned short)ports_avail)));
if (family == AF_INET)
{
@@ -1416,18 +1429,53 @@ void check_servers(void)
{
struct irec *iface;
struct server *serv;
int port = 0;
int port = 0, count;
/* interface may be new since startup */
if (!option_bool(OPT_NOWILD))
enumerate_interfaces(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)
{
if (!(serv->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND)))
{
port = prettyprint_addr(&serv->addr, daemon->namebuff);
serv->flags |= SERV_DO_DNSSEC;
#endif
for (count = 0, serv = daemon->servers; serv; serv = serv->next)
{
if (!(serv->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND)))
{
/* Init edns_pktsz for newly created server records. */
if (serv->edns_pktsz == 0)
serv->edns_pktsz = daemon->edns_pktsz;
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID))
{
if (serv->flags & SERV_HAS_DOMAIN)
{
struct ds_config *ds;
char *domain = serv->domain;
/* .example.com is valid */
while (*domain == '.')
domain++;
for (ds = daemon->ds; ds; ds = ds->next)
if (ds->name[0] != 0 && hostname_isequal(domain, ds->name))
break;
if (!ds)
serv->flags &= ~SERV_DO_DNSSEC;
}
else if (serv->flags & SERV_FOR_NODOTS)
serv->flags &= ~SERV_DO_DNSSEC;
}
#endif
port = prettyprint_addr(&serv->addr, daemon->namebuff);
/* 0.0.0.0 is nothing, the stack treats it like 127.0.0.1 */
if (serv->addr.sa.sa_family == AF_INET &&
serv->addr.in.sin_addr.s_addr == 0)
@@ -1461,9 +1509,16 @@ void check_servers(void)
if (!(serv->flags & SERV_NO_REBIND) && !(serv->flags & SERV_LITERAL_ADDRESS))
{
if (++count > SERVERS_LOGGED)
continue;
if (serv->flags & (SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_USE_RESOLV))
{
char *s1, *s2;
char *s1, *s2, *s3 = "";
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && !(serv->flags & SERV_DO_DNSSEC))
s3 = _("(no DNSSEC)");
#endif
if (!(serv->flags & SERV_HAS_DOMAIN))
s1 = _("unqualified"), s2 = _("names");
else if (strlen(serv->domain) == 0)
@@ -1476,7 +1531,7 @@ void check_servers(void)
else if (serv->flags & SERV_USE_RESOLV)
my_syslog(LOG_INFO, _("using standard nameservers for %s %s"), s1, s2);
else
my_syslog(LOG_INFO, _("using nameserver %s#%d for %s %s"), daemon->namebuff, port, s1, s2);
my_syslog(LOG_INFO, _("using nameserver %s#%d for %s %s %s"), daemon->namebuff, port, s1, s2, s3);
}
#ifdef HAVE_LOOP
else if (serv->flags & SERV_LOOP)
@@ -1488,6 +1543,9 @@ void check_servers(void)
my_syslog(LOG_INFO, _("using nameserver %s#%d"), daemon->namebuff, port);
}
}
if (count - 1 > SERVERS_LOGGED)
my_syslog(LOG_INFO, _("using %d more nameservers"), count - SERVERS_LOGGED - 1);
cleanup_servers();
}

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2016 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
@@ -153,7 +153,13 @@ struct myoption {
#define LOPT_DHOPT_INOTIFY 341
#define LOPT_HOST_INOTIFY 342
#define LOPT_DNSSEC_STAMP 343
#define LOPT_TFTP_NO_FAIL 344
#define LOPT_MAXPORT 345
#define LOPT_CPE_ID 346
#define LOPT_SCRIPT_ARP 347
#define LOPT_DHCPTTL 348
#define LOPT_TFTP_MTU 349
#ifdef HAVE_GETOPT_LONG
static const struct option opts[] =
#else
@@ -235,9 +241,11 @@ static const struct myoption opts[] =
{ "dhcp-ignore-names", 2, 0, LOPT_NO_NAMES },
{ "enable-tftp", 2, 0, LOPT_TFTP },
{ "tftp-secure", 0, 0, LOPT_SECURE },
{ "tftp-no-fail", 0, 0, LOPT_TFTP_NO_FAIL },
{ "tftp-unique-root", 0, 0, LOPT_APREF },
{ "tftp-root", 1, 0, LOPT_PREFIX },
{ "tftp-max", 1, 0, LOPT_TFTP_MAX },
{ "tftp-mtu", 1, 0, LOPT_TFTP_MTU },
{ "tftp-lowercase", 0, 0, LOPT_TFTP_LC },
{ "ptr-record", 1, 0, LOPT_PTR },
{ "naptr-record", 1, 0, LOPT_NAPTR },
@@ -268,6 +276,7 @@ static const struct myoption opts[] =
{ "dhcp-alternate-port", 2, 0, LOPT_ALTPORT },
{ "dhcp-scriptuser", 1, 0, LOPT_SCRIPTUSR },
{ "min-port", 1, 0, LOPT_MINPORT },
{ "max-port", 1, 0, LOPT_MAXPORT },
{ "dhcp-fqdn", 0, 0, LOPT_DHCP_FQDN },
{ "cname", 1, 0, LOPT_CNAME },
{ "pxe-prompt", 1, 0, LOPT_PXE_PROMT },
@@ -277,8 +286,9 @@ static const struct myoption opts[] =
{ "dhcp-proxy", 2, 0, LOPT_PROXY },
{ "dhcp-generate-names", 2, 0, LOPT_GEN_NAMES },
{ "rebind-localhost-ok", 0, 0, LOPT_LOC_REBND },
{ "add-mac", 0, 0, LOPT_ADD_MAC },
{ "add-mac", 2, 0, LOPT_ADD_MAC },
{ "add-subnet", 2, 0, LOPT_ADD_SBNET },
{ "add-cpe-id", 1, 0 , LOPT_CPE_ID },
{ "proxy-dnssec", 0, 0, LOPT_DNSSEC },
{ "dhcp-sequential-ip", 0, 0, LOPT_INCR_ADDR },
{ "conntrack", 0, 0, LOPT_CONNTRACK },
@@ -311,6 +321,8 @@ static const struct myoption opts[] =
{ "quiet-dhcp6", 0, 0, LOPT_QUIET_DHCP6 },
{ "quiet-ra", 0, 0, LOPT_QUIET_RA },
{ "dns-loop-detect", 0, 0, LOPT_LOOP_DETECT },
{ "script-arp", 0, 0, LOPT_SCRIPT_ARP },
{ "dhcp-ttl", 1, 0 , LOPT_DHCPTTL },
{ NULL, 0, 0, 0 }
};
@@ -390,7 +402,7 @@ static struct {
{ 'v', 0, NULL, gettext_noop("Display dnsmasq version and copyright information."), NULL },
{ 'V', ARG_DUP, "<ipaddr>,<ipaddr>,<netmask>", gettext_noop("Translate IPv4 addresses from upstream servers."), NULL },
{ 'W', ARG_DUP, "<name>,<target>,...", gettext_noop("Specify a SRV record."), NULL },
{ 'w', 0, NULL, gettext_noop("Display this message. Use --help dhcp for known DHCP options."), NULL },
{ 'w', 0, NULL, gettext_noop("Display this message. Use --help dhcp or --help dhcp6 for known DHCP options."), NULL },
{ 'x', ARG_ONE, "<path>", gettext_noop("Specify path of PID file (defaults to %s)."), RUNFILE },
{ 'X', ARG_ONE, "<integer>", gettext_noop("Specify maximum number of DHCP leases (defaults to %s)."), "&" },
{ 'y', OPT_LOCALISE, NULL, gettext_noop("Answer DNS queries based on the interface a query was sent to."), NULL },
@@ -408,6 +420,7 @@ static struct {
{ '6', ARG_ONE, "<path>", gettext_noop("Shell script to run on DHCP lease creation and destruction."), NULL },
{ LOPT_LUASCRIPT, ARG_DUP, "path", gettext_noop("Lua script to run on DHCP lease creation and destruction."), NULL },
{ LOPT_SCRIPTUSR, ARG_ONE, "<username>", gettext_noop("Run lease-change scripts as this user."), NULL },
{ LOPT_SCRIPT_ARP, OPT_SCRIPT_ARP, NULL, gettext_noop("Call dhcp-script with changes to local ARP table."), NULL },
{ '7', ARG_DUP, "<path>", gettext_noop("Read configuration from all the files in this directory."), NULL },
{ '8', ARG_ONE, "<facilty>|<file>", gettext_noop("Log to this syslog facility or file. (defaults to DAEMON)"), NULL },
{ '9', OPT_LEASE_RO, NULL, gettext_noop("Do not use leasefile."), NULL },
@@ -419,7 +432,9 @@ static struct {
{ LOPT_PREFIX, ARG_DUP, "<dir>[,<iface>]", gettext_noop("Export files by TFTP only from the specified subtree."), NULL },
{ LOPT_APREF, OPT_TFTP_APREF, NULL, gettext_noop("Add client IP address to tftp-root."), NULL },
{ LOPT_SECURE, OPT_TFTP_SECURE, NULL, gettext_noop("Allow access only to files owned by the user running dnsmasq."), NULL },
{ LOPT_TFTP_NO_FAIL, OPT_TFTP_NO_FAIL, NULL, gettext_noop("Do not terminate the service if TFTP directories are inaccessible."), NULL },
{ LOPT_TFTP_MAX, ARG_ONE, "<integer>", gettext_noop("Maximum number of conncurrent TFTP transfers (defaults to %s)."), "#" },
{ LOPT_TFTP_MTU, ARG_ONE, "<integer>", gettext_noop("Maximum MTU to use for TFTP transfers."), NULL },
{ LOPT_NOBLOCK, OPT_TFTP_NOBLOCK, NULL, gettext_noop("Disable the TFTP blocksize extension."), NULL },
{ LOPT_TFTP_LC, OPT_TFTP_LC, NULL, gettext_noop("Convert TFTP filenames to lowercase"), NULL },
{ LOPT_TFTPPORTS, ARG_ONE, "<start>,<end>", gettext_noop("Ephemeral port range for use by TFTP transfers."), NULL },
@@ -433,23 +448,25 @@ static struct {
{ 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 },
{ LOPT_MAXPORT, ARG_ONE, "<port>", gettext_noop("Specify highest port available for DNS query transmission."), NULL },
{ LOPT_DHCP_FQDN, OPT_DHCP_FQDN, NULL, gettext_noop("Use only fully qualified domain names for DHCP clients."), NULL },
{ LOPT_GEN_NAMES, ARG_DUP, "[=tag:<tag>]", gettext_noop("Generate hostnames based on MAC address for nameless clients."), NULL},
{ LOPT_PROXY, ARG_DUP, "[=<ipaddr>]...", gettext_noop("Use these DHCP relays as full proxies."), NULL },
{ LOPT_RELAY, ARG_DUP, "<local-addr>,<server>[,<interface>]", gettext_noop("Relay DHCP requests to a remote server"), NULL},
{ LOPT_CNAME, ARG_DUP, "<alias>,<target>", gettext_noop("Specify alias name for LOCAL DNS name."), NULL },
{ LOPT_CNAME, ARG_DUP, "<alias>,<target>[,<ttl>]", gettext_noop("Specify alias name for LOCAL DNS name."), NULL },
{ LOPT_PXE_PROMT, ARG_DUP, "<prompt>,[<timeout>]", gettext_noop("Prompt to send to PXE clients."), NULL },
{ LOPT_PXE_SERV, ARG_DUP, "<service>", gettext_noop("Boot service for PXE menu."), NULL },
{ LOPT_TEST, 0, NULL, gettext_noop("Check configuration syntax."), NULL },
{ LOPT_ADD_MAC, OPT_ADD_MAC, NULL, gettext_noop("Add requestor's MAC address to forwarded DNS queries."), NULL },
{ LOPT_ADD_SBNET, ARG_ONE, "<v4 pref>[,<v6 pref>]", gettext_noop("Add requestor's IP subnet to forwarded DNS queries."), NULL },
{ LOPT_ADD_MAC, ARG_DUP, "[=base64|text]", gettext_noop("Add requestor's MAC address to forwarded DNS queries."), NULL },
{ LOPT_ADD_SBNET, ARG_ONE, "<v4 pref>[,<v6 pref>]", gettext_noop("Add specified IP subnet to forwarded DNS queries."), NULL },
{ LOPT_CPE_ID, ARG_ONE, "<text>", gettext_noop("Add client identification to forwarded DNS queries."), NULL },
{ LOPT_DNSSEC, OPT_DNSSEC_PROXY, NULL, gettext_noop("Proxy DNSSEC validation results from upstream nameservers."), NULL },
{ LOPT_INCR_ADDR, OPT_CONSEC_ADDR, NULL, gettext_noop("Attempt to allocate sequential IP addresses to DHCP clients."), NULL },
{ LOPT_CONNTRACK, OPT_CONNTRACK, NULL, gettext_noop("Copy connection-track mark from queries to upstream connections."), NULL },
{ LOPT_FQDN, OPT_FQDN_UPDATE, NULL, gettext_noop("Allow DHCP clients to do their own DDNS updates."), NULL },
{ 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>", gettext_noop("Specify host (A/AAAA and PTR) records"), NULL },
{ LOPT_HOST_REC, ARG_DUP, "<name>,<address>[,<ttl>]", gettext_noop("Specify host (A/AAAA and PTR) records"), 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 },
@@ -473,9 +490,10 @@ static struct {
{ LOPT_QUIET_DHCP, OPT_QUIET_DHCP, NULL, gettext_noop("Do not log routine DHCP."), NULL },
{ LOPT_QUIET_DHCP6, OPT_QUIET_DHCP6, NULL, gettext_noop("Do not log routine DHCPv6."), NULL },
{ LOPT_QUIET_RA, OPT_QUIET_RA, NULL, gettext_noop("Do not log RA."), NULL },
{ LOPT_LOCAL_SERVICE, OPT_LOCAL_SERVICE, NULL, gettext_noop("Accept queries only from directly-connected networks"), NULL },
{ LOPT_LOOP_DETECT, OPT_LOOP_DETECT, NULL, gettext_noop("Detect and remove DNS forwarding loops"), NULL },
{ LOPT_LOCAL_SERVICE, OPT_LOCAL_SERVICE, NULL, gettext_noop("Accept queries only from directly-connected networks."), NULL },
{ LOPT_LOOP_DETECT, OPT_LOOP_DETECT, NULL, gettext_noop("Detect and remove DNS forwarding loops."), NULL },
{ 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 },
{ 0, 0, NULL, NULL, NULL }
};
@@ -719,6 +737,20 @@ static void do_usage(void)
#define ret_err(x) do { strcpy(errstr, (x)); return 0; } while (0)
static char *parse_mysockaddr(char *arg, union mysockaddr *addr)
{
if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0)
addr->sa.sa_family = AF_INET;
#ifdef HAVE_IPV6
else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)
addr->sa.sa_family = AF_INET6;
#endif
else
return _("bad address");
return NULL;
}
char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_addr, char *interface, int *flags)
{
int source_port = 0, serv_port = NAMESERVER_PORT;
@@ -1167,7 +1199,8 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags)
cp = comma;
comma = split(cp);
slash = split_chr(cp, '/');
inet_pton(AF_INET, cp, &in);
if (!inet_pton(AF_INET, cp, &in))
ret_err(_("bad IPv4 address"));
if (!slash)
{
memcpy(op, &in, INADDRSZ);
@@ -1498,10 +1531,16 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
li = opt_malloc(sizeof(struct list));
if (*arg == '*')
{
li->next = match_suffix;
match_suffix = li;
/* Have to copy: buffer is overwritten */
li->suffix = opt_string_alloc(arg+1);
/* "*" with no suffix is a no-op */
if (arg[1] == 0)
free(li);
else
{
li->next = match_suffix;
match_suffix = li;
/* Have to copy: buffer is overwritten */
li->suffix = opt_string_alloc(arg+1);
}
}
else
{
@@ -1582,7 +1621,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
li = match_suffix->next;
free(match_suffix->suffix);
free(match_suffix);
}
}
break;
}
@@ -1590,10 +1629,46 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
set_option_bool(OPT_CLIENT_SUBNET);
if (arg)
{
char *err, *end;
comma = split(arg);
if (!atoi_check(arg, &daemon->addr4_netmask) ||
(comma && !atoi_check(comma, &daemon->addr6_netmask)))
ret_err(gen_err);
struct mysubnet* new = opt_malloc(sizeof(struct mysubnet));
if ((end = split_chr(arg, '/')))
{
/* has subnet+len */
err = parse_mysockaddr(arg, &new->addr);
if (err)
ret_err(err);
if (!atoi_check(end, &new->mask))
ret_err(gen_err);
new->addr_used = 1;
}
else if (!atoi_check(arg, &new->mask))
ret_err(gen_err);
daemon->add_subnet4 = new;
if (comma)
{
new = opt_malloc(sizeof(struct mysubnet));
if ((end = split_chr(comma, '/')))
{
/* has subnet+len */
err = parse_mysockaddr(comma, &new->addr);
if (err)
ret_err(err);
if (!atoi_check(end, &new->mask))
ret_err(gen_err);
new->addr_used = 1;
}
else
{
if (!atoi_check(comma, &new->mask))
ret_err(gen_err);
}
daemon->add_subnet6 = new;
}
}
break;
@@ -1916,11 +1991,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
comma = split(arg);
daemon->soa_retry = (u32)atoi(arg);
if (comma)
{
arg = comma;
comma = split(arg);
daemon->soa_expiry = (u32)atoi(arg);
}
daemon->soa_expiry = (u32)atoi(comma);
}
}
}
@@ -2092,6 +2163,26 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
}
break;
case LOPT_CPE_ID: /* --add-dns-client */
if (arg)
daemon->dns_client_id = opt_string_alloc(arg);
break;
case LOPT_ADD_MAC: /* --add-mac */
if (!arg)
set_option_bool(OPT_ADD_MAC);
else
{
unhide_metas(arg);
if (strcmp(arg, "base64") == 0)
set_option_bool(OPT_MAC_B64);
else if (strcmp(arg, "text") == 0)
set_option_bool(OPT_MAC_HEX);
else
ret_err(gen_err);
}
break;
case 'u': /* --user */
daemon->username = opt_string_alloc(arg);
break;
@@ -2445,6 +2536,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
ret_err(gen_err);
break;
case LOPT_MAXPORT: /* --max-port */
if (!atoi_check16(arg, &daemon->max_port))
ret_err(gen_err);
break;
case '0': /* --dns-forward-max */
if (!atoi_check(arg, &daemon->ftabsize))
ret_err(gen_err);
@@ -2488,6 +2584,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
case LOPT_MINCTTL: /* --min-cache-ttl */
case LOPT_MAXCTTL: /* --max-cache-ttl */
case LOPT_AUTHTTL: /* --auth-ttl */
case LOPT_DHCPTTL: /* --dhcp-ttl */
{
int ttl;
if (!atoi_check(arg, &ttl))
@@ -2506,6 +2603,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
daemon->max_cache_ttl = (unsigned long)ttl;
else if (option == LOPT_AUTHTTL)
daemon->auth_ttl = (unsigned long)ttl;
else if (option == LOPT_DHCPTTL)
{
daemon->dhcp_ttl = (unsigned long)ttl;
daemon->use_dhcp_ttl = 1;
}
else
daemon->local_ttl = (unsigned long)ttl;
break;
@@ -2524,6 +2626,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
ret_err(gen_err);
break;
case LOPT_TFTP_MTU: /* --tftp-mtu */
if (!atoi_check(arg, &daemon->tftp_mtu))
ret_err(gen_err);
break;
case LOPT_PREFIX: /* --tftp-prefix */
comma = split(arg);
if (comma)
@@ -2666,13 +2773,14 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
leasepos = 3;
if (!is_same_net(new->start, new->end, new->netmask))
ret_err(_("inconsistent DHCP range"));
}
if (k >= 4 && strchr(a[3], '.') &&
(inet_pton(AF_INET, a[3], &new->broadcast) > 0))
{
new->flags |= CONTEXT_BRDCAST;
leasepos = 4;
if (k >= 4 && strchr(a[3], '.') &&
(inet_pton(AF_INET, a[3], &new->broadcast) > 0))
{
new->flags |= CONTEXT_BRDCAST;
leasepos = 4;
}
}
}
#ifdef HAVE_DHCP6
@@ -2696,6 +2804,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
new->flags |= CONTEXT_RA_ROUTER | CONTEXT_RA;
else if (strcmp(a[leasepos], "ra-stateless") == 0)
new->flags |= CONTEXT_RA_STATELESS | CONTEXT_DHCP | CONTEXT_RA;
else if (strcmp(a[leasepos], "off-link") == 0)
new->flags |= CONTEXT_RA_OFF_LINK;
else if (leasepos == 1 && inet_pton(AF_INET6, a[leasepos], &new->end6))
new->flags |= CONTEXT_DHCP;
else if (strstr(a[leasepos], "constructor:") == a[leasepos])
@@ -2760,6 +2870,9 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
if (leasepos < k)
{
if (leasepos != k-1)
ret_err(_("bad dhcp-range"));
if (strcmp(a[leasepos], "infinite") == 0)
new->lease_time = 0xffffffff;
else if (strcmp(a[leasepos], "deprecated") == 0)
@@ -2854,7 +2967,6 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
}
if (len == -1)
ret_err(_("bad hex constant"));
else if ((new->clid = opt_malloc(len)))
{
@@ -3186,7 +3298,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
{
struct pxe_service *new = opt_malloc(sizeof(struct pxe_service));
char *CSA[] = { "x86PC", "PC98", "IA64_EFI", "Alpha", "Arc_x86", "Intel_Lean_Client",
"IA32_EFI", "BC_EFI", "Xscale_EFI", "x86-64_EFI", NULL };
"IA32_EFI", "x86-64_EFI", "Xscale_EFI", "BC_EFI",
"ARM32_EFI", "ARM64_EFI", NULL };
static int boottype = 32768;
new->netid = NULL;
@@ -3547,8 +3660,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
(!(inet_pton(AF_INET, a[1], &new->out) > 0)))
option = '?';
if (k == 3)
inet_pton(AF_INET, a[2], &new->mask);
if (k == 3 && !inet_pton(AF_INET, a[2], &new->mask))
option = '?';
if (dash &&
(!(inet_pton(AF_INET, dash, &new->end) > 0) ||
@@ -3598,12 +3711,15 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
case LOPT_CNAME: /* --cname */
{
struct cname *new;
char *alias;
char *target;
char *alias, *target, *ttls;
int ttl = -1;
if (!(comma = split(arg)))
ret_err(gen_err);
if ((ttls = split(comma)) && !atoi_check(ttls, &ttl))
ret_err(_("bad TTL"));
alias = canonicalise_opt(arg);
target = canonicalise_opt(comma);
@@ -3619,6 +3735,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
daemon->cnames = new;
new->alias = alias;
new->target = target;
new->ttl = ttl;
}
break;
@@ -3684,7 +3801,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
case LOPT_RR: /* dns-rr */
{
struct txt_record *new;
size_t len = len;
size_t len = 0;
char *data;
int val;
@@ -3792,13 +3909,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
if (!atoi_check16(arg, &priority))
ret_err(_("invalid priority"));
if (comma)
{
arg = comma;
comma = split(arg);
if (!atoi_check16(arg, &weight))
ret_err(_("invalid weight"));
}
if (comma && !atoi_check16(comma, &weight))
ret_err(_("invalid weight"));
}
}
}
@@ -3819,14 +3931,22 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
{
struct host_record *new = opt_malloc(sizeof(struct host_record));
memset(new, 0, sizeof(struct host_record));
new->ttl = -1;
if (!arg || !(comma = split(arg)))
ret_err(_("Bad host-record"));
while (arg)
{
struct all_addr addr;
if (inet_pton(AF_INET, arg, &addr))
char *dig;
for (dig = arg; *dig != 0; dig++)
if (*dig < '0' || *dig > '9')
break;
if (*dig == 0)
new->ttl = atoi(arg);
else if (inet_pton(AF_INET, arg, &addr))
new->addr = addr.addr.addr4;
#ifdef HAVE_IPV6
else if (inet_pton(AF_INET6, arg, &addr))
@@ -4366,7 +4486,7 @@ void read_opts(int argc, char **argv, char *compile_opts)
{
char *buff = opt_malloc(MAXDNAME);
int option, conffile_opt = '7', testmode = 0;
char *arg, *conffile = NULL;
char *arg, *conffile = CONFFILE;
opterr = 0;
@@ -4393,6 +4513,7 @@ void read_opts(int argc, char **argv, char *compile_opts)
daemon->soa_refresh = SOA_REFRESH;
daemon->soa_retry = SOA_RETRY;
daemon->soa_expiry = SOA_EXPIRY;
daemon->max_port = MAX_PORT;
add_txt("version.bind", "dnsmasq-" VERSION, 0 );
add_txt("authors.bind", "Simon Kelley", 0);
@@ -4483,11 +4604,8 @@ void read_opts(int argc, char **argv, char *compile_opts)
if (conffile)
{
one_file(conffile, conffile_opt);
free(conffile);
}
else
{
one_file(CONFFILE, conffile_opt);
if (conffile_opt == 0)
free(conffile);
}
/* port might not be known when the address is parsed - fill in here */
@@ -4503,9 +4621,27 @@ void read_opts(int argc, char **argv, char *compile_opts)
else if (tmp->source_addr.sa.sa_family == AF_INET6)
tmp->source_addr.in6.sin6_port = htons(daemon->query_port);
#endif
}
}
}
}
if (daemon->host_records)
{
struct host_record *hr;
for (hr = daemon->host_records; hr; hr = hr->next)
if (hr->ttl == -1)
hr->ttl = daemon->local_ttl;
}
if (daemon->cnames)
{
struct cname *cn;
for (cn = daemon->cnames; cn; cn = cn->next)
if (cn->ttl == -1)
cn->ttl = daemon->local_ttl;
}
if (daemon->if_addrs)
{
struct iname *tmp;

View File

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

125
src/poll.c Normal file
View File

@@ -0,0 +1,125 @@
/* dnsmasq is Copyright (c) 2000-2016 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"
/* Wrapper for poll(). Allocates and extends array of struct pollfds,
keeps them in fd order so that we can set and test conditions on
fd using a simple but efficient binary chop. */
/* poll_reset()
poll_listen(fd, event)
.
.
poll_listen(fd, event);
hits = do_poll(timeout);
if (poll_check(fd, event)
.
.
if (poll_check(fd, event)
.
.
event is OR of POLLIN, POLLOUT, POLLERR, etc
*/
static struct pollfd *pollfds = NULL;
static nfds_t nfds, arrsize = 0;
/* Binary search. Returns either the pollfd with fd, or
if the fd doesn't match, or return equals nfds, the entry
to the left of which a new record should be inserted. */
static nfds_t fd_search(int fd)
{
nfds_t left, right, mid;
if ((right = nfds) == 0)
return 0;
left = 0;
while (1)
{
if (right == left + 1)
return (pollfds[left].fd >= fd) ? left : right;
mid = (left + right)/2;
if (pollfds[mid].fd > fd)
right = mid;
else
left = mid;
}
}
void poll_reset(void)
{
nfds = 0;
}
int do_poll(int timeout)
{
return poll(pollfds, nfds, timeout);
}
int poll_check(int fd, short event)
{
nfds_t i = fd_search(fd);
if (i < nfds && pollfds[i].fd == fd)
return pollfds[i].revents & event;
return 0;
}
void poll_listen(int fd, short event)
{
nfds_t i = fd_search(fd);
if (i < nfds && pollfds[i].fd == fd)
pollfds[i].events |= event;
else
{
if (arrsize != nfds)
memmove(&pollfds[i+1], &pollfds[i], (nfds - i) * sizeof(struct pollfd));
else
{
/* Array too small, extend. */
struct pollfd *new;
arrsize = (arrsize == 0) ? 64 : arrsize * 2;
if (!(new = whine_malloc(arrsize * sizeof(struct pollfd))))
return;
if (pollfds)
{
memcpy(new, pollfds, i * sizeof(struct pollfd));
memcpy(&new[i+1], &pollfds[i], (nfds - i) * sizeof(struct pollfd));
free(pollfds);
}
pollfds = new;
}
pollfds[i].fd = fd;
pollfds[i].events = event;
nfds++;
}
}

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2016 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-2015 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2016 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
@@ -28,11 +28,12 @@
struct ra_param {
time_t now;
int ind, managed, other, found_context, first, adv_router;
int ind, managed, other, first, adv_router;
char *if_name;
struct dhcp_netid *tags;
struct in6_addr link_local, link_global, ula;
unsigned int glob_pref_time, link_pref_time, ula_pref_time, adv_interval, prio;
struct dhcp_context *found_context;
};
struct search_param {
@@ -40,7 +41,18 @@ struct search_param {
char name[IF_NAMESIZE+1];
};
struct alias_param {
int iface;
struct dhcp_bridge *bridge;
int num_alias_ifs;
int max_alias_ifs;
int *alias_ifs;
};
static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *dest);
static void send_ra_alias(time_t now, int iface, char *iface_name, struct in6_addr *dest,
int send_iface);
static int send_ra_to_aliases(int index, unsigned int type, char *mac, size_t maclen, void *parm);
static int add_prefixes(struct in6_addr *local, int prefix,
int scope, int if_index, int flags,
unsigned int preferred, unsigned int valid, void *vparam);
@@ -181,6 +193,7 @@ void icmp6_packet(time_t now)
else if (packet[0] == ND_ROUTER_SOLICIT)
{
char *mac = "";
struct dhcp_bridge *bridge, *alias;
/* look for link-layer address option for logging */
if (sz >= 16 && packet[8] == ICMP6_OPT_SOURCE_MAC && (packet[9] * 8) + 8 <= sz)
@@ -191,12 +204,37 @@ void icmp6_packet(time_t now)
if (!option_bool(OPT_QUIET_RA))
my_syslog(MS_DHCP | LOG_INFO, "RTR-SOLICIT(%s) %s", interface, mac);
/* source address may not be valid in solicit request. */
send_ra(now, if_index, interface, !IN6_IS_ADDR_UNSPECIFIED(&from.sin6_addr) ? &from.sin6_addr : NULL);
/* If the incoming interface is an alias of some other one (as
specified by the --bridge-interface option), send an RA using
the context of the aliased interface. */
for (bridge = daemon->bridges; bridge; bridge = bridge->next)
{
int bridge_index = if_nametoindex(bridge->iface);
if (bridge_index)
{
for (alias = bridge->alias; alias; alias = alias->next)
if (wildcard_matchn(alias->iface, interface, IF_NAMESIZE))
{
/* Send an RA on if_index with information from
bridge_index. */
send_ra_alias(now, bridge_index, bridge->iface, NULL, if_index);
break;
}
if (alias)
break;
}
}
/* If the incoming interface wasn't an alias, send an RA using
the context of the incoming interface. */
if (!bridge)
/* source address may not be valid in solicit request. */
send_ra(now, if_index, interface, !IN6_IS_ADDR_UNSPECIFIED(&from.sin6_addr) ? &from.sin6_addr : NULL);
}
}
static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *dest)
static void send_ra_alias(time_t now, int iface, char *iface_name, struct in6_addr *dest, int send_iface)
{
struct ra_packet *ra;
struct ra_param parm;
@@ -214,7 +252,7 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
parm.ind = iface;
parm.managed = 0;
parm.other = 0;
parm.found_context = 0;
parm.found_context = NULL;
parm.adv_router = 0;
parm.if_name = iface_name;
parm.first = 1;
@@ -271,8 +309,14 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
unsigned int old = difftime(now, context->address_lost_time);
if (old > context->saved_valid)
{
{
/* We've advertised this enough, time to go */
/* If this context held the timeout, and there's another context in use
transfer the timeout there. */
if (context->ra_time != 0 && parm.found_context && parm.found_context->ra_time == 0)
new_timeout(parm.found_context, iface_name, now);
*up = context->next;
free(context);
}
@@ -313,8 +357,10 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
opt->type = ICMP6_OPT_PREFIX;
opt->len = 4;
opt->prefix_len = context->prefix;
/* autonomous only if we're not doing dhcp, always set "on-link" */
opt->flags = do_slaac ? 0xC0 : 0x80;
/* autonomous only if we're not doing dhcp, set
"on-link" unless "off-link" was specified */
opt->flags = (do_slaac ? 0x40 : 0) |
((context->flags & CONTEXT_RA_OFF_LINK) ? 0 : 0x80);
opt->valid_lifetime = htonl(context->saved_valid - old);
opt->preferred_lifetime = htonl(0);
opt->reserved = 0;
@@ -368,7 +414,7 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
}
#endif
iface_enumerate(AF_LOCAL, &iface, add_lla);
iface_enumerate(AF_LOCAL, &send_iface, add_lla);
/* RDNSS, RFC 6106, use relevant DHCP6 options */
(void)option_filter(parm.tags, NULL, daemon->dhcp_opts6);
@@ -476,7 +522,7 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
else
{
inet_pton(AF_INET6, ALL_NODES, &addr.sin6_addr);
setsockopt(daemon->icmp6fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &iface, sizeof(iface));
setsockopt(daemon->icmp6fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &send_iface, sizeof(send_iface));
}
while (retry_send(sendto(daemon->icmp6fd, daemon->outpacket.iov_base,
@@ -485,6 +531,13 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
}
static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *dest)
{
/* Send an RA on the same interface that the RA content is based
on. */
send_ra_alias(now, iface, iface_name, dest, iface);
}
static int add_prefixes(struct in6_addr *local, int prefix,
int scope, int if_index, int flags,
unsigned int preferred, unsigned int valid, void *vparam)
@@ -514,6 +567,7 @@ static int add_prefixes(struct in6_addr *local, int prefix,
int deprecate = 0;
int constructed = 0;
int adv_router = 0;
int off_link = 0;
unsigned int time = 0xffffffff;
struct dhcp_context *context;
@@ -586,10 +640,13 @@ static int add_prefixes(struct in6_addr *local, int prefix,
context->ra_time = 0;
context->flags |= CONTEXT_RA_DONE;
real_prefix = context->prefix;
off_link = (context->flags & CONTEXT_RA_OFF_LINK);
}
param->first = 0;
param->found_context = 1;
param->first = 0;
/* found_context is the _last_ one we found, so if there's
more than one, it's not the first. */
param->found_context = context;
}
/* configured time is ceiling */
@@ -636,8 +693,9 @@ static int add_prefixes(struct in6_addr *local, int prefix,
opt->type = ICMP6_OPT_PREFIX;
opt->len = 4;
opt->prefix_len = real_prefix;
/* autonomous only if we're not doing dhcp, always set "on-link" */
opt->flags = 0x80;
/* autonomous only if we're not doing dhcp, set
"on-link" unless "off-link" was specified */
opt->flags = (off_link ? 0 : 0x80);
if (do_slaac)
opt->flags |= 0x40;
if (adv_router)
@@ -683,6 +741,7 @@ time_t periodic_ra(time_t now)
struct search_param param;
struct dhcp_context *context;
time_t next_event;
struct alias_param aparam;
param.now = now;
param.iface = 0;
@@ -730,12 +789,84 @@ time_t periodic_ra(time_t now)
if (tmp->name && wildcard_match(tmp->name, param.name))
break;
if (!tmp)
send_ra(now, param.iface, param.name, NULL);
{
send_ra(now, param.iface, param.name, NULL);
/* Also send on all interfaces that are aliases of this
one. */
for (aparam.bridge = daemon->bridges;
aparam.bridge;
aparam.bridge = aparam.bridge->next)
if ((int)if_nametoindex(aparam.bridge->iface) == param.iface)
{
/* Count the number of alias interfaces for this
'bridge', by calling iface_enumerate with
send_ra_to_aliases and NULL alias_ifs. */
aparam.iface = param.iface;
aparam.alias_ifs = NULL;
aparam.num_alias_ifs = 0;
iface_enumerate(AF_LOCAL, &aparam, send_ra_to_aliases);
my_syslog(MS_DHCP | LOG_INFO, "RTR-ADVERT(%s) %s => %d alias(es)",
param.name, daemon->addrbuff, aparam.num_alias_ifs);
/* Allocate memory to store the alias interface
indices. */
aparam.alias_ifs = (int *)whine_malloc(aparam.num_alias_ifs *
sizeof(int));
if (aparam.alias_ifs)
{
/* Use iface_enumerate again to get the alias
interface indices, then send on each of
those. */
aparam.max_alias_ifs = aparam.num_alias_ifs;
aparam.num_alias_ifs = 0;
iface_enumerate(AF_LOCAL, &aparam, send_ra_to_aliases);
for (; aparam.num_alias_ifs; aparam.num_alias_ifs--)
{
my_syslog(MS_DHCP | LOG_INFO, "RTR-ADVERT(%s) %s => i/f %d",
param.name, daemon->addrbuff,
aparam.alias_ifs[aparam.num_alias_ifs - 1]);
send_ra_alias(now,
param.iface,
param.name,
NULL,
aparam.alias_ifs[aparam.num_alias_ifs - 1]);
}
free(aparam.alias_ifs);
}
/* The source interface can only appear in at most
one --bridge-interface. */
break;
}
}
}
}
return next_event;
}
static int send_ra_to_aliases(int index, unsigned int type, char *mac, size_t maclen, void *parm)
{
struct alias_param *aparam = (struct alias_param *)parm;
char ifrn_name[IFNAMSIZ];
struct dhcp_bridge *alias;
(void)type;
(void)mac;
(void)maclen;
if (if_indextoname(index, ifrn_name))
for (alias = aparam->bridge->alias; alias; alias = alias->next)
if (wildcard_matchn(alias->iface, ifrn_name, IFNAMSIZ))
{
if (aparam->alias_ifs && (aparam->num_alias_ifs < aparam->max_alias_ifs))
aparam->alias_ifs[aparam->num_alias_ifs] = index;
aparam->num_alias_ifs++;
}
return 1;
}
static int iface_search(struct in6_addr *local, int prefix,
int scope, int if_index, int flags,
int preferred, int valid, void *vparam)

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2016 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 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
char *name, int isExtract, int extrabytes)
{
unsigned char *cp = (unsigned char *)name, *p = *pp, *p1 = NULL;
unsigned int j, l, hops = 0;
unsigned int j, l, namelen = 0, hops = 0;
int retvalue = 1;
if (isExtract)
@@ -77,49 +77,10 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
p = l + (unsigned char *)header;
}
else if (label_type == 0x80)
return 0; /* reserved */
else if (label_type == 0x40)
{ /* ELT */
unsigned int count, digs;
if ((l & 0x3f) != 1)
return 0; /* we only understand bitstrings */
if (!isExtract)
return 0; /* Cannot compare bitsrings */
count = *p++;
if (count == 0)
count = 256;
digs = ((count-1)>>2)+1;
/* output is \[x<hex>/siz]. which is digs+9 chars */
if (cp - (unsigned char *)name + digs + 9 >= MAXDNAME)
return 0;
if (!CHECK_LEN(header, p, plen, (count-1)>>3))
return 0;
*cp++ = '\\';
*cp++ = '[';
*cp++ = 'x';
for (j=0; j<digs; j++)
{
unsigned int dig;
if (j%2 == 0)
dig = *p >> 4;
else
dig = *p++ & 0x0f;
*cp++ = dig < 10 ? dig + '0' : dig + 'A' - 10;
}
cp += sprintf((char *)cp, "/%d]", count);
/* do this here to overwrite the zero char from sprintf */
*cp++ = '.';
}
else
else if (label_type == 0x00)
{ /* label_type = 0 -> label. */
if (cp - (unsigned char *)name + l + 1 >= MAXDNAME)
namelen += l + 1; /* include period */
if (namelen >= MAXDNAME)
return 0;
if (!CHECK_LEN(header, p, plen, l))
return 0;
@@ -128,6 +89,19 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
if (isExtract)
{
unsigned char c = *p;
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID))
{
if (c == 0 || c == '.' || c == NAME_ESCAPE)
{
*cp++ = NAME_ESCAPE;
*cp++ = c+1;
}
else
*cp++ = c;
}
else
#endif
if (c != 0 && c != '.')
*cp++ = c;
else
@@ -144,19 +118,26 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
cp++;
if (c1 >= 'A' && c1 <= 'Z')
c1 += 'a' - 'A';
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && c1 == NAME_ESCAPE)
c1 = (*cp++)-1;
#endif
if (c2 >= 'A' && c2 <= 'Z')
c2 += 'a' - 'A';
if (c1 != c2)
retvalue = 2;
}
}
if (isExtract)
*cp++ = '.';
else if (*cp != 0 && *cp++ != '.')
retvalue = 2;
}
else
return 0; /* label types 0x40 and 0x80 not supported */
}
}
@@ -427,331 +408,22 @@ size_t resize_packet(struct dns_header *header, size_t plen, unsigned char *phea
return ansp - (unsigned char *)header;
}
unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t *len, unsigned char **p, int *is_sign)
{
/* See if packet has an RFC2671 pseudoheader, and if so return a pointer to it.
also return length of pseudoheader in *len and pointer to the UDP size in *p
Finally, check to see if a packet is signed. If it is we cannot change a single bit before
forwarding. We look for SIG and TSIG in the addition section, and TKEY queries (for GSS-TSIG) */
int i, arcount = ntohs(header->arcount);
unsigned char *ansp = (unsigned char *)(header+1);
unsigned short rdlen, type, class;
unsigned char *ret = NULL;
if (is_sign)
{
*is_sign = 0;
if (OPCODE(header) == QUERY)
{
for (i = ntohs(header->qdcount); i != 0; i--)
{
if (!(ansp = skip_name(ansp, header, plen, 4)))
return NULL;
GETSHORT(type, ansp);
GETSHORT(class, ansp);
if (class == C_IN && type == T_TKEY)
*is_sign = 1;
}
}
}
else
{
if (!(ansp = skip_questions(header, plen)))
return NULL;
}
if (arcount == 0)
return NULL;
if (!(ansp = skip_section(ansp, ntohs(header->ancount) + ntohs(header->nscount), header, plen)))
return NULL;
for (i = 0; i < arcount; i++)
{
unsigned char *save, *start = ansp;
if (!(ansp = skip_name(ansp, header, plen, 10)))
return NULL;
GETSHORT(type, ansp);
save = ansp;
GETSHORT(class, ansp);
ansp += 4; /* TTL */
GETSHORT(rdlen, ansp);
if (!ADD_RDLEN(header, ansp, plen, rdlen))
return NULL;
if (type == T_OPT)
{
if (len)
*len = ansp - start;
if (p)
*p = save;
ret = start;
}
else if (is_sign &&
i == arcount - 1 &&
class == C_ANY &&
type == T_TSIG)
*is_sign = 1;
}
return ret;
}
struct macparm {
unsigned char *limit;
struct dns_header *header;
size_t plen;
union mysockaddr *l3;
};
static size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit,
int optno, unsigned char *opt, size_t optlen, int set_do)
{
unsigned char *lenp, *datap, *p;
int rdlen, is_sign;
if (!(p = find_pseudoheader(header, plen, NULL, NULL, &is_sign)))
{
if (is_sign)
return plen;
/* We are adding the pseudoheader */
if (!(p = skip_questions(header, plen)) ||
!(p = skip_section(p,
ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount),
header, plen)))
return plen;
*p++ = 0; /* empty name */
PUTSHORT(T_OPT, p);
PUTSHORT(daemon->edns_pktsz, p); /* max packet length */
PUTSHORT(0, p); /* extended RCODE and version */
PUTSHORT(set_do ? 0x8000 : 0, p); /* DO flag */
lenp = p;
PUTSHORT(0, p); /* RDLEN */
rdlen = 0;
if (((ssize_t)optlen) > (limit - (p + 4)))
return plen; /* Too big */
header->arcount = htons(ntohs(header->arcount) + 1);
datap = p;
}
else
{
int i;
unsigned short code, len, flags;
/* Must be at the end, if exists */
if (ntohs(header->arcount) != 1 ||
is_sign ||
(!(p = skip_name(p, header, plen, 10))))
return plen;
p += 6; /* skip UDP length and RCODE */
GETSHORT(flags, p);
if (set_do)
{
p -=2;
PUTSHORT(flags | 0x8000, p);
}
lenp = p;
GETSHORT(rdlen, p);
if (!CHECK_LEN(header, p, plen, rdlen))
return plen; /* bad packet */
datap = p;
/* no option to add */
if (optno == 0)
return plen;
/* check if option already there */
for (i = 0; i + 4 < rdlen; i += len + 4)
{
GETSHORT(code, p);
GETSHORT(len, p);
if (code == optno)
return plen;
p += len;
}
if (((ssize_t)optlen) > (limit - (p + 4)))
return plen; /* Too big */
}
if (optno != 0)
{
PUTSHORT(optno, p);
PUTSHORT(optlen, p);
memcpy(p, opt, optlen);
p += optlen;
}
PUTSHORT(p - datap, lenp);
return p - (unsigned char *)header;
}
static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv)
{
struct macparm *parm = parmv;
int match = 0;
if (family == parm->l3->sa.sa_family)
{
if (family == AF_INET && memcmp(&parm->l3->in.sin_addr, addrp, INADDRSZ) == 0)
match = 1;
#ifdef HAVE_IPV6
else
if (family == AF_INET6 && memcmp(&parm->l3->in6.sin6_addr, addrp, IN6ADDRSZ) == 0)
match = 1;
#endif
}
if (!match)
return 1; /* continue */
parm->plen = add_pseudoheader(parm->header, parm->plen, parm->limit, EDNS0_OPTION_MAC, (unsigned char *)mac, maclen, 0);
return 0; /* done */
}
size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysockaddr *l3)
{
struct macparm parm;
/* Must have an existing pseudoheader as the only ar-record,
or have no ar-records. Must also not be signed */
if (ntohs(header->arcount) > 1)
return plen;
parm.header = header;
parm.limit = (unsigned char *)limit;
parm.plen = plen;
parm.l3 = l3;
iface_enumerate(AF_UNSPEC, &parm, filter_mac);
return parm.plen;
}
struct subnet_opt {
u16 family;
u8 source_netmask, scope_netmask;
#ifdef HAVE_IPV6
u8 addr[IN6ADDRSZ];
#else
u8 addr[INADDRSZ];
#endif
};
static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source)
{
/* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */
int len;
void *addrp;
#ifdef HAVE_IPV6
if (source->sa.sa_family == AF_INET6)
{
opt->family = htons(2);
opt->source_netmask = daemon->addr6_netmask;
addrp = &source->in6.sin6_addr;
}
else
#endif
{
opt->family = htons(1);
opt->source_netmask = daemon->addr4_netmask;
addrp = &source->in.sin_addr;
}
opt->scope_netmask = 0;
len = 0;
if (opt->source_netmask != 0)
{
len = ((opt->source_netmask - 1) >> 3) + 1;
memcpy(opt->addr, addrp, len);
if (opt->source_netmask & 7)
opt->addr[len-1] &= 0xff << (8 - (opt->source_netmask & 7));
}
return len + 4;
}
size_t add_source_addr(struct dns_header *header, size_t plen, char *limit, union mysockaddr *source)
{
/* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */
int len;
struct subnet_opt opt;
len = calc_subnet_opt(&opt, source);
return add_pseudoheader(header, plen, (unsigned char *)limit, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0);
}
#ifdef HAVE_DNSSEC
size_t add_do_bit(struct dns_header *header, size_t plen, char *limit)
{
return add_pseudoheader(header, plen, (unsigned char *)limit, 0, NULL, 0, 1);
}
#endif
int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer)
{
/* Section 9.2, Check that subnet option in reply matches. */
int len, calc_len;
struct subnet_opt opt;
unsigned char *p;
int code, i, rdlen;
calc_len = calc_subnet_opt(&opt, peer);
if (!(p = skip_name(pseudoheader, header, plen, 10)))
return 1;
p += 8; /* skip UDP length and RCODE */
GETSHORT(rdlen, p);
if (!CHECK_LEN(header, p, plen, rdlen))
return 1; /* bad packet */
/* check if option there */
for (i = 0; i + 4 < rdlen; i += len + 4)
{
GETSHORT(code, p);
GETSHORT(len, p);
if (code == EDNS0_OPTION_CLIENT_SUBNET)
{
/* make sure this doesn't mismatch. */
opt.scope_netmask = p[3];
if (len != calc_len || memcmp(p, &opt, len) != 0)
return 0;
}
p += len;
}
return 1;
}
/* is addr in the non-globally-routed IP space? */
int private_net(struct in_addr addr, int ban_localhost)
{
in_addr_t ip_addr = ntohl(addr.s_addr);
return
(((ip_addr & 0xFF000000) == 0x7F000000) && ban_localhost) /* 127.0.0.0/8 (loopback) */ ||
((ip_addr & 0xFFFF0000) == 0xC0A80000) /* 192.168.0.0/16 (private) */ ||
(((ip_addr & 0xFF000000) == 0x7F000000) && ban_localhost) /* 127.0.0.0/8 (loopback) */ ||
((ip_addr & 0xFF000000) == 0x00000000) /* RFC 5735 section 3. "here" network */ ||
((ip_addr & 0xFF000000) == 0x0A000000) /* 10.0.0.0/8 (private) */ ||
((ip_addr & 0xFFF00000) == 0xAC100000) /* 172.16.0.0/12 (private) */ ||
((ip_addr & 0xFFFF0000) == 0xA9FE0000) /* 169.254.0.0/16 (zeroconf) */ ;
((ip_addr & 0xFFFF0000) == 0xC0A80000) /* 192.168.0.0/16 (private) */ ||
((ip_addr & 0xFFFF0000) == 0xA9FE0000) /* 169.254.0.0/16 (zeroconf) */ ||
((ip_addr & 0xFFFFFF00) == 0xC0000200) /* 192.0.2.0/24 (test-net) */ ||
((ip_addr & 0xFFFFFF00) == 0xC6336400) /* 198.51.100.0/24(test-net) */ ||
((ip_addr & 0xFFFFFF00) == 0xCB007100) /* 203.0.113.0/24 (test-net) */ ||
((ip_addr & 0xFFFFFFFF) == 0xFFFFFFFF) /* 255.255.255.255/32 (broadcast)*/ ;
}
static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *header, size_t qlen, char *name, int *doctored)
@@ -1092,10 +764,23 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
memcpy(&addr, p1, addrlen);
/* check for returned address in private space */
if (check_rebind &&
(flags & F_IPV4) &&
private_net(addr.addr.addr4, !option_bool(OPT_LOCAL_REBIND)))
return 1;
if (check_rebind)
{
if ((flags & F_IPV4) &&
private_net(addr.addr.addr4, !option_bool(OPT_LOCAL_REBIND)))
return 1;
#ifdef HAVE_IPV6
if ((flags & F_IPV6) &&
IN6_IS_ADDR_V4MAPPED(&addr.addr.addr6))
{
struct in_addr v4;
v4.s_addr = ((const uint32_t *) (&addr.addr.addr6))[3];
if (private_net(v4, !option_bool(OPT_LOCAL_REBIND)))
return 1;
}
#endif
}
#ifdef HAVE_IPSET
if (ipsets && (flags & (F_IPV4 | F_IPV6)))
@@ -1198,7 +883,10 @@ unsigned int extract_request(struct dns_header *header, size_t qlen, char *name,
size_t setup_reply(struct dns_header *header, size_t qlen,
struct all_addr *addrp, unsigned int flags, unsigned long ttl)
{
unsigned char *p = skip_questions(header, qlen);
unsigned char *p;
if (!(p = skip_questions(header, qlen)))
return 0;
/* clear authoritative and truncated flags, set QR flag */
header->hb3 = (header->hb3 & ~(HB3_AA | HB3_TC)) | HB3_QR;
@@ -1208,13 +896,11 @@ size_t setup_reply(struct dns_header *header, size_t qlen,
header->nscount = htons(0);
header->arcount = htons(0);
header->ancount = htons(0); /* no answers unless changed below */
if (flags == F_NEG)
SET_RCODE(header, SERVFAIL); /* couldn't get memory */
else if (flags == F_NOERR)
if (flags == F_NOERR)
SET_RCODE(header, NOERROR); /* empty domain */
else if (flags == F_NXDOMAIN)
SET_RCODE(header, NXDOMAIN);
else if (p && flags == F_IPV4)
else if (flags == F_IPV4)
{ /* we know the address */
SET_RCODE(header, NOERROR);
header->ancount = htons(1);
@@ -1222,7 +908,7 @@ size_t setup_reply(struct dns_header *header, size_t qlen,
add_resource_record(header, NULL, NULL, sizeof(struct dns_header), &p, ttl, NULL, T_A, C_IN, "4", addrp);
}
#ifdef HAVE_IPV6
else if (p && flags == F_IPV6)
else if (flags == F_IPV6)
{
SET_RCODE(header, NOERROR);
header->ancount = htons(1);
@@ -1247,11 +933,9 @@ int check_for_local_domain(char *name, time_t now)
struct naptr *naptr;
/* Note: the call to cache_find_by_name is intended to find any record which matches
ie A, AAAA, CNAME, DS. Because RRSIG records are marked by setting both F_DS and F_DNSKEY,
cache_find_by name ordinarily only returns records with an exact match on those bits (ie
for the call below, only DS records). The F_NSIGMATCH bit changes this behaviour */
ie A, AAAA, CNAME. */
if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_CNAME | F_DS | F_NO_RR | F_NSIGMATCH)) &&
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;
@@ -1483,11 +1167,23 @@ int add_resource_record(struct dns_header *header, char *limit, int *truncp, int
static unsigned long crec_ttl(struct crec *crecp, time_t now)
{
/* Return 0 ttl for DHCP entries, which might change
before the lease expires. */
before the lease expires, unless configured otherwise. */
if (crecp->flags & (F_IMMORTAL | F_DHCP))
return daemon->local_ttl;
if (crecp->flags & F_DHCP)
{
int conf_ttl = daemon->use_dhcp_ttl ? daemon->dhcp_ttl : daemon->local_ttl;
/* Apply ceiling of actual lease length to configured TTL. */
if (!(crecp->flags & F_IMMORTAL) && (crecp->ttd - now) < conf_ttl)
return crecp->ttd - now;
return conf_ttl;
}
/* Immortal entries other than DHCP are local, and hold TTL in TTD field. */
if (crecp->flags & F_IMMORTAL)
return crecp->ttd;
/* Return the Max TTL value if it is lower then the actual TTL */
if (daemon->max_ttl == 0 || ((unsigned)(crecp->ttd - now) < daemon->max_ttl))
return crecp->ttd - now;
@@ -1499,64 +1195,37 @@ static unsigned long crec_ttl(struct crec *crecp, time_t now)
/* return zero if we can't answer from cache, or packet size if we can */
size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
struct in_addr local_addr, struct in_addr local_netmask,
time_t now, int *ad_reqd, int *do_bit)
time_t now, int ad_reqd, int do_bit, int have_pseudoheader)
{
char *name = daemon->namebuff;
unsigned char *p, *ansp, *pheader;
unsigned char *p, *ansp;
unsigned int qtype, qclass;
struct all_addr addr;
int nameoffset;
unsigned short flag;
int q, ans, anscount = 0, addncount = 0;
int dryrun = 0, sec_reqd = 0, have_pseudoheader = 0;
int is_sign;
int dryrun = 0;
struct crec *crecp;
int nxdomain = 0, auth = 1, trunc = 0, sec_data = 1;
struct mx_srv_record *rec;
size_t len;
if (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;
/* RFC 6840 5.7 */
*ad_reqd = header->hb4 & HB4_AD;
*do_bit = 0;
/* If there is an RFC2671 pseudoheader then it will be overwritten by
/* If there is an additional data section then it will be overwritten by
partial replies, so we have to do a dry run to see if we can answer
the query. We check to see if the do bit is set, if so we always
forward rather than answering from the cache, which doesn't include
security information, unless we're in DNSSEC validation mode. */
the query. */
if (ntohs(header->arcount) != 0)
dryrun = 1;
if (find_pseudoheader(header, qlen, NULL, &pheader, &is_sign))
{
unsigned short udpsz, flags;
unsigned char *psave = pheader;
have_pseudoheader = 1;
GETSHORT(udpsz, pheader);
pheader += 2; /* ext_rcode */
GETSHORT(flags, pheader);
if ((sec_reqd = flags & 0x8000))
*do_bit = 1;/* do bit */
*ad_reqd = 1;
/* If our client is advertising a larger UDP packet size
than we allow, trim it so that we don't get an overlarge
response from upstream */
if (!is_sign && (udpsz > daemon->edns_pktsz))
PUTSHORT(daemon->edns_pktsz, psave);
dryrun = 1;
}
if (ntohs(header->qdcount) == 0 || OPCODE(header) != QUERY )
return 0;
for (rec = daemon->mxnames; rec; rec = rec->next)
rec->offset = 0;
@@ -1613,98 +1282,6 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
}
}
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && (qtype == T_DNSKEY || qtype == T_DS))
{
int gotone = 0;
struct blockdata *keydata;
/* Do we have RRSIG? Can't do DS or DNSKEY otherwise. */
if (sec_reqd)
{
crecp = NULL;
while ((crecp = cache_find_by_name(crecp, name, now, F_DNSKEY | F_DS)))
if (crecp->uid == qclass && crecp->addr.sig.type_covered == qtype)
break;
}
if (!sec_reqd || crecp)
{
if (qtype == T_DS)
{
crecp = NULL;
while ((crecp = cache_find_by_name(crecp, name, now, F_DS)))
if (crecp->uid == qclass)
{
gotone = 1;
if (!dryrun)
{
if (crecp->flags & F_NEG)
{
if (crecp->flags & F_NXDOMAIN)
nxdomain = 1;
log_query(F_UPSTREAM, name, NULL, "no DS");
}
else if ((keydata = blockdata_retrieve(crecp->addr.ds.keydata, crecp->addr.ds.keylen, NULL)))
{
struct all_addr a;
a.addr.keytag = crecp->addr.ds.keytag;
log_query(F_KEYTAG | (crecp->flags & F_CONFIG), name, &a, "DS keytag %u");
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
crec_ttl(crecp, now), &nameoffset,
T_DS, qclass, "sbbt",
crecp->addr.ds.keytag, crecp->addr.ds.algo,
crecp->addr.ds.digest, crecp->addr.ds.keylen, keydata))
anscount++;
}
}
}
}
else /* DNSKEY */
{
crecp = NULL;
while ((crecp = cache_find_by_name(crecp, name, now, F_DNSKEY)))
if (crecp->uid == qclass)
{
gotone = 1;
if (!dryrun && (keydata = blockdata_retrieve(crecp->addr.key.keydata, crecp->addr.key.keylen, NULL)))
{
struct all_addr a;
a.addr.keytag = crecp->addr.key.keytag;
log_query(F_KEYTAG | (crecp->flags & F_CONFIG), name, &a, "DNSKEY keytag %u");
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
crec_ttl(crecp, now), &nameoffset,
T_DNSKEY, qclass, "sbbt",
crecp->addr.key.flags, 3, crecp->addr.key.algo, crecp->addr.key.keylen, keydata))
anscount++;
}
}
}
}
/* Now do RRSIGs */
if (gotone)
{
ans = 1;
auth = 0;
if (!dryrun && sec_reqd)
{
crecp = NULL;
while ((crecp = cache_find_by_name(crecp, name, now, F_DNSKEY | F_DS)))
if (crecp->uid == qclass && crecp->addr.sig.type_covered == qtype &&
(keydata = blockdata_retrieve(crecp->addr.sig.keydata, crecp->addr.sig.keylen, NULL)))
{
add_resource_record(header, limit, &trunc, nameoffset, &ansp,
crec_ttl(crecp, now), &nameoffset,
T_RRSIG, qclass, "t", crecp->addr.sig.keylen, keydata);
anscount++;
}
}
}
}
#endif
if (qclass == C_IN)
{
struct txt_record *t;
@@ -1713,6 +1290,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if ((t->class == qtype || qtype == T_ANY) && hostname_isequal(name, t->name))
{
ans = 1;
sec_data = 0;
if (!dryrun)
{
log_query(F_CONFIG | F_RRNAME, name, NULL, "<RR>");
@@ -1769,6 +1347,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (intr)
{
sec_data = 0;
ans = 1;
if (!dryrun)
{
@@ -1782,6 +1361,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
else if (ptr)
{
ans = 1;
sec_data = 0;
if (!dryrun)
{
log_query(F_CONFIG | F_RRNAME, name, NULL, "<PTR>");
@@ -1796,38 +1376,12 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
}
else if ((crecp = cache_find_by_addr(NULL, &addr, now, is_arpa)))
{
if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) && sec_reqd)
{
if (!option_bool(OPT_DNSSEC_VALID) || ((crecp->flags & F_NEG) && (crecp->flags & F_DNSSECOK)))
crecp = NULL;
#ifdef HAVE_DNSSEC
else if (crecp->flags & F_DNSSECOK)
{
int gotsig = 0;
struct crec *rr_crec = NULL;
while ((rr_crec = cache_find_by_name(rr_crec, name, now, F_DS | F_DNSKEY)))
{
if (rr_crec->addr.sig.type_covered == T_PTR && rr_crec->uid == C_IN)
{
char *sigdata = blockdata_retrieve(rr_crec->addr.sig.keydata, rr_crec->addr.sig.keylen, NULL);
gotsig = 1;
if (!dryrun &&
add_resource_record(header, limit, &trunc, nameoffset, &ansp,
rr_crec->ttd - now, &nameoffset,
T_RRSIG, C_IN, "t", crecp->addr.sig.keylen, sigdata))
anscount++;
}
}
if (!gotsig)
crecp = NULL;
}
#endif
}
if (crecp)
/* Don't use cache when DNSSEC data required, unless we know that
the zone is unsigned, which implies that we're doing
validation. */
if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) ||
!do_bit ||
(option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK)))
{
do
{
@@ -1837,19 +1391,19 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (!(crecp->flags & F_DNSSECOK))
sec_data = 0;
ans = 1;
if (crecp->flags & F_NEG)
{
ans = 1;
auth = 0;
if (crecp->flags & F_NXDOMAIN)
nxdomain = 1;
if (!dryrun)
log_query(crecp->flags & ~F_FORWARD, name, &addr, NULL);
}
else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd || option_bool(OPT_DNSSEC_VALID))
else
{
ans = 1;
if (!(crecp->flags & (F_HOSTS | F_DHCP)))
auth = 0;
if (!dryrun)
@@ -1869,6 +1423,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
else if (is_rev_synth(is_arpa, &addr, name))
{
ans = 1;
sec_data = 0;
if (!dryrun)
{
log_query(F_CONFIG | F_REVERSE | is_arpa, name, &addr, NULL);
@@ -1885,6 +1440,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
{
/* if not in cache, enabled and private IPV4 address, return NXDOMAIN */
ans = 1;
sec_data = 0;
nxdomain = 1;
if (!dryrun)
log_query(F_CONFIG | F_REVERSE | F_IPV4 | F_NEG | F_NXDOMAIN,
@@ -1932,6 +1488,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (i == 4)
{
ans = 1;
sec_data = 0;
if (!dryrun)
{
addr.addr.addr4.s_addr = htonl(a);
@@ -1970,6 +1527,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
continue;
#endif
ans = 1;
sec_data = 0;
if (!dryrun)
{
gotit = 1;
@@ -2009,48 +1567,8 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
crecp = save;
}
/* If the client asked for DNSSEC and we can't provide RRSIGs, either
because we've not doing DNSSEC or the cached answer is signed by negative,
don't answer from the cache, forward instead. */
if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) && sec_reqd)
{
if (!option_bool(OPT_DNSSEC_VALID) || ((crecp->flags & F_NEG) && (crecp->flags & F_DNSSECOK)))
crecp = NULL;
#ifdef HAVE_DNSSEC
else if (crecp->flags & F_DNSSECOK)
{
/* We're returning validated data, need to return the RRSIG too. */
struct crec *rr_crec = NULL;
int sigtype = type;
/* The signature may have expired even though the data is still in cache,
forward instead of answering from cache if so. */
int gotsig = 0;
if (crecp->flags & F_CNAME)
sigtype = T_CNAME;
while ((rr_crec = cache_find_by_name(rr_crec, name, now, F_DS | F_DNSKEY)))
{
if (rr_crec->addr.sig.type_covered == sigtype && rr_crec->uid == C_IN)
{
char *sigdata = blockdata_retrieve(rr_crec->addr.sig.keydata, rr_crec->addr.sig.keylen, NULL);
gotsig = 1;
if (!dryrun &&
add_resource_record(header, limit, &trunc, nameoffset, &ansp,
rr_crec->ttd - now, &nameoffset,
T_RRSIG, C_IN, "t", rr_crec->addr.sig.keylen, sigdata))
anscount++;
}
}
if (!gotsig)
crecp = NULL;
}
#endif
}
if (crecp)
/* 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))
do
{
/* don't answer wildcard queries with data not from /etc/hosts
@@ -2084,17 +1602,12 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (crecp->flags & F_NEG)
{
/* We don't cache NSEC records, so if a DNSSEC-validated negative answer
is cached and the client wants DNSSEC, forward rather than answering from the cache */
if (!sec_reqd || !(crecp->flags & F_DNSSECOK))
{
ans = 1;
auth = 0;
if (crecp->flags & F_NXDOMAIN)
nxdomain = 1;
if (!dryrun)
log_query(crecp->flags, name, NULL, NULL);
}
ans = 1;
auth = 0;
if (crecp->flags & F_NXDOMAIN)
nxdomain = 1;
if (!dryrun)
log_query(crecp->flags, name, NULL, NULL);
}
else
{
@@ -2332,14 +1845,14 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
len = ansp - (unsigned char *)header;
/* Advertise our packet size limit in our reply */
if (have_pseudoheader)
len = add_pseudoheader(header, len, (unsigned char *)limit, 0, NULL, 0, sec_reqd);
len = add_pseudoheader(header, len, (unsigned char *)limit, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0);
if (*ad_reqd && sec_data)
if (ad_reqd && sec_data)
header->hb4 |= HB4_AD;
else
header->hb4 &= ~HB4_AD;
return len;
}

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2016 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
@@ -52,7 +52,9 @@ static void do_options(struct dhcp_context *context,
int null_term, int pxearch,
unsigned char *uuid,
int vendor_class_len,
time_t now);
time_t now,
unsigned int lease_time,
unsigned short fuzz);
static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt);
@@ -61,7 +63,7 @@ static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char
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);
struct dhcp_boot *find_boot(struct dhcp_netid *netid);
static int pxe_uefi_workaround(int pxe_arch, struct dhcp_netid *netid, struct dhcp_packet *mess, struct in_addr local, time_t now, int pxe);
size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
size_t sz, time_t now, int unicast_dest, int *is_inform, int pxe, struct in_addr fallback)
@@ -610,7 +612,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
clear_packet(mess, end);
do_options(context, mess, end, NULL, hostname, get_domain(mess->yiaddr),
netid, subnet_addr, 0, 0, -1, NULL, vendor_class_len, now);
netid, subnet_addr, 0, 0, -1, NULL, vendor_class_len, now, 0xffffffff, 0);
}
}
@@ -803,9 +805,14 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
if (service->type == type)
break;
if (!service || !service->basename)
return 0;
for (; context; context = context->current)
if (match_netid(context->filter, tagif_netid, 1) &&
is_same_net(mess->ciaddr, context->start, context->netmask))
break;
if (!service || !service->basename || !context)
return 0;
clear_packet(mess, end);
mess->yiaddr = mess->ciaddr;
@@ -817,7 +824,10 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
else
mess->siaddr = context->local;
snprintf((char *)mess->file, sizeof(mess->file), "%s.%d", service->basename, layer);
snprintf((char *)mess->file, sizeof(mess->file),
strchr(service->basename, '.') ? "%s" :"%s.%d",
service->basename, layer);
option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr));
pxe_misc(mess, end, uuid);
@@ -844,6 +854,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
if ((mess_type == DHCPDISCOVER || (pxe && mess_type == DHCPREQUEST)))
{
struct dhcp_context *tmp;
int workaround = 0;
for (tmp = context; tmp; tmp = tmp->current)
if ((tmp->flags & CONTEXT_PROXY) &&
@@ -853,7 +864,8 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
if (tmp)
{
struct dhcp_boot *boot;
int redirect4011 = 0;
if (tmp->netid.net)
{
tmp->netid.next = netid;
@@ -871,10 +883,21 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
clear_packet(mess, end);
/* Provide the bootfile here, for gPXE, and in case we have no menu items
and set discovery_control = 8 */
if (boot)
/* Redirect EFI clients to port 4011 */
if (pxearch >= 6)
{
redirect4011 = 1;
mess->siaddr = tmp->local;
}
/* Returns true if only one matching service is available. On port 4011,
it also inserts the boot file and server name. */
workaround = pxe_uefi_workaround(pxearch, tagif_netid, mess, tmp->local, now, pxe);
if (!workaround && boot)
{
/* Provide the bootfile here, for gPXE, and in case we have no menu items
and set discovery_control = 8 */
if (boot->next_server.s_addr)
mess->siaddr = boot->next_server;
else if (boot->tftp_sname)
@@ -886,11 +909,12 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
option_put(mess, end, OPTION_MESSAGE_TYPE, 1,
mess_type == DHCPDISCOVER ? DHCPOFFER : DHCPACK);
option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr));
option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(tmp->local.s_addr));
pxe_misc(mess, end, uuid);
prune_vendor_opts(tagif_netid);
do_encap_opts(pxe_opts(pxearch, tagif_netid, context->local, now), OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
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);
log_packet("PXE", NULL, emac, emac_len, iface_name, ignore ? "proxy-ignored" : "proxy", NULL, mess->xid);
log_tags(tagif_netid, ntohl(mess->xid));
return ignore ? 0 : dhcp_packet_size(mess, agent_id, real_end);
@@ -1042,13 +1066,8 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
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);
/* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */
if (time != 0xffffffff)
{
option_put(mess, end, OPTION_T1, 4, (time/2));
option_put(mess, end, OPTION_T2, 4, (time*7)/8);
}
do_options(context, mess, end, req_options, offer_hostname, get_domain(mess->yiaddr),
netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now);
netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz);
return dhcp_packet_size(mess, agent_id, real_end);
@@ -1306,7 +1325,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
/* If the user-class option started as counted strings, the first byte will be zero. */
if (len != 0 && ucp[0] == 0)
ucp++, len--;
lease_add_extradata(lease, ucp, len, 0);
lease_add_extradata(lease, ucp, len, -1);
}
}
#endif
@@ -1367,15 +1386,8 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
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);
if (time != 0xffffffff)
{
while (fuzz > (time/16))
fuzz = fuzz/2;
option_put(mess, end, OPTION_T1, 4, (time/2) - fuzz);
option_put(mess, end, OPTION_T2, 4, ((time/8)*7) - fuzz);
}
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);
netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz);
}
return dhcp_packet_size(mess, agent_id, real_end);
@@ -1440,7 +1452,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
}
do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr),
netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now);
netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, 0xffffffff, 0);
*is_inform = 1; /* handle reply differently */
return dhcp_packet_size(mess, agent_id, real_end);
@@ -1980,6 +1992,56 @@ static int prune_vendor_opts(struct dhcp_netid *netid)
return force;
}
/* Many UEFI PXE implementations have badly broken menu code.
If there's exactly one relevant menu item, we abandon the menu system,
and jamb the data direct into the DHCP file, siaddr and sname fields.
Note that in this case, we have to assume that layer zero would be requested
by the client PXE stack. */
static int pxe_uefi_workaround(int pxe_arch, struct dhcp_netid *netid, struct dhcp_packet *mess, struct in_addr local, time_t now, int pxe)
{
struct pxe_service *service, *found;
/* Only workaround UEFI archs. */
if (pxe_arch < 6)
return 0;
for (found = NULL, service = daemon->pxe_services; service; service = service->next)
if (pxe_arch == service->CSA && service->basename && match_netid(service->netid, netid, 1))
{
if (found)
return 0; /* More than one relevant menu item */
found = service;
}
if (!found)
return 0; /* No relevant menu items. */
if (!pxe)
return 1;
if (found->sname)
{
mess->siaddr = a_record_from_hosts(found->sname, now);
snprintf((char *)mess->sname, sizeof(mess->sname), "%s", found->sname);
}
else
{
if (found->server.s_addr != 0)
mess->siaddr = found->server;
else
mess->siaddr = local;
inet_ntop(AF_INET, &mess->siaddr, (char *)mess->sname, INET_ADDRSTRLEN);
}
snprintf((char *)mess->file, sizeof(mess->file),
strchr(found->basename, '.') ? "%s" : "%s.0", found->basename);
return 1;
}
static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid, struct in_addr local, time_t now)
{
#define NUM_OPTS 4
@@ -2137,7 +2199,9 @@ static void do_options(struct dhcp_context *context,
int null_term, int pxe_arch,
unsigned char *uuid,
int vendor_class_len,
time_t now)
time_t now,
unsigned int lease_time,
unsigned short fuzz)
{
struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
struct dhcp_boot *boot;
@@ -2261,7 +2325,42 @@ static void do_options(struct dhcp_context *context,
/* rfc3011 says this doesn't need to be in the requested options list. */
if (subnet_addr.s_addr)
option_put(mess, end, OPTION_SUBNET_SELECT, INADDRSZ, ntohl(subnet_addr.s_addr));
if (lease_time != 0xffffffff)
{
unsigned int t1val = lease_time/2;
unsigned int t2val = (lease_time*7)/8;
unsigned int hval;
/* If set by user, sanity check, so not longer than lease. */
if ((opt = option_find2(OPTION_T1)))
{
hval = ntohl(*((unsigned int *)opt->val));
if (hval < lease_time && hval > 2)
t1val = hval;
}
if ((opt = option_find2(OPTION_T2)))
{
hval = ntohl(*((unsigned int *)opt->val));
if (hval < lease_time && hval > 2)
t2val = hval;
}
/* ensure T1 is still < T2 */
if (t2val <= t1val)
t1val = t2val - 1;
while (fuzz > (t1val/8))
fuzz = fuzz/2;
t1val -= fuzz;
t2val -= fuzz;
option_put(mess, end, OPTION_T1, 4, t1val);
option_put(mess, end, OPTION_T2, 4, t2val);
}
/* replies to DHCPINFORM may not have a valid context */
if (context)
{
@@ -2356,12 +2455,14 @@ static void do_options(struct dhcp_context *context,
if (!(opt->flags & DHOPT_FORCE) && !in_list(req_options, optno))
continue;
/* prohibit some used-internally options */
/* prohibit some used-internally options. T1 and T2 already handled. */
if (optno == OPTION_CLIENT_FQDN ||
optno == OPTION_MAXMESSAGE ||
optno == OPTION_OVERLOAD ||
optno == OPTION_PAD ||
optno == OPTION_END)
optno == OPTION_END ||
optno == OPTION_T1 ||
optno == OPTION_T2)
continue;
if (optno == OPTION_SNAME && done_server)
@@ -2475,7 +2576,8 @@ static void do_options(struct dhcp_context *context,
if (context && pxe_arch != -1)
{
pxe_misc(mess, end, uuid);
config_opts = pxe_opts(pxe_arch, tagif, context->local, now);
if (!pxe_uefi_workaround(pxe_arch, tagif, mess, context->local, now, 0))
config_opts = pxe_opts(pxe_arch, tagif, context->local, now);
}
if ((force_encap || in_list(req_options, OPTION_VENDOR_CLASS_OPT)) &&

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2016 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
@@ -130,7 +130,7 @@ static int dhcp6_maybe_relay(struct state *state, void *inbuff, size_t sz,
MAC address from the local ND cache. */
if (!state->link_address)
get_client_mac(client_addr, state->interface, state->mac, &state->mac_len, &state->mac_type);
get_client_mac(client_addr, state->interface, state->mac, &state->mac_len, &state->mac_type, now);
else
{
struct dhcp_context *c;
@@ -824,25 +824,21 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
}
else
{
/* Windows 8 always requests an address even if the Managed bit
in RA is 0 and it keeps retrying if it receives a reply
stating that no addresses are available. We solve this
by not replying at all if we're not configured to give any
addresses by DHCPv6. RFC 3315 17.2.1. appears to allow this. */
for (c = state->context; c; c = c->current)
if (!(c->flags & CONTEXT_RA_STATELESS))
break;
if (!c)
return 0;
/* no address, return error */
o1 = new_opt6(OPTION6_STATUS_CODE);
put_opt6_short(DHCP6NOADDRS);
put_opt6_string(_("no addresses available"));
end_opt6(o1);
log6_packet(state, state->lease_allocate ? "DHCPREPLY" : "DHCPADVERTISE", NULL, _("no addresses available"));
/* Some clients will ask repeatedly when we're not giving
out addresses because we're in stateless mode. Avoid spamming
the log in that case. */
for (c = state->context; c; c = c->current)
if (!(c->flags & CONTEXT_RA_STATELESS))
{
log6_packet(state, state->lease_allocate ? "DHCPREPLY" : "DHCPADVERTISE", NULL, _("no addresses available"));
break;
}
}
break;
@@ -1051,9 +1047,9 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
{
preferred_time = valid_time = 0;
message = _("address invalid");
}
}
if (message)
if (message && (message != state->hostname))
log6_packet(state, "DHCPREPLY", req_addr, message);
else
log6_quiet(state, "DHCPREPLY", req_addr, message);
@@ -1093,7 +1089,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
{
struct in6_addr *req_addr = opt6_ptr(ia_option, 0);
if (!address6_available(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);
@@ -1324,15 +1320,15 @@ static struct dhcp_netid *add_options(struct state *state, int do_refresh)
if (opt_cfg->opt == OPTION6_REFRESH_TIME)
done_refresh = 1;
if (opt_cfg->opt == OPTION6_DNS_SERVER)
done_dns = 1;
if (opt_cfg->flags & DHOPT_ADDR6)
{
int len, j;
struct in6_addr *a;
if (opt_cfg->opt == OPTION6_DNS_SERVER)
done_dns = 1;
for (a = (struct in6_addr *)opt_cfg->val, len = opt_cfg->len, j = 0;
j < opt_cfg->len; j += IN6ADDRSZ, a++)
if ((IN6_IS_ADDR_ULA_ZERO(a) && IN6_IS_ADDR_UNSPECIFIED(state->ula_addr)) ||
@@ -2058,7 +2054,8 @@ static unsigned int opt6_uint(unsigned char *opt, int offset, int size)
return ret;
}
void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, struct in6_addr *peer_address, u32 scope_id)
void relay_upstream6(struct dhcp_relay *relay, ssize_t sz,
struct in6_addr *peer_address, u32 scope_id, time_t now)
{
/* ->local is same value for all relays on ->current chain */
@@ -2072,7 +2069,7 @@ void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, struct in6_addr *peer
unsigned char mac[DHCP_CHADDR_MAX];
inet_pton(AF_INET6, ALL_SERVERS, &multicast);
get_client_mac(peer_address, scope_id, mac, &maclen, &mactype);
get_client_mac(peer_address, scope_id, mac, &maclen, &mactype, now);
/* source address == relay address */
from.addr.addr6 = relay->local.addr.addr6;

339
src/rrfilter.c Normal file
View File

@@ -0,0 +1,339 @@
/* dnsmasq is Copyright (c) 2000-2016 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/>.
*/
/* Code to safely remove RRs from an DNS answer */
#include "dnsmasq.h"
/* Go through a domain name, find "pointers" and fix them up based on how many bytes
we've chopped out of the packet, or check they don't point into an elided part. */
static int check_name(unsigned char **namep, struct dns_header *header, size_t plen, int fixup, unsigned char **rrs, int rr_count)
{
unsigned char *ansp = *namep;
while(1)
{
unsigned int label_type;
if (!CHECK_LEN(header, ansp, plen, 1))
return 0;
label_type = (*ansp) & 0xc0;
if (label_type == 0xc0)
{
/* pointer for compression. */
unsigned int offset;
int i;
unsigned char *p;
if (!CHECK_LEN(header, ansp, plen, 2))
return 0;
offset = ((*ansp++) & 0x3f) << 8;
offset |= *ansp++;
p = offset + (unsigned char *)header;
for (i = 0; i < rr_count; i++)
if (p < rrs[i])
break;
else
if (i & 1)
offset -= rrs[i] - rrs[i-1];
/* does the pointer end up in an elided RR? */
if (i & 1)
return 0;
/* No, scale the pointer */
if (fixup)
{
ansp -= 2;
*ansp++ = (offset >> 8) | 0xc0;
*ansp++ = offset & 0xff;
}
break;
}
else if (label_type == 0x80)
return 0; /* reserved */
else if (label_type == 0x40)
{
/* Extended label type */
unsigned int count;
if (!CHECK_LEN(header, ansp, plen, 2))
return 0;
if (((*ansp++) & 0x3f) != 1)
return 0; /* we only understand bitstrings */
count = *(ansp++); /* Bits in bitstring */
if (count == 0) /* count == 0 means 256 bits */
ansp += 32;
else
ansp += ((count-1)>>3)+1;
}
else
{ /* label type == 0 Bottom six bits is length */
unsigned int len = (*ansp++) & 0x3f;
if (!ADD_RDLEN(header, ansp, plen, len))
return 0;
if (len == 0)
break; /* zero length label marks the end. */
}
}
*namep = ansp;
return 1;
}
/* Go through RRs and check or fixup the domain names contained within */
static int check_rrs(unsigned char *p, struct dns_header *header, size_t plen, int fixup, unsigned char **rrs, int rr_count)
{
int i, j, type, class, rdlen;
unsigned char *pp;
for (i = 0; i < ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount); i++)
{
pp = p;
if (!(p = skip_name(p, header, plen, 10)))
return 0;
GETSHORT(type, p);
GETSHORT(class, p);
p += 4; /* TTL */
GETSHORT(rdlen, p);
/* If this RR is to be elided, don't fix up its contents */
for (j = 0; j < rr_count; j += 2)
if (rrs[j] == pp)
break;
if (j >= rr_count)
{
/* fixup name of RR */
if (!check_name(&pp, header, plen, fixup, rrs, rr_count))
return 0;
if (class == C_IN)
{
u16 *d;
for (pp = p, d = rrfilter_desc(type); *d != (u16)-1; d++)
{
if (*d != 0)
pp += *d;
else if (!check_name(&pp, header, plen, fixup, rrs, rr_count))
return 0;
}
}
}
if (!ADD_RDLEN(header, p, plen, rdlen))
return 0;
}
return 1;
}
/* mode is 0 to remove EDNS0, 1 to filter DNSSEC RRs */
size_t rrfilter(struct dns_header *header, size_t plen, int mode)
{
static unsigned char **rrs;
static int rr_sz = 0;
unsigned char *p = (unsigned char *)(header+1);
int i, rdlen, qtype, qclass, rr_found, chop_an, chop_ns, chop_ar;
if (ntohs(header->qdcount) != 1 ||
!(p = skip_name(p, header, plen, 4)))
return plen;
GETSHORT(qtype, p);
GETSHORT(qclass, p);
/* First pass, find pointers to start and end of all the records we wish to elide:
records added for DNSSEC, unless explicity queried for */
for (rr_found = 0, chop_ns = 0, chop_an = 0, chop_ar = 0, i = 0;
i < ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount);
i++)
{
unsigned char *pstart = p;
int type, class;
if (!(p = skip_name(p, header, plen, 10)))
return plen;
GETSHORT(type, p);
GETSHORT(class, p);
p += 4; /* TTL */
GETSHORT(rdlen, p);
if (!ADD_RDLEN(header, p, plen, rdlen))
return plen;
/* Don't remove the answer. */
if (i < ntohs(header->ancount) && type == qtype && class == qclass)
continue;
if (mode == 0) /* EDNS */
{
/* EDNS mode, remove T_OPT from additional section only */
if (i < (ntohs(header->nscount) + ntohs(header->ancount)) || type != T_OPT)
continue;
}
else if (type != T_NSEC && type != T_NSEC3 && type != T_RRSIG)
/* DNSSEC mode, remove SIGs and NSECs from all three sections. */
continue;
if (!expand_workspace(&rrs, &rr_sz, rr_found + 1))
return plen;
rrs[rr_found++] = pstart;
rrs[rr_found++] = p;
if (i < ntohs(header->ancount))
chop_an++;
else if (i < (ntohs(header->nscount) + ntohs(header->ancount)))
chop_ns++;
else
chop_ar++;
}
/* Nothing to do. */
if (rr_found == 0)
return plen;
/* Second pass, look for pointers in names in the records we're keeping and make sure they don't
point to records we're going to elide. This is theoretically possible, but unlikely. If
it happens, we give up and leave the answer unchanged. */
p = (unsigned char *)(header+1);
/* question first */
if (!check_name(&p, header, plen, 0, rrs, rr_found))
return plen;
p += 4; /* qclass, qtype */
/* Now answers and NS */
if (!check_rrs(p, header, plen, 0, rrs, rr_found))
return plen;
/* Third pass, elide records */
for (p = rrs[0], i = 1; i < rr_found; i += 2)
{
unsigned char *start = rrs[i];
unsigned char *end = (i != rr_found - 1) ? rrs[i+1] : ((unsigned char *)header) + plen;
memmove(p, start, end-start);
p += end-start;
}
plen = p - (unsigned char *)header;
header->ancount = htons(ntohs(header->ancount) - chop_an);
header->nscount = htons(ntohs(header->nscount) - chop_ns);
header->arcount = htons(ntohs(header->arcount) - chop_ar);
/* Fourth pass, fix up pointers in the remaining records */
p = (unsigned char *)(header+1);
check_name(&p, header, plen, 1, rrs, rr_found);
p += 4; /* qclass, qtype */
check_rrs(p, header, plen, 1, rrs, rr_found);
return plen;
}
/* This is used in the DNSSEC code too, hence it's exported */
u16 *rrfilter_desc(int type)
{
/* List of RRtypes which include domains in the data.
0 -> domain
integer -> no of plain bytes
-1 -> end
zero is not a valid RRtype, so the final entry is returned for
anything which needs no mangling.
*/
static u16 rr_desc[] =
{
T_NS, 0, -1,
T_MD, 0, -1,
T_MF, 0, -1,
T_CNAME, 0, -1,
T_SOA, 0, 0, -1,
T_MB, 0, -1,
T_MG, 0, -1,
T_MR, 0, -1,
T_PTR, 0, -1,
T_MINFO, 0, 0, -1,
T_MX, 2, 0, -1,
T_RP, 0, 0, -1,
T_AFSDB, 2, 0, -1,
T_RT, 2, 0, -1,
T_SIG, 18, 0, -1,
T_PX, 2, 0, 0, -1,
T_NXT, 0, -1,
T_KX, 2, 0, -1,
T_SRV, 6, 0, -1,
T_DNAME, 0, -1,
0, -1 /* wildcard/catchall */
};
u16 *p = rr_desc;
while (*p != type && *p != 0)
while (*p++ != (u16)-1);
return p+1;
}
int expand_workspace(unsigned char ***wkspc, int *szp, int new)
{
unsigned char **p;
int old = *szp;
if (old >= new+1)
return 1;
if (new >= 100)
return 0;
new += 5;
if (!(p = whine_malloc(new * sizeof(unsigned char *))))
return 0;
if (old != 0 && *wkspc)
{
memcpy(p, *wkspc, old * sizeof(unsigned char *));
free(*wkspc);
}
*wkspc = p;
*szp = new;
return 1;
}

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2016 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-2015 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2016 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,8 +103,10 @@ void tftp_request(struct listener *listen, time_t now)
if (listen->iface)
{
addr = listen->iface->addr;
mtu = listen->iface->mtu;
name = listen->iface->name;
mtu = listen->iface->mtu;
if (daemon->tftp_mtu != 0 && daemon->tftp_mtu < mtu)
mtu = daemon->tftp_mtu;
}
else
{
@@ -234,9 +236,17 @@ void tftp_request(struct listener *listen, time_t now)
strncpy(ifr.ifr_name, name, IF_NAMESIZE);
if (ioctl(listen->tftpfd, SIOCGIFMTU, &ifr) != -1)
mtu = ifr.ifr_mtu;
{
mtu = ifr.ifr_mtu;
if (daemon->tftp_mtu != 0 && daemon->tftp_mtu < mtu)
mtu = daemon->tftp_mtu;
}
}
/* Failed to get interface mtu - can use configured value. */
if (mtu == 0)
mtu = daemon->tftp_mtu;
if (name)
{
/* check for per-interface prefix */
@@ -336,14 +346,15 @@ void tftp_request(struct listener *listen, time_t now)
{
if ((opt = next(&p, end)) && !option_bool(OPT_TFTP_NOBLOCK))
{
/* 32 bytes for IP, UDP and TFTP headers, 52 bytes for IPv6 */
int overhead = (listen->family == AF_INET) ? 32 : 52;
transfer->blocksize = atoi(opt);
if (transfer->blocksize < 1)
transfer->blocksize = 1;
if (transfer->blocksize > (unsigned)daemon->packet_buff_sz - 4)
transfer->blocksize = (unsigned)daemon->packet_buff_sz - 4;
/* 32 bytes for IP, UDP and TFTP headers */
if (mtu != 0 && transfer->blocksize > (unsigned)mtu - 32)
transfer->blocksize = (unsigned)mtu - 32;
if (mtu != 0 && transfer->blocksize > (unsigned)mtu - overhead)
transfer->blocksize = (unsigned)mtu - overhead;
transfer->opt_blocksize = 1;
transfer->block = 0;
}
@@ -502,7 +513,7 @@ static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix)
return NULL;
}
void check_tftp_listeners(fd_set *rset, time_t now)
void check_tftp_listeners(time_t now)
{
struct tftp_transfer *transfer, *tmp, **up;
ssize_t len;
@@ -518,7 +529,7 @@ void check_tftp_listeners(fd_set *rset, time_t now)
prettyprint_addr(&transfer->peer, daemon->addrbuff);
if (FD_ISSET(transfer->sockfd, rset))
if (poll_check(transfer->sockfd, POLLIN))
{
/* we overwrote the buffer... */
daemon->srv_save = NULL;

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2016 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
@@ -226,7 +226,14 @@ unsigned char *do_rfc1035_name(unsigned char *p, char *sval)
{
unsigned char *cp = p++;
for (j = 0; *sval && (*sval != '.'); sval++, j++)
*p++ = *sval;
{
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && *sval == NAME_ESCAPE)
*p++ = (*(++sval))-1;
else
#endif
*p++ = *sval;
}
*cp = j;
if (*sval)
sval++;
@@ -563,12 +570,6 @@ char *print_mac(char *buff, unsigned char *mac, int len)
return buff;
}
void bump_maxfd(int fd, int *max)
{
if (fd > *max)
*max = fd;
}
/* rc is return from sendto and friends.
Return 1 if we should retry.
Set errno to zero if we succeeded. */