Compare commits
296 Commits
v2.61test2
...
v2.67test4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3f2873d42c | ||
|
|
ab915f837c | ||
|
|
ddd9a6b499 | ||
|
|
7abb69b5dc | ||
|
|
d5052fb24f | ||
|
|
b5a7ff42bb | ||
|
|
48fd1c4dd6 | ||
|
|
2bb73af7d1 | ||
|
|
86e92f9983 | ||
|
|
1c10b9de11 | ||
|
|
a66d36ea11 | ||
|
|
aa63a21ce0 | ||
|
|
797a7afba4 | ||
|
|
4b5ea12e90 | ||
|
|
2b6390fdc9 | ||
|
|
bd08ae67f9 | ||
|
|
4582c0efe7 | ||
|
|
834f36fe6d | ||
|
|
6f130def07 | ||
|
|
3931a7bd85 | ||
|
|
d9ee9c0872 | ||
|
|
0b0a73c1c9 | ||
|
|
81925ab73a | ||
|
|
9de1aa9b7f | ||
|
|
6f9aaa93e9 | ||
|
|
7e5664bdbc | ||
|
|
83f28bef6c | ||
|
|
96c727fda6 | ||
|
|
49dc570a72 | ||
|
|
cd1e04a234 | ||
|
|
27cb314e54 | ||
|
|
56a1142f03 | ||
|
|
5b37aa8c19 | ||
|
|
8ac9787350 | ||
|
|
9f9bd08af8 | ||
|
|
4c985dac39 | ||
|
|
3d77c0460d | ||
|
|
3ddad24608 | ||
|
|
6e37ab595c | ||
|
|
a1a79edaea | ||
|
|
49333cbdbe | ||
|
|
de92b479d9 | ||
|
|
0f128eb58c | ||
|
|
c630924d66 | ||
|
|
ff59fc82b3 | ||
|
|
52a1ae72f0 | ||
|
|
3a654c506f | ||
|
|
2763d4b51a | ||
|
|
e28836bf45 | ||
|
|
a6ebfacf7b | ||
|
|
c7961075c4 | ||
|
|
ab6ede7e04 | ||
|
|
b3538f1100 | ||
|
|
3b323bda58 | ||
|
|
13d86c7372 | ||
|
|
208fb610a6 | ||
|
|
4038ae2005 | ||
|
|
dd1721c799 | ||
|
|
a21e27bc99 | ||
|
|
b0ff858e78 | ||
|
|
54dae552b1 | ||
|
|
25c4198f7c | ||
|
|
4ead40cf67 | ||
|
|
04a0612e8a | ||
|
|
aa608c84b4 | ||
|
|
38365ff040 | ||
|
|
9c4270bcd9 | ||
|
|
46b066565e | ||
|
|
4dc9c657ad | ||
|
|
39595cfe31 | ||
|
|
ffa3d7d6a2 | ||
|
|
aa67fe7a8c | ||
|
|
bb2509fd2c | ||
|
|
61744359de | ||
|
|
095f62551f | ||
|
|
e25db1f273 | ||
|
|
79cb46c0e9 | ||
|
|
22ce550e53 | ||
|
|
30393100c1 | ||
|
|
459380965a | ||
|
|
21bac1bccd | ||
|
|
b1a1b6def5 | ||
|
|
baeb3adf21 | ||
|
|
39f6a04ca4 | ||
|
|
37c9ccebd1 | ||
|
|
71c73ac17c | ||
|
|
c6cb7407b3 | ||
|
|
333b2ceb97 | ||
|
|
b456b9fdfe | ||
|
|
34d0a36a1d | ||
|
|
355736f36f | ||
|
|
771287be11 | ||
|
|
dc9476b670 | ||
|
|
1e14cc0f48 | ||
|
|
55b548ae2b | ||
|
|
3b43646a08 | ||
|
|
3bc0d932d0 | ||
|
|
60225f4e75 | ||
|
|
1962446269 | ||
|
|
be37986a0f | ||
|
|
d7346a1e8c | ||
|
|
87d346f6a7 | ||
|
|
f0dd7f807d | ||
|
|
0c0502426f | ||
|
|
7f035f58c6 | ||
|
|
81e84f8dac | ||
|
|
55b42f6de3 | ||
|
|
ed8b68ad06 | ||
|
|
bad7b875eb | ||
|
|
5d162f20a9 | ||
|
|
9d29949440 | ||
|
|
1b75c1e61f | ||
|
|
293fd0f700 | ||
|
|
c1be917782 | ||
|
|
bb86e858b6 | ||
|
|
8445f5d2e2 | ||
|
|
72c9c3b11b | ||
|
|
6e3dba3fde | ||
|
|
7558ecd9ac | ||
|
|
1f776932a1 | ||
|
|
4820dce97a | ||
|
|
f8abe0c566 | ||
|
|
9def963c65 | ||
|
|
990123a937 | ||
|
|
1d6c639310 | ||
|
|
429798fd08 | ||
|
|
b5a8dd1dec | ||
|
|
95a0bd3701 | ||
|
|
8ff556739e | ||
|
|
496787677e | ||
|
|
e1ff419cf9 | ||
|
|
ee86ce68fc | ||
|
|
b75e936372 | ||
|
|
aa79235194 | ||
|
|
7c305be1bd | ||
|
|
f7fe362721 | ||
|
|
36bec089f7 | ||
|
|
45dd1fece4 | ||
|
|
29d28dda95 | ||
|
|
421594f83d | ||
|
|
d89fb4ed4f | ||
|
|
295a54eed3 | ||
|
|
5c0bd5b112 | ||
|
|
86e3b9a026 | ||
|
|
2f38141f43 | ||
|
|
8273ea5a19 | ||
|
|
4f7b304f53 | ||
|
|
8e4b87918f | ||
|
|
83b2198e86 | ||
|
|
d1a5975f9b | ||
|
|
52002051ad | ||
|
|
b191a77901 | ||
|
|
23780dd577 | ||
|
|
d1e9a582ad | ||
|
|
819ff4dd0f | ||
|
|
de604c18a0 | ||
|
|
be6cfb42ab | ||
|
|
2022310f95 | ||
|
|
657ed09693 | ||
|
|
c99df938d7 | ||
|
|
cf568a3726 | ||
|
|
e4807d8bb2 | ||
|
|
35239a302a | ||
|
|
db3946c358 | ||
|
|
0d28af84d0 | ||
|
|
42698cb7ab | ||
|
|
1d860415f2 | ||
|
|
289a253569 | ||
|
|
faafb3f7b7 | ||
|
|
2b127a1eab | ||
|
|
dfb23b3f77 | ||
|
|
b269221c00 | ||
|
|
8b46061e73 | ||
|
|
4d0f5b4c44 | ||
|
|
1dedeb87cc | ||
|
|
79cfefd856 | ||
|
|
0c0d4793ac | ||
|
|
12d71ed28c | ||
|
|
9fed0f71c2 | ||
|
|
2e34ac1403 | ||
|
|
bc54ae392b | ||
|
|
00acd06340 | ||
|
|
476e4a03c1 | ||
|
|
5f11b3e5e0 | ||
|
|
3169daad46 | ||
|
|
fd05f12790 | ||
|
|
ad094275b0 | ||
|
|
c740e4f342 | ||
|
|
132255b5da | ||
|
|
c4c0488ac6 | ||
|
|
a2ce6fcc91 | ||
|
|
12090548d2 | ||
|
|
8223cb15e7 | ||
|
|
4ba9b38cc5 | ||
|
|
42243214b5 | ||
|
|
23245c0cb2 | ||
|
|
b271446f82 | ||
|
|
611ebc5f1e | ||
|
|
be0f45cdbc | ||
|
|
9b40cbf587 | ||
|
|
c4a7f90ebb | ||
|
|
9609baee41 | ||
|
|
395eb71931 | ||
|
|
8bc4cecee6 | ||
|
|
6b617c0d15 | ||
|
|
55d290a3bf | ||
|
|
e17b4b3871 | ||
|
|
236e072cab | ||
|
|
05ff1ed7cc | ||
|
|
2b5bae9a8f | ||
|
|
39f1b8e73d | ||
|
|
af576b56c2 | ||
|
|
54dd393f39 | ||
|
|
4ce4f3779b | ||
|
|
8b3ae2fd43 | ||
|
|
ed55cb66e6 | ||
|
|
2cd9a0de1f | ||
|
|
c514ab9907 | ||
|
|
078a630bba | ||
|
|
43c271b07c | ||
|
|
24ce681e51 | ||
|
|
5ae34bf3c8 | ||
|
|
51931b888a | ||
|
|
9f7f3b1216 | ||
|
|
97c83bb05b | ||
|
|
8767ceecd4 | ||
|
|
18c63eff8f | ||
|
|
c64b7f6a78 | ||
|
|
068b4b51e3 | ||
|
|
919dd7cf14 | ||
|
|
f632e56793 | ||
|
|
2021c66251 | ||
|
|
8358e0f4b2 | ||
|
|
7f61b3ad59 | ||
|
|
a9ab732e35 | ||
|
|
11263a462c | ||
|
|
231d061b45 | ||
|
|
cdbee9a40b | ||
|
|
7b4ad2eb34 | ||
|
|
19d69be220 | ||
|
|
04363607aa | ||
|
|
dcffad2a86 | ||
|
|
6a69ab5ebd | ||
|
|
fc92ead0dd | ||
|
|
61ce600b20 | ||
|
|
7a14dfebbb | ||
|
|
42fb8153ba | ||
|
|
6f13e53886 | ||
|
|
d1c759c5c1 | ||
|
|
e46164e0bd | ||
|
|
7389ce7ff5 | ||
|
|
2f77797b17 | ||
|
|
9380ba70d6 | ||
|
|
1023dcbc9e | ||
|
|
83e854e359 | ||
|
|
50303b19d8 | ||
|
|
89382bacaa | ||
|
|
6c559c34df | ||
|
|
adaa6888dd | ||
|
|
a813111379 | ||
|
|
18f0fb050b | ||
|
|
05e92e5afe | ||
|
|
4723d49dad | ||
|
|
fbbc14541a | ||
|
|
5ef33279f2 | ||
|
|
1e02a85970 | ||
|
|
0e88d53faa | ||
|
|
01d1b8ddf2 | ||
|
|
c8257540bc | ||
|
|
2240704863 | ||
|
|
e8ca69ea16 | ||
|
|
da632e7cc1 | ||
|
|
30cd96663f | ||
|
|
7dbe98147d | ||
|
|
5d71d83420 | ||
|
|
38a59a9ff7 | ||
|
|
4b028ad612 | ||
|
|
442560beb4 | ||
|
|
7d2b5c9583 | ||
|
|
29689cfa5a | ||
|
|
52d4abf2f9 | ||
|
|
a953096485 | ||
|
|
884a6dfe6d | ||
|
|
0068301d24 | ||
|
|
353ae4d270 | ||
|
|
e759d426fa | ||
|
|
40ef23b547 | ||
|
|
f5e8562f96 | ||
|
|
1567feae3c | ||
|
|
daf061c9de | ||
|
|
d0e2c6c9ab | ||
|
|
8643ec7fea | ||
|
|
5cfea3d402 | ||
|
|
6c8f21e4a4 | ||
|
|
1d0f91c4a9 | ||
|
|
2a82db4caf |
13
.gitignore
vendored
Normal file
13
.gitignore
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
src/*.o
|
||||
src/*.mo
|
||||
src/dnsmasq.pot
|
||||
src/dnsmasq
|
||||
src/.configured
|
||||
contrib/wrt/dhcp_lease_time
|
||||
contrib/wrt/dhcp_release
|
||||
debian/base/
|
||||
debian/daemon/
|
||||
debian/files
|
||||
debian/substvars
|
||||
debian/utils-substvars
|
||||
debian/utils/
|
||||
360
CHANGELOG
360
CHANGELOG
@@ -1,14 +1,368 @@
|
||||
version 2.67
|
||||
Fix crash if upstream server returns SERVFAIL when
|
||||
--conntrack in use. Thanks to Giacomo Tazzari for finding
|
||||
this and supplying the patch.
|
||||
|
||||
Repair regression in 2.64. That release stopped sending
|
||||
lease-time information in the reply to DHCPINFORM
|
||||
requests, on the correct grounds that it was a standards
|
||||
violation. However, this broke the dnsmasq-specific
|
||||
dhcp_lease_time utility. Now, DHCPINFORM returns
|
||||
lease-time only if it's specifically requested
|
||||
(maintaining standards) and the dhcp_lease_time utility
|
||||
has been taught to ask for it (restoring functionality).
|
||||
|
||||
Fix --dhcp-match, --dhcp-vendorclass and --dhcp-userclass
|
||||
to work with BOOTP and well as DHCP. Thanks to Peter
|
||||
Korsgaard for spotting the problem.
|
||||
|
||||
Add --synth-domain. Thanks to Vishvananda Ishaya for
|
||||
suggesting this.
|
||||
|
||||
Fix failure to compile ipset.c if old kernel headers are
|
||||
in use. Thanks to Eugene Rudoy for pointing this out.
|
||||
|
||||
Handle IPv4 interface-address labels in Linux. These are
|
||||
often used to emulate the old IP-alias addresses. Before,
|
||||
using --interface=eth0 would service all the addresses of
|
||||
eth0, including ones configured as aliases, which appear
|
||||
in ifconfig as eth0:0. Now, only addresses with the label
|
||||
eth0 are active. This is not backwards compatible: if you
|
||||
want to continue to bind the aliases too, you need to add
|
||||
eg. --interface=eth0:0 to the config.
|
||||
|
||||
|
||||
version 2.66
|
||||
Add the ability to act as an authoritative DNS
|
||||
server. Dnsmasq can now answer queries from the wider 'net
|
||||
with local data, as long as the correct NS records are set
|
||||
up. Only local data is provided, to avoid creating an open
|
||||
DNS relay. Zone transfer is supported, to allow secondary
|
||||
servers to be configured.
|
||||
|
||||
Add "constructed DHCP ranges" for DHCPv6. This is intended
|
||||
for IPv6 routers which get prefixes dynamically via prefix
|
||||
delegation. With suitable configuration, stateful DHCPv6
|
||||
and RA can happen automatically as prefixes are delegated
|
||||
and then deprecated, without having to re-write the
|
||||
dnsmasq configuration file or restart the daemon. Thanks to
|
||||
Steven Barth for extensive testing and development work on
|
||||
this idea.
|
||||
|
||||
Fix crash on startup on Solaris 11. Regression probably
|
||||
introduced in 2.61. Thanks to Geoff Johnstone for the
|
||||
patch.
|
||||
|
||||
Add code to make behaviour for TCP DNS requests that same
|
||||
as for UDP requests, when a request arrives for an allowed
|
||||
address, but via a banned interface. This change is only
|
||||
active on Linux, since the relevant API is missing (AFAIK)
|
||||
on other platforms. Many thanks to Tomas Hozza for
|
||||
spotting the problem, and doing invaluable discovery of
|
||||
the obscure and undocumented API required for the solution.
|
||||
|
||||
Don't send the default DHCP option advertising dnsmasq as
|
||||
the local DNS server if dnsmasq is configured to not act
|
||||
as DNS server, or it's configured to a non-standard port.
|
||||
|
||||
Add DNSMASQ_CIRCUIT_ID, DNSMASQ_SUBCRIBER_ID,
|
||||
DNSMASQ_REMOTE_ID variables to the environment of the
|
||||
lease-change script (and the corresponding Lua). These hold
|
||||
information inserted into the DHCP request by a DHCP relay
|
||||
agent. Thanks to Lakefield Communications for providing a
|
||||
bounty for this addition.
|
||||
|
||||
Fixed crash, introduced in 2.64, whilst handling DHCPv6
|
||||
information-requests with some common configurations.
|
||||
Thanks to Robert M. Albrecht for the bug report and
|
||||
chasing the problem.
|
||||
|
||||
Add --ipset option. Thanks to Jason A. Donenfeld for the
|
||||
patch.
|
||||
|
||||
Don't erroneously reject some option names in --dhcp-match
|
||||
options. Thanks to Benedikt Hochstrasser for the bug report.
|
||||
|
||||
Allow a trailing '*' wildcard in all interface-name
|
||||
configurations. Thanks to Christian Parpart for the patch.
|
||||
|
||||
Handle the situation where libc headers define
|
||||
SO_REUSEPORT, but the kernel in use doesn't, to cope with
|
||||
the introduction of this option to Linux. Thanks to Rich
|
||||
Felker for the bug report.
|
||||
|
||||
Update Polish translation. Thanks to Jan Psota.
|
||||
|
||||
Fix crash if the configured DHCP lease limit is
|
||||
reached. Regression occurred in 2.61. Thanks to Tsachi for
|
||||
the bug report.
|
||||
|
||||
Update the French translation. Thanks to Gildas le Nadan.
|
||||
|
||||
|
||||
version 2.65
|
||||
Fix regression which broke forwarding of queries sent via
|
||||
TCP which are not for A and AAAA and which were directed to
|
||||
non-default servers. Thanks to Niax for the bug report.
|
||||
|
||||
Fix failure to build with DHCP support excluded. Thanks to
|
||||
Gustavo Zacarias for the patch.
|
||||
|
||||
Fix nasty regression in 2.64 which completely broke cacheing.
|
||||
|
||||
|
||||
version 2.64
|
||||
Handle DHCP FQDN options with all flag bits zero and
|
||||
--dhcp-client-update set. Thanks to Bernd Krumbroeck for
|
||||
spotting the problem.
|
||||
|
||||
Finesse the check for /etc/hosts names which conflict with
|
||||
DHCP names. Previously a name/address pair in /etc/hosts
|
||||
which didn't match the name/address of a DHCP lease would
|
||||
generate a warning. Now that only happesn if there is not
|
||||
also a match. This allows multiple addresses for a name in
|
||||
/etc/hosts with one of them assigned via DHCP.
|
||||
|
||||
Fix broken vendor-option processing for BOOTP. Thanks to
|
||||
Hans-Joachim Baader for the bug report.
|
||||
|
||||
Don't report spurious netlink errors, regression in
|
||||
2.63. Thanks to Vladislav Grishenko for the patch.
|
||||
|
||||
Flag DHCP or DHCPv6 in starup logging. Thanks to
|
||||
Vladislav Grishenko for the patch.
|
||||
|
||||
Add SetServersEx method in DBus interface. Thanks to Dan
|
||||
Williams for the patch.
|
||||
|
||||
Add SetDomainServers method in DBus interface. Thanks to
|
||||
Roy Marples for the patch.
|
||||
|
||||
Fix build with later Lua libraries. Thansk to Cristian
|
||||
Rodriguez for the patch.
|
||||
|
||||
Add --max-cache-ttl option. Thanks to Dennis Kaarsemaker
|
||||
for the patch.
|
||||
|
||||
Fix breakage of --host-record parsing, resulting in
|
||||
infinte loop at startup. Regression in 2.63. Thanks to
|
||||
Haim Gelfenbeyn for spotting this.
|
||||
|
||||
Set SO_REUSEADDRESS and SO_V6ONLY options on the DHCPv6
|
||||
socket, this allows multiple instances of dnsmasq on a
|
||||
single machine, in the same way as for DHCPv4. Thanks to
|
||||
Gene Czarcinski and Vladislav Grishenko for work on this.
|
||||
|
||||
Fix DHCPv6 to do access control correctly when it's
|
||||
configured with --listen-address. Thanks to
|
||||
Gene Czarcinski for sorting this out.
|
||||
|
||||
Add a "wildcard" dhcp-range which works for any IPv6
|
||||
subnet, --dhcp-range=::,static Useful for Stateless
|
||||
DHCPv6. Thanks to Vladislav Grishenko for the patch.
|
||||
|
||||
Don't include lease-time in DHCPACK replies to DHCPINFORM
|
||||
queries, since RFC-2131 says we shouldn't. Thanks to
|
||||
Wouter Ibens for pointing this out.
|
||||
|
||||
Makefile tweak to do dependency checking on header files.
|
||||
Thanks to Johan Peeters for the patch.
|
||||
|
||||
Check interface for outgoing unsolicited router
|
||||
advertisements, rather than relying on interface address
|
||||
configuration. Thanks to Gene Czarinski for the patch.
|
||||
|
||||
Handle better attempts to transmit on interfaces which are
|
||||
still doing DAD, and specifically do not just transmit
|
||||
without setting source address and interface, since this
|
||||
can cause very puzzling effects when a router
|
||||
advertisement goes astray. Thanks again to Gene Czarinski.
|
||||
|
||||
Get RA timers right when there is more than one
|
||||
dhcp-range on a subnet.
|
||||
|
||||
|
||||
version 2.63
|
||||
Do duplicate dhcp-host address check in --test mode.
|
||||
|
||||
Check that tftp-root directories are accessible before
|
||||
start-up. Thanks to Daniel Veillard for the initial patch.
|
||||
|
||||
Allow more than one --tfp-root flag. The per-interface
|
||||
stuff is pointless without that.
|
||||
|
||||
Add --bind-dynamic. A hybrid mode between the default and
|
||||
--bind-interfaces which copes with dynamically created
|
||||
interfaces.
|
||||
|
||||
A couple of fixes to the build system for Android. Thanks
|
||||
to Metin Kaya for the patches.
|
||||
|
||||
Remove the interface:<interface> argument in --dhcp-range, and
|
||||
the interface argument to --enable-tftp. These were a
|
||||
still-born attempt to allow automatic isolated
|
||||
configuration by libvirt, but have never (to my knowledge)
|
||||
been used, had very strange semantics, and have been
|
||||
superceded by other mechanisms.
|
||||
|
||||
Fixed bug logging filenames when duplicate dhcp-host
|
||||
addresses are found. Thanks to John Hanks for the patch.
|
||||
|
||||
Fix regression in 2.61 which broke caching of CNAME
|
||||
chains. Thanks to Atul Gupta for the bug report.
|
||||
|
||||
Allow the target of a --cname flag to be another --cname.
|
||||
|
||||
Teach DHCPv6 about the RFC 4242 information-refresh-time
|
||||
option, and add parsing if the minutes, hours and days
|
||||
format for options. Thanks to Francois-Xavier Le Bail for
|
||||
the suggestion.
|
||||
|
||||
Allow "w" (for week) as multiplier in lease times, as well
|
||||
as seconds, minutes, hours and days. Álvaro Gámez Machado
|
||||
spotted the ommission.
|
||||
|
||||
Update French translation. Thanks to Gildas Le Nadan.
|
||||
|
||||
Allow a DBus service name to be given with --enable-dbus
|
||||
which overrides the default,
|
||||
uk.org.thekelleys.dnsmasq. Thanks to Mathieu
|
||||
Trudel-Lapierre for the patch.
|
||||
|
||||
Set the "prefix on-link" bit in Router
|
||||
Advertisements. Thanks to Gui Iribarren for the patch.
|
||||
|
||||
|
||||
version 2.62
|
||||
Update German translation. Thanks to Conrad Kostecki.
|
||||
|
||||
Cope with router-solict packets wich don't have a valid
|
||||
source address. Thanks to Vladislav Grishenko for the patch.
|
||||
|
||||
Fixed bug which caused missing periodic router
|
||||
advertisements with some configurations. Thanks to
|
||||
Vladislav Grishenko for the patch.
|
||||
|
||||
Fixed bug which broke DHCPv6/RA with prefix lengths
|
||||
which are not divisible by 8. Thanks to Andre Coetzee
|
||||
for spotting this.
|
||||
|
||||
Fix non-response to router-solicitations when
|
||||
router-advertisement configured, but DHCPv6 not
|
||||
configured. Thanks to Marien Zwart for the patch.
|
||||
|
||||
Add --dns-rr, to allow arbitrary DNS resource records.
|
||||
|
||||
Fixed bug which broke RA scheduling when an interface had
|
||||
two addresses in the same network. Thanks to Jim Bos for
|
||||
his help nailing this.
|
||||
|
||||
version 2.61
|
||||
Re-write interface discovery code on *BSD to use
|
||||
getifaddrs. This is more portable, more straightforward,
|
||||
and allows us to find the prefix length for IPv6
|
||||
addresses.
|
||||
|
||||
Add ra-names DHCPv6 keyword which adds AAAA records
|
||||
for dual-stack hosts which get IPv6 addresses via SLAAC.
|
||||
Add ra-names, ra-stateless and slaac keywords for DHCPv6.
|
||||
Dnsmasq can now synthesise AAAA records for dual-stack
|
||||
hosts which get IPv6 addresses via SLAAC. It is also now
|
||||
possible to use SLAAC and stateless DHCPv6, and to
|
||||
tell clients to use SLAAC addresses as well as DHCP ones.
|
||||
Thanks to Dave Taht for help with this.
|
||||
|
||||
Add --dhcp-duid to allow DUID-EN uids to be used.
|
||||
|
||||
Explicity send DHCPv6 replies to the correct port, instead
|
||||
of relying on clients to send requests with the correct
|
||||
source address, since at least one client in the wild gets
|
||||
this wrong. Thanks to Conrad Kostecki for help tracking
|
||||
this down.
|
||||
|
||||
Send a preference value of 255 in DHCPv6 replies when
|
||||
--dhcp-authoritative is in effect. This tells clients not
|
||||
to wait around for other DHCP servers.
|
||||
|
||||
Better logging of DHCPv6 options.
|
||||
|
||||
Add --host-record. Thanks to Rob Zwissler for the
|
||||
suggestion.
|
||||
|
||||
Invoke the DHCP script with action "tftp" when a TFTP file
|
||||
transfer completes. The size of the file, address to which
|
||||
it was sent and complete pathname are supplied. Note that
|
||||
version 2.60 introduced some script incompatibilties
|
||||
associated with DHCPv6, and this is a further change. To
|
||||
be safe, scripts should ignore unknown actions, and if
|
||||
not IPv6-aware, should exit if the environment
|
||||
variable DNSMASQ_IAID is set. The use-case for this is
|
||||
to track netboot/install. Suggestion from Shantanu
|
||||
Gadgil.
|
||||
|
||||
Update contrib/port-forward/dnsmasq-portforward to reflect
|
||||
the above.
|
||||
|
||||
Set the environment variable DNSMASQ_LOG_DHCP when running
|
||||
the script id --log-dhcp is in effect, so that script can
|
||||
taylor their logging verbosity. Suggestion from Malte
|
||||
Forkel.
|
||||
|
||||
Arrange that addresses specified with --listen-address
|
||||
work even if there is no interface carrying the
|
||||
address. This is chiefly useful for IPv4 loopback
|
||||
addresses, where any address in 127.0.0.0/8 is a valid
|
||||
loopback address, but normally only 127.0.0.1 appears on
|
||||
the lo interface. Thanks to Mathieu Trudel-Lapierre for
|
||||
the idea and initial patch.
|
||||
|
||||
Fix crash, introduced in 2.60, when a DHCPINFORM is
|
||||
received from a network which has no valid dhcp-range.
|
||||
Thanks to Stephane Glondu for the bug report.
|
||||
|
||||
Add a new DHCP lease time keyword, "deprecated" for
|
||||
--dhcp-range. This is only valid for IPv6, and sets the
|
||||
preffered lease time for both DHCP and RA to zero. The
|
||||
effect is that clients can continue to use the address
|
||||
for existing connections, but new connections will use
|
||||
other addresses, if they exist. This makes hitless
|
||||
renumbering at least possible.
|
||||
|
||||
Fix bug in address6_available() which caused DHCPv6 lease
|
||||
aquisition to fail if more than one dhcp-range in use.
|
||||
|
||||
Provide RDNSS and DNSSL data in router advertisements,
|
||||
using the settings provided for DHCP options
|
||||
option6:domain-search and option6:dns-server.
|
||||
|
||||
Tweak logo/favicon.ico to add some transparency. Thanks to
|
||||
SamLT for work on this.
|
||||
|
||||
Don't cache data from non-recursive nameservers, since it
|
||||
may erroneously look like a valid CNAME to a non-exitant
|
||||
name. Thanks to Ben Winslow for finding this.
|
||||
|
||||
Call SO_BINDTODEVICE on the DHCP socket(s) when doing DHCP
|
||||
on exactly one interface and --bind-interfaces is set. This
|
||||
makes the OpenStack use-case of one dnsmasq per virtual
|
||||
interface work. This is only available on Linux; it's not
|
||||
supported on other platforms. Thanks to Vishvananda Ishaya
|
||||
and the OpenStack team for the suggestion.
|
||||
|
||||
Updated French translation. Thanks to Gildas Le Nadan.
|
||||
|
||||
Give correct from-cache answers to explict CNAME queries.
|
||||
Thanks to Rob Zwissler for spotting this.
|
||||
|
||||
Add --tftp-lowercase option. Thanks to Oliver Rath for the
|
||||
patch.
|
||||
|
||||
Ensure that the DBus DhcpLeaseUpdated events are generated
|
||||
when a lease goes through INIT_REBOOT state, even if the
|
||||
dhcp-script is not in use. Thanks to Antoaneta-Ecaterina
|
||||
Ene for the patch.
|
||||
|
||||
Fix failure of TFTP over IPv4 on OpenBSD platform. Thanks
|
||||
to Brad Smith for spotting this.
|
||||
|
||||
|
||||
version 2.60
|
||||
Fix compilation problem in Mac OS X Lion. Thanks to Olaf
|
||||
@@ -189,7 +543,7 @@ version 2.58
|
||||
|
||||
Fix regression in TFTP server on *BSD platforms introduced
|
||||
in version 2.56, due to confusion with sockaddr
|
||||
length. Many thanks to Loïc Pefferkorn for finding this.
|
||||
length. Many thanks to Loic Pefferkorn for finding this.
|
||||
|
||||
Support scope-ids in IPv6 addresses of nameservers from
|
||||
/etc/resolv.conf and in --server options. Eg
|
||||
|
||||
109
FAQ
109
FAQ
@@ -22,7 +22,7 @@ A: The high ports that dnsmasq opens are for replies from the upstream
|
||||
now uses a new, randomly selected, port for each query. The old
|
||||
default behaviour (use one port allocated by the OS) is available by
|
||||
setting --query-port=0, and setting the query port to a positive
|
||||
value is still works. You should think hard and know what you are
|
||||
value still works. You should think hard and know what you are
|
||||
doing before using either of these options.
|
||||
|
||||
Q: Why doesn't dnsmasq support DNS queries over TCP? Don't the RFC's specify
|
||||
@@ -112,7 +112,7 @@ A: Resolver code sometime does strange things when given names without
|
||||
hostname will fix things. (ie "ping myhost" fails, but "ping
|
||||
myhost." works. The solution is to make sure that all your hosts
|
||||
have a domain set ("domain" in resolv.conf, or set a domain in
|
||||
your DHCP server, see below fr Windows XP and Mac OS X).
|
||||
your DHCP server, see below for Windows XP and Mac OS X).
|
||||
Any domain will do, but "localnet" is traditional. Now when you
|
||||
resolve "myhost" the resolver will attempt to look up
|
||||
"myhost.localnet" so you need to have dnsmasq reply to that name.
|
||||
@@ -236,53 +236,70 @@ Q: What network types are supported by the DHCP server?
|
||||
A: Ethernet (and 802.11 wireless) are supported on all platforms. On
|
||||
Linux all network types (including FireWire) are supported.
|
||||
|
||||
Q: What is this strange "bind-interface" option?
|
||||
Q: What are these strange "bind-interface" and "bind-dynamic" options?
|
||||
|
||||
A: The DNS spec says that the reply to a DNS query must come from the
|
||||
same address it was sent to. The traditional way to write an UDP
|
||||
server to do this is to find all of the addresses belonging to the
|
||||
machine (ie all the interfaces on the machine) and then create a
|
||||
socket for each interface which is bound to the address of the
|
||||
interface. Then when a packet is sent to address A, it is received
|
||||
on the socket bound to address A and when the reply is also sent
|
||||
via that socket, the source address is set to A by the kernel and
|
||||
everything works. This is the how dnsmasq works when
|
||||
"bind-interfaces" is set, with the obvious extension that is misses
|
||||
out creating sockets for some interfaces depending on the
|
||||
--interface, --address and --except-interface flags. The
|
||||
disadvantage of this approach is that it breaks if interfaces don't
|
||||
exist or are not configured when the daemon starts and does the
|
||||
socket creation step. In a hotplug-aware world this is a real
|
||||
problem.
|
||||
A: Dnsmasq from v2.63 can operate in one of three different "networking
|
||||
modes". This is unfortunate as it requires users configuring dnsmasq
|
||||
to take into account some rather bizzare contraints and select the
|
||||
mode which best fits the requirements of a particular installation.
|
||||
The origin of these are deficiencies in the Unix networking
|
||||
model and APIs and each mode has different advantages and
|
||||
problems. Just to add to the confusion, not all modes are available on
|
||||
all platforms (due the to lack of supporting network APIs).To further
|
||||
add to the confusion, the rules for the DHCP subsystem on dnsmasq are
|
||||
different to the rules for the DNS and TFTP subsystems.
|
||||
|
||||
The alternative approach is to have only one socket, which is bound
|
||||
to the correct port and the wildcard IP address (0.0.0.0). That
|
||||
socket will receive _all_ packets sent to port 53, no matter what
|
||||
destination address they have. This solves the problem of
|
||||
interfaces which are created or reconfigured after daemon
|
||||
start-up. To make this work is more complicated because of the
|
||||
"reply source address" problem. When a UDP packet is sent by a
|
||||
socket bound to 0.0.0.0 its source address will be set to the
|
||||
address of one of the machine's interfaces, but which one is not
|
||||
determined and can vary depending on the OS being run. To get round
|
||||
this it is neccessary to use a scary advanced API to determine the
|
||||
address to which a query was sent, and force that to be the source
|
||||
address in the reply. For IPv4 this stuff in non-portable and quite
|
||||
often not even available (It's different between FreeBSD 5.x and
|
||||
Linux, for instance, and FreeBSD 4.x, Linux 2.0.x and OpenBSD don't
|
||||
have it at all.) Hence "bind-interfaces" has to always be available
|
||||
as a fall back. For IPv6 the API is standard and universally
|
||||
available.
|
||||
The three modes are "wildcard", "bind-interfaces" and "bind-dynamic".
|
||||
|
||||
It could be argued that if the --interface or --address flags are
|
||||
used then binding interfaces is more appropriate, but using
|
||||
wildcard binding means that dnsmasq will quite happily start up
|
||||
after being told to use interfaces which don't exist, but which are
|
||||
created later. Wildcard binding breaks the scenario when dnsmasq is
|
||||
listening on one interface and another server (most probably BIND)
|
||||
is listening on another. It's not possible for BIND to bind to an
|
||||
(address,port) pair when dnsmasq has bound (wildcard,port), hence
|
||||
the ability to explicitly turn off wildcard binding.
|
||||
In "wildcard" mode, dnsmasq binds the wildcard IP address (0.0.0.0 or
|
||||
::). This allows it to recieve all the packets sent to the server on
|
||||
the relevant port. Access control (--interface, --except-interface,
|
||||
--listen-address, etc) is implemented by dnsmasq: it queries the
|
||||
kernel to determine the interface on which a packet was recieved and
|
||||
the address to which it was sent, and applies the configured
|
||||
rules. Wildcard mode is the default if neither of the other modes are
|
||||
specified.
|
||||
|
||||
In "bind-interfaces" mode, dnsmasq runs through all the network
|
||||
interfaces available when it starts, finds the set of IP addresses on
|
||||
those interfaces, filters that set using the access control
|
||||
configuration, and then binds the set of IP addresses. Only packets
|
||||
sent to the allowed addresses are delivered by the kernel to dnsmasq.
|
||||
|
||||
In "bind-dynamic" mode, access control filtering is done both by
|
||||
binding individual IP addresses, as for bind-interfaces, and by
|
||||
inspecting individual packets on arrival as for wildcard mode. In
|
||||
addition, dnsmasq notices when new interfaces appear or new addresses
|
||||
appear on existing interfaces, and the resulting IP addresses are
|
||||
bound automatically without having to restart dnsmasq.
|
||||
|
||||
The mode chosen has four different effects: co-existence with other
|
||||
servers, semantics of --interface access control, effect of new
|
||||
interfaces, and legality of --interface specifications for
|
||||
non-existent inferfaces. We will deal with these in order.
|
||||
|
||||
A dnsmasq instance running in wildcard mode precludes a machine from
|
||||
running a second instance of dnsmasq or any other DNS, TFTP or DHCP
|
||||
server. Attempts to do so will fail with an "address in use" error.
|
||||
Dnsmasq running in --bind-interfaces or bind-dynamic mode allow other
|
||||
instances of dnsmasq or other servers, as long as no two servers are
|
||||
configured to listen on the same interface address.
|
||||
|
||||
The semantics of --interface varies subtly between wildcard or
|
||||
bind-dynamic mode and bind-interfaces mode. The situation where this
|
||||
matters is a request which arrives via one interface (A), but with a
|
||||
destination address of a second interface (B) and when dnsmasq is
|
||||
configured to listen only on B. In wildcard or bind-dynamic mode, such
|
||||
a request will be ignored, in bind-interfaces mode, it will be
|
||||
accepted.
|
||||
|
||||
The creation of new network interfaces after dnsmasq starts is ignored
|
||||
by dnsmasq when in --bind-interfaces mode. In wildcard or bind-dynamic
|
||||
mode, such interfaces are handled normally.
|
||||
|
||||
A --interface specification for a non-existent interface is a fatal
|
||||
error at start-up when in --bind-interfaces mode, by just generates a
|
||||
warning in wildcard or bind-dynamic mode.
|
||||
|
||||
Q: Why doesn't Kerberos work/why can't I get sensible answers to
|
||||
queries for SRV records.
|
||||
|
||||
15
Makefile
15
Makefile
@@ -1,4 +1,4 @@
|
||||
# dnsmasq is Copyright (c) 2000-2012 Simon Kelley
|
||||
# dnsmasq is Copyright (c) 2000-2013 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
|
||||
@@ -65,7 +65,7 @@ version = -DVERSION='\"`$(top)/bld/get-version $(top)`\"'
|
||||
objs = cache.o rfc1035.o util.o option.o forward.o network.o \
|
||||
dnsmasq.o dhcp.o lease.o rfc2131.o netlink.o dbus.o bpf.o \
|
||||
helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o \
|
||||
dhcp-common.o outpacket.o radv.o
|
||||
dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o domain.o
|
||||
|
||||
hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \
|
||||
dns-protocol.h radv-protocol.h
|
||||
@@ -79,7 +79,8 @@ all : $(BUILDDIR)
|
||||
|
||||
clean :
|
||||
rm -f *~ $(BUILDDIR)/*.mo contrib/*/*~ */*~ $(BUILDDIR)/*.pot
|
||||
rm -f $(BUILDDIR)/*.o $(BUILDDIR)/dnsmasq.a $(BUILDDIR)/dnsmasq core */core
|
||||
rm -f $(BUILDDIR)/.configured $(BUILDDIR)/*.o $(BUILDDIR)/dnsmasq.a $(BUILDDIR)/dnsmasq
|
||||
rm -rf core */core
|
||||
|
||||
install : all install-common
|
||||
|
||||
@@ -113,7 +114,11 @@ $(BUILDDIR):
|
||||
mkdir -p $(BUILDDIR)
|
||||
|
||||
|
||||
# rules below are targets in recusive makes with cwd=$(SRC)
|
||||
# rules below are targets in recusive makes with cwd=$(BUILDDIR)
|
||||
|
||||
.configured: $(hdrs)
|
||||
@rm -f *.o
|
||||
@touch $@
|
||||
|
||||
$(objs:.o=.c) $(hdrs):
|
||||
ln -s $(top)/$(SRC)/$@ .
|
||||
@@ -121,7 +126,7 @@ $(objs:.o=.c) $(hdrs):
|
||||
.c.o:
|
||||
$(CC) $(CFLAGS) $(COPTS) $(i18n) $(build_cflags) $(RPM_OPT_FLAGS) -c $<
|
||||
|
||||
dnsmasq : $(hdrs) $(objs)
|
||||
dnsmasq : .configured $(hdrs) $(objs)
|
||||
$(CC) $(LDFLAGS) -o $@ $(objs) $(build_libs) $(LIBS)
|
||||
|
||||
dnsmasq.pot : $(objs:.o=.c) $(hdrs)
|
||||
|
||||
@@ -8,7 +8,7 @@ LOCAL_SRC_FILES := bpf.c cache.c dbus.c dhcp.c dnsmasq.c \
|
||||
netlink.c network.c option.c rfc1035.c \
|
||||
rfc2131.c tftp.c util.c conntrack.c \
|
||||
dhcp6.c rfc3315.c dhcp-common.c outpacket.c \
|
||||
radv.c
|
||||
radv.c slaac.c auth.c ipset.c domain.c
|
||||
|
||||
LOCAL_MODULE := dnsmasq
|
||||
|
||||
@@ -17,4 +17,6 @@ LOCAL_C_INCLUDES := external/dnsmasq/src
|
||||
LOCAL_CFLAGS := -O2 -g -W -Wall -D__ANDROID__ -DNO_IPV6 -DNO_TFTP -DNO_SCRIPT
|
||||
LOCAL_SYSTEM_SHARED_LIBRARIES := libc libcutils
|
||||
|
||||
LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog
|
||||
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
# which has a set of references substituted into it by git.
|
||||
# If we can find one which matches $v[0-9].* then we assume it's
|
||||
# a version-number tag, else we just use the whole string.
|
||||
# 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
|
||||
@@ -15,10 +17,10 @@ elif grep '\$Format:%d\$' $1/VERSION >/dev/null 2>&1; then
|
||||
# unsubstituted VERSION, but no git available.
|
||||
echo UNKNOWN
|
||||
else
|
||||
vers=`cat $1/VERSION | sed 's/[(), ]/,/ g' | tr ',' '\n' | grep $v[0-9]`
|
||||
vers=`cat $1/VERSION | sed 's/[(), ]/,/ g' | tr ',' '\n' | grep ^v[0-9]`
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "${vers}" | head -n 1 | tail -c +2
|
||||
echo "${vers}" | sort | head -n 1 | sed 's/^v//'
|
||||
else
|
||||
cat $1/VERSION
|
||||
fi
|
||||
|
||||
43
contrib/dbus-test/dbus-test.py
Executable file
43
contrib/dbus-test/dbus-test.py
Executable file
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/python
|
||||
import dbus
|
||||
|
||||
bus = dbus.SystemBus()
|
||||
p = bus.get_object("uk.org.thekelleys.dnsmasq", "/uk/org/thekelleys/dnsmasq")
|
||||
l = dbus.Interface(p, dbus_interface="uk.org.thekelleys.dnsmasq")
|
||||
|
||||
# The new more flexible SetServersEx method
|
||||
array = dbus.Array()
|
||||
array.append(["1.2.3.5"])
|
||||
array.append(["1.2.3.4#664", "foobar.com"])
|
||||
array.append(["1003:1234:abcd::1%eth0", "eng.mycorp.com", "lab.mycorp.com"])
|
||||
print l.SetServersEx(array)
|
||||
|
||||
# Must create a new object for dnsmasq as the introspection gives the wrong
|
||||
# signature for SetServers (av) while the code only expects a bunch of arguments
|
||||
# instead of an array of variants
|
||||
p = bus.get_object("uk.org.thekelleys.dnsmasq", "/uk/org/thekelleys/dnsmasq", introspect=False)
|
||||
l = dbus.Interface(p, dbus_interface="uk.org.thekelleys.dnsmasq")
|
||||
|
||||
# The previous method; all addresses in machine byte order
|
||||
print l.SetServers(dbus.UInt32(16909060), # 1.2.3.5
|
||||
dbus.UInt32(16909061), # 1.2.3.4
|
||||
"foobar.com",
|
||||
dbus.Byte(0x10), # 1003:1234:abcd::1
|
||||
dbus.Byte(0x03),
|
||||
dbus.Byte(0x12),
|
||||
dbus.Byte(0x34),
|
||||
dbus.Byte(0xab),
|
||||
dbus.Byte(0xcd),
|
||||
dbus.Byte(0x00),
|
||||
dbus.Byte(0x00),
|
||||
dbus.Byte(0x00),
|
||||
dbus.Byte(0x00),
|
||||
dbus.Byte(0x00),
|
||||
dbus.Byte(0x00),
|
||||
dbus.Byte(0x00),
|
||||
dbus.Byte(0x00),
|
||||
dbus.Byte(0x00),
|
||||
dbus.Byte(0x01),
|
||||
"eng.mycorp.com",
|
||||
"lab.mycorp.com")
|
||||
|
||||
@@ -34,11 +34,21 @@ if [ ${DNSMASQ_OLD_HOSTNAME} ] && [ ${action} = old ] ; then
|
||||
hostname=${DNSMASQ_OLD_HOSTNAME}
|
||||
fi
|
||||
|
||||
# IPv6 leases are not our concern. no NAT there!
|
||||
if [ ${DNSMASQ_IAID} ] ; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# action init is not relevant, and will only be seen when leasefile-ro is set.
|
||||
if [ ${action} = init ] ; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# action tftp is not relevant.
|
||||
if [ ${action} = tftp ] ; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ ${hostname} ]; then
|
||||
ports=$(sed -n -e "/^${hostname}\ .*/ s/^.* //p" ${PORTSFILE})
|
||||
|
||||
|
||||
57
contrib/systemd/dbus_activation
Normal file
57
contrib/systemd/dbus_activation
Normal file
@@ -0,0 +1,57 @@
|
||||
To: dnsmasq-discuss@lists.thekelleys.org.uk
|
||||
From: Alex Elsayed <eternaleye+usenet@gmail.com>
|
||||
Date: Tue, 15 May 2012 01:53:54 -0700
|
||||
Subject: [Dnsmasq-discuss] [PATCH] Support dbus activation
|
||||
|
||||
Introduce dbus service file and turn dbus on in the systemd
|
||||
unit.
|
||||
|
||||
Note to packagers:
|
||||
To add support for dbus activation, you must install the dbus
|
||||
service file (dbus/uk.org.thekelleys.dnsmasq.service) into
|
||||
$DATADIR/dbus-1/system-services.
|
||||
|
||||
---
|
||||
contrib/systemd/dnsmasq.service | 2 +-
|
||||
dbus/uk.org.thekelleys.dnsmasq.service | 7 +++++++
|
||||
2 files changed, 8 insertions(+), 1 deletion(-)
|
||||
create mode 100644 dbus/uk.org.thekelleys.dnsmasq.service
|
||||
|
||||
diff --git a/contrib/systemd/dnsmasq.service
|
||||
b/contrib/systemd/dnsmasq.service
|
||||
index a27fe6d..4a784d3 100644
|
||||
--- a/contrib/systemd/dnsmasq.service
|
||||
+++ b/contrib/systemd/dnsmasq.service
|
||||
@@ -5,7 +5,7 @@ Description=A lightweight DHCP and caching DNS server
|
||||
Type=dbus
|
||||
BusName=uk.org.thekelleys.dnsmasq
|
||||
ExecStartPre=/usr/sbin/dnsmasq --test
|
||||
-ExecStart=/usr/sbin/dnsmasq -k
|
||||
+ExecStart=/usr/sbin/dnsmasq -k -1
|
||||
ExecReload=/bin/kill -HUP $MAINPID
|
||||
|
||||
[Install]
|
||||
diff --git a/dbus/uk.org.thekelleys.dnsmasq.service
|
||||
b/dbus/uk.org.thekelleys.dnsmasq.service
|
||||
new file mode 100644
|
||||
index 0000000..f5fe98d
|
||||
--- /dev/null
|
||||
+++ b/dbus/uk.org.thekelleys.dnsmasq.service
|
||||
@@ -0,0 +1,7 @@
|
||||
+[D-BUS Service]
|
||||
+Name=uk.org.thekelleys.dnsmasq
|
||||
+Exec=/usr/sbin/dnsmasq -k -1
|
||||
+User=root
|
||||
+SystemdService=dnsmasq.service
|
||||
+
|
||||
+
|
||||
--
|
||||
1.7.10.2
|
||||
|
||||
|
||||
|
||||
_______________________________________________
|
||||
Dnsmasq-discuss mailing list
|
||||
Dnsmasq-discuss@lists.thekelleys.org.uk
|
||||
http://lists.thekelleys.org.uk/mailman/listinfo/dnsmasq-discuss
|
||||
|
||||
@@ -12,9 +12,11 @@ If an error occurs or no lease exists for the given address,
|
||||
nothing is sent to stdout a message is sent to stderr and a
|
||||
non-zero error code is returned.
|
||||
|
||||
Requires dnsmasq 2.40 or later and may not work with other DHCP servers.
|
||||
Requires dnsmasq 2.67 or later and may not work with other DHCP servers.
|
||||
|
||||
The address argument is a dotted-quad IP addresses and mandatory.
|
||||
The address argument is a dotted-quad IP addresses and mandatory.
|
||||
.SH LIMITATIONS
|
||||
Only works with IPv4 addresses and DHCP leases.
|
||||
.SH SEE ALSO
|
||||
.BR dnsmasq (8)
|
||||
.SH AUTHOR
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
nothing is sent to stdout a message is sent to stderr and a
|
||||
non-zero error code is returned.
|
||||
|
||||
Requires dnsmasq 2.40 or later.
|
||||
This version requires dnsmasq 2.67 or later.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
@@ -46,6 +46,7 @@
|
||||
#define OPTION_LEASE_TIME 51
|
||||
#define OPTION_OVERLOAD 52
|
||||
#define OPTION_MESSAGE_TYPE 53
|
||||
#define OPTION_REQUESTED_OPTIONS 55
|
||||
#define OPTION_END 255
|
||||
#define DHCPINFORM 8
|
||||
#define DHCP_SERVER_PORT 67
|
||||
@@ -167,6 +168,12 @@ int main(int argc, char **argv)
|
||||
*(p++) = 1;
|
||||
*(p++) = DHCPINFORM;
|
||||
|
||||
/* Explicity request the lease time, it won't be sent otherwise:
|
||||
this is a dnsmasq extension, not standard. */
|
||||
*(p++) = OPTION_REQUESTED_OPTIONS;
|
||||
*(p++) = 1;
|
||||
*(p++) = OPTION_LEASE_TIME;
|
||||
|
||||
*(p++) = OPTION_END;
|
||||
|
||||
dest.sin_family = AF_INET;
|
||||
|
||||
@@ -27,6 +27,8 @@ for ethernet. This encoding is the one used in dnsmasq lease files.
|
||||
The client-id is optional. If it is "*" then it treated as being missing.
|
||||
.SH NOTES
|
||||
MUST be run as root - will fail otherwise.
|
||||
.SH LIMITATIONS
|
||||
Only usable on IPv4 DHCP leases.
|
||||
.SH SEE ALSO
|
||||
.BR dnsmasq (8)
|
||||
.SH AUTHOR
|
||||
|
||||
@@ -255,10 +255,6 @@ int main(int argc, char **argv)
|
||||
struct ifreq ifr;
|
||||
int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
int nl = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
|
||||
struct iovec iov;
|
||||
|
||||
iov.iov_len = 200;
|
||||
iov.iov_base = malloc(iov.iov_len);
|
||||
|
||||
if (argc < 4 || argc > 5)
|
||||
{
|
||||
|
||||
@@ -19,7 +19,8 @@ and avoids startup races with the provider of nameserver information.
|
||||
|
||||
|
||||
Dnsmasq provides one service on the DBus: uk.org.thekelleys.dnsmasq
|
||||
and a single object: /uk/org/thekelleys/dnsmasq
|
||||
and a single object: /uk/org/thekelleys/dnsmasq
|
||||
The name of the service may be changed by giving an argument to --enable-dbus.
|
||||
|
||||
1. METHODS
|
||||
----------
|
||||
@@ -94,6 +95,65 @@ Each call to SetServers completely replaces the set of servers
|
||||
specified by via the DBus, but it leaves any servers specified via the
|
||||
command line or /etc/dnsmasq.conf or /etc/resolv.conf alone.
|
||||
|
||||
SetServersEx
|
||||
------------
|
||||
|
||||
This function is more flexible and the SetServers function, in that it can
|
||||
handle address scoping, port numbers, and is easier for clients to use.
|
||||
|
||||
Returns nothing. Takes a set of arguments representing the new
|
||||
upstream DNS servers to be used by dnsmasq. All addresses (both IPv4 and IPv6)
|
||||
are represented as STRINGS. Each server address may be followed by one or more
|
||||
STRINGS, which are the domains for which the preceding server should be used.
|
||||
|
||||
This function takes an array of STRING arrays, where each inner array represents
|
||||
a set of DNS servers and domains for which those servers may be used. Each
|
||||
string represents a list of upstream DNS servers first, and domains second.
|
||||
Mixing of domains and servers within a the string array is not allowed.
|
||||
|
||||
Examples.
|
||||
|
||||
[
|
||||
["1.2.3.4", "foobar.com"],
|
||||
["1003:1234:abcd::1%eth0", "eng.mycorp.com", "lab.mycorp.com"]
|
||||
]
|
||||
|
||||
is equivalent to
|
||||
|
||||
--server=/foobar.com/1.2.3.4 \
|
||||
--server=/eng.mycorp.com/lab.mycorp.com/1003:1234:abcd::1%eth0
|
||||
|
||||
An IPv4 address of 0.0.0.0 is interpreted as "no address, local only",
|
||||
so
|
||||
|
||||
[ ["0.0.0.0", "local.domain"] ]
|
||||
|
||||
is equivalent to
|
||||
|
||||
--local=/local.domain/
|
||||
|
||||
|
||||
Each call to SetServersEx completely replaces the set of servers
|
||||
specified by via the DBus, but it leaves any servers specified via the
|
||||
command line or /etc/dnsmasq.conf or /etc/resolv.conf alone.
|
||||
|
||||
|
||||
SetDomainServers
|
||||
----------------
|
||||
|
||||
Yes another variation for setting DNS servers, with the capability of
|
||||
SetServersEx, but without using arrays of arrays, which are not
|
||||
sendable with dbus-send. The arguments are an array of strings which
|
||||
are identical to the equivalent arguments --server, so the example
|
||||
for SetServersEx is represented as
|
||||
|
||||
[
|
||||
"/foobar.com/1.2.3.4"
|
||||
"/eng.mycorp.com/lab.mycorp.com/1003:1234:abcd::1%eth0"
|
||||
]
|
||||
|
||||
|
||||
|
||||
2. SIGNALS
|
||||
----------
|
||||
|
||||
|
||||
82
debian/changelog
vendored
82
debian/changelog
vendored
@@ -1,11 +1,93 @@
|
||||
dnsmasq (2.67-1) unstable; urgency=low
|
||||
|
||||
* New upstream.
|
||||
|
||||
-- Simon Kelley <simon@thekelleys.org.uk> Fri, 19 Apr 2013 10:23:31 +0000
|
||||
|
||||
dnsmasq (2.66-1) unstable; urgency=low
|
||||
|
||||
* New upstream.
|
||||
* Add support for noipset in DEB_BUILD_OPTIONS.
|
||||
|
||||
-- Simon Kelley <simon@thekelleys.org.uk> Fri, 22 Feb 2013 21:52:13 +0000
|
||||
|
||||
dnsmasq (2.65-1) unstable; urgency=low
|
||||
|
||||
* New upstream.
|
||||
|
||||
-- Simon Kelley <simon@thekelleys.org.uk> Fri, 14 Dec 2012 11:34:12 +0000
|
||||
|
||||
dnsmasq (2.64-1) unstable; urgency=low
|
||||
|
||||
* New upstream.
|
||||
|
||||
-- Simon Kelley <simon@thekelleys.org.uk> Fri, 21 Sep 2012 17:17:22 +0000
|
||||
|
||||
dnsmasq (2.63-4) unstable; urgency=low
|
||||
|
||||
* Make pid-file creation immune to symlink attacks. (closes: #686484)
|
||||
|
||||
-- Simon Kelley <simon@thekelleys.org.uk> Fri, 21 Sep 2012 17:16:34 +0000
|
||||
|
||||
dnsmasq (2.63-3) unstable; urgency=low
|
||||
|
||||
* Move adduser dependency to dnsmasq-base. (closes: #686694)
|
||||
|
||||
-- Simon Kelley <simon@thekelleys.org.uk> Tue, 4 Sep 2012 21:44:15 +0000
|
||||
|
||||
dnsmasq (2.63-2) unstable; urgency=low
|
||||
|
||||
* Fix version script to report correct version.
|
||||
* Unbotch move of dbus config file by using correct versions in
|
||||
Replaces: and Breaks: lines. (closes: #685204)
|
||||
* Create dnsmasq user in dnsmasq-base so that Dbus doesn't complain if
|
||||
only dnsmasq-base is installed. (closes: #685987)
|
||||
|
||||
-- Simon Kelley <simon@thekelleys.org.uk> Tue, 28 Aug 2012 16:18:35 +0000
|
||||
|
||||
dnsmasq (2.63-1) unstable; urgency=low
|
||||
|
||||
* New upstream.
|
||||
* Move /etc/dbus-1/system.d/dnsmasq.conf from dnsmasq to dnsmasq-base.
|
||||
|
||||
-- Simon Kelley <simon@thekelleys.org.uk> Mon, 11 Jun 2012 21:55:35 +0000
|
||||
|
||||
dnsmasq (2.62-3) unstable; urgency=low
|
||||
|
||||
* Do resolvconf and /etc/default startup logic when
|
||||
starting with systemd. (closes: #675854)
|
||||
|
||||
-- Simon Kelley <simon@thekelleys.org.uk> Mon, 11 Jun 2012 21:50:11 +0000
|
||||
|
||||
dnsmasq (2.62-2) unstable; urgency=low
|
||||
|
||||
* Pass LDFLAGS to make to get hardening in linker.
|
||||
|
||||
-- Simon Kelley <simon@thekelleys.org.uk> Thu, 7 Jun 2012 09:53:43 +0000
|
||||
|
||||
dnsmasq (2.62-1) unstable; urgency=low
|
||||
|
||||
* New upstream.
|
||||
* Use dpkg-buildflags. (Enables hardening).
|
||||
|
||||
-- Simon Kelley <simon@thekelleys.org.uk> Sat, 12 May 2012 15:25:23 +0000
|
||||
|
||||
dnsmasq (2.61-1) unstable; urgency=low
|
||||
|
||||
* New upstream.
|
||||
* Provide "dump-stats" initscript method. (closes: #654656)
|
||||
* Add (empty) build-indep and build-arch rules targets.
|
||||
* Bump standards-version to 3.9.3
|
||||
* Add port option to example dnsmasq.conf (closes: #668386)
|
||||
|
||||
-- Simon Kelley <simon@thekelleys.org.uk> Tue, 6 Mar 2012 19:45:43 +0000
|
||||
|
||||
dnsmasq (2.60-2) unstable; urgency=high
|
||||
|
||||
* Fix DHCPv4 segfault. (closes: #665008)
|
||||
|
||||
-- Simon Kelley <simon@thekelleys.org.uk> Fri, 23 Mar 2012 09:37:23 +0000
|
||||
|
||||
dnsmasq (2.60-1) unstable; urgency=low
|
||||
|
||||
* New upstream.
|
||||
|
||||
1
debian/conffiles
vendored
1
debian/conffiles
vendored
@@ -2,5 +2,4 @@
|
||||
/etc/default/dnsmasq
|
||||
/etc/dnsmasq.conf
|
||||
/etc/resolvconf/update.d/dnsmasq
|
||||
/etc/dbus-1/system.d/dnsmasq.conf
|
||||
/etc/insserv.conf.d/dnsmasq
|
||||
|
||||
9
debian/control
vendored
9
debian/control
vendored
@@ -3,11 +3,11 @@ Section: net
|
||||
Priority: optional
|
||||
Build-depends: gettext, libnetfilter-conntrack-dev [linux-any], libidn11-dev, libdbus-1-dev (>=0.61)
|
||||
Maintainer: Simon Kelley <simon@thekelleys.org.uk>
|
||||
Standards-Version: 3.9.2
|
||||
Standards-Version: 3.9.3
|
||||
|
||||
Package: dnsmasq
|
||||
Architecture: all
|
||||
Depends: netbase, adduser, dnsmasq-base(>= ${source:Version})
|
||||
Depends: netbase, dnsmasq-base(>= ${source:Version})
|
||||
Suggests: resolvconf
|
||||
Conflicts: resolvconf (<<1.15)
|
||||
Description: Small caching DNS proxy and DHCP/TFTP server
|
||||
@@ -22,8 +22,9 @@ Description: Small caching DNS proxy and DHCP/TFTP server
|
||||
|
||||
Package: dnsmasq-base
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends}
|
||||
Conflicts: dnsmasq (<<2.41)
|
||||
Depends: adduser, ${shlibs:Depends}
|
||||
Breaks: dnsmasq (<< 2.63-1~)
|
||||
Replaces: dnsmasq (<< 2.63-1~)
|
||||
Description: Small caching DNS proxy and DHCP/TFTP server
|
||||
This package contains the dnsmasq executable and documentation, but
|
||||
not the infrastructure required to run it as a system daemon. For
|
||||
|
||||
2
debian/copyright
vendored
2
debian/copyright
vendored
@@ -1,4 +1,4 @@
|
||||
dnsmasq is Copyright (c) 2000-2012 Simon Kelley
|
||||
dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
|
||||
It was downloaded from: http://www.thekelleys.org.uk/dnsmasq/
|
||||
|
||||
|
||||
1
debian/dnsmasq-base.conffiles
vendored
Normal file
1
debian/dnsmasq-base.conffiles
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/etc/dbus-1/system.d/dnsmasq.conf
|
||||
24
debian/dnsmasq-base.postinst
vendored
Normal file
24
debian/dnsmasq-base.postinst
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
# Create the dnsmasq user in dnsmasq-base, so that Dbus doesn't complain.
|
||||
|
||||
# create a user to run as (code stolen from dovecot-common)
|
||||
if [ "$1" = "configure" ]; then
|
||||
if [ -z "`id -u dnsmasq 2> /dev/null`" ]; then
|
||||
adduser --system --home /var/lib/misc --gecos "dnsmasq" \
|
||||
--no-create-home --disabled-password \
|
||||
--quiet dnsmasq || true
|
||||
fi
|
||||
|
||||
# Make the directory where we keep the pid file - this
|
||||
# has to be owned by "dnsmasq" so that the file can be unlinked.
|
||||
# This is only actually used by the dnsmasq binary package, not
|
||||
# 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
|
||||
fi
|
||||
fi
|
||||
11
debian/dnsmasq-base.postrm
vendored
Normal file
11
debian/dnsmasq-base.postrm
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
if [ purge = "$1" ]; then
|
||||
if [ -x "$(command -v deluser)" ]; then
|
||||
deluser --quiet --system dnsmasq > /dev/null || true
|
||||
else
|
||||
echo >&2 "not removing dnsmasq system account because deluser command was not found"
|
||||
fi
|
||||
rm -rf /var/run/dnsmasq
|
||||
fi
|
||||
21
debian/init
vendored
21
debian/init
vendored
@@ -259,6 +259,27 @@ case "$1" in
|
||||
dump-stats)
|
||||
kill -s USR1 `cat /var/run/dnsmasq/$NAME.pid`
|
||||
;;
|
||||
systemd-start-resolvconf)
|
||||
start_resolvconf
|
||||
;;
|
||||
systemd-stop-resolvconf)
|
||||
stop_resolvconf
|
||||
;;
|
||||
systemd-exec)
|
||||
# --pid-file without argument disables writing a PIDfile, we don't need one with sytemd.
|
||||
# Enable DBus by default because we use DBus activation with systemd.
|
||||
exec $DAEMON --keep-in-foreground --pid-file --enable-dbus \
|
||||
${MAILHOSTNAME:+ -m $MAILHOSTNAME} \
|
||||
${MAILTARGET:+ -t $MAILTARGET} \
|
||||
${DNSMASQ_USER:+ -u $DNSMASQ_USER} \
|
||||
${DNSMASQ_INTERFACES:+ $DNSMASQ_INTERFACES} \
|
||||
${DHCP_LEASE:+ -l $DHCP_LEASE} \
|
||||
${DOMAIN_SUFFIX:+ -s $DOMAIN_SUFFIX} \
|
||||
${RESOLV_CONF:+ -r $RESOLV_CONF} \
|
||||
${CACHESIZE:+ -c $CACHESIZE} \
|
||||
${CONFIG_DIR:+ -7 $CONFIG_DIR} \
|
||||
${DNSMASQ_OPTS:+ $DNSMASQ_OPTS}
|
||||
;;
|
||||
*)
|
||||
echo "Usage: /etc/init.d/$NAME {start|stop|restart|force-reload|dump-stats|status}" >&2
|
||||
exit 3
|
||||
|
||||
27
debian/postinst
vendored
27
debian/postinst
vendored
@@ -1,27 +1,6 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
# create a user to run as (code stolen from dovecot-common)
|
||||
if [ "$1" = "configure" ]; then
|
||||
if [ -z "`id -u dnsmasq 2> /dev/null`" ]; then
|
||||
adduser --system --home /var/lib/misc --gecos "dnsmasq" \
|
||||
--no-create-home --disabled-password \
|
||||
--quiet dnsmasq || true
|
||||
fi
|
||||
|
||||
# Make the directory where we keep the pid file - this
|
||||
# has to be owned by "dnsmasq" do that the file can be unlinked.
|
||||
if [ ! -d /var/run/dnsmasq ]; then
|
||||
mkdir /var/run/dnsmasq
|
||||
chown dnsmasq:nogroup /var/run/dnsmasq
|
||||
fi
|
||||
|
||||
# handle new location of pidfile during an upgrade
|
||||
if [ -e /var/run/dnsmasq.pid ]; then
|
||||
mv /var/run/dnsmasq.pid /var/run/dnsmasq
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -x /etc/init.d/dnsmasq ]; then
|
||||
update-rc.d dnsmasq defaults 15 85 >/dev/null
|
||||
|
||||
@@ -40,10 +19,4 @@ if [ -x /etc/init.d/dnsmasq ]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# dpkg can botch the change of /usr/share/doc/dnsmasq from
|
||||
# directory to symlink. Fix up here.
|
||||
if [ ! -h /usr/share/doc/dnsmasq ] && { rmdir /usr/share/doc/dnsmasq; }; then
|
||||
cd /usr/share/doc/
|
||||
ln -s /usr/share/doc/dnsmasq-base dnsmasq
|
||||
fi
|
||||
|
||||
|
||||
6
debian/postrm
vendored
6
debian/postrm
vendored
@@ -3,10 +3,4 @@ set -e
|
||||
|
||||
if [ purge = "$1" ]; then
|
||||
update-rc.d dnsmasq remove >/dev/null
|
||||
if [ -x "$(command -v deluser)" ]; then
|
||||
deluser --quiet --system dnsmasq > /dev/null || true
|
||||
else
|
||||
echo >&2 "not removing dnsmasq system account because deluser command was not found"
|
||||
fi
|
||||
rm -rf /var/run/dnsmasq
|
||||
fi
|
||||
|
||||
1
debian/readme
vendored
1
debian/readme
vendored
@@ -59,6 +59,7 @@ Notes on configuring dnsmasq as packaged for Debian.
|
||||
noipv6 : omit IPv6 support.
|
||||
nodbus : omit DBus support.
|
||||
noconntrack : omit connection tracking support.
|
||||
noipset : omit IPset support.
|
||||
nortc : compile alternate mode suitable for systems without an RTC.
|
||||
noi18n : omit translations and internationalisation support.
|
||||
noidn : omit international domain name support, must be
|
||||
|
||||
27
debian/rules
vendored
27
debian/rules
vendored
@@ -11,14 +11,14 @@
|
||||
|
||||
package=dnsmasq-base
|
||||
|
||||
# policy manual, section 10.1
|
||||
ifneq (,$(filter noopt,$(DEB_BUILD_OPTIONS)))
|
||||
CFLAGS = -g -O0 -Wall -W
|
||||
else
|
||||
CFLAGS = -g -O2 -Wall -W
|
||||
endif
|
||||
CFLAGS = $(shell export DEB_BUILD_OPTIONS=$(DEB_BUILD_OPTIONS); dpkg-buildflags --get CFLAGS)
|
||||
CFLAGS += $(shell dpkg-buildflags --get CPPFLAGS)
|
||||
CFLAGS += -Wall -W
|
||||
|
||||
LDFLAGS = $(shell dpkg-buildflags --get LDFLAGS)
|
||||
|
||||
COPTS =
|
||||
|
||||
TARGET = install-i18n
|
||||
|
||||
DEB_BUILD_ARCH_OS := $(shell dpkg-architecture -qDEB_BUILD_ARCH_OS)
|
||||
@@ -33,6 +33,10 @@ ifeq ($(DEB_BUILD_ARCH_OS),linux)
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq (,$(filter noipset,$(DEB_BUILD_OPTIONS)))
|
||||
COPTS += -DNO_IPSET
|
||||
endif
|
||||
|
||||
ifneq (,$(filter nodhcp6,$(DEB_BUILD_OPTIONS)))
|
||||
COPTS += -DNO_DHCP6
|
||||
endif
|
||||
@@ -85,7 +89,6 @@ binary-indep: checkroot
|
||||
-d debian/daemon/etc/resolvconf/update.d \
|
||||
-d debian/daemon/usr/lib/resolvconf/dpkg-event.d \
|
||||
-d debian/daemon/etc/default \
|
||||
-d debian/daemon/etc/dbus-1/system.d \
|
||||
-d debian/daemon/lib/systemd/system \
|
||||
-d debian/daemon/etc/insserv.conf.d
|
||||
install -m 644 debian/conffiles debian/daemon/DEBIAN
|
||||
@@ -96,7 +99,6 @@ binary-indep: checkroot
|
||||
install -m 644 debian/default debian/daemon/etc/default/dnsmasq
|
||||
install -m 644 dnsmasq.conf.example debian/daemon/etc/dnsmasq.conf
|
||||
install -m 644 debian/readme.dnsmasq.d debian/daemon/etc/dnsmasq.d/README
|
||||
install -m 644 debian/dbus.conf debian/daemon/etc/dbus-1/system.d/dnsmasq.conf
|
||||
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
|
||||
@@ -111,11 +113,12 @@ binary-arch: checkroot
|
||||
rm -rf debian/base
|
||||
install -m 755 \
|
||||
-d debian/base/DEBIAN \
|
||||
-d debian/base/etc/dbus-1/system.d \
|
||||
-d debian/base/usr/share/doc/$(package) \
|
||||
-d debian/base/usr/share/doc/$(package)/examples \
|
||||
-d debian/base/var/run \
|
||||
-d debian/base/var/lib/misc
|
||||
make $(TARGET) PREFIX=/usr DESTDIR=`pwd`/debian/base CFLAGS="$(CFLAGS)" COPTS="$(COPTS)" CC=gcc
|
||||
make $(TARGET) PREFIX=/usr DESTDIR=`pwd`/debian/base CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" COPTS="$(COPTS)" CC=gcc
|
||||
ifeq (,$(findstring nodocs,$(DEB_BUILD_OPTIONS)))
|
||||
install -m 644 doc.html debian/base/usr/share/doc/$(package)/.
|
||||
install -m 644 setup.html debian/base/usr/share/doc/$(package)/.
|
||||
@@ -129,10 +132,14 @@ ifeq (,$(findstring nodocs,$(DEB_BUILD_OPTIONS)))
|
||||
install -m 644 dbus/DBus-interface debian/base/usr/share/doc/$(package)/.
|
||||
gzip -9 debian/base/usr/share/doc/$(package)/DBus-interface
|
||||
endif
|
||||
install -m 644 debian/dnsmasq-base.conffiles debian/base/DEBIAN/conffiles
|
||||
install -m 755 debian/dnsmasq-base.postinst debian/base/DEBIAN/postinst
|
||||
install -m 755 debian/dnsmasq-base.postrm debian/base/DEBIAN/postrm
|
||||
install -m 644 debian/changelog debian/base/usr/share/doc/$(package)/changelog.Debian
|
||||
gzip -9 debian/base/usr/share/doc/$(package)/changelog.Debian
|
||||
install -m 644 debian/readme debian/base/usr/share/doc/$(package)/README.Debian
|
||||
install -m 644 debian/copyright debian/base/usr/share/doc/$(package)/copyright
|
||||
install -m 644 debian/dbus.conf debian/base/etc/dbus-1/system.d/dnsmasq.conf
|
||||
gzip -9 debian/base/usr/share/man/man8/dnsmasq.8
|
||||
for f in debian/base/usr/share/man/*; do \
|
||||
if [ -f $$f/man8/dnsmasq.8 ]; then \
|
||||
@@ -155,7 +162,7 @@ ifeq ($(DEB_BUILD_ARCH_OS),linux)
|
||||
-d debian/utils/usr/share/man/man1 \
|
||||
-d debian/utils/usr/bin \
|
||||
-d debian/utils/usr/share/doc/dnsmasq-utils
|
||||
make -C contrib/wrt PREFIX=/usr DESTDIR=`pwd`/debian/utils CFLAGS="$(CFLAGS)" COPTS="$(COPTS)" CC=gcc
|
||||
make -C contrib/wrt PREFIX=/usr DESTDIR=`pwd`/debian/utils CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" COPTS="$(COPTS)" CC=gcc
|
||||
install -m 755 contrib/wrt/dhcp_release debian/utils/usr/bin/dhcp_release
|
||||
install -m 644 contrib/wrt/dhcp_release.1 debian/utils/usr/share/man/man1/dhcp_release.1
|
||||
gzip -9 debian/utils/usr/share/man/man1/dhcp_release.1
|
||||
|
||||
30
debian/systemd.service
vendored
30
debian/systemd.service
vendored
@@ -8,22 +8,24 @@ BusName=uk.org.thekelleys.dnsmasq
|
||||
# Test the config file and refuse starting if it is not valid.
|
||||
ExecStartPre=/usr/sbin/dnsmasq --test
|
||||
|
||||
# Enable DBus by default because we use DBus activation.
|
||||
# We run dnsmasq via the /etc/init.d/dnsmasq script which acts as a
|
||||
# wrapper picking up extra configuration files and then execs dnsmasq
|
||||
# itself, when called with the "systemd-exec" function.
|
||||
#
|
||||
# Drop privileges and become the 'dnsmasq' user. It is recommended by dnsmasq
|
||||
# upstream to run dnsmasq as an isolated user that does not run any other
|
||||
# processes, owns no files and has no shell. The default 'nobody' user has a
|
||||
# shell and might be used for other processes.
|
||||
# It also adds the command-line flags
|
||||
# --keep-in-foreground --pid-file --enable-dbus
|
||||
# to disable writing a pid-file (not needed with systemd) and
|
||||
# enable DBus by default because we use DBus activation.
|
||||
#
|
||||
# Debian-specific: add /etc/dnsmasq.d to config search path (with the exception
|
||||
# of .dpkg-*). Packages such as libvirt leave config files there.
|
||||
#
|
||||
# --pid-file without argument disables writing a PIDfile, we don't need one.
|
||||
ExecStart=/usr/sbin/dnsmasq -k \
|
||||
--enable-dbus \
|
||||
--user=dnsmasq \
|
||||
-7 /etc/dnsmasq.d,.dpkg-dist,.dpkg-old,.dpkg-new \
|
||||
--pid-file
|
||||
ExecStart=/etc/init.d/dnsmasq systemd-exec
|
||||
|
||||
# The systemd-*-resolvconf functions configure (and deconfigure)
|
||||
# resolvconf to work with the dnsmasq DNS server. They're called liek
|
||||
# this to get correct error handling (ie don't start-resolvconf if the
|
||||
# dnsmasq daemon fails to start.
|
||||
ExecStartPost=/etc/init.d/dnsmasq systemd-start-resolvconf
|
||||
ExecStop=/etc/init.d/dnsmasq systemd-stop-resolvconf
|
||||
|
||||
|
||||
ExecReload=/bin/kill -HUP $MAINPID
|
||||
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
# as the long options legal on the command line. See
|
||||
# "/usr/sbin/dnsmasq --help" or "man 8 dnsmasq" for details.
|
||||
|
||||
# Listen on this specific port instead of the standard DNS port
|
||||
# (53). Setting this to zero completely disables DNS function,
|
||||
# leaving only DHCP and/or TFTP.
|
||||
#port=5353
|
||||
|
||||
# The following two options make you a better netizen, since they
|
||||
# tell dnsmasq to filter out queries which the public DNS cannot
|
||||
# answer, and which load the servers (especially the root servers)
|
||||
@@ -64,6 +69,10 @@
|
||||
# --address (and --server) work with IPv6 addresses too.
|
||||
#address=/www.thekelleys.org.uk/fe80::20d:60ff:fe36:f83
|
||||
|
||||
# Add the IPs of all queries to yahoo.com, google.com, and their
|
||||
# subdomains to the vpn and search ipsets:
|
||||
#ipset=/yahoo.com/google.com/vpn,search
|
||||
|
||||
# You can control how dnsmasq talks to a server: this forces
|
||||
# queries to 10.1.2.3 to be routed via eth1
|
||||
# server=10.1.2.3@eth1
|
||||
@@ -169,14 +178,32 @@
|
||||
# hosts. Use the DHCPv4 lease to derive the name, network segment and
|
||||
# MAC address and assume that the host will also have an
|
||||
# IPv6 address calculated using the SLAAC alogrithm.
|
||||
# This will generate an erroneous AAAA record if a host is using privacy
|
||||
# extensions or does not support IPv6. Use with care.
|
||||
#dhcp-range=1234::, ra-names
|
||||
|
||||
# Do Router Advertisements, BUT NOT DHCP for this subnet.
|
||||
# Set the lifetime to 46 hours. (Note: minimum lifetime is 2 hours.)
|
||||
#dhcp-range=1234::, ra-only, 48h
|
||||
|
||||
# Do DHCP and Router Advertisements for this subnet. Set the A bit in the RA
|
||||
# so that clients can use SLAAC addresses as well as DHCP ones.
|
||||
#dhcp-range=1234::2, 1234::500, slaac
|
||||
|
||||
# Do Router Advertisements and stateless DHCP for this subnet. Clients will
|
||||
# not get addresses from DHCP, but they will get other configuration information.
|
||||
# They will use SLAAC for addresses.
|
||||
#dhcp-range=1234::, ra-stateless
|
||||
|
||||
# Do stateless DHCP, SLAAC, and generate DNS names for SLAAC addresses
|
||||
# from DHCPv4 leases.
|
||||
#dhcp-range=1234::, ra-stateless, ra-names
|
||||
|
||||
# Do router advertisements for all subnets where we're doing DHCPv6
|
||||
# Unless overriden by ra-stateless, ra-names, et al, the router
|
||||
# advertisements will have the M and O bits set, so that the clients
|
||||
# get addresses and configuration from DHCPv6, and the A bit reset, so the
|
||||
# clients don't use SLAAC addresses.
|
||||
#enable-ra
|
||||
|
||||
# Supply parameters for specified hosts using DHCP. There are lots
|
||||
# of valid alternatives, so we will give examples of each. Note that
|
||||
# IP addresses DO NOT have to be in the range given above, they just
|
||||
@@ -303,6 +330,9 @@
|
||||
# dnsmasq and another.
|
||||
#dhcp-option=option6:dns-server,[::],[1234::88]
|
||||
|
||||
# Ask client to poll for option changes every six hours. (RFC4242)
|
||||
#dhcp-option=option6:information-refresh-time,6h
|
||||
|
||||
# Set the NTP time server address to be the same machine as
|
||||
# is running dnsmasq
|
||||
#dhcp-option=42,0.0.0.0
|
||||
@@ -454,7 +484,7 @@
|
||||
#tftp-no-blocksize
|
||||
|
||||
# Set the boot file name only when the "red" tag is set.
|
||||
#dhcp-boot=net:red,pxelinux.red-net
|
||||
#dhcp-boot=tag:red,pxelinux.red-net
|
||||
|
||||
# An example of dhcp-boot with an external TFTP server: the name and IP
|
||||
# address of the server are given after the filename.
|
||||
@@ -499,7 +529,7 @@
|
||||
# If you want to disable negative caching, uncomment this.
|
||||
#no-negcache
|
||||
|
||||
# Normally responses which come form /etc/hosts and the DHCP lease
|
||||
# Normally responses which come from /etc/hosts and the DHCP lease
|
||||
# file have Time-To-Live set as zero, which conventionally means
|
||||
# do not cache further. If you are happy to trade lower load on the
|
||||
# server for potentially stale date, you can set a time-to-live (in
|
||||
@@ -595,6 +625,6 @@
|
||||
# Log lots of extra information about DHCP transactions.
|
||||
#log-dhcp
|
||||
|
||||
# Include a another lot of configuration options.
|
||||
# Include another lot of configuration options.
|
||||
#conf-file=/etc/dnsmasq.more.conf
|
||||
#conf-dir=/etc/dnsmasq.d
|
||||
|
||||
BIN
logo/favicon.ico
BIN
logo/favicon.ico
Binary file not shown.
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
517
man/dnsmasq.8
517
man/dnsmasq.8
@@ -6,24 +6,28 @@ dnsmasq \- A lightweight DHCP and caching DNS server.
|
||||
.I [OPTION]...
|
||||
.SH "DESCRIPTION"
|
||||
.BR dnsmasq
|
||||
is a lightweight DNS, TFTP and DHCP server. It is intended to provide
|
||||
is a lightweight DNS, TFTP, PXE, router advertisement and DHCP server. It is intended to provide
|
||||
coupled DNS and DHCP service to a LAN.
|
||||
.PP
|
||||
Dnsmasq accepts DNS queries and either answers them from a small, local,
|
||||
cache or forwards them to a real, recursive, DNS server. It loads the
|
||||
contents of /etc/hosts so that local hostnames
|
||||
which do not appear in the global DNS can be resolved and also answers
|
||||
DNS queries for DHCP configured hosts.
|
||||
DNS queries for DHCP configured hosts. It can also act as the authoritative DNS server for one or more domains, allowing local names to appear in the global DNS.
|
||||
.PP
|
||||
The dnsmasq DHCP server supports static address assignments and multiple
|
||||
networks. It automatically
|
||||
sends a sensible default set of DHCP options, and can be configured to
|
||||
send any desired set of DHCP options, including vendor-encapsulated
|
||||
options. It includes a secure, read-only,
|
||||
TFTP server to allow net/PXE boot of DHCP hosts and also supports BOOTP.
|
||||
TFTP server to allow net/PXE boot of DHCP hosts and also supports BOOTP. The PXE support is full featured, and includes a proxy mode which supplies PXE information to clients whilst DHCP address allocation is done by another server.
|
||||
.PP
|
||||
Dnsmasq
|
||||
supports IPv6 for all functions and a minimal router-advertisemnet daemon.
|
||||
The dnsmasq DHCPv6 server provides the same set of features as the
|
||||
DHCPv4 server, and in addition, it includes router advertisements and
|
||||
a neat feature which allows nameing for clients which use DHCPv4 and
|
||||
stateless autoconfiguration only for IPv6 configuration. There is support for doing address allocation (both DHCPv6 and RA) from subnets which are dynamically delegated via DHCPv6 prefix delegation.
|
||||
.PP
|
||||
Dnsmasq is coded with small embedded systems in mind. It aims for the smallest possible memory footprint compatible with the supported functions, and allows uneeded functions to be omitted from the compiled binary.
|
||||
.SH OPTIONS
|
||||
Note that in general missing parameters are allowed and switch off
|
||||
functions, for instance "--pid-file" disables writing a PID file. On
|
||||
@@ -71,6 +75,12 @@ maximum TTL will be given to clients instead of the true TTL value if it is
|
||||
lower. The true TTL value is however kept in the cache to avoid flooding
|
||||
the upstream DNS servers.
|
||||
.TP
|
||||
.B --max-cache-ttl=<time>
|
||||
Set a maximum TTL value for entries in the cache.
|
||||
.TP
|
||||
.B --auth-ttl=<time>
|
||||
Set the TTL value returned in answers from the authoritative server.
|
||||
.TP
|
||||
.B \-k, --keep-in-foreground
|
||||
Do not go into the background at startup but otherwise run as
|
||||
normal. This is intended for use when dnsmasq is run under daemontools
|
||||
@@ -80,7 +90,9 @@ or launchd.
|
||||
Debug mode: don't fork to the background, don't write a pid file,
|
||||
don't change user id, generate a complete cache dump on receipt on
|
||||
SIGUSR1, log to stderr as well as syslog, don't fork new processes
|
||||
to handle TCP queries.
|
||||
to handle TCP queries. Note that this option is for use in debugging
|
||||
only, to stop dnsmasq daemonising in production, use
|
||||
.B -k.
|
||||
.TP
|
||||
.B \-q, --log-queries
|
||||
Log the results of DNS queries handled by dnsmasq. Enable a full cache dump on receipt of SIGUSR1.
|
||||
@@ -162,7 +174,12 @@ options. IP alias interfaces (eg "eth1:0") cannot be used with
|
||||
.B --interface
|
||||
or
|
||||
.B --except-interface
|
||||
options, use --listen-address instead.
|
||||
options, use --listen-address instead. A simple wildcard, consisting
|
||||
of a trailing '*', can be used in
|
||||
.B \--interface
|
||||
and
|
||||
.B \--except-interface
|
||||
options.
|
||||
.TP
|
||||
.B \-I, --except-interface=<interface name>
|
||||
Do not listen on the specified interface. Note that the order of
|
||||
@@ -173,6 +190,16 @@ and
|
||||
options does not matter and that
|
||||
.B --except-interface
|
||||
options always override the others.
|
||||
.TP
|
||||
.B --auth-server=<domain>,<interface>|<ip-address>
|
||||
Enable DNS authoritative mode for queries arriving at an interface or address. Note that the interface or address
|
||||
need not be mentioned in
|
||||
.B --interface
|
||||
or
|
||||
.B --listen-address
|
||||
configuration, indeed
|
||||
.B --auth-server
|
||||
will overide these and provide a different DNS service on the specified interface. The <domain> is the "glue record". It should resolve in the global DNS to a A and/or AAAA record which points to the address dnsmasq is listening on.
|
||||
.TP
|
||||
.B \-2, --no-dhcp-interface=<interface name>
|
||||
Do not provide DHCP or TFTP on the specified interface, but do provide DNS service.
|
||||
@@ -204,6 +231,17 @@ running another nameserver (or another instance of dnsmasq) on the
|
||||
same machine. Setting this option also enables multiple instances of
|
||||
dnsmasq which provide DHCP service to run in the same machine.
|
||||
.TP
|
||||
.B --bind-dynamic
|
||||
Enable a network mode which is a hybrid between
|
||||
.B --bind-interfaces
|
||||
and the default. Dnsmasq binds the address of individual interfaces,
|
||||
allowing multiple dnsmasq instances, but if new interfaces or
|
||||
addresses appear, it automatically listens on those (subject to any
|
||||
access-control configuration). This makes dynamically created
|
||||
interfaces work in the same way as the default. Implementing this
|
||||
option requires non-standard networking APIs and it is only available
|
||||
under Linux. On other platforms it falls-back to --bind-interfaces mode.
|
||||
.TP
|
||||
.B \-y, --localise-queries
|
||||
Return answers to DNS queries from /etc/hosts which depend on the interface over which the query was
|
||||
received. If a name in /etc/hosts has more than one address associated with
|
||||
@@ -260,11 +298,13 @@ time is the one used.
|
||||
Don't read /etc/resolv.conf. Get upstream servers only from the command
|
||||
line or the dnsmasq configuration file.
|
||||
.TP
|
||||
.B \-1, --enable-dbus
|
||||
.B \-1, --enable-dbus[=<service-name>]
|
||||
Allow dnsmasq configuration to be updated via DBus method calls. The
|
||||
configuration which can be changed is upstream DNS servers (and
|
||||
corresponding domains) and cache clear. Requires that dnsmasq has
|
||||
been built with DBus support.
|
||||
been built with DBus support. If the service name is given, dnsmasq
|
||||
provides service at that name, rather than the default which is
|
||||
.B uk.org.thekelleys.dnsmasq
|
||||
.TP
|
||||
.B \-o, --strict-order
|
||||
By default, dnsmasq will send queries to any of the upstream servers
|
||||
@@ -380,6 +420,12 @@ additional facility that /#/ matches any domain. Thus
|
||||
answered from /etc/hosts or DHCP and not sent to an upstream
|
||||
nameserver by a more specific --server directive.
|
||||
.TP
|
||||
.B --ipset=/<domain>/[domain/]<ipset>[,<ipset>]
|
||||
Places the resolved IP addresses of queries for the specified domains
|
||||
in the specified netfilter ip sets. Domains and subdomains are matched
|
||||
in the same way as --address. These ip sets must already exist. See
|
||||
ipset(8) for more details.
|
||||
.TP
|
||||
.B \-m, --mx-host=<mx name>[[,<hostname>],<preference>]
|
||||
Return an MX record named <mx name> pointing to the given hostname (if
|
||||
given), or
|
||||
@@ -416,6 +462,24 @@ 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>]
|
||||
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
|
||||
.B host-record
|
||||
and therefore be assigned more than one address. Only the first
|
||||
address creates a PTR record linking the address to the name. This is
|
||||
the same rule as is used reading hosts-files.
|
||||
.B host-record
|
||||
options are considered to be read before host-files, so a name
|
||||
appearing there inhibits PTR-record creation if it appears in
|
||||
hosts-file also. Unlike hosts-files, names are not expanded, even when
|
||||
.B expand-hosts
|
||||
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
|
||||
.TP
|
||||
.B \-Y, --txt-record=<name>[[,<text>],<text>]
|
||||
Return a TXT DNS record. The value of TXT record is a set of strings,
|
||||
so any number may be included, delimited by commas; use quotes to put
|
||||
@@ -432,10 +496,18 @@ Return an NAPTR DNS record, as specified in RFC3403.
|
||||
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
|
||||
hosts files) or from DHCP. If the target does not satisfy this
|
||||
hosts files), from DHCP or from another
|
||||
.B --cname.
|
||||
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.
|
||||
.TP
|
||||
.B --dns-rr=<name>,<RR-number>,[<hex data>]
|
||||
Return an arbitrary DNS Resource Record. The number is the type of the
|
||||
record (which is always in the C_IN class). The value of the record is
|
||||
given by the hex data, which may be of the form 01:23:45 or 01 23 45 or
|
||||
012345 or any mixture of these.
|
||||
.TP
|
||||
.B --interface-name=<name>,<interface>
|
||||
Return a DNS record associating the name with the primary address on
|
||||
the given interface. This flag specifies an A record for the given
|
||||
@@ -447,6 +519,22 @@ the name. More than one name may be associated with an interface
|
||||
address by repeating the flag; in that case the first instance is used
|
||||
for the reverse address-to-name mapping.
|
||||
.TP
|
||||
.B --synth-domain=<domain>,<address range>[,<prefix>]
|
||||
Create artificial A/AAAA and PTR records for an address range. The
|
||||
records use the address, with periods (or colons for IPv6) replaced
|
||||
with dashes.
|
||||
|
||||
An example should make this clearer.
|
||||
.B --synth-domain=thekelleys.org.uk,192.168.0.0/24,internal-
|
||||
will result in a query for internal-192-168-0-56.thekelleys.org.uk returning
|
||||
192.168.0.56 and a reverse query vice versa. The same applies to IPv6,
|
||||
but IPv6 addresses may start with '::'
|
||||
but DNS labels may not start with '-' so in this case if no prefix is
|
||||
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
|
||||
Add the MAC address of the requestor to DNS queries which are
|
||||
forwarded upstream. This may be used to DNS filtering by the upstream
|
||||
@@ -484,6 +572,30 @@ If you use the first DNSSEC mode, validating resolvers in clients,
|
||||
this option is not required. Dnsmasq always returns all the data
|
||||
needed for a client to do validation itself.
|
||||
.TP
|
||||
.B --auth-zone=<domain>[,<subnet>[,<subnet>.....]]
|
||||
Define a DNS zone for which dnsmasq acts as authoritative server. Locally defined DNS records which are in the domain
|
||||
will be served, except that A and AAAA records must be in one of the
|
||||
specified subnets, or in a subnet corresponding to a constructed DHCP
|
||||
range. The subnet(s) are also used to define in-addr.arpa and
|
||||
ipv6.arpa domains which are served for reverse-DNS queries. For IPv4
|
||||
subnets, the prefix length is limited to the values 8, 16 or 24.
|
||||
.TP
|
||||
.B --auth-soa=<serial>[,<hostmaster>[,<refresh>[,<retry>[,<expiry>]]]]
|
||||
Specify fields in the SOA record associated with authoritative
|
||||
zones. Note that this is optional, all the values are set to sane defaults.
|
||||
.TP
|
||||
.B --auth-sec-servers=<domain>[,<domain>[,<domain>...]]
|
||||
Specify any secondary servers for a zone for which dnsmasq is
|
||||
authoritative. These servers must be configured to get zone data from
|
||||
dnsmasq by zone transfer, and answer queries for the same
|
||||
authoritative zones as dnsmasq.
|
||||
.TP
|
||||
.B --auth-peer=<ip-address>[,<ip-address>[,<ip-address>...]]
|
||||
Specify the addresses of secondary servers which are allowed to
|
||||
initiate zone transfer (AXFR) requests for zones for which dnsmasq is
|
||||
authoritative. If this option is not given, then AXFR requests will be
|
||||
accepted from any secondary.
|
||||
.TP
|
||||
.B --conntrack
|
||||
Read the Linux connection track mark associated with incoming DNS
|
||||
queries and set the same mark value on upstream traffic used to answer
|
||||
@@ -494,9 +606,9 @@ 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=[interface:<interface>,][tag:<tag>[,tag:<tag>],][set:<tag],]<start-addr>,<end-addr>[,<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=[interface:<interface>,][tag:<tag>[,tag:<tag>],][set:<tag],]<start-IPv6addr>,<end-IPv6addr>[,<prefix-len>][,<lease time>]
|
||||
.B \-F, --dhcp-range=[tag:<tag>[,tag:<tag>],][set:<tag>,]<start-IPv6addr>[,<end-IPv6addr>|constructor:<interface>][,<mode>][,<prefix-len>][,<lease time>]
|
||||
|
||||
Enable the DHCP server. Addresses will be given out from the range
|
||||
<start-addr> to <end-addr> and from statically defined addresses given
|
||||
@@ -506,8 +618,12 @@ options. If the lease time is given, then leases
|
||||
will be given for that length of time. The lease time is in seconds,
|
||||
or minutes (eg 45m) or hours (eg 1h) or "infinite". If not given,
|
||||
the default lease time is one hour. The
|
||||
minimum lease time is two minutes. This
|
||||
option may be repeated, with different addresses, to enable DHCP
|
||||
minimum lease time is two minutes. For IPv6 ranges, the lease time
|
||||
maybe "deprecated"; this sets the preferred lifetime sent in a DHCP
|
||||
lease or router advertisement to zero, which causes clients to use
|
||||
other addresses, if available, for new connections as a prelude to renumbering.
|
||||
|
||||
This option may be repeated, with different addresses, to enable DHCP
|
||||
service to more than one network. For directly connected networks (ie,
|
||||
networks on which the machine running dnsmasq has an interface) the
|
||||
netmask is optional: dnsmasq will determine it from the interface
|
||||
@@ -524,6 +640,14 @@ given, this defaults to 64. Unlike the IPv4 case, the prefix length is not
|
||||
automatically derived from the interface configuration. The mimimum
|
||||
size of the prefix length is 64.
|
||||
|
||||
IPv6 (only) supports another type of range. In this, the start address and optional end address contain only the network part (ie ::1) and they are followed by
|
||||
.B constructor:<interface>.
|
||||
This forms a template which describes how to create ranges, based on the addresses assigned to the interface. For instance
|
||||
|
||||
.B --dhcp-range=::1,::400,constructor:eth0
|
||||
|
||||
will look for addresses of the form <network>::1 on eth0 and then create a range from <network>::1 to <network>::400. If the interface is assigned more than one network, then the corresponding ranges will be automatically created, and then deprecated and finally removed again as the address is deprecated and then deleted. The interface name may have a final "*" wildcard.
|
||||
|
||||
The optional
|
||||
.B set:<tag>
|
||||
sets an alphanumeric label which marks this network so that
|
||||
@@ -532,45 +656,63 @@ When it is prefixed with 'tag:' instead, then its meaning changes from setting
|
||||
a tag to matching it. Only one tag may be set, but more than one tag
|
||||
may be matched.
|
||||
|
||||
The end address may be replaced by the keyword
|
||||
The optional <mode> keyword may be
|
||||
.B static
|
||||
which tells dnsmasq to enable DHCP for the network specified, but not
|
||||
to dynamically allocate IP addresses: only hosts which have static
|
||||
addresses given via
|
||||
.B dhcp-host
|
||||
or from /etc/ethers will be served.
|
||||
or from /etc/ethers will be served. A static-only subnet with address
|
||||
all zeros may be used as a "catch-all" address to enable replies to all
|
||||
Information-request packets on a subnet which is provided with
|
||||
stateless DHCPv6, ie
|
||||
.B --dhcp=range=::,static
|
||||
|
||||
The end address may be replaced by
|
||||
the keyword
|
||||
For IPv4, the <mode> may be
|
||||
.B proxy
|
||||
in which case dnsmasq will provide proxy-DHCP on the specified
|
||||
subnet. (See
|
||||
.B pxe-prompt
|
||||
and
|
||||
.B pxe-service
|
||||
for details, applies to IPv4 only.)
|
||||
for details.)
|
||||
|
||||
For IPv6, the mode may be some combination of
|
||||
.B ra-only, slaac, ra-names, ra-stateless.
|
||||
|
||||
The end address may be replaced by
|
||||
the keyword
|
||||
.B ra-only
|
||||
which tells dnsmasq to offer Router Advertisement only on this subnet,
|
||||
and not DHCP. This applies to IPv6 only, see
|
||||
.B enable-ra
|
||||
for details. Instead, the keyword
|
||||
.B ra-names
|
||||
may be used. This does the same at ra-only, but also enables a mode
|
||||
which gives DNS names to dual-stack hosts which do SLAAC for
|
||||
IPv6. Dnsmasq uses the hosts IPv4 lease to derive the name, network
|
||||
segment and MAC address and assumes that the host will also have an
|
||||
IPv6 address calculated using the SLAAC alogrithm, on the same network
|
||||
segment. An AAAA record is added to the DNS for this IPv6
|
||||
address. Note that this is only happens for directly-connected
|
||||
networks, (not one doing DHCP via a relay) and it will generate an
|
||||
erroneous AAAA record if a host is using privacy extensions or does
|
||||
not support IPv6. Use with care.
|
||||
tells dnsmasq to offer Router Advertisement only on this subnet,
|
||||
and not DHCP.
|
||||
|
||||
.B slaac
|
||||
tells dnsmasq to offer Router Advertisement on this subnet and to set
|
||||
the A bit in the router advertisement, so that the client will use
|
||||
SLAAC addresses. When used with a DHCP range or static DHCP address
|
||||
this results in the client having both a DHCP-assigned and a SLAAC
|
||||
address.
|
||||
|
||||
.B ra-stateless
|
||||
sends router advertisements with the O and A bits set, and provides a
|
||||
stateless DHCP service. The client will use a SLAAC address, and use
|
||||
DHCP for other configuration information.
|
||||
|
||||
.B ra-names
|
||||
enables a mode
|
||||
which gives DNS names to dual-stack hosts which do SLAAC for
|
||||
IPv6. Dnsmasq uses the host's IPv4 lease to derive the name, network
|
||||
segment and MAC address and assumes that the host will also have an
|
||||
IPv6 address calculated using the SLAAC algorithm, on the same network
|
||||
segment. The address is pinged, and if a reply is received, an AAAA
|
||||
record is added to the DNS for this IPv6
|
||||
address. Note that this is only happens for directly-connected
|
||||
networks, (not one doing DHCP via a relay) and it will not work
|
||||
if a host is using privacy extensions.
|
||||
.B ra-names
|
||||
can be combined with
|
||||
.B ra-stateless
|
||||
and
|
||||
.B slaac.
|
||||
|
||||
The interface:<interface name> section is not normally used. See the
|
||||
NOTES section for details of this.
|
||||
.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
|
||||
@@ -606,7 +748,13 @@ A single
|
||||
.B dhcp-host
|
||||
may contain an IPv4 address or an IPv6 address, or both. IPv6 addresses must be bracketed by square brackets thus:
|
||||
.B --dhcp-host=laptop,[1234::56]
|
||||
Note that in IPv6 DHCP, the hardware address is not normally available, so a client must be identified by client-id (called client DUID in IPv6-land) or hostname.
|
||||
IPv6 addresses may contain only the host-identifier part:
|
||||
.B --dhcp-host=laptop,[::56]
|
||||
in which case they act as wildcards in constructed dhcp ranges, with
|
||||
the appropriate network part inserted.
|
||||
Note that in IPv6 DHCP, the hardware address is not normally
|
||||
available, so a client must be identified by client-id (called client
|
||||
DUID in IPv6-land) or hostname.
|
||||
|
||||
The special option id:* means "ignore any client-id
|
||||
and use MAC addresses only." This is useful when a client presents a client-id sometimes
|
||||
@@ -631,7 +779,7 @@ This is
|
||||
useful when there is another DHCP server on the network which should
|
||||
be used by some machines.
|
||||
|
||||
The set:<tag> contruct sets the tag
|
||||
The set:<tag> construct sets the tag
|
||||
whenever this dhcp-host directive is in use. This can be used to
|
||||
selectively send DHCP options just for this host. More than one tag
|
||||
can be set in a dhcp-host directive (but not in other places where
|
||||
@@ -846,7 +994,7 @@ agent ID and one provided by a relay agent, the tag is set.
|
||||
(IPv4 only) A normal DHCP relay agent is only used to forward the initial parts of
|
||||
a DHCP interaction to the DHCP server. Once a client is configured, it
|
||||
communicates directly with the server. This is undesirable if the
|
||||
relay agent is addding extra information to the DHCP packets, such as
|
||||
relay agent is adding extra information to the DHCP packets, such as
|
||||
that used by
|
||||
.B dhcp-circuitid
|
||||
and
|
||||
@@ -863,7 +1011,7 @@ relays at those addresses are affected.
|
||||
Without a value, set the tag if the client sends a DHCP
|
||||
option of the given number or name. When a value is given, set the tag only if
|
||||
the option is sent and matches the value. The value may be of the form
|
||||
"01:ff:*:02" in which case the value must match (apart from widcards)
|
||||
"01:ff:*:02" in which case the value must match (apart from wildcards)
|
||||
but the option sent may have unmatched data past the end of the
|
||||
value. The value may also be of the same form as in
|
||||
.B dhcp-option
|
||||
@@ -876,7 +1024,7 @@ will set the tag "efi-ia32" if the the number 6 appears in the list of
|
||||
architectures sent by the client in option 93. (See RFC 4578 for
|
||||
details.) If the value is a string, substring matching is used.
|
||||
|
||||
The special form with vi-encap:<enterpise number> matches against
|
||||
The special form with vi-encap:<enterprise number> matches against
|
||||
vendor-identifying vendor classes for the specified enterprise. Please
|
||||
see RFC 3925 for more details of these rare and interesting beasts.
|
||||
.TP
|
||||
@@ -904,7 +1052,7 @@ dhcp-host configuration in dnsmasq and the contents of /etc/hosts and
|
||||
.TP
|
||||
.B --dhcp-generate-names=tag:<tag>[,tag:<tag>]
|
||||
(IPv4 only) Generate a name for DHCP clients which do not otherwise have one,
|
||||
using the MAC address expressed in hex, seperated by dashes. Note that
|
||||
using the MAC address expressed in hex, separated by dashes. Note that
|
||||
if a host provides a name, it will be used by preference to this,
|
||||
unless
|
||||
.B --dhcp-ignore-names
|
||||
@@ -981,7 +1129,7 @@ timeout has elapsed with no keyboard input, the first available menu
|
||||
option will be automatically executed. If the timeout is zero then the first available menu
|
||||
item will be executed immediately. If
|
||||
.B pxe-prompt
|
||||
is ommitted the system will wait for user input if there are multiple
|
||||
is omitted the system will wait for user input if there are multiple
|
||||
items in the menu, but boot immediately if
|
||||
there is only one. See
|
||||
.B pxe-service
|
||||
@@ -1005,12 +1153,13 @@ create thousands of leases and use lots of memory in the dnsmasq
|
||||
process.
|
||||
.TP
|
||||
.B \-K, --dhcp-authoritative
|
||||
(IPv4 only) Should be set when dnsmasq is definitely the only DHCP server on a network.
|
||||
It changes the behaviour from strict RFC compliance so that DHCP requests on
|
||||
Should be set when dnsmasq is definitely the only DHCP server on a network.
|
||||
For DHCPv4, it changes the behaviour from strict RFC compliance so that DHCP requests on
|
||||
unknown leases from unknown hosts are not ignored. This allows new hosts
|
||||
to get a lease without a tedious timeout under all circumstances. It also
|
||||
allows dnsmasq to rebuild its lease database without each client needing to
|
||||
reacquire a lease, if the database is lost.
|
||||
reacquire a lease, if the database is lost. For DHCPv6 it sets the
|
||||
priority in replies to 255 (the maximum) instead of 0 (the minimum).
|
||||
.TP
|
||||
.B --dhcp-alternate-port[=<server port>[,<client port>]]
|
||||
(IPv4 only) Change the ports used for DHCP from the default. If this option is
|
||||
@@ -1053,7 +1202,8 @@ re-intialised. The enterprise-id is assigned by IANA, and the uid is a
|
||||
string of hex octets unique to a particular device.
|
||||
.TP
|
||||
.B \-6 --dhcp-script=<path>
|
||||
Whenever a new DHCP lease is created, or an old one destroyed, the
|
||||
Whenever a new DHCP lease is created, or an old one destroyed, or a
|
||||
TFTP file transfer completes, the
|
||||
executable specified by this option is run. <path>
|
||||
must be an absolute pathname, no PATH search occurs.
|
||||
The arguments to the process
|
||||
@@ -1103,10 +1253,17 @@ is known.
|
||||
DNSMASQ_TAGS contains all the tags set during the
|
||||
DHCP transaction, separated by spaces.
|
||||
|
||||
DNSMASQ_LOG_DHCP is set if
|
||||
.B --log-dhcp
|
||||
is in effect.
|
||||
|
||||
For IPv4 only:
|
||||
|
||||
DNSMASQ_CLIENT_ID if the host provided a client-id.
|
||||
|
||||
DNSMASQ_CIRCUIT_ID, DNSMASQ_SUBSCRIBER_ID, DNSMASQ_REMOTE_ID if a
|
||||
DHCP relay-agent added any of these options.
|
||||
|
||||
If the client provides vendor-class, DNSMASQ_VENDOR_CLASS.
|
||||
|
||||
For IPv6 only:
|
||||
@@ -1129,6 +1286,8 @@ only supplied for
|
||||
since these data are not held in dnsmasq's lease
|
||||
database.
|
||||
|
||||
|
||||
|
||||
All file descriptors are
|
||||
closed except stdin, stdout and stderr which are open to /dev/null
|
||||
(except in debug mode).
|
||||
@@ -1147,6 +1306,17 @@ 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.
|
||||
|
||||
|
||||
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
|
||||
scripts should be written to ignore unknown actions. "init" is
|
||||
described below in
|
||||
.B --leasefile-ro
|
||||
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.
|
||||
|
||||
.TP
|
||||
.B --dhcp-luascript=<path>
|
||||
Specify a script written in Lua, to be run when leases are created,
|
||||
@@ -1160,11 +1330,13 @@ function, and may provide
|
||||
and
|
||||
.B shutdown
|
||||
functions, which are called, without arguments when dnsmasq starts up
|
||||
and terminates.
|
||||
and terminates. It may also provide a
|
||||
.B tftp
|
||||
function.
|
||||
|
||||
The
|
||||
.B lease
|
||||
method receives the information detailed in
|
||||
function receives the information detailed in
|
||||
.B --dhcp-script.
|
||||
It gets two arguments, firstly the action, which is a string
|
||||
containing, "add", "old" or "del", and secondly a table of tag value
|
||||
@@ -1181,7 +1353,16 @@ for IPv4, and
|
||||
.B client_duid, ip_address
|
||||
and
|
||||
.B hostname
|
||||
for IPv6.
|
||||
for IPv6.
|
||||
|
||||
The
|
||||
.B tftp
|
||||
function is called in the same way as the lease function, and the
|
||||
table holds the tags
|
||||
.B destination_address,
|
||||
.B file_name
|
||||
and
|
||||
.B file_size.
|
||||
.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.
|
||||
@@ -1247,7 +1428,7 @@ In the default mode, dnsmasq inserts the unqualified names of
|
||||
DHCP clients into the DNS. For this reason, the names must be unique,
|
||||
even if two clients which have the same name are in different
|
||||
domains. If a second DHCP client appears which has the same name as an
|
||||
existing client, the name is transfered to the new client. If
|
||||
existing client, the name is transferred to the new client. If
|
||||
.B --dhcp-fqdn
|
||||
is set, this behaviour changes: the unqualified name is no longer
|
||||
put in the DNS, only the qualified name. Two DHCP clients with the
|
||||
@@ -1276,23 +1457,20 @@ 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. The "managed address" bits are set,
|
||||
except for a dhcp-range which is marked as "ra-only", in which case RA
|
||||
is provided but no DHCPv6 service and the managed address bits are
|
||||
cleared.
|
||||
.B enable-ra
|
||||
enables router advertisement for prefixes where dnsmasq is doing
|
||||
DHCPv6. It is not needed to "ra-only" prefixes. Creating an "ra-only"
|
||||
prefix and not setting
|
||||
.B enable-ra
|
||||
sends advertisements only to "ra-only" prefixes.
|
||||
the machine running dnsmasq. By default, he "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.
|
||||
.TP
|
||||
.B --enable-tftp[=<interface>]
|
||||
.B --enable-tftp
|
||||
Enable the TFTP server function. This is deliberately limited to that
|
||||
needed to net-boot a client. Only reading is allowed; the tsize and
|
||||
blksize extensions are supported (tsize is only supported in octet
|
||||
mode). See NOTES section for use of the interface argument.
|
||||
|
||||
mode).
|
||||
.TP
|
||||
.B --tftp-root=<directory>[,<interface>]
|
||||
Look for files to transfer using TFTP relative to the given
|
||||
@@ -1320,6 +1498,12 @@ are accessible. It is not recommended to run dnsmasq as root with TFTP
|
||||
enabled, and certainly not without specifying --tftp-root. Doing so
|
||||
can expose any world-readable file on the server to any host on the net.
|
||||
.TP
|
||||
.B --tftp-lowercase
|
||||
Convert filenames in TFTP requests to all lowercase. This is useful
|
||||
for requests from Windows machines, which have case-insensitive
|
||||
filesystems and tend to play fast-and-loose with case in filenames.
|
||||
Note that dnsmasq's tftp server always converts "\\" to "/" in filenames.
|
||||
.TP
|
||||
.B --tftp-max=<connections>
|
||||
Set the maximum number of concurrent TFTP connections allowed. This
|
||||
defaults to 50. When serving a large number of TFTP connections,
|
||||
@@ -1498,7 +1682,7 @@ used to allocate the address, one from any matching
|
||||
The tag "bootp" is set for BOOTP requests, and a tag whose name is the
|
||||
name of the interface on which the request arrived is also set.
|
||||
|
||||
Any configuration lines which includes one or more tag:<tag> contructs
|
||||
Any configuration lines which include one or more tag:<tag> constructs
|
||||
will only be valid if all that tags are matched in the set derived
|
||||
above. Typically this is dhcp-option.
|
||||
.B dhcp-option
|
||||
@@ -1550,51 +1734,164 @@ parameter in a BOOTP request is used as a tag,
|
||||
as is the tag "bootp", allowing some control over the options returned to
|
||||
different classes of hosts.
|
||||
|
||||
.B dhcp-range
|
||||
may have an interface name supplied as
|
||||
"interface:<interface-name>". The semantics if this are as follows:
|
||||
For DHCP, if any other dhcp-range exists _without_ an interface name,
|
||||
then the interface name is ignored and and dnsmasq behaves as if the
|
||||
interface parts did not exist, otherwise DHCP is only provided to
|
||||
interfaces mentioned in dhcp-range
|
||||
declarations. For DNS, if there are no
|
||||
.B --interface
|
||||
or
|
||||
.B --listen-address
|
||||
flags, behaviour is unchanged by the interface part. If either of
|
||||
these flags are present, the interfaces mentioned in
|
||||
dhcp-ranges are added to the set which get DNS service.
|
||||
.SH AUTHORITATIVE CONFIGURATION
|
||||
.PP
|
||||
Configuring dnsmasq to act as an authoritative DNS server is
|
||||
complicated by the fact that it involves configuration of external DNS
|
||||
servers to provide delegation. We will walk through three scenarios of
|
||||
increasing complexity. Prerequisites for all of these scenarios
|
||||
are a globally accessible IP address, an A or AAAA record pointing to that address,
|
||||
and an external DNS server capable of doing delegation of the zone in
|
||||
question. For the first part of this explanation, we will call the A (or AAAA) record
|
||||
for the globally accessible address server.example.com, and the zone
|
||||
for which dnsmasq is authoritative our.zone.com.
|
||||
|
||||
Similarly,
|
||||
.B enable-tftp
|
||||
may take an interface name, which enables TFTP only for a particular
|
||||
interface, ignoring
|
||||
.B --interface
|
||||
or
|
||||
.B --listen-address
|
||||
flags. In addition
|
||||
.B --tftp-secure
|
||||
and
|
||||
.B --tftp-unique-root
|
||||
and
|
||||
.B --tftp-no-blocksize
|
||||
are ignored for requests from such interfaces. (A
|
||||
.B --tftp-root
|
||||
directive giving a root path and an interface should be
|
||||
provided too.)
|
||||
The simplest configuration consists of two lines of dnsmasq configuration; something like
|
||||
|
||||
.nf
|
||||
.B auth-server=server.example.com,eth0
|
||||
.B auth-zone=our.zone.com,1.2.3.0/24
|
||||
.fi
|
||||
|
||||
and two records in the external DNS
|
||||
|
||||
.nf
|
||||
server.example.com A 192.0.43.10
|
||||
our.zone.com NS server.example.com
|
||||
.fi
|
||||
|
||||
eth0 is the external network interface on which dnsmasq is listening,
|
||||
and has (globally accessible) address 192.0.43.10.
|
||||
|
||||
Note that the external IP address may well be dynamic (ie assigned
|
||||
from an ISP by DHCP or PPP) If so, the A record must be linked to this
|
||||
dynamic assignment by one of the usual dynamic-DNS systems.
|
||||
|
||||
A more complex, but practically useful configuration has the address
|
||||
record for the globally accessible IP address residing in the
|
||||
authoritative zone which dnsmasq is serving, typically at the root. Now
|
||||
we have
|
||||
|
||||
.nf
|
||||
.B auth-server=our.zone.com,eth0
|
||||
.B auth-zone=our.zone.com,1.2.3.0/24
|
||||
.fi
|
||||
|
||||
.nf
|
||||
our.zone.com A 1.2.3.4
|
||||
our.zone.com NS our.zone.com
|
||||
.fi
|
||||
|
||||
The A record for our.zone.com has now become a glue record, it solves
|
||||
the chicken-and-egg problem of finding the IP address of the
|
||||
nameserver for our.zone.com when the A record is within that
|
||||
zone. Note that this is the only role of this record: as dnsmasq is
|
||||
now authoritative from our.zone.com it too must provide this
|
||||
record. If the external address is static, this can be done with an
|
||||
.B /etc/hosts
|
||||
entry or
|
||||
.B --host-record.
|
||||
|
||||
.nf
|
||||
.B auth-server=our.zone.com,eth0
|
||||
.B host-record=our.zone.com,1.2.3.4
|
||||
.B auth-zone=our.zone.com,1.2.3.0/24
|
||||
.fi
|
||||
|
||||
If the external address is dynamic, the address
|
||||
associated with our.zone.com must be derived from the address of the
|
||||
relevant interface. This is done using
|
||||
.B interface-name
|
||||
Something like:
|
||||
|
||||
.nf
|
||||
.B auth-server=our.zone.com,eth0
|
||||
.B interface-name=our.zone.com,eth0
|
||||
.B auth-zone=our.zone.com,1.2.3.0/24
|
||||
.fi
|
||||
|
||||
Our final configuration builds on that above, but also adds a
|
||||
secondary DNS server. This is another DNS server which learns the DNS data
|
||||
for the zone by doing zones transfer, and acts as a backup should
|
||||
the primary server become inaccessible. The configuration of the
|
||||
secondary is beyond the scope of this man-page, but the extra
|
||||
configuration of dnsmasq is simple:
|
||||
|
||||
.nf
|
||||
.B auth-sec-servers=secondary.myisp.com
|
||||
.fi
|
||||
|
||||
and
|
||||
|
||||
.nf
|
||||
our.zone.com NS secondary.myisp.com
|
||||
.fi
|
||||
|
||||
Adding auth-sec-servers enables zone transfer in dnsmasq, to allow the
|
||||
secondary to collect the DNS data. If you wish to restrict this data
|
||||
to particular hosts then
|
||||
|
||||
.nf
|
||||
.B auth-peer=<IP address of secondary>
|
||||
.fi
|
||||
|
||||
will do so.
|
||||
|
||||
Dnsmasq acts as an authoritative server for in-addr.arpa and
|
||||
ipv6.arpa domains associated with the subnets given in auth-zone
|
||||
declarations, so reverse (address to name) lookups can be simply
|
||||
configured with a suitable NS record, for instance in this example,
|
||||
where we allow 1.2.3.0/24 addresses.
|
||||
|
||||
.nf
|
||||
3.2.1.in-addr.arpa NS our.zone.com
|
||||
.fi
|
||||
|
||||
Note that at present, reverse (in-addr.arpa and ip6.arpa) zones are
|
||||
not available in zone transfers, so there is no point arranging
|
||||
secondary servers for reverse lookups.
|
||||
|
||||
.PP
|
||||
When dnsmasq is configured to act as an authoritative server, the
|
||||
following data is used to populate the authoritative zone.
|
||||
.PP
|
||||
.B --mx-host, --srv-host, --dns-rr, --txt-record, --naptr-record
|
||||
, as long as the record names are in the authoritative domain.
|
||||
.PP
|
||||
.B --cname
|
||||
as long as the record name is in the authoritative domain. If the
|
||||
target of the CNAME is unqualified, then it is qualified with the
|
||||
authoritative zone name.
|
||||
.PP
|
||||
IPv4 and IPv6 addresses from /etc/hosts (and
|
||||
.B --addn-hosts
|
||||
) and
|
||||
.B --host-record
|
||||
provided the address falls into one of the subnets specified in the
|
||||
.B --auth-zone.
|
||||
.PP
|
||||
Addresses specified by
|
||||
.B --interface-name.
|
||||
In this case, the address is not contrained to a subnet from
|
||||
.B --auth-zone.
|
||||
|
||||
.PP
|
||||
Addresses of DHCP leases, provided the address falls into one of the subnets specified in the
|
||||
.B --auth-zone
|
||||
OR a constructed DHCP range. In the default mode, where a DHCP lease
|
||||
has an unqualified name, and possibly a qualified name constructed
|
||||
using
|
||||
.B --domain
|
||||
then the name in the authoritative zone is constructed from the
|
||||
unqualified name and the zone's domain. This may or may not equal
|
||||
that specified by
|
||||
.B --domain.
|
||||
If
|
||||
.B --dhcp-fqdn
|
||||
is set, then the fully qualified names associated with DHCP leases are
|
||||
used, and must match the zone's domain.
|
||||
|
||||
|
||||
These rules may seem odd at first sight, but they
|
||||
allow a single line of the form "dhcp-range=interface:virt0,192.168.0.4,192.168.0.200"
|
||||
to be added to dnsmasq configuration which then supplies
|
||||
DHCP and DNS services to that interface, without affecting
|
||||
what services are supplied to other interfaces and irrespective of
|
||||
the existance or lack of "interface=<interface>"
|
||||
lines elsewhere in the dnsmasq configuration.
|
||||
"enable-tftp=virt0" and "tftp-root=<root>,virt0" do the same job for TFTP.
|
||||
The idea is
|
||||
that such a line can be added automatically by libvirt
|
||||
or equivalent systems, without disturbing any manual
|
||||
configuration.
|
||||
|
||||
.SH EXIT CODES
|
||||
.PP
|
||||
|
||||
529
man/fr/dnsmasq.8
529
man/fr/dnsmasq.8
@@ -6,23 +6,40 @@ Dnsmasq \- Un serveur DHCP et cache DNS poids-plume.
|
||||
.I [OPTION]...
|
||||
.SH "DESCRIPTION"
|
||||
.BR dnsmasq
|
||||
est un serveur DHCP et DNS à faible empreinte mémoire. Il offre à la fois les
|
||||
services DNS et DHCP pour un réseau local (LAN).
|
||||
est un serveur à faible empreinte mémoire faisant DNS, TFTP, PXE, annonces de
|
||||
routeurs et DHCP. Il offre à la fois les services DNS et DHCP pour un réseau
|
||||
local (LAN).
|
||||
.PP
|
||||
Dnsmasq accepte les requêtes DNS et y réponds soit en utilisant un petit cache
|
||||
local, soit en effectuant une requête à un serveur DNS récursif externe (par
|
||||
exemple celui de votre fournisseur d'accès internet). Il charge le contenu du
|
||||
fichier /etc/hosts afin que les noms locaux n'apparaissant pas dans les DNS
|
||||
globaux soient tout de même résolus, et assure également la résolution de nom
|
||||
pour les hôtes présents dans le service DHCP.
|
||||
pour les hôtes présents dans le service DHCP. Il peut aussi agir en temps que
|
||||
serveur DNS faisant autorité pour un ou plusieurs domaines, permettant à des
|
||||
noms locaux d'apparaitre dans le DNS global.
|
||||
.PP
|
||||
Le serveur DHCP Dnsmasq DHCP supporte les définitions d'adresses statiques et les
|
||||
réseaux multiples. Il envoie par défaut un jeu raisonnable de paramètres DHCP, et
|
||||
peut être configuré pour envoyer n'importe quel option DHCP.
|
||||
réseaux multiples. Il fournit par défaut un jeu raisonnable de paramètres DHCP,
|
||||
et peut être configuré pour fournir n'importe quelle option DHCP.
|
||||
Il inclut un serveur TFTP sécurisé en lecture seule permettant le démarrage via
|
||||
le réseau/PXE de clients DHCP et supporte également le protocole BOOTP.
|
||||
le réseau/PXE de clients DHCP et supporte également le protocole BOOTP. Le
|
||||
support PXE est complet, et comprend un mode proxy permettant de fournir des
|
||||
informations PXE aux clients alors que l'allocation DHCP est effectuée par un
|
||||
autre serveur.
|
||||
.PP
|
||||
Dnsmasq supporte IPv6 pour le DNS et TFTP mais pas pour le DHCP.
|
||||
Le serveur DHCPv6 de dnsmasq possède non seulement les mêmes fonctionalités
|
||||
que le serveur DHCPv4, mais aussi le support des annonces de routeurs ainsi
|
||||
qu'une fonctionalité permettant l'addition de ressources AAAA pour des
|
||||
clients utilisant DHCPv4 et la configuration IPv6 sans état (stateless
|
||||
autoconfiguration).
|
||||
Il inclut le support d'allocations d'adresses (à la fois en DHCPv6 et en
|
||||
annonces de routeurs - RA) pour des sous-réseaux dynamiquement délégués via
|
||||
une délégation de préfixe DHCPv6.
|
||||
.PP
|
||||
Dnsmasq est developpé pour de petits systèmes embarqués. It tends à avoir
|
||||
l'empreinte mémoire la plus faible possible pour les fonctions supportées,
|
||||
et permet d'exclure les fonctions inutiles du binaire compilé.
|
||||
.SH OPTIONS
|
||||
Notes : Il est possible d'utiliser des options sans leur donner de paramètre.
|
||||
Dans ce cas, la fonction correspondante sera désactivée. Par exemple
|
||||
@@ -75,9 +92,16 @@ l'absence d'enregistrement SOA.
|
||||
.TP
|
||||
.B --max-ttl=<durée>
|
||||
Définie la valeur de TTL maximum qui sera fournie aux clients. La valeur maximum
|
||||
de TTL spécifiée sera fournie aux clients en remplacement de la vraie valeur de TTL
|
||||
si cette dernière est supérieure. La valeur réelle de TTL est cependant conservée dans
|
||||
le cache afin d'éviter de saturer les serveurs DNS en amont.
|
||||
de TTL spécifiée sera fournie aux clients en remplacement de la vraie valeur de
|
||||
TTL si cette dernière est supérieure. La valeur réelle de TTL est cependant
|
||||
conservée dans le cache afin d'éviter de saturer les serveurs DNS en amont.
|
||||
.TP
|
||||
.B --max-cache-ttl=<durée>
|
||||
Définie la valeur de TTL maximum pour les entrées dans le cache
|
||||
.TP
|
||||
.B --auth-ttl=<durée>
|
||||
Définie la valeur de TTL retournée pour les réponses du serveur faisant
|
||||
autorité.
|
||||
.TP
|
||||
.B \-k, --keep-in-foreground
|
||||
Ne pas aller en tâche de fond au lancement, mais en dehors de cela, fonctionner
|
||||
@@ -89,7 +113,10 @@ Mode debug (déverminage) : ne pas aller en tâche de fond, ne pas écrire de
|
||||
fichier pid, ne pas changer d'identifiant utilisateur, générer un état complet
|
||||
du cache lors de la réception d'un signal SIGUSR1, envoyer les logs sur la
|
||||
sortie standard d'erreur ("stderr") de même que dans le syslog, ne pas créer de
|
||||
processus fils pour traiter les requêtes TCP.
|
||||
processus fils pour traiter les requêtes TCP. A noter que cette option est à
|
||||
user pour du déverminage seulement : pour empêcher dnsmasq se fonctionner en
|
||||
mode démon en production, utiliser
|
||||
.B -k.
|
||||
.TP
|
||||
.B \-q, --log-queries
|
||||
Enregistrer les résultats des requêtes DNS traitées par Dnsmasq dans un fichier
|
||||
@@ -184,7 +211,11 @@ ni
|
||||
.B \--except-interface.
|
||||
Utiliser l'option
|
||||
.B --listen-address
|
||||
à la place.
|
||||
à la place. Un simple joker, consistant d'un '*' final, peut-être utilisé dans
|
||||
les options
|
||||
.B \--interface
|
||||
et
|
||||
.B \--except-interface
|
||||
.TP
|
||||
.B \-I, --except-interface=<interface name>
|
||||
Ne pas écouter sur l'interface spécifiée. Notez que l'ordre dans lesquelles les
|
||||
@@ -197,6 +228,21 @@ et
|
||||
sont fournies n'importe pas, et que l'option
|
||||
.B --except-interface
|
||||
l'emporte toujours sur les autres.
|
||||
.TP
|
||||
.B --auth-server=<domaine>,<interface>|<addresse IP>
|
||||
Active le mode DNS faisant autorité pour les requêtes arrivant sur cette
|
||||
interface ou sur cette adresse. Noter que l'interface ou l'adresse n'ont
|
||||
pas besoin d'être mentionées ni dans
|
||||
.B --interface
|
||||
ni dans
|
||||
.B --listen-address
|
||||
En effet,
|
||||
.B --auth-server
|
||||
va passer outre ceux-ci et fournir un service DNS différent sur l'interface
|
||||
spécifiée. La valeur de <domaine> est l'enregistrement de type "colle"
|
||||
("glue record"). Il doit correspondre dans le service DNS global avec un
|
||||
enregistrement de type A et/ou AAAA pointant sur l'adresse sur laquelle dnsmasq
|
||||
écoute pour le mode DNS faisant autorité.
|
||||
.TP
|
||||
.B \-2, --no-dhcp-interface=<nom d'interface>
|
||||
Ne pas fournir de service DHCP sur l'interface spécifiée, mais fournir tout de
|
||||
@@ -231,6 +277,18 @@ autre serveur de nom (ou une autre instance de Dnsmasq) tourne sur la même
|
||||
machine. Utiliser cette option permet également d'avoir plusieurs instances de
|
||||
Dnsmasq fournissant un service DHCP sur la même machine.
|
||||
.TP
|
||||
.B --bind-dynamic
|
||||
Autorise un mode réseau intermédiaire entre
|
||||
.B --bind-interfaces
|
||||
et le mode par défaut. Dnsmasq s'associe à une seule interface, ce qui permet
|
||||
plusieurs instances de dnsmasq, mais si une interface ou adresse apparaissent,
|
||||
il se mettra automatiquement à écouter sur celles-ci (les règles de contrôle
|
||||
d'accès s'appliquent).
|
||||
De fait, les interfaces créées dynamiquement fonctionnent de la même façon que
|
||||
dans le comportement par défaut. Ce fonctionnement nécessite des APIs réseau
|
||||
non standard et n'est disponible que sous Linux. Sur les autres plateformes,
|
||||
le fonctionnement est celui du mode --bind-interfaces.
|
||||
.TP
|
||||
.B \-y, --localise-queries
|
||||
Retourne des réponses aux requêtes DNS dépendantes de l'interface sur laquelle
|
||||
la requête a été reçue, à partir du fichier /etc/hosts. Si un nom dans
|
||||
@@ -299,11 +357,14 @@ Ne pas lire le contenu du fichier /etc/resolv.conf. N'obtenir l'adresse des
|
||||
serveurs de nom amont que depuis la ligne de commande ou le fichier de
|
||||
configuration de Dnsmasq.
|
||||
.TP
|
||||
.B \-1, --enable-dbus
|
||||
.B \-1, --enable-dbus[=<nom de service>]
|
||||
Autoriser la mise à jour de la configuration de Dnsmasq par le biais d'appel de
|
||||
méthodes DBus. Il est possible par ce biais de mettre à jour l'adresse de
|
||||
serveurs DNS amont (et les domaines correspondants) et de vider le cache. Cette
|
||||
option nécessite que Dnsmasq soit compilé avec le support DBus.
|
||||
option nécessite que Dnsmasq soit compilé avec le support DBus. Si un nom de
|
||||
service est fourni, dnsmasq fourni un service à ce nom, plutôt qu'avec la
|
||||
valeur par défaut :
|
||||
.B uk.org.thekelleys.dnsmasq
|
||||
.TP
|
||||
.B \-o, --strict-order
|
||||
Par défaut, Dnsmasq envoie les requêtes à n'importe lequel des serveurs amonts
|
||||
@@ -442,6 +503,12 @@ n'ayant de réponse ni dans /etc/hosts, ni dans les baux DHCP, et n'étant pas
|
||||
transmise à un serveur spécifique par le biais d'une directive
|
||||
.B --server.
|
||||
.TP
|
||||
.B --ipset=/<domaine>/[domaine/]<ipset>[,<ipset>]
|
||||
Obtient les adresses IP des domaines spécifiés et les place dans les groupes
|
||||
d'IP netfilter (ipset) indiqués. Domaines et sous-domaines sont résolus de la
|
||||
même façon que pour --address. Ces groupes d'IP doivent déjà exister. Voir
|
||||
ipset(8) pour plus de détails.
|
||||
.TP
|
||||
.B \-m, --mx-host=<nom de l'hôte>[[,<nom du MX>],<préference>]
|
||||
Spécifie un enregistrement de type MX pour <nom de l'hôte> retournant le nom
|
||||
donné dans <nom du MX> (s'il est présent), ou sinon le nom spécifié dans
|
||||
@@ -492,6 +559,27 @@ dans un ordre différents. Pour un service/domaine donné, plus d'un
|
||||
enregistrement SRV est autorisé et tous les enregistrements qui coïncident sont
|
||||
retournés dans la réponse.
|
||||
.TP
|
||||
.B --host-record=<nom>[,<nom>....][<adresse IPv4>],[<adresse IPv6>]
|
||||
Ajoute des enregistrements A, AAAA et PTR dans le DNS. Ceci permet d'ajouter
|
||||
un ou plusieurs noms dans le DNS et de les associer à des enregistrements IPv4
|
||||
(A) ou IPv6 (AAAA). Un nom peut apparaître dans plus d'une entrée
|
||||
.B host-record
|
||||
et de fait être associé à plus d'une adresse. Seule la première entrée créée
|
||||
l'enregistrement PTR associée au nom. Ceci correspond à la même règle que celle
|
||||
utilisée lors de la lecture du fichier hosts.
|
||||
Les options
|
||||
.B host-record
|
||||
sont considérées lues avant le fichier hosts, ainsi un nom apparaissant dans
|
||||
une option host-record et dans le fichier hosts n'aura pas d'enregistrement
|
||||
PTR associé à l'entrée dans le fichier hosts. A l'inverse du fichier hosts, les
|
||||
noms ne sont pas étendus, même lorsque l'option
|
||||
.B expand-hosts
|
||||
est activée. Les noms longs et les noms courts peuvent apparaitre dans la même
|
||||
entrée
|
||||
.B host-record,
|
||||
c-à-d
|
||||
.B --host-record=laptop,laptop.thekelleys.org,192.168.0.1,1234::100
|
||||
.TP
|
||||
.B \-Y, --txt-record=<nom>[[,<texte>],<texte>]
|
||||
Définit un enregistrement DNS de type TXT. La valeur de l'enregistrement TXT est
|
||||
un ensemble de chaînes de caractères, donc un nombre variable de chaînes de
|
||||
@@ -510,9 +598,17 @@ Retourne un enregistrement de type NAPTR, tel que spécifié dans le RFC3403.
|
||||
Retourne un enregistrement de type CNAME qui indique que <cname> est en
|
||||
réalité <cible>. Il existe des contraintes significatives sur la valeur
|
||||
de cible; il doit s'agir d'un nom DNS qui est connu de dnsmasq via /etc/hosts
|
||||
(ou un fichier hôtes additionnel) ou via DHCP. Si une cible ne satisfait
|
||||
pas ces critères, le CNAME est ignoré. Le CNAME doit être unique, mais
|
||||
il est autorisé d'avoir plus d'un CNAME pointant vers la même cible.
|
||||
(ou un fichier hôtes additionnel), ou via DHCP, ou par un autre
|
||||
.B --cname.
|
||||
Si une cible ne satisfait pas ces critères, le CNAME est ignoré. Le CNAME
|
||||
doit être unique, mais il est autorisé d'avoir plus d'un CNAME pointant
|
||||
vers la même cible.
|
||||
.TP
|
||||
.B --dns-rr=<nom>,<numéro-RR>,[<données hexadécimales>]
|
||||
Retourne un enregistrement DNS arbitraire. Le numéro correspond au type
|
||||
d'enregistrement (qui est toujours de la classe C_IN). La valeur de
|
||||
l'enregistrement est donnée dans les données hexadécimales, qui peuvent
|
||||
être de la forme 01:23:45, 01 23 45,+012345 ou n'importe quelle combinaison.
|
||||
.TP
|
||||
.B --interface-name=<nom>,<interface>
|
||||
Définit un entregistrement DNS associant le nom avec l'adresse primaire sur
|
||||
@@ -569,6 +665,34 @@ Si vous utilisez le premier mode DNSSEC, la validation par le resolveur des
|
||||
clients, cette option n'est pas requise. Dnsmasq retourne toujours toutes les
|
||||
données nécessaires par un client pour effectuer la validation lui-même.
|
||||
.TP
|
||||
.B --auth-zone=<domaine>[,<sous-réseau>[,<sous-réseau>.....]]
|
||||
Définie une zone DNS pour laquelle dnsmasq agit en temps que serveur faisant
|
||||
autorité. Les enregistrements DNS définis localement et correspondant à ce
|
||||
domaine seront fournis, à ceci près que les enregistrements A et AAAA doivent
|
||||
se situer dans l'un des sous-réseaux précisés si ceux-ci sont définis, ou dans
|
||||
un réseau correspondant à une plage DHCP. Le ou les sous-réseaux sont également
|
||||
utilisé pour définir les domaines in-addr.arpa et ipv6.arpa servant à
|
||||
l'interrogation DNS inverse. Dans le cas d'IPv4, la longueur du masque de
|
||||
réseau doit être de 8, 16 ou 24.
|
||||
.TP
|
||||
.B --auth-soa=<numéro de série>[,<mainteneur de zone (hostmaster)>[,<rafraichissement>[,<nombre de réessais>[,<expiration>]]]]
|
||||
Spécifie les champs de l'enregistrement de type SOA (Start Of Authority)
|
||||
associé à une zone pour laquelle le serveur fait autorité. A noter que cela est
|
||||
optionnel, les valeurs par défaut devant convenir à la majorité des cas.
|
||||
.TP
|
||||
.B --auth-sec-servers=<domaine>[,<domaine>[,<domaine>...]]
|
||||
Spécifie un ou plusieurs serveur de nom secondaires pour une zone pour
|
||||
laquelle dnsmasq fait autorité. Ces serveurs doivent-être configurés pour
|
||||
récupérer auprès de dnsmasq les informations liées à la zone au travers d'un
|
||||
transfert de zone, et répondre aux requêtes pour toutes les zones pour
|
||||
lesquelles dnsmasq fait autorité.
|
||||
.TP
|
||||
.B --auth-peer=<adresse IP>[,<adresse IP>[,<adresse IP>...]]
|
||||
Spécifie la ou les adresses de serveurs secondaires autorisés à initier des
|
||||
requêtes de transfert de zone (AXFR) pour les zones pour lesquelles
|
||||
dnsmasq fait autorité. Si cette option n'est pas fournie, les requêtes AXFR
|
||||
seront acceptées pour tous les serveurs secondaires.
|
||||
.TP
|
||||
.B --conntrack
|
||||
Lis le marquage de suivi de connexion Linux associé aux requêtes DNS entrantes
|
||||
et positionne la même marque au trafic amont utilisé pour répondre à ces
|
||||
@@ -577,11 +701,12 @@ l'ayant déclenché, ce qui est pratique pour la gestion de la bande passante
|
||||
(accounting) et le filtrage (firewall). Dnsmasq doit pour cela être compilé
|
||||
avec le support conntrack, le noyau doit également inclure conntrack et être
|
||||
configuré pour cela. Cette option ne peut pas être combinée avec
|
||||
--query-port.
|
||||
--query-port.
|
||||
.TP
|
||||
.B \-F, --dhcp-range=[interface:<interface>,][tag:<label>[,tag:<label>],][set:<label>],]<adresse de début>,<adresse de fin>[,<masque de réseau>[,<broadcast>]][,<durée de bail>]
|
||||
.B \-F, --dhcp-range=[tag:<label>[,tag:<label>],][set:<label>],]<adresse de début>[,<adresse de fin>][,<mode>][,<masque de réseau>[,<broadcast>]][,<durée de bail>]
|
||||
.TP
|
||||
.B \-F, --dhcp-range=[interface:<interface>,][tag:<label>[,tag:<label>],][set:<label>],]<addresse IPv6 de début>,<adresse IPv6 de fin>[,<longueur de préfixe>][,<durée de bail>]
|
||||
.B \-F, --dhcp-range=[tag:<label>[,tag:<label>],][set:<label>],]<addresse IPv6 de début>[,<adresse IPv6 de fin>|constructor:<interface>][,<mode>][,<longueur de préfixe>][,<durée de bail>]
|
||||
|
||||
Active le serveur DHCP. Les adresses seront données dans la plage comprise entre
|
||||
<adresse de début> et <adresse de fin> et à partir des adresses définies
|
||||
statiquement dans l'option
|
||||
@@ -592,6 +717,13 @@ en heures (exemple : 1h) ou être la chaine de caractère "infinite" pour une
|
||||
durée indéterminée. Si aucune valeur n'est donnée, une durée de bail par défaut
|
||||
de une heure est appliquée. La valeur minimum pour un bail DHCP est de 2
|
||||
minutes.
|
||||
|
||||
Pour les plages IPv6, la durée de bail peut-être égale au mot-clef "deprecated"
|
||||
(obsolète); Cela positionne la durée de vie préférée envoyée dans les baux DHCP
|
||||
ou les annonces routeurs à zéro, ce qui incite les clients à utiliser d'autres
|
||||
adresses autant que possible, pour toute nouvelle connexion, en préalable à
|
||||
la renumérotation.
|
||||
|
||||
Cette option peut être répétée, avec différentes adresses,
|
||||
pour activer le service DHCP sur plus d'un réseau. Pour des réseaux directement
|
||||
connectés (c'est-à-dire des réseaux dans lesquels la machine sur laquelle tourne
|
||||
@@ -614,6 +746,22 @@ d'IPv4, la longueur de préfixe n'est pas automatiquement déduite de la
|
||||
configuration de l'interface. La taille minimale pour la longueur de préfixe
|
||||
est 64.
|
||||
|
||||
Pour IPv6 (et IPv6 uniquement), il est possible de définir les plages d'une
|
||||
autre façon. Dans ce cas, l'adresse de départ et l'adresse de fin optionnelle
|
||||
contiennent uniquement la partie réseau (par exemple ::1) et sont suivies par
|
||||
.B constructor:<interface>.
|
||||
Cela forme un modèle décrivant comment construire la plage, à partir des
|
||||
adresses assignées à l'interface. Par exemple
|
||||
|
||||
.B --dhcp-range=::1,::400,constructor:eth0
|
||||
|
||||
provoque la recherche d'adresses de la forme <réseau>::1 sur eth0 et crée une
|
||||
plage allant de <réseau>::1 à <réseau>:400. Si une interface est assignée à
|
||||
plus d'un réseau, les plages correspondantes seront automatiquement créées,
|
||||
rendues obsolètes puis supprimées lorsque l'adress est rendue obsolète puis
|
||||
supprimée. Le nom de l'interface peut être spécifié avec un caractère joker '*'
|
||||
final.
|
||||
|
||||
L'identifiant de label optionnel
|
||||
.B set:<label>
|
||||
fournie une étiquette alphanumérique qui identifie ce réseau, afin de permettre
|
||||
@@ -622,33 +770,66 @@ Lorsque préfixé par 'tag:', la signification change, et au lieu de définir un
|
||||
label, il définit le label pour laquelle la règle s'applique. Un seul label peut-
|
||||
être défini mais plusieurs labels peuvent coïncider.
|
||||
|
||||
L'adresse de fin peut être remplacée par le mot-clef
|
||||
Le mot clef optionnel <mode> peut être égal à
|
||||
.B static
|
||||
("statique") qui indique à Dnsmasq d'activer le service DHCP pour le réseau
|
||||
("statique") ce qui indique à Dnsmasq d'activer le service DHCP pour le réseau
|
||||
spécifié, mais de ne pas activer l'allocation dynamique d'adresses IP : Seuls
|
||||
les hôtes possédant des adresses IP statiques fournies via
|
||||
.B dhcp-host
|
||||
ou présentes dans le fichier /etc/ethers seront alors servis par le DHCP.
|
||||
ou présentes dans le fichier /etc/ethers seront alors servis par le DHCP. Il est
|
||||
possible d'activer un mode "fourre-tout" en définissant un réseau statique
|
||||
comportant uniquement des zéros, c'est à dire :
|
||||
.B --dhcp=range=::,static
|
||||
Cela permet de retourner des réponses à tous les paquets de type
|
||||
Information-request (requête d'information) en mode DHCPv6 sans état sur le
|
||||
sous-réseau configuré.
|
||||
|
||||
L'adresse de fin peut-être remplacée par le mot-clef
|
||||
Pour IPv4, le <mode> peut est égal à
|
||||
.B proxy
|
||||
, auquel cas Dnsmasq fournira un service de DHCP proxy pour le sous-réseau
|
||||
spécifié. (voir
|
||||
.B pxe-prompt
|
||||
et
|
||||
.B pxe-service
|
||||
pour plus de détails, s'applique à IPv4 seulement).
|
||||
pour plus de détails).
|
||||
|
||||
Pour IPv6, le mode peut-être une combinaison des valeurs
|
||||
.B ra-only, slaac, ra-names, ra-stateless.
|
||||
|
||||
L'adresse de fin peut être remplacée par le mot-clef
|
||||
.B ra-only
|
||||
qui indique à dnsmasq de n'effectuer que des annonces de routeur (Router
|
||||
Advertisement, RA) sur ce sous-réseau, et de ne pas faire de DHCP. Ceci
|
||||
s'applique uniquement à IPv6, voir
|
||||
.B enable-ra
|
||||
pour plus de détails.
|
||||
indique à dnsmasq de n'effectuer que des annonces de routeur (Router
|
||||
Advertisement, RA) sur ce sous-réseau, et de ne pas faire de DHCP.
|
||||
|
||||
.B slaac
|
||||
indique à dnsmasq d'effectuer des annonces de routeur sur ce sous-réseau
|
||||
et de positionner dans celles-ci le bit A, afin que les clients utilisent
|
||||
des adresses SLAAC. Lorsqu'utilisé conjointement avec une plage DHCP ou des
|
||||
affectations statiques d'adresses DHCP, les clients disposeront à la fois
|
||||
d'adresses DHCP assignées et d'adresses SLAAC.
|
||||
|
||||
.B ra-stateless
|
||||
indique à dnsmasq d'effectuer des annonces de routeur avec les bits 0 et A
|
||||
positionnés, et de fournir un service DHCP sans état ("stateless"). Les clients
|
||||
utiliseront des adresses SLAAC, et utiliseront DHCP pour toutes les autres
|
||||
informations de configuration.
|
||||
|
||||
.B ra-names
|
||||
active un mode qui fourni des noms DNS aux hôtes fonctionnant en double pile
|
||||
("dual stack") et configurés pour faire du SLAAC en IPv6. Dnsmasq utilise le
|
||||
bail IPv4 de l'hôte afin de dériver le nom, le segment de réseau et l'adresse
|
||||
MAC et assume que l'hôte disposera d'une adresse IPv6 calculée via l'algorithme
|
||||
SLAAC, sur le même segment de réseau. Un ping est envoyé à l'adresse, et si une
|
||||
réponse est obtenue, un enregistrement AAAA est rajouté dans le DNS pour cette
|
||||
adresse IPv6. Veuillez-noter que cela n'arrive que pour les réseaux directement
|
||||
connectés (et non ceux pour lesquels DHCP se fait via relai), et ne
|
||||
fonctionnera pas si un hôte utilise les "extensions de vie privée"
|
||||
("privacy extensions").
|
||||
.B ra-names
|
||||
peut-être combiné avec
|
||||
.B ra-stateless
|
||||
et
|
||||
.B slaac.
|
||||
|
||||
La section interface:<nom d'interface> n'est normalement pas utilisée. Se
|
||||
référer aux indications de la section NOTES pour plus de détail à ce sujet.
|
||||
.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
|
||||
@@ -686,6 +867,10 @@ Un seul
|
||||
peut contenir une adresse IPv4, une adresse IPv6, ou les deux en même temps.
|
||||
Les adresses IPv6 doivent-être mises entre crochets comme suit :
|
||||
.B --dhcp-host=laptop,[1234::56]
|
||||
Les adresses IPv6 peuvent ne contenir que la partie identifiant de client :
|
||||
.B --dhcp-host=laptop,[::56]
|
||||
Dans ce cas, lorsque des plages dhcp sont définies automatiquement par le biais
|
||||
de constructeurs, la partie réseau correspondante est rajoutée à l'adresse.
|
||||
A noter que pour le DHCP IPv6, l'adresse matérielle n'est en principe pas
|
||||
disponible, aussi un client doit-être identifié par un identifiant de client
|
||||
(appellé "DUID client") ou un nom d'hôte.
|
||||
@@ -1128,14 +1313,16 @@ créant des milliers de baux et utilisant beaucoup de mémoire dans le processus
|
||||
Dnsmasq.
|
||||
.TP
|
||||
.B \-K, --dhcp-authoritative
|
||||
(IPv4 seulement) Cette option doit être donnée lorsque Dnsmasq est le seul
|
||||
serveur DHCP sur le réseau. Cela change le comportement par défaut qui est
|
||||
Doit être spécifié lorsque dnsmasq est réellement le seul serveur DHCP
|
||||
sur le réseau. Pour DHCPv4, cela change le comportement par défaut qui est
|
||||
celui d'un strict respect des RFC, afin que les requêtes DHCP pour des baux
|
||||
inconnus par des hôtes inconnus ne soient pas ignorées. Cela permet à de
|
||||
nouveaux hôtes d'obtenir des baux sans tenir compte de fastidieuses
|
||||
temporisations ("timeout"). Cela permet également à Dnsmasq de reconstruire
|
||||
sa base de données contenant les baux sans que les clients n'aient besoin de
|
||||
redemander un bail, si celle-ci est perdue.
|
||||
Dans le cas de DHCPv6, cela positionne la priorité des réponses à 255 (le
|
||||
maximum) au lieu de 0 (le minimum).
|
||||
.TP
|
||||
.B --dhcp-alternate-port[=<port serveur>[,<port client>]]
|
||||
(IPv4 seulement) Change les ports utilisés par défaut pour le DHCP. Si cette
|
||||
@@ -1170,9 +1357,21 @@ détermination de celles-ci.
|
||||
.B \-l, --dhcp-leasefile=<chemin de fichier>
|
||||
Utilise le fichier dont le chemin est fourni pour stocker les informations de
|
||||
baux DHCP.
|
||||
.TP
|
||||
.TP
|
||||
.B --dhcp-duid=<ID d'entreprise>,<uid>
|
||||
(IPv6 seulement) Spécifie le numéro d'UID de serveur persistant que le serveur
|
||||
DHCPv6 doit utiliser. Cette option n'est normalement pas requise, Dnsmasq
|
||||
créant un DUID automatiquement lorsque cela est nécessaire. Lorsque cette
|
||||
option est positionnée, elle fournit à Dnsmasq les données nécessaires à la
|
||||
création d'un DUID de type DUID-EN. Veuillez noter qu'une fois créé, le DUID
|
||||
est stocké dans la base des baux, aussi changer entre un DUID créé
|
||||
automatiquement et un DUID-EN et vice-versa impose de réinitialiser la base de
|
||||
baux. Le numéro d'ID d'entreprise est assigné par l'IANA, et l'uid est une
|
||||
chaine hexadécimale unique à chaque serveur.
|
||||
.TP
|
||||
.B \-6 --dhcp-script=<chemin de fichier>
|
||||
Lorsqu'un bail DHCP est créé, ou qu'un ancien est supprimé, le fichier dont le
|
||||
Lorsqu'un bail DHCP est créé, qu'un ancien est supprimé, ou qu'un transfert
|
||||
TFTP est terminé, le fichier dont le
|
||||
chemin est spécifié est exécuté. Le <chemin de fichier> doit être un chemin
|
||||
absolu, aucune recherche n'est effectuée via la variable d'environnement PATH.
|
||||
Les arguments fournis à celui-ci sont soit
|
||||
@@ -1223,10 +1422,17 @@ relai DHCP pour contacter Dnsmasq, si l'adresse IP du relai est connue.
|
||||
DNSMASQ_TAGS contient tous les labels fournis pendant la transaction DHCP,
|
||||
séparés par des espaces.
|
||||
|
||||
DNSMASQ_LOG_DHCP est positionné si
|
||||
.B --log-dhcp
|
||||
est activé.
|
||||
|
||||
Pour IPv4 seulement :
|
||||
|
||||
DNSMASQ_CLIENT_ID, si l'hôte a fourni un identifiant de client.
|
||||
|
||||
DNSMASQ_CIRCUIT_ID, DNSMASQ_SUBSCRIBER_ID, DNSMASQ_REMOTE_ID si un relai DHCP a
|
||||
rajouté l'une de ces options.
|
||||
|
||||
Si le client fournit une information de classe de vendeur, DNSMASQ_VENDOR_CLASS.
|
||||
|
||||
Pour IPv6 seulement :
|
||||
@@ -1262,6 +1468,16 @@ Au démarrage de Dnsmasq, le script sera invoqué pour chacun des baux existants
|
||||
dans le fichier des baux. Le script sera lancé avec l'action "del" pour les
|
||||
baux expirés, et "old" pour les autres. Lorsque Dnsmasq reçoit un signal HUP,
|
||||
le script sera invoqué avec une action "old" pour tous les baux existants.
|
||||
|
||||
Il existe deux autres actions pouvant apparaître comme argument au script :
|
||||
"init" et "tftp". D'autres sont susceptibles d'être rajoutées dans le futur,
|
||||
aussi les scripts devraient-être écrits de sorte à ignorer les actions
|
||||
inconnues. "init" est décrite ci-dessous dans
|
||||
.B --leasefile-ro.
|
||||
L'action "tftp" est invoquée lorsqu'un transfert de fichier TFTP s'est
|
||||
terminé. Ses arguments sont la taille du fichier en octets, l'adresse à
|
||||
laquelle le fichier a été envoyé, ainsi que le chemin complet du fichier.
|
||||
|
||||
.TP
|
||||
.B --dhcp-luascript=<chemin>
|
||||
Spécifie un script écrit en Lua, devant être exécuté lorsque des baux sont
|
||||
@@ -1276,8 +1492,10 @@ et peut fournir des fonctions
|
||||
et
|
||||
.B shutdown
|
||||
qui sont appellées, sans arguments, lorsque dnsmasq démarre ou s'arrête.
|
||||
Il peut également fournir une fonction
|
||||
.B tftp.
|
||||
|
||||
La méthode
|
||||
La fonction
|
||||
.B lease
|
||||
reçoit les informations détaillées dans
|
||||
.B --dhcp-script.
|
||||
@@ -1301,6 +1519,16 @@ et
|
||||
ainsi que
|
||||
.B hostname
|
||||
(le nom d'hôte) dans le cas d'IPv6.
|
||||
|
||||
La fonction
|
||||
.B tftp
|
||||
est appelée de la même façon que la fonction "lease", et la table contient les
|
||||
labels
|
||||
.B destination_address,
|
||||
.B file_name
|
||||
et
|
||||
.B file_size
|
||||
(respectivement "adresse de destination", "nom de fichier" et "taille de fichier").
|
||||
.TP
|
||||
.B --dhcp-scriptuser
|
||||
Spécifie l'utilisateur sous lequel le script shell lease-change ou le script
|
||||
@@ -1367,7 +1595,7 @@ Si la gamme d'adresse est fournie sous la forme
|
||||
qui a pour effect d'ajouter --local-declarations aux requêtes DNS directes et
|
||||
inverses. C-à-d
|
||||
.B --domain=thekelleys.org.uk,192.168.0.0/24,local
|
||||
est indentique à
|
||||
est identique à
|
||||
.B --domain=thekelleys.org.uk,192.168.0.0/24
|
||||
--local=/thekelleys.org.uk/ --local=/0.168.192.in-addr.arpa/
|
||||
La taille de réseau doit-être de 8, 16 ou 24 pour être valide.
|
||||
@@ -1411,17 +1639,21 @@ Advertisement") sont activées, dnsmasq va annoncer un préfixe pour chaque
|
||||
dhcp-range et, par défaut, fournir comme valeur de routeur et de DNS récursif
|
||||
la valeur d'adresse link-local appropriée parmi celles de la machine sur
|
||||
laquelle tourne dnsmasq.
|
||||
Les bits "managed address" sont positionnés, sauf pour un dhcp-range marqué
|
||||
comme "ra-only" (annonce routeur uniquement). Dans ce cas le service d'annonce
|
||||
routeur est rendu mais aucun service DHCPv6 n'est assuré, et les bits "managed
|
||||
address" ne sont pas positionnés.
|
||||
Par défaut, les bits "managed address" sont positionnés, et le bit "use SLAAC"
|
||||
("utiliser SLAAC") est réinitialisé. Cela peut-être changé pour des
|
||||
sous-réseaux donnés par le biais du mot clef de mode décris dans
|
||||
.B --dhcp-range.
|
||||
Les paramètres DNS du RFC6106 sont inclus dans les annonces. Par défaut,
|
||||
l'adresse link-local appropriée parmi celles de la machine sur laquelle tourne
|
||||
dnsmasq est spécifiée comme DNS récursif. Si elles sont fournies, les
|
||||
options dns-server et domain-search sont utilisées respectivement pour RDNSS et
|
||||
DNSSL.
|
||||
.TP
|
||||
.B --enable-tftp[=<interface>]
|
||||
.B --enable-tftp
|
||||
Active la fonction serveur TFTP. Celui-ci est de manière délibérée limité aux
|
||||
fonctions nécessaires au démarrage par le réseau ("net-boot") d'un client. Seul
|
||||
un accès en lecture est possible; les extensions tsize et blksize sont supportées
|
||||
(tsize est seulement supporté en mode octet). Voir dans la section NOTES les
|
||||
informations relatives à la spécification de l'interface.
|
||||
(tsize est seulement supporté en mode octet).
|
||||
.TP
|
||||
.B --tftp-root=<répertoire>[,<interface>]
|
||||
Les fichiers à fournir dans les transferts TFTP seront cherchés en prenant le
|
||||
@@ -1459,6 +1691,13 @@ Sans cela, en effet, l'accès de tous les fichiers du serveur pour lequel le
|
||||
droit de lecture pour tout le monde est positionné ("world-readable") devient
|
||||
possible par n'importe quel hôte sur le réseau.
|
||||
.TP
|
||||
.B --tftp-lowercase
|
||||
Converti les noms de fichiers des requêtes TFTP en minuscules. Cela est utile
|
||||
pour les requêtes effectuées depuis les machines Windows, dont les systèmes
|
||||
de fichiers sont insensibles à la casse et pour lesquels la détermination
|
||||
de la casse est parfois un peu aléatoire. A noter que le serveur tftp de
|
||||
dnsmasq converti systématiquement les "\\" en "/" dans les noms de fichiers.
|
||||
.TP
|
||||
.B --tftp-max=<connexions>
|
||||
Définit le nombre maximum de connexions TFTP simultanées autorisées. La valeur
|
||||
par défaut est de 50. Lorsqu'un grand nombre de connexions TFTP est spécifié,
|
||||
@@ -1722,50 +1961,166 @@ supprime la nécessité des associations statiques). Le paramètre
|
||||
que le label "bootp", permettant un certain contrôle sur les options retournées
|
||||
aux différentes classes d'hôtes.
|
||||
|
||||
Il est possible de spécifier un nom d'interface à
|
||||
.B dhcp-range
|
||||
sous la forme "interface:<nom d'interface>". La sémantique est comme suit :
|
||||
Pour le DHCP, s'il existe une autre valeur de dhcp-range pour laquelle
|
||||
_aucun_ nom d'interface n'est donné, alors le nom d'interface est ignoré
|
||||
et dnsmasq se comporte comme si la partie spécifiant l'interface n'existait
|
||||
pas, sinon le service DHCP n'est fourni qu'aux interfaces mentionnées dans
|
||||
les déclarations dhcp-range. Pour le DNS, si il n'y a pas d'option
|
||||
.B --interface
|
||||
ou
|
||||
.B --listen-address
|
||||
, alors le comportement n'est pas impacté par la spécification d'interface. Si
|
||||
l'une ou l'autre de ces options est présente, alors les interfaces mentionnées
|
||||
dans les plages d'adresses dhcp-range sont rajoutées à la liste de celles
|
||||
où le service DNS est assuré.
|
||||
|
||||
De manière similaire,
|
||||
.B enable-tftp
|
||||
peut prendre un nom d'interface, ce qui active le TFTP pour cette seule
|
||||
interface, en ignorant les options
|
||||
.B --interface
|
||||
ou
|
||||
.B --listen-address
|
||||
De plus,
|
||||
.B --tftp-secure
|
||||
,
|
||||
.B --tftp-unique-root
|
||||
.SH CONFIGURATION EN TEMPS QUE SERVEUR FAISANT AUTORITÉ
|
||||
.PP
|
||||
Configurer dnsmasq pour agir en temps que serveur DNS faisant autorité est
|
||||
compliqué par le fait que cela implique la configuration de serveurs DNS
|
||||
externes pour mettre en place la délégation. Seront présentés ci-dessous trois
|
||||
scénarios de complexité croissante. Le pré-requis pour chacun de ces scénarios
|
||||
est l'existence d'une adresse IP globalement disponible, d'un enregistrement de
|
||||
type A ou AAAA pointant vers cette adresse, ainsi que d'un serveur DNS externe
|
||||
capable d'effectuer la délégation de la zone en question. Pour la première
|
||||
partie de ces explications, nous allons appeller serveur.exemple.com
|
||||
l'enregistrement A (ou AAAA) de l'adresse globalement accessible, et
|
||||
notre.zone.com la zone pour laquelle dnsmasq fait autorité.
|
||||
|
||||
La configuration la plus simple consiste en deux lignes de configuration,
|
||||
sous la forme :
|
||||
.nf
|
||||
.B auth-server=serveur.exemple.com,eth0
|
||||
.B auth-zone=notre.zone.com,1.2.3.0/24
|
||||
.fi
|
||||
|
||||
ainsi que deux enregistrements dans le DNS externe :
|
||||
|
||||
.nf
|
||||
serveur.exemple.com A 192.0.43.10
|
||||
notre.zone.com NS serveur.exemple.com
|
||||
.fi
|
||||
|
||||
eth0 est l'interface réseau externe sur laquelle dnsmasq écoute, dont l'adresse
|
||||
IP (globalement accessible) est 192.0.43.10.
|
||||
|
||||
A noter que l'adresse IP externe peut parfaitement être dynamique (par exemple
|
||||
attribuée par un FAI via DHCP ou PPP). Dans ce cas, l'enregistrement de type A
|
||||
doit être lié à cet enregistrement dynamique par l'une ou l'autre des techniques
|
||||
habituelles de système DNS dynamique.
|
||||
|
||||
Un exemple plus complexe mais en pratique plus utile correspond au cas où
|
||||
l'adresse IP globalement accessible se trouve dans la zone pour laquelle
|
||||
dnsmasq fait autorité, le plus souvent à la racine. Dans ce cas nous avons :
|
||||
|
||||
.nf
|
||||
.B auth-server=notre.zone.com,eth0
|
||||
.B auth-zone=notre.zone.com,1.2.3.0/24
|
||||
.fi
|
||||
|
||||
.nf
|
||||
notre.zone.com A 1.2.3.4
|
||||
notre.zone.com NS our.zone.com
|
||||
.fi
|
||||
|
||||
L'enregistrement A pour notre.zone.com est dorénavant un enregistrement "colle"
|
||||
qui résoud le problème de poule et d'oeuf consistant à trouver l'adresse IP
|
||||
du serveur de nom pour notre.zone.com lorsque l'enregistrement se trouve dans
|
||||
la zone en question. Il s'agit du seul rôle de cet enregistrement : comme dnsmasq
|
||||
fait désormais autorité pour notre.zone.com, il doit également fournir cet
|
||||
enregistrement. Si l'adresse externe est statique, cela peut-être réalisé par
|
||||
le biais d'une entrée dans
|
||||
.B /etc/hosts
|
||||
ou via un
|
||||
.B --host-record.
|
||||
|
||||
.nf
|
||||
.B auth-server=notre.zone.com,eth0
|
||||
.B host-record=notre.zone.com,1.2.3.4
|
||||
.B auth-zone=notre.zone.com,1.2.3.0/24
|
||||
.fi
|
||||
|
||||
Si l'adresse externe est dynamique, l'adresse associée à notre.zone.com doit
|
||||
être dérivée de l'interface correspondante. Cela peut être fait en utilisant
|
||||
.B interface-name
|
||||
Sous la forme :
|
||||
|
||||
.nf
|
||||
.B auth-server=notre.zone.com,eth0
|
||||
.B interface-name=notre.zone.com,eth0
|
||||
.B auth-zone=notre.zone.com,1.2.3.0/24
|
||||
.fi
|
||||
|
||||
La configuration finale rajoute à cette base un serveur DNS secondaire. Il
|
||||
s'agit d'un autre serveur DNS qui apprend les données DNS de la zone en
|
||||
effectuant un transfert de zone, et qui joue le rôle de serveur de secours
|
||||
au cas où le serveur principal devenait inaccessible. La configuration
|
||||
de ce serveur secondaire sort du cadre de cette page de manuel. Les éléments
|
||||
de configuration à rajouter dans dnsmasq sont les simples :
|
||||
|
||||
.nf
|
||||
.B auth-sec-servers=secondaire.monfai.com
|
||||
.fi
|
||||
|
||||
et
|
||||
.B --tftp-no-blocksize
|
||||
sont ignorées pour les requêtes sur de telles interfaces. (une directive
|
||||
.B --tftp-root
|
||||
donnant le chemin de la racine et une interface doit-être fournie).
|
||||
|
||||
Ces règles peuvent paraître étrange à première vue, mais elles permettent
|
||||
d'ajouter à la configuration de dnsmasq des lignes de configuration de la
|
||||
forme "dhcp-range=interface:virt0,192.168.0.4,192.168.0.200" afin de fournir
|
||||
un service DHCP et DNS sur cette interface, sans pour autant affecter les
|
||||
services fournis sur d'autres interfaces, malgré l'absence de paramètres
|
||||
"interface=<interface>" sur les autres lignes de configuration.
|
||||
"enable-tftp=virt0" et "tftp-root=<root>,virt0" effectuent la même chose pour
|
||||
TFTP.
|
||||
L'idée de tout cela est de permettre l'addition de telles lignes
|
||||
automatiquement par libvirt ou un système équivalent, sans perturbation
|
||||
d'une configuration manuelle existant par ailleurs.
|
||||
.nf
|
||||
notre.zone.com NS secondaire.monfai.com
|
||||
.fi
|
||||
|
||||
L'addition d'une option auth-sec-servers active les transferts de zone dans
|
||||
dnsmasq, ce qui permet au serveur secondaire de venir collecter les données
|
||||
DNS. Si vous souhaitez restreindre l'accès à ces données à des hôtes
|
||||
spécifiques, vous pouvez le faire via :
|
||||
|
||||
.nf
|
||||
.B auth-peer=<adresse IP du serveur secondaire>
|
||||
.fi
|
||||
|
||||
Dnsmasq joue le rôle de serveur faisant autorité pour les domaines in-addr.arpa
|
||||
et ipv6.arpa associés aux sous-réseaux définis dans la déclaration de zone
|
||||
auth-zone, ce qui fait que les requêtes DNS inversées (de l'adresse vers
|
||||
le nom) peuvent-simplement être configurées avec un enregistrement NS
|
||||
adéquat. Par exemple, comme nous définissons plus haut les adresses
|
||||
1.2.3.0/24 :
|
||||
.nf
|
||||
3.2.1.in-addr.arpa NS notre.zone.com
|
||||
.fi
|
||||
|
||||
Veuillez noter que pour l'instant, les zones inverses ne sont pas
|
||||
disponibles dans les transferts de zone, donc il est inutile de configurer
|
||||
de serveur secondaire pour la résolution inverse.
|
||||
|
||||
.PP
|
||||
Lorsque dnsmasq est configuré en temps que serveur faisant autorité,
|
||||
les données suivantes sont utilisées pour peupler la zone considérée :
|
||||
.PP
|
||||
.B --mx-host, --srv-host, --dns-rr, --txt-record, --naptr-record
|
||||
, pour autant que les noms des enregistrements se trouvent dans la zone en
|
||||
question.
|
||||
.PP
|
||||
.B --cname
|
||||
pour peu que le nom soit dans le domaine. Si la cible du CNAME n'est
|
||||
pas pleinement qualifiée, alors elle est qualifiée avec le nom de la
|
||||
zone pour laquelle le serveur fait autorité.
|
||||
.PP
|
||||
Les adresses IPv4 et IPv6 extraites de /etc/hosts (et
|
||||
.B --addn-hosts
|
||||
) ainsi que les options
|
||||
.B --host-record
|
||||
fournissant des adresses situées dans l'un des sous-réseaux spécifiés dans
|
||||
.B --auth-zone.
|
||||
.PP
|
||||
Adresses spécifiées par
|
||||
.B --interface-name.
|
||||
Dans ce cas, l'adresse n'est pas limitée à l'un des sous-réseaux donné dans
|
||||
.B --auth-zone.
|
||||
|
||||
.PP
|
||||
Les adresses de baux DHCP, si l'adresse est située dans l'un des sous-réseaux de
|
||||
.B --auth-zone
|
||||
OU dans une plage DHCP construite. Dans le mode par défaut, où le bail
|
||||
DHCP a un nom non qualifié, et éventuellement pour un nom qualifié construit
|
||||
via
|
||||
.B --domain
|
||||
, alors le nom dans la zone faisant autorité est construit à partir du nom
|
||||
non qualifié et du nom de domaine de la zone. Cela peut on non être égal
|
||||
celui fourni par
|
||||
.B --domain.
|
||||
Si l'option
|
||||
.B --dhcp-fqdn
|
||||
est fournie, alors les noms pleinemenet qualifiés associés aux baux DHCP
|
||||
sont utilisés, dès lors qu'ils correspondent au nom de domaine associé
|
||||
à la zone.
|
||||
|
||||
|
||||
.SH CODES DE SORTIE
|
||||
.PP
|
||||
|
||||
1005
po/pt_BR.po
1005
po/pt_BR.po
File diff suppressed because it is too large
Load Diff
756
src/auth.c
Normal file
756
src/auth.c
Normal file
@@ -0,0 +1,756 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 dated June, 1991, or
|
||||
(at your option) version 3 dated 29 June, 2007.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
#ifdef HAVE_AUTH
|
||||
|
||||
static struct subnet *filter_zone(struct auth_zone *zone, int flag, struct all_addr *addr_u)
|
||||
{
|
||||
struct subnet *subnet;
|
||||
|
||||
for (subnet = zone->subnet; subnet; subnet = subnet->next)
|
||||
{
|
||||
if (subnet->is6 && (flag & F_IPV4))
|
||||
continue;
|
||||
|
||||
if (!subnet->is6)
|
||||
{
|
||||
struct in_addr addr = addr_u->addr.addr4;
|
||||
struct in_addr mask;
|
||||
|
||||
mask.s_addr = htonl(~((1 << (32 - subnet->prefixlen)) - 1));
|
||||
|
||||
if (is_same_net(addr, subnet->addr4, mask))
|
||||
return subnet;
|
||||
}
|
||||
#ifdef HAVE_IPV6
|
||||
else if (is_same_net6(&(addr_u->addr.addr6), &subnet->addr6, subnet->prefixlen))
|
||||
return subnet;
|
||||
#endif
|
||||
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int filter_constructed_dhcp(struct auth_zone *zone, int flag, struct all_addr *addr_u)
|
||||
{
|
||||
#ifdef HAVE_DHCP6
|
||||
struct dhcp_context *context;
|
||||
|
||||
if (flag & F_IPV6)
|
||||
for (context = daemon->dhcp6; context; context = context->next)
|
||||
if ((context->flags & CONTEXT_CONSTRUCTED) &&
|
||||
is_same_net6(&(addr_u->addr.addr6), &context->start6, context->prefix))
|
||||
return 1;
|
||||
#endif
|
||||
|
||||
return filter_zone(zone, flag, addr_u) != NULL;
|
||||
}
|
||||
|
||||
static int in_zone(struct auth_zone *zone, char *name, char **cut)
|
||||
{
|
||||
size_t namelen = strlen(name);
|
||||
size_t domainlen = strlen(zone->domain);
|
||||
|
||||
if (cut)
|
||||
*cut = NULL;
|
||||
|
||||
if (namelen >= domainlen &&
|
||||
hostname_isequal(zone->domain, &name[namelen - domainlen]))
|
||||
{
|
||||
|
||||
if (namelen == domainlen)
|
||||
return 1;
|
||||
|
||||
if (name[namelen - domainlen - 1] == '.')
|
||||
{
|
||||
if (cut)
|
||||
*cut = &name[namelen - domainlen - 1];
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t now, union mysockaddr *peer_addr)
|
||||
{
|
||||
char *name = daemon->namebuff;
|
||||
unsigned char *p, *ansp;
|
||||
int qtype, qclass;
|
||||
int nameoffset, axfroffset = 0;
|
||||
int q, anscount = 0, authcount = 0;
|
||||
struct crec *crecp;
|
||||
int auth = 1, trunc = 0, nxdomain = 1, soa = 0, ns = 0, axfr = 0;
|
||||
struct auth_zone *zone = NULL;
|
||||
struct subnet *subnet = NULL;
|
||||
char *cut;
|
||||
struct mx_srv_record *rec, *move, **up;
|
||||
struct txt_record *txt;
|
||||
struct interface_name *intr;
|
||||
struct naptr *na;
|
||||
struct all_addr addr;
|
||||
struct cname *a;
|
||||
|
||||
if (ntohs(header->qdcount) == 0 || OPCODE(header) != QUERY )
|
||||
return 0;
|
||||
|
||||
/* determine end of question section (we put answers there) */
|
||||
if (!(ansp = skip_questions(header, qlen)))
|
||||
return 0; /* bad packet */
|
||||
|
||||
/* now process each question, answers go in RRs after the question */
|
||||
p = (unsigned char *)(header+1);
|
||||
|
||||
for (q = ntohs(header->qdcount); q != 0; q--)
|
||||
{
|
||||
unsigned short flag = 0;
|
||||
int found = 0;
|
||||
|
||||
/* save pointer to name for copying into answers */
|
||||
nameoffset = p - (unsigned char *)header;
|
||||
|
||||
/* now extract name as .-concatenated string into name */
|
||||
if (!extract_name(header, qlen, &p, name, 1, 4))
|
||||
return 0; /* bad packet */
|
||||
|
||||
GETSHORT(qtype, p);
|
||||
GETSHORT(qclass, p);
|
||||
|
||||
if (qclass != C_IN)
|
||||
{
|
||||
auth = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (qtype == T_PTR)
|
||||
{
|
||||
if (!(flag = in_arpa_name_2_addr(name, &addr)))
|
||||
continue;
|
||||
|
||||
for (zone = daemon->auth_zones; zone; zone = zone->next)
|
||||
if ((subnet = filter_zone(zone, flag, &addr)))
|
||||
break;
|
||||
|
||||
if (!zone)
|
||||
{
|
||||
auth = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (flag == F_IPV4)
|
||||
{
|
||||
for (intr = daemon->int_names; intr; intr = intr->next)
|
||||
{
|
||||
if (addr.addr.addr4.s_addr == get_ifaddr(intr->intr).s_addr)
|
||||
break;
|
||||
else
|
||||
while (intr->next && strcmp(intr->intr, intr->next->intr) == 0)
|
||||
intr = intr->next;
|
||||
}
|
||||
|
||||
if (intr)
|
||||
{
|
||||
if (in_zone(zone, intr->name, NULL))
|
||||
{
|
||||
found = 1;
|
||||
log_query(F_IPV4 | F_REVERSE | F_CONFIG, intr->name, &addr, NULL);
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
daemon->auth_ttl, NULL,
|
||||
T_PTR, C_IN, "d", intr->name))
|
||||
anscount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((crecp = cache_find_by_addr(NULL, &addr, now, flag)))
|
||||
do {
|
||||
strcpy(name, cache_get_name(crecp));
|
||||
|
||||
if (crecp->flags & F_DHCP && !option_bool(OPT_DHCP_FQDN))
|
||||
{
|
||||
char *p = strchr(name, '.');
|
||||
if (p)
|
||||
*p = 0; /* must be bare name */
|
||||
|
||||
/* add external domain */
|
||||
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,
|
||||
daemon->auth_ttl, NULL,
|
||||
T_PTR, C_IN, "d", name))
|
||||
anscount++;
|
||||
}
|
||||
else if (crecp->flags & (F_DHCP | F_HOSTS) && in_zone(zone, name, NULL))
|
||||
{
|
||||
log_query(crecp->flags & ~F_FORWARD, name, &addr, record_source(crecp->uid));
|
||||
found = 1;
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
daemon->auth_ttl, NULL,
|
||||
T_PTR, C_IN, "d", name))
|
||||
anscount++;
|
||||
}
|
||||
else
|
||||
continue;
|
||||
|
||||
} while ((crecp = cache_find_by_addr(crecp, &addr, now, flag)));
|
||||
|
||||
if (!found)
|
||||
log_query(flag | F_NEG | F_NXDOMAIN | F_REVERSE | F_AUTH, NULL, &addr, NULL);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
cname_restart:
|
||||
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)
|
||||
if (!rec->issrv && hostname_isequal(name, rec->name))
|
||||
{
|
||||
nxdomain = 0;
|
||||
|
||||
if (qtype == T_MX)
|
||||
{
|
||||
found = 1;
|
||||
log_query(F_CONFIG | F_RRNAME, name, NULL, "<MX>");
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
|
||||
NULL, T_MX, C_IN, "sd", rec->weight, rec->target))
|
||||
anscount++;
|
||||
}
|
||||
}
|
||||
|
||||
for (move = NULL, up = &daemon->mxnames, rec = daemon->mxnames; rec; rec = rec->next)
|
||||
if (rec->issrv && hostname_isequal(name, rec->name))
|
||||
{
|
||||
nxdomain = 0;
|
||||
|
||||
if (qtype == T_SRV)
|
||||
{
|
||||
found = 1;
|
||||
log_query(F_CONFIG | F_RRNAME, name, NULL, "<SRV>");
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
|
||||
NULL, T_SRV, C_IN, "sssd",
|
||||
rec->priority, rec->weight, rec->srvport, rec->target))
|
||||
|
||||
anscount++;
|
||||
}
|
||||
|
||||
/* unlink first SRV record found */
|
||||
if (!move)
|
||||
{
|
||||
move = rec;
|
||||
*up = rec->next;
|
||||
}
|
||||
else
|
||||
up = &rec->next;
|
||||
}
|
||||
else
|
||||
up = &rec->next;
|
||||
|
||||
/* put first SRV record back at the end. */
|
||||
if (move)
|
||||
{
|
||||
*up = move;
|
||||
move->next = NULL;
|
||||
}
|
||||
|
||||
for (txt = daemon->rr; txt; txt = txt->next)
|
||||
if (hostname_isequal(name, txt->name))
|
||||
{
|
||||
nxdomain = 0;
|
||||
if (txt->class == qtype)
|
||||
{
|
||||
found = 1;
|
||||
log_query(F_CONFIG | F_RRNAME, name, NULL, "<RR>");
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
|
||||
NULL, txt->class, C_IN, "t", txt->len, txt->txt))
|
||||
anscount++;
|
||||
}
|
||||
}
|
||||
|
||||
for (txt = daemon->txt; txt; txt = txt->next)
|
||||
if (txt->class == C_IN && hostname_isequal(name, txt->name))
|
||||
{
|
||||
nxdomain = 0;
|
||||
if (qtype == T_TXT)
|
||||
{
|
||||
found = 1;
|
||||
log_query(F_CONFIG | F_RRNAME, name, NULL, "<TXT>");
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
|
||||
NULL, T_TXT, C_IN, "t", txt->len, txt->txt))
|
||||
anscount++;
|
||||
}
|
||||
}
|
||||
|
||||
for (na = daemon->naptr; na; na = na->next)
|
||||
if (hostname_isequal(name, na->name))
|
||||
{
|
||||
nxdomain = 0;
|
||||
if (qtype == T_NAPTR)
|
||||
{
|
||||
found = 1;
|
||||
log_query(F_CONFIG | F_RRNAME, name, NULL, "<NAPTR>");
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
|
||||
NULL, T_NAPTR, C_IN, "sszzzd",
|
||||
na->order, na->pref, na->flags, na->services, na->regexp, na->replace))
|
||||
anscount++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (intr = daemon->int_names; intr; intr = intr->next)
|
||||
if (hostname_isequal(name, intr->name))
|
||||
{
|
||||
nxdomain = 0;
|
||||
if (qtype == T_A && (addr.addr.addr4 = get_ifaddr(intr->intr)).s_addr != (in_addr_t) -1)
|
||||
{
|
||||
found = 1;
|
||||
log_query(F_FORWARD | F_CONFIG | F_IPV4, name, &addr, NULL);
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
daemon->auth_ttl, NULL, T_A, C_IN, "4", &addr))
|
||||
anscount++;
|
||||
}
|
||||
}
|
||||
|
||||
for (a = daemon->cnames; a; a = a->next)
|
||||
if (hostname_isequal(name, a->alias) )
|
||||
{
|
||||
log_query(F_CONFIG | F_CNAME, name, NULL, NULL);
|
||||
strcpy(name, a->target);
|
||||
if (!strchr(name, '.'))
|
||||
{
|
||||
strcat(name, ".");
|
||||
strcat(name, zone->domain);
|
||||
}
|
||||
found = 1;
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
daemon->auth_ttl, NULL,
|
||||
T_CNAME, C_IN, "d", name))
|
||||
anscount++;
|
||||
|
||||
goto cname_restart;
|
||||
}
|
||||
|
||||
if (qtype == T_A)
|
||||
flag = F_IPV4;
|
||||
|
||||
#ifdef HAVE_IPV6
|
||||
if (qtype == T_AAAA)
|
||||
flag = F_IPV6;
|
||||
#endif
|
||||
|
||||
if (!cut)
|
||||
{
|
||||
nxdomain = 0;
|
||||
|
||||
if (qtype == T_SOA)
|
||||
{
|
||||
soa = 1; /* inhibits auth section */
|
||||
found = 1;
|
||||
log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<SOA>");
|
||||
}
|
||||
else if (qtype == T_AXFR)
|
||||
{
|
||||
struct iname *peers;
|
||||
|
||||
if (peer_addr->sa.sa_family == AF_INET)
|
||||
peer_addr->in.sin_port = 0;
|
||||
#ifdef HAVE_IPV6
|
||||
else
|
||||
peer_addr->in6.sin6_port = 0;
|
||||
#endif
|
||||
|
||||
for (peers = daemon->auth_peers; peers; peers = peers->next)
|
||||
if (sockaddr_isequal(peer_addr, &peers->addr))
|
||||
break;
|
||||
|
||||
/* Refuse all AXFR unless --auth-sec-servers is set */
|
||||
if ((!peers && daemon->auth_peers) || !daemon->secondary_forward_server)
|
||||
{
|
||||
if (peer_addr->sa.sa_family == AF_INET)
|
||||
inet_ntop(AF_INET, &peer_addr->in.sin_addr, daemon->addrbuff, ADDRSTRLEN);
|
||||
#ifdef HAVE_IPV6
|
||||
else
|
||||
inet_ntop(AF_INET6, &peer_addr->in6.sin6_addr, daemon->addrbuff, ADDRSTRLEN);
|
||||
#endif
|
||||
|
||||
my_syslog(LOG_WARNING, _("ignoring zone transfer request from %s"), daemon->addrbuff);
|
||||
return 0;
|
||||
}
|
||||
|
||||
soa = 1; /* inhibits auth section */
|
||||
ns = 1; /* ensure we include NS records! */
|
||||
axfr = 1;
|
||||
found = 1;
|
||||
axfroffset = nameoffset;
|
||||
log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<AXFR>");
|
||||
}
|
||||
else if (qtype == T_NS)
|
||||
{
|
||||
ns = 1; /* inhibits auth section */
|
||||
found = 1;
|
||||
log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<NS>");
|
||||
}
|
||||
}
|
||||
|
||||
if (!option_bool(OPT_DHCP_FQDN) && cut)
|
||||
{
|
||||
*cut = 0; /* remove domain part */
|
||||
|
||||
if (!strchr(name, '.') && (crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6)))
|
||||
{
|
||||
if (crecp->flags & F_DHCP)
|
||||
do
|
||||
{
|
||||
nxdomain = 0;
|
||||
if ((crecp->flags & flag) &&
|
||||
(filter_constructed_dhcp(zone, flag, &(crecp->addr.addr))))
|
||||
{
|
||||
*cut = '.'; /* restore domain part */
|
||||
log_query(crecp->flags, name, &crecp->addr.addr, record_source(crecp->uid));
|
||||
*cut = 0; /* remove domain part */
|
||||
found = 1;
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
daemon->auth_ttl, NULL, qtype, C_IN,
|
||||
qtype == T_A ? "4" : "6", &crecp->addr))
|
||||
anscount++;
|
||||
}
|
||||
} while ((crecp = cache_find_by_name(crecp, name, now, F_IPV4 | F_IPV6)));
|
||||
}
|
||||
|
||||
*cut = '.'; /* restore domain part */
|
||||
}
|
||||
|
||||
if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6)))
|
||||
{
|
||||
if ((crecp->flags & F_HOSTS) || (((crecp->flags & F_DHCP) && option_bool(OPT_DHCP_FQDN))))
|
||||
do
|
||||
{
|
||||
nxdomain = 0;
|
||||
if ((crecp->flags & flag) && filter_constructed_dhcp(zone, flag, &(crecp->addr.addr)))
|
||||
{
|
||||
log_query(crecp->flags, name, &crecp->addr.addr, record_source(crecp->uid));
|
||||
found = 1;
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
daemon->auth_ttl, NULL, qtype, C_IN,
|
||||
qtype == T_A ? "4" : "6", &crecp->addr))
|
||||
anscount++;
|
||||
}
|
||||
} while ((crecp = cache_find_by_name(crecp, name, now, F_IPV4 | F_IPV6)));
|
||||
}
|
||||
|
||||
if (!found)
|
||||
log_query(flag | F_NEG | (nxdomain ? F_NXDOMAIN : 0) | F_FORWARD | F_AUTH, name, NULL, NULL);
|
||||
|
||||
}
|
||||
|
||||
/* Add auth section */
|
||||
if (auth && zone)
|
||||
{
|
||||
char *authname;
|
||||
int newoffset, offset = 0;
|
||||
|
||||
if (!subnet)
|
||||
authname = zone->domain;
|
||||
else
|
||||
{
|
||||
/* handle NS and SOA for PTR records */
|
||||
|
||||
authname = name;
|
||||
|
||||
if (!subnet->is6)
|
||||
{
|
||||
in_addr_t a = ntohl(subnet->addr4.s_addr) >> 8;
|
||||
char *p = name;
|
||||
|
||||
if (subnet->prefixlen == 24)
|
||||
p += sprintf(p, "%d.", a & 0xff);
|
||||
a = a >> 8;
|
||||
if (subnet->prefixlen != 8)
|
||||
p += sprintf(p, "%d.", a & 0xff);
|
||||
a = a >> 8;
|
||||
p += sprintf(p, "%d.in-addr.arpa", a & 0xff);
|
||||
|
||||
}
|
||||
#ifdef HAVE_IPV6
|
||||
else
|
||||
{
|
||||
char *p = name;
|
||||
int i;
|
||||
|
||||
for (i = subnet->prefixlen-1; i >= 0; i -= 4)
|
||||
{
|
||||
int dig = ((unsigned char *)&subnet->addr6)[i>>3];
|
||||
p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
|
||||
}
|
||||
p += sprintf(p, "ip6.arpa");
|
||||
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* handle NS and SOA in auth section or for explicit queries */
|
||||
newoffset = ansp - (unsigned char *)header;
|
||||
if (((anscount == 0 && !ns) || soa) &&
|
||||
add_resource_record(header, limit, &trunc, 0, &ansp,
|
||||
daemon->auth_ttl, NULL, T_SOA, C_IN, "ddlllll",
|
||||
authname, daemon->authserver, daemon->hostmaster,
|
||||
daemon->soa_sn, daemon->soa_refresh,
|
||||
daemon->soa_retry, daemon->soa_expiry,
|
||||
daemon->auth_ttl))
|
||||
{
|
||||
offset = newoffset;
|
||||
if (soa)
|
||||
anscount++;
|
||||
else
|
||||
authcount++;
|
||||
}
|
||||
|
||||
if (anscount != 0 || ns)
|
||||
{
|
||||
struct name_list *secondary;
|
||||
|
||||
newoffset = ansp - (unsigned char *)header;
|
||||
if (add_resource_record(header, limit, &trunc, -offset, &ansp,
|
||||
daemon->auth_ttl, NULL, T_NS, C_IN, "d", offset == 0 ? authname : NULL, daemon->authserver))
|
||||
{
|
||||
if (offset == 0)
|
||||
offset = newoffset;
|
||||
if (ns)
|
||||
anscount++;
|
||||
else
|
||||
authcount++;
|
||||
}
|
||||
|
||||
if (!subnet)
|
||||
for (secondary = daemon->secondary_forward_server; secondary; secondary = secondary->next)
|
||||
if (add_resource_record(header, limit, &trunc, offset, &ansp,
|
||||
daemon->auth_ttl, NULL, T_NS, C_IN, "d", secondary->name))
|
||||
{
|
||||
if (ns)
|
||||
anscount++;
|
||||
else
|
||||
authcount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (axfr)
|
||||
{
|
||||
for (rec = daemon->mxnames; rec; rec = rec->next)
|
||||
if (in_zone(zone, rec->name, &cut))
|
||||
{
|
||||
if (cut)
|
||||
*cut = 0;
|
||||
|
||||
if (rec->issrv)
|
||||
{
|
||||
if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl,
|
||||
NULL, T_SRV, C_IN, "sssd", cut ? rec->name : NULL,
|
||||
rec->priority, rec->weight, rec->srvport, rec->target))
|
||||
|
||||
anscount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl,
|
||||
NULL, T_MX, C_IN, "sd", cut ? rec->name : NULL, rec->weight, rec->target))
|
||||
anscount++;
|
||||
}
|
||||
|
||||
/* restore config data */
|
||||
if (cut)
|
||||
*cut = '.';
|
||||
}
|
||||
|
||||
for (txt = daemon->rr; txt; txt = txt->next)
|
||||
if (in_zone(zone, txt->name, &cut))
|
||||
{
|
||||
if (cut)
|
||||
*cut = 0;
|
||||
|
||||
if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl,
|
||||
NULL, txt->class, C_IN, "t", cut ? txt->name : NULL, txt->len, txt->txt))
|
||||
anscount++;
|
||||
|
||||
/* restore config data */
|
||||
if (cut)
|
||||
*cut = '.';
|
||||
}
|
||||
|
||||
for (txt = daemon->txt; txt; txt = txt->next)
|
||||
if (txt->class == C_IN && in_zone(zone, txt->name, &cut))
|
||||
{
|
||||
if (cut)
|
||||
*cut = 0;
|
||||
|
||||
if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl,
|
||||
NULL, T_TXT, C_IN, "t", cut ? txt->name : NULL, txt->len, txt->txt))
|
||||
anscount++;
|
||||
|
||||
/* restore config data */
|
||||
if (cut)
|
||||
*cut = '.';
|
||||
}
|
||||
|
||||
for (na = daemon->naptr; na; na = na->next)
|
||||
if (in_zone(zone, na->name, &cut))
|
||||
{
|
||||
if (cut)
|
||||
*cut = 0;
|
||||
|
||||
if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl,
|
||||
NULL, T_NAPTR, C_IN, "sszzzd", cut ? na->name : NULL,
|
||||
na->order, na->pref, na->flags, na->services, na->regexp, na->replace))
|
||||
anscount++;
|
||||
|
||||
/* restore config data */
|
||||
if (cut)
|
||||
*cut = '.';
|
||||
}
|
||||
|
||||
for (intr = daemon->int_names; intr; intr = intr->next)
|
||||
if (in_zone(zone, intr->name, &cut) && (addr.addr.addr4 = get_ifaddr(intr->intr)).s_addr != (in_addr_t) -1)
|
||||
{
|
||||
if (cut)
|
||||
*cut = 0;
|
||||
|
||||
if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp,
|
||||
daemon->auth_ttl, NULL, T_A, C_IN, "4", cut ? intr->name : NULL, &addr))
|
||||
anscount++;
|
||||
|
||||
/* restore config data */
|
||||
if (cut)
|
||||
*cut = '.';
|
||||
}
|
||||
|
||||
for (a = daemon->cnames; a; a = a->next)
|
||||
if (in_zone(zone, a->alias, &cut))
|
||||
{
|
||||
strcpy(name, a->target);
|
||||
if (!strchr(name, '.'))
|
||||
{
|
||||
strcat(name, ".");
|
||||
strcat(name, zone->domain);
|
||||
}
|
||||
|
||||
if (cut)
|
||||
*cut = 0;
|
||||
|
||||
if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp,
|
||||
daemon->auth_ttl, NULL,
|
||||
T_CNAME, C_IN, "d", cut ? a->alias : NULL, name))
|
||||
anscount++;
|
||||
}
|
||||
|
||||
cache_enumerate(1);
|
||||
while ((crecp = cache_enumerate(0)))
|
||||
{
|
||||
if ((crecp->flags & (F_IPV4 | F_IPV6)) &&
|
||||
!(crecp->flags & (F_NEG | F_NXDOMAIN)) &&
|
||||
(crecp->flags & F_FORWARD))
|
||||
{
|
||||
if ((crecp->flags & F_DHCP) && !option_bool(OPT_DHCP_FQDN))
|
||||
{
|
||||
char *cache_name = cache_get_name(crecp);
|
||||
if (!strchr(cache_name, '.') && filter_constructed_dhcp(zone, (crecp->flags & (F_IPV6 | F_IPV4)), &(crecp->addr.addr)))
|
||||
{
|
||||
qtype = T_A;
|
||||
#ifdef HAVE_IPV6
|
||||
if (crecp->flags & F_IPV6)
|
||||
qtype = T_AAAA;
|
||||
#endif
|
||||
if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp,
|
||||
daemon->auth_ttl, NULL, qtype, C_IN,
|
||||
(crecp->flags & F_IPV4) ? "4" : "6", cache_name, &crecp->addr))
|
||||
anscount++;
|
||||
}
|
||||
}
|
||||
|
||||
if ((crecp->flags & F_HOSTS) || (((crecp->flags & F_DHCP) && option_bool(OPT_DHCP_FQDN))))
|
||||
{
|
||||
strcpy(name, cache_get_name(crecp));
|
||||
if (in_zone(zone, name, &cut) && filter_constructed_dhcp(zone, (crecp->flags & (F_IPV6 | F_IPV4)), &(crecp->addr.addr)))
|
||||
{
|
||||
qtype = T_A;
|
||||
#ifdef HAVE_IPV6
|
||||
if (crecp->flags & F_IPV6)
|
||||
qtype = T_AAAA;
|
||||
#endif
|
||||
if (cut)
|
||||
*cut = 0;
|
||||
|
||||
if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp,
|
||||
daemon->auth_ttl, NULL, qtype, C_IN,
|
||||
(crecp->flags & F_IPV4) ? "4" : "6", cut ? name : NULL, &crecp->addr))
|
||||
anscount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* repeat SOA as last record */
|
||||
if (add_resource_record(header, limit, &trunc, axfroffset, &ansp,
|
||||
daemon->auth_ttl, NULL, T_SOA, C_IN, "ddlllll",
|
||||
daemon->authserver, daemon->hostmaster,
|
||||
daemon->soa_sn, daemon->soa_refresh,
|
||||
daemon->soa_retry, daemon->soa_expiry,
|
||||
daemon->auth_ttl))
|
||||
anscount++;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* done all questions, set up header and return length of result */
|
||||
/* clear authoritative and truncated flags, set QR flag */
|
||||
header->hb3 = (header->hb3 & ~(HB3_AA | HB3_TC)) | HB3_QR;
|
||||
/* clear RA flag */
|
||||
header->hb4 &= ~HB4_RA;
|
||||
|
||||
/* authoritive */
|
||||
if (auth)
|
||||
header->hb3 |= HB3_AA;
|
||||
|
||||
/* truncation */
|
||||
if (trunc)
|
||||
header->hb3 |= HB3_TC;
|
||||
|
||||
if (anscount == 0 && auth && nxdomain)
|
||||
SET_RCODE(header, NXDOMAIN);
|
||||
else
|
||||
SET_RCODE(header, NOERROR); /* no error */
|
||||
header->ancount = htons(anscount);
|
||||
header->nscount = htons(authcount);
|
||||
header->arcount = htons(0);
|
||||
return ansp - (unsigned char *)header;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
15
src/bpf.c
15
src/bpf.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2013 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,6 +20,7 @@
|
||||
#include <ifaddrs.h>
|
||||
|
||||
#if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
|
||||
#include <sys/param.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <net/route.h>
|
||||
#include <net/if_dl.h>
|
||||
@@ -110,7 +111,7 @@ int iface_enumerate(int family, void *parm, int (*callback)())
|
||||
{
|
||||
int iface_index = if_nametoindex(addrs->ifa_name);
|
||||
|
||||
if (iface_index == 0)
|
||||
if (iface_index == 0 || !addrs->ifa_addr || !addrs->ifa_netmask)
|
||||
continue;
|
||||
|
||||
if (family == AF_INET)
|
||||
@@ -118,8 +119,11 @@ int iface_enumerate(int family, void *parm, int (*callback)())
|
||||
struct in_addr addr, netmask, broadcast;
|
||||
addr = ((struct sockaddr_in *) addrs->ifa_addr)->sin_addr;
|
||||
netmask = ((struct sockaddr_in *) addrs->ifa_netmask)->sin_addr;
|
||||
broadcast = ((struct sockaddr_in *) addrs->ifa_broadaddr)->sin_addr;
|
||||
if (!((*callback)(addr, iface_index, netmask, broadcast, parm)))
|
||||
if (addrs->ifa_broadaddr)
|
||||
broadcast = ((struct sockaddr_in *) addrs->ifa_broadaddr)->sin_addr;
|
||||
else
|
||||
broadcast.s_addr = 0;
|
||||
if (!((*callback)(addr, iface_index, NULL, netmask, broadcast, parm)))
|
||||
goto err;
|
||||
}
|
||||
#ifdef HAVE_IPV6
|
||||
@@ -146,7 +150,8 @@ int iface_enumerate(int family, void *parm, int (*callback)())
|
||||
addr->s6_addr[3] = 0;
|
||||
}
|
||||
|
||||
if (!((*callback)(addr, prefix, scope_id, iface_index, 0, parm)))
|
||||
/* preferred and valid times == forever until we known how to dtermine them. */
|
||||
if (!((*callback)(addr, prefix, scope_id, iface_index, 0, -1, -1, parm)))
|
||||
goto err;
|
||||
}
|
||||
#endif
|
||||
|
||||
373
src/cache.c
373
src/cache.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -25,6 +25,9 @@ static int cache_inserted = 0, cache_live_freed = 0, insert_error;
|
||||
static union bigname *big_free = NULL;
|
||||
static int bignames_left, hash_size;
|
||||
static int uid = 0;
|
||||
#ifdef HAVE_DNSSEC
|
||||
static struct keydata *keyblock_free = NULL;
|
||||
#endif
|
||||
|
||||
/* type->string mapping: this is also used by the name-hash function as a mixing table. */
|
||||
static const struct {
|
||||
@@ -190,6 +193,10 @@ static void cache_free(struct crec *crecp)
|
||||
big_free = crecp->name.bname;
|
||||
crecp->flags &= ~F_BIGNAME;
|
||||
}
|
||||
#ifdef HAVE_DNSSEC
|
||||
else if (crecp->flags & (F_DNSKEY | F_DS))
|
||||
keydata_free(crecp->addr.key.keydata);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* insert a new cache entry at the head of the list (youngest entry) */
|
||||
@@ -228,12 +235,39 @@ char *cache_get_name(struct crec *crecp)
|
||||
return crecp->name.sname;
|
||||
}
|
||||
|
||||
struct crec *cache_enumerate(int init)
|
||||
{
|
||||
static int bucket;
|
||||
static struct crec *cache;
|
||||
|
||||
if (init)
|
||||
{
|
||||
bucket = 0;
|
||||
cache = NULL;
|
||||
}
|
||||
else if (cache && cache->hash_next)
|
||||
cache = cache->hash_next;
|
||||
else
|
||||
{
|
||||
cache = NULL;
|
||||
while (bucket < hash_size)
|
||||
if ((cache = hash_table[bucket++]))
|
||||
break;
|
||||
}
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
static int is_outdated_cname_pointer(struct crec *crecp)
|
||||
{
|
||||
if (!(crecp->flags & F_CNAME))
|
||||
return 0;
|
||||
|
||||
if (crecp->addr.cname.cache && crecp->addr.cname.uid == crecp->addr.cname.cache->uid)
|
||||
/* NB. record may be reused as DS or DNSKEY, where uid is
|
||||
overloaded for something completely different */
|
||||
if (crecp->addr.cname.cache &&
|
||||
(crecp->addr.cname.cache->flags & (F_IPV4 | F_IPV6 | F_CNAME)) &&
|
||||
crecp->addr.cname.uid == crecp->addr.cname.cache->uid)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
@@ -280,7 +314,7 @@ static int cache_scan_free(char *name, struct all_addr *addr, time_t now, unsign
|
||||
}
|
||||
}
|
||||
else if ((crecp->flags & F_FORWARD) &&
|
||||
((flags & crecp->flags & (F_IPV4 | F_IPV6)) || ((crecp->flags | flags) & F_CNAME)) &&
|
||||
((flags & crecp->flags & F_TYPE) || ((crecp->flags | flags) & F_CNAME)) &&
|
||||
hostname_isequal(cache_get_name(crecp), name))
|
||||
{
|
||||
if (crecp->flags & (F_HOSTS | F_DHCP))
|
||||
@@ -360,7 +394,12 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
|
||||
int freed_all = flags & F_REVERSE;
|
||||
int free_avail = 0;
|
||||
|
||||
log_query(flags | F_UPSTREAM, name, addr, NULL);
|
||||
if (daemon->max_cache_ttl != 0 && daemon->max_cache_ttl < ttl)
|
||||
ttl = daemon->max_cache_ttl;
|
||||
|
||||
/* Don't log keys */
|
||||
if (flags & (F_IPV4 | F_IPV6))
|
||||
log_query(flags | F_UPSTREAM, name, addr, NULL);
|
||||
|
||||
/* if previous insertion failed give up now. */
|
||||
if (insert_error)
|
||||
@@ -452,9 +491,7 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
|
||||
|
||||
if (addr)
|
||||
new->addr.addr = *addr;
|
||||
else
|
||||
new->addr.cname.cache = NULL;
|
||||
|
||||
|
||||
new->ttd = now + (time_t)ttl;
|
||||
new->next = new_chain;
|
||||
new_chain = new;
|
||||
@@ -634,12 +671,29 @@ struct crec *cache_find_by_addr(struct crec *crecp, struct all_addr *addr,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrlen,
|
||||
unsigned short flags, int index, struct crec **rhash, int hashsz)
|
||||
static void add_hosts_cname(struct crec *target)
|
||||
{
|
||||
struct crec *lookup = cache_find_by_name(NULL, cache->name.sname, 0, flags & (F_IPV4 | F_IPV6));
|
||||
int i, nameexists = 0;
|
||||
struct crec *crec;
|
||||
struct cname *a;
|
||||
|
||||
for (a = daemon->cnames; a; a = a->next)
|
||||
if (hostname_isequal(cache_get_name(target), a->target) &&
|
||||
(crec = whine_malloc(sizeof(struct crec))))
|
||||
{
|
||||
crec->flags = F_FORWARD | F_IMMORTAL | F_NAMEP | F_HOSTS | F_CNAME;
|
||||
crec->name.namep = a->alias;
|
||||
crec->addr.cname.cache = target;
|
||||
crec->addr.cname.uid = target->uid;
|
||||
cache_hash(crec);
|
||||
add_hosts_cname(crec); /* handle chains */
|
||||
}
|
||||
}
|
||||
|
||||
static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrlen,
|
||||
int index, struct crec **rhash, int hashsz)
|
||||
{
|
||||
struct crec *lookup = cache_find_by_name(NULL, cache_get_name(cache), 0, cache->flags & (F_IPV4 | F_IPV6));
|
||||
int i, nameexists = 0;
|
||||
unsigned int j;
|
||||
|
||||
/* Remove duplicates in hosts files. */
|
||||
@@ -670,10 +724,10 @@ static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrl
|
||||
j = (j*2 +((unsigned char *)addr)[i]) % hashsz;
|
||||
|
||||
for (lookup = rhash[j]; lookup; lookup = lookup->next)
|
||||
if ((lookup->flags & flags & (F_IPV4 | F_IPV6)) &&
|
||||
if ((lookup->flags & cache->flags & (F_IPV4 | F_IPV6)) &&
|
||||
memcmp(&lookup->addr.addr, addr, addrlen) == 0)
|
||||
{
|
||||
flags &= ~F_REVERSE;
|
||||
cache->flags &= ~F_REVERSE;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -684,23 +738,13 @@ static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrl
|
||||
rhash[j] = cache;
|
||||
}
|
||||
|
||||
cache->flags = flags;
|
||||
cache->uid = index;
|
||||
memcpy(&cache->addr.addr, addr, addrlen);
|
||||
cache_hash(cache);
|
||||
|
||||
/* don't need to do alias stuff for second and subsequent addresses. */
|
||||
if (!nameexists)
|
||||
for (a = daemon->cnames; a; a = a->next)
|
||||
if (hostname_isequal(cache->name.sname, a->target) &&
|
||||
(lookup = whine_malloc(sizeof(struct crec))))
|
||||
{
|
||||
lookup->flags = F_FORWARD | F_IMMORTAL | F_NAMEP | F_HOSTS | F_CNAME;
|
||||
lookup->name.namep = a->alias;
|
||||
lookup->addr.cname.cache = cache;
|
||||
lookup->addr.cname.uid = index;
|
||||
cache_hash(lookup);
|
||||
}
|
||||
add_hosts_cname(cache);
|
||||
}
|
||||
|
||||
static int eatspace(FILE *f)
|
||||
@@ -771,26 +815,19 @@ static int read_hostsfile(char *filename, int index, int cache_size, struct crec
|
||||
{
|
||||
lineno++;
|
||||
|
||||
#ifdef HAVE_IPV6
|
||||
if (inet_pton(AF_INET, token, &addr) > 0)
|
||||
{
|
||||
flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4;
|
||||
addrlen = INADDRSZ;
|
||||
domain_suffix = get_domain(addr.addr.addr4);
|
||||
}
|
||||
#ifdef HAVE_IPV6
|
||||
else if (inet_pton(AF_INET6, token, &addr) > 0)
|
||||
{
|
||||
flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6;
|
||||
addrlen = IN6ADDRSZ;
|
||||
domain_suffix = get_domain6(&addr.addr.addr6);
|
||||
}
|
||||
#else
|
||||
if ((addr.addr.addr4.s_addr = inet_addr(token)) != (in_addr_t) -1)
|
||||
{
|
||||
flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4;
|
||||
addrlen = INADDRSZ;
|
||||
domain_suffix = get_domain(addr.addr.addr4);
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
@@ -830,13 +867,15 @@ static int read_hostsfile(char *filename, int index, int cache_size, struct crec
|
||||
strcpy(cache->name.sname, canon);
|
||||
strcat(cache->name.sname, ".");
|
||||
strcat(cache->name.sname, domain_suffix);
|
||||
add_hosts_entry(cache, &addr, addrlen, flags, index, rhash, hashsz);
|
||||
cache->flags = flags;
|
||||
add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz);
|
||||
name_count++;
|
||||
}
|
||||
if ((cache = whine_malloc(sizeof(struct crec) + strlen(canon)+1-SMALLDNAME)))
|
||||
{
|
||||
strcpy(cache->name.sname, canon);
|
||||
add_hosts_entry(cache, &addr, addrlen, flags, index, rhash, hashsz);
|
||||
cache->flags = flags;
|
||||
add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz);
|
||||
name_count++;
|
||||
}
|
||||
free(canon);
|
||||
@@ -860,6 +899,8 @@ void cache_reload(void)
|
||||
struct crec *cache, **up, *tmp;
|
||||
int revhashsz, i, total_size = daemon->cachesize;
|
||||
struct hostsfile *ah;
|
||||
struct host_record *hr;
|
||||
struct name_list *nl;
|
||||
|
||||
cache_inserted = cache_live_freed = 0;
|
||||
|
||||
@@ -886,6 +927,34 @@ void cache_reload(void)
|
||||
up = &cache->hash_next;
|
||||
}
|
||||
|
||||
/* borrow the packet buffer for a temporary by-address hash */
|
||||
memset(daemon->packet, 0, daemon->packet_buff_sz);
|
||||
revhashsz = daemon->packet_buff_sz / sizeof(struct crec *);
|
||||
/* we overwrote the buffer... */
|
||||
daemon->srv_save = NULL;
|
||||
|
||||
/* Do host_records in config. */
|
||||
for (hr = daemon->host_records; hr; hr = hr->next)
|
||||
for (nl = hr->names; nl; nl = nl->next)
|
||||
{
|
||||
if (hr->addr.s_addr != 0 &&
|
||||
(cache = whine_malloc(sizeof(struct crec))))
|
||||
{
|
||||
cache->name.namep = nl->name;
|
||||
cache->flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4 | F_NAMEP | F_CONFIG;
|
||||
add_hosts_entry(cache, (struct all_addr *)&hr->addr, INADDRSZ, 0, (struct crec **)daemon->packet, revhashsz);
|
||||
}
|
||||
#ifdef HAVE_IPV6
|
||||
if (!IN6_IS_ADDR_UNSPECIFIED(&hr->addr6) &&
|
||||
(cache = whine_malloc(sizeof(struct crec))))
|
||||
{
|
||||
cache->name.namep = nl->name;
|
||||
cache->flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6 | F_NAMEP | F_CONFIG;
|
||||
add_hosts_entry(cache, (struct all_addr *)&hr->addr6, IN6ADDRSZ, 0, (struct crec **)daemon->packet, revhashsz);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (option_bool(OPT_NO_HOSTS) && !daemon->addn_hosts)
|
||||
{
|
||||
if (daemon->cachesize > 0)
|
||||
@@ -893,12 +962,6 @@ void cache_reload(void)
|
||||
return;
|
||||
}
|
||||
|
||||
/* borrow the packet buffer for a temporary by-address hash */
|
||||
memset(daemon->packet, 0, daemon->packet_buff_sz);
|
||||
revhashsz = daemon->packet_buff_sz / sizeof(struct crec *);
|
||||
/* we overwrote the buffer... */
|
||||
daemon->srv_save = NULL;
|
||||
|
||||
if (!option_bool(OPT_NO_HOSTS))
|
||||
total_size = read_hostsfile(HOSTSFILE, 0, total_size, (struct crec **)daemon->packet, revhashsz);
|
||||
|
||||
@@ -908,38 +971,6 @@ void cache_reload(void)
|
||||
total_size = read_hostsfile(ah->fname, ah->index, total_size, (struct crec **)daemon->packet, revhashsz);
|
||||
}
|
||||
|
||||
char *get_domain(struct in_addr addr)
|
||||
{
|
||||
struct cond_domain *c;
|
||||
|
||||
for (c = daemon->cond_domain; c; c = c->next)
|
||||
if (!c->is6 &&
|
||||
ntohl(addr.s_addr) >= ntohl(c->start.s_addr) &&
|
||||
ntohl(addr.s_addr) <= ntohl(c->end.s_addr))
|
||||
return c->domain;
|
||||
|
||||
return daemon->domain_suffix;
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_IPV6
|
||||
char *get_domain6(struct in6_addr *addr)
|
||||
{
|
||||
struct cond_domain *c;
|
||||
|
||||
u64 addrpart = addr6part(addr);
|
||||
|
||||
for (c = daemon->cond_domain; c; c = c->next)
|
||||
if (c->is6 &&
|
||||
is_same_net6(addr, &c->start6, 64) &&
|
||||
addrpart >= addr6part(&c->start6) &&
|
||||
addrpart <= addr6part(&c->end6))
|
||||
return c->domain;
|
||||
|
||||
return daemon->domain_suffix;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DHCP
|
||||
struct in_addr a_record_from_hosts(char *name, time_t now)
|
||||
{
|
||||
@@ -973,13 +1004,41 @@ void cache_unhash_dhcp(void)
|
||||
up = &cache->hash_next;
|
||||
}
|
||||
|
||||
static void add_dhcp_cname(struct crec *target, time_t ttd)
|
||||
{
|
||||
struct crec *aliasc;
|
||||
struct cname *a;
|
||||
|
||||
for (a = daemon->cnames; a; a = a->next)
|
||||
if (hostname_isequal(cache_get_name(target), a->target))
|
||||
{
|
||||
if ((aliasc = dhcp_spare))
|
||||
dhcp_spare = dhcp_spare->next;
|
||||
else /* need new one */
|
||||
aliasc = whine_malloc(sizeof(struct crec));
|
||||
|
||||
if (aliasc)
|
||||
{
|
||||
aliasc->flags = F_FORWARD | F_NAMEP | F_DHCP | F_CNAME;
|
||||
if (ttd == 0)
|
||||
aliasc->flags |= F_IMMORTAL;
|
||||
else
|
||||
aliasc->ttd = ttd;
|
||||
aliasc->name.namep = a->alias;
|
||||
aliasc->addr.cname.cache = target;
|
||||
aliasc->addr.cname.uid = target->uid;
|
||||
cache_hash(aliasc);
|
||||
add_dhcp_cname(aliasc, ttd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cache_add_dhcp_entry(char *host_name, int prot,
|
||||
struct all_addr *host_address, time_t ttd)
|
||||
{
|
||||
struct crec *crec = NULL, *aliasc;
|
||||
struct crec *crec = NULL, *fail_crec = NULL;
|
||||
unsigned short flags = F_IPV4;
|
||||
int in_hosts = 0;
|
||||
struct cname *a;
|
||||
size_t addrlen = sizeof(struct in_addr);
|
||||
|
||||
#ifdef HAVE_IPV6
|
||||
@@ -990,29 +1049,21 @@ void cache_add_dhcp_entry(char *host_name, int prot,
|
||||
}
|
||||
#endif
|
||||
|
||||
inet_ntop(prot, host_address, daemon->addrbuff, ADDRSTRLEN);
|
||||
|
||||
while ((crec = cache_find_by_name(crec, host_name, 0, flags | F_CNAME)))
|
||||
{
|
||||
/* check all addresses associated with name */
|
||||
if (crec->flags & F_HOSTS)
|
||||
{
|
||||
/* if in hosts, don't need DHCP record */
|
||||
in_hosts = 1;
|
||||
|
||||
inet_ntop(prot, host_address, daemon->addrbuff, ADDRSTRLEN);
|
||||
if (crec->flags & F_CNAME)
|
||||
|
||||
my_syslog(MS_DHCP | LOG_WARNING,
|
||||
_("%s is a CNAME, not giving it to the DHCP lease of %s"),
|
||||
host_name, daemon->addrbuff);
|
||||
else if (memcmp(&crec->addr.addr, host_address, addrlen) != 0)
|
||||
{
|
||||
inet_ntop(prot, &crec->addr.addr, daemon->namebuff, MAXDNAME);
|
||||
my_syslog(MS_DHCP | LOG_WARNING,
|
||||
_("not giving name %s to the DHCP lease of %s because "
|
||||
"the name exists in %s with address %s"),
|
||||
host_name, daemon->addrbuff,
|
||||
record_source(crec->uid), daemon->namebuff);
|
||||
}
|
||||
else if (memcmp(&crec->addr.addr, host_address, addrlen) == 0)
|
||||
in_hosts = 1;
|
||||
else
|
||||
fail_crec = crec;
|
||||
}
|
||||
else if (!(crec->flags & F_DHCP))
|
||||
{
|
||||
@@ -1022,21 +1073,34 @@ void cache_add_dhcp_entry(char *host_name, int prot,
|
||||
}
|
||||
}
|
||||
|
||||
if (in_hosts)
|
||||
/* if in hosts, don't need DHCP record */
|
||||
if (in_hosts)
|
||||
return;
|
||||
|
||||
if ((crec = cache_find_by_addr(NULL, (struct all_addr *)host_address, 0, flags)))
|
||||
{
|
||||
if (crec->flags & F_NEG)
|
||||
{
|
||||
flags |= F_REVERSE;
|
||||
cache_scan_free(NULL, (struct all_addr *)host_address, 0, flags);
|
||||
}
|
||||
}
|
||||
else
|
||||
flags |= F_REVERSE;
|
||||
|
||||
if ((crec = dhcp_spare))
|
||||
|
||||
/* Name in hosts, address doesn't match */
|
||||
if (fail_crec)
|
||||
{
|
||||
inet_ntop(prot, &fail_crec->addr.addr, daemon->namebuff, MAXDNAME);
|
||||
my_syslog(MS_DHCP | LOG_WARNING,
|
||||
_("not giving name %s to the DHCP lease of %s because "
|
||||
"the name exists in %s with address %s"),
|
||||
host_name, daemon->addrbuff,
|
||||
record_source(fail_crec->uid), daemon->namebuff);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((crec = cache_find_by_addr(NULL, (struct all_addr *)host_address, 0, flags)))
|
||||
{
|
||||
if (crec->flags & F_NEG)
|
||||
{
|
||||
flags |= F_REVERSE;
|
||||
cache_scan_free(NULL, (struct all_addr *)host_address, 0, flags);
|
||||
}
|
||||
}
|
||||
else
|
||||
flags |= F_REVERSE;
|
||||
|
||||
if ((crec = dhcp_spare))
|
||||
dhcp_spare = dhcp_spare->next;
|
||||
else /* need new one */
|
||||
crec = whine_malloc(sizeof(struct crec));
|
||||
@@ -1053,27 +1117,7 @@ void cache_add_dhcp_entry(char *host_name, int prot,
|
||||
crec->uid = uid++;
|
||||
cache_hash(crec);
|
||||
|
||||
for (a = daemon->cnames; a; a = a->next)
|
||||
if (hostname_isequal(host_name, a->target))
|
||||
{
|
||||
if ((aliasc = dhcp_spare))
|
||||
dhcp_spare = dhcp_spare->next;
|
||||
else /* need new one */
|
||||
aliasc = whine_malloc(sizeof(struct crec));
|
||||
|
||||
if (aliasc)
|
||||
{
|
||||
aliasc->flags = F_FORWARD | F_NAMEP | F_DHCP | F_CNAME;
|
||||
if (ttd == 0)
|
||||
aliasc->flags |= F_IMMORTAL;
|
||||
else
|
||||
aliasc->ttd = ttd;
|
||||
aliasc->name.namep = a->alias;
|
||||
aliasc->addr.cname.cache = crec;
|
||||
aliasc->addr.cname.uid = crec->uid;
|
||||
cache_hash(aliasc);
|
||||
}
|
||||
}
|
||||
add_dhcp_cname(crec, ttd);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1131,22 +1175,35 @@ void dump_cache(time_t now)
|
||||
if (!is_outdated_cname_pointer(cache))
|
||||
a = cache_get_name(cache->addr.cname.cache);
|
||||
}
|
||||
#ifdef HAVE_IPV6
|
||||
#ifdef HAVE_DNSSEC
|
||||
else if (cache->flags & F_DNSKEY)
|
||||
{
|
||||
a = daemon->addrbuff;
|
||||
sprintf(a, "%3u %u", cache->addr.key.algo, cache->uid);
|
||||
}
|
||||
else if (cache->flags & F_DS)
|
||||
{
|
||||
a = daemon->addrbuff;
|
||||
sprintf(a, "%5u %3u %3u %u", cache->addr.key.flags_or_keyid,
|
||||
cache->addr.key.algo, cache->addr.key.digest, cache->uid);
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
a = daemon->addrbuff;
|
||||
if (cache->flags & F_IPV4)
|
||||
inet_ntop(AF_INET, &cache->addr.addr, a, ADDRSTRLEN);
|
||||
#ifdef HAVE_IPV6
|
||||
else if (cache->flags & F_IPV6)
|
||||
inet_ntop(AF_INET6, &cache->addr.addr, a, ADDRSTRLEN);
|
||||
}
|
||||
#else
|
||||
else
|
||||
a = inet_ntoa(cache->addr.addr.addr.addr4);
|
||||
#endif
|
||||
p += sprintf(p, "%-30.30s %s%s%s%s%s%s%s%s%s%s ", a,
|
||||
}
|
||||
|
||||
p += sprintf(p, "%-30.30s %s%s%s%s%s%s%s%s%s%s%s%s%s ", a,
|
||||
cache->flags & F_IPV4 ? "4" : "",
|
||||
cache->flags & F_IPV6 ? "6" : "",
|
||||
cache->flags & F_DNSKEY ? "K" : "",
|
||||
cache->flags & F_DS ? "S" : "",
|
||||
cache->flags & F_CNAME ? "C" : "",
|
||||
cache->flags & F_FORWARD ? "F" : " ",
|
||||
cache->flags & F_REVERSE ? "R" : " ",
|
||||
@@ -1154,7 +1211,8 @@ void dump_cache(time_t now)
|
||||
cache->flags & F_DHCP ? "D" : " ",
|
||||
cache->flags & F_NEG ? "N" : " ",
|
||||
cache->flags & F_NXDOMAIN ? "X" : " ",
|
||||
cache->flags & F_HOSTS ? "H" : " ");
|
||||
cache->flags & F_HOSTS ? "H" : " ",
|
||||
cache->flags & F_DNSSECOK ? "V" : " ");
|
||||
#ifdef HAVE_BROKEN_RTC
|
||||
p += sprintf(p, "%lu", cache->flags & F_IMMORTAL ? 0: (unsigned long)(cache->ttd - now));
|
||||
#else
|
||||
@@ -1181,14 +1239,14 @@ char *record_source(int index)
|
||||
return "<unknown>";
|
||||
}
|
||||
|
||||
void querystr(char *str, unsigned short type)
|
||||
void querystr(char *desc, char *str, unsigned short type)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
sprintf(str, "query[type=%d]", type);
|
||||
sprintf(str, "%s[type=%d]", desc, type);
|
||||
for (i = 0; i < (sizeof(typestr)/sizeof(typestr[0])); i++)
|
||||
if (typestr[i].type == type)
|
||||
sprintf(str,"query[%s]", typestr[i].name);
|
||||
sprintf(str,"%s[%s]", desc, typestr[i].name);
|
||||
}
|
||||
|
||||
void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
|
||||
@@ -1249,6 +1307,8 @@ void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
|
||||
source = arg;
|
||||
else if (flags & F_UPSTREAM)
|
||||
source = "reply";
|
||||
else if (flags & F_AUTH)
|
||||
source = "auth";
|
||||
else if (flags & F_SERVER)
|
||||
{
|
||||
source = "forwarded";
|
||||
@@ -1268,3 +1328,50 @@ void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
|
||||
my_syslog(LOG_INFO, "%s %s %s %s", source, name, verb, dest);
|
||||
}
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
struct keydata *keydata_alloc(char *data, size_t len)
|
||||
{
|
||||
struct keydata *block, *ret = NULL;
|
||||
struct keydata **prev = &ret;
|
||||
while (len > 0)
|
||||
{
|
||||
if (keyblock_free)
|
||||
{
|
||||
block = keyblock_free;
|
||||
keyblock_free = block->next;
|
||||
}
|
||||
else
|
||||
block = whine_malloc(sizeof(struct keydata));
|
||||
|
||||
if (!block)
|
||||
{
|
||||
/* failed to alloc, free partial chain */
|
||||
keydata_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(block->key, data, len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len);
|
||||
data += KEYBLOCK_LEN;
|
||||
len -= KEYBLOCK_LEN;
|
||||
*prev = block;
|
||||
prev = &block->next;
|
||||
block->next = NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void keydata_free(struct keydata *blocks)
|
||||
{
|
||||
struct keydata *tmp;
|
||||
|
||||
if (blocks)
|
||||
{
|
||||
for (tmp = blocks; tmp->next; tmp = tmp->next);
|
||||
tmp->next = keyblock_free;
|
||||
keyblock_free = blocks;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
61
src/config.h
61
src/config.h
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2013 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 @@
|
||||
#define MAX_PROCS 20 /* max no children for TCP requests */
|
||||
#define CHILD_LIFETIME 150 /* secs 'till terminated (RFC1035 suggests > 120s) */
|
||||
#define EDNS_PKTSZ 4096 /* default max EDNS.0 UDP packet from RFC5625 */
|
||||
#define KEYBLOCK_LEN 140 /* choose to mininise fragmentation when storing DNSSEC keys */
|
||||
#define 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 */
|
||||
@@ -32,7 +33,6 @@
|
||||
#define SMALLDNAME 40 /* most domain names are smaller than this */
|
||||
#define HOSTSFILE "/etc/hosts"
|
||||
#define ETHERSFILE "/etc/ethers"
|
||||
#define RUNFILE "/var/run/dnsmasq.pid"
|
||||
#define DEFLEASE 3600 /* default lease time, 1 hour */
|
||||
#define CHUSER "nobody"
|
||||
#define CHGRP "dip"
|
||||
@@ -40,9 +40,14 @@
|
||||
#define LOG_MAX 5 /* log-queue length */
|
||||
#define RANDFILE "/dev/urandom"
|
||||
#define EDNS0_OPTION_MAC 5 /* dyndns.org temporary assignment */
|
||||
#define DNSMASQ_SERVICE "uk.org.thekelleys.dnsmasq" /* DBUS interface specifics */
|
||||
#define DNSMASQ_SERVICE "uk.org.thekelleys.dnsmasq" /* Default - may be overridden by config */
|
||||
#define DNSMASQ_PATH "/uk/org/thekelleys/dnsmasq"
|
||||
|
||||
#define AUTH_TTL 600 /* default TTL for auth DNS */
|
||||
#define SOA_REFRESH 1200 /* SOA refresh default */
|
||||
#define SOA_RETRY 180 /* SOA retry default */
|
||||
#define SOA_EXPIRY 1209600 /* SOA expiry default */
|
||||
#define RA_INTERVAL 600 /* Send unsolicited RA's this often when not provoked. */
|
||||
|
||||
/* compile-time options: uncomment below to enable or do eg.
|
||||
make COPTS=-DHAVE_BROKEN_RTC
|
||||
|
||||
@@ -92,12 +97,22 @@ HAVE_CONNTRACK
|
||||
a build-dependency on libnetfilter_conntrack, but the resulting binary will
|
||||
still run happily on a kernel without conntrack support.
|
||||
|
||||
HAVE_IPSET
|
||||
define this to include the ability to selectively add resolved ip addresses
|
||||
to given ipsets.
|
||||
|
||||
HAVE_AUTH
|
||||
define this to include the facility to act as an authoritative DNS
|
||||
server for one or more zones.
|
||||
|
||||
|
||||
NO_IPV6
|
||||
NO_TFTP
|
||||
NO_DHCP
|
||||
NO_DHCP6
|
||||
NO_SCRIPT
|
||||
NO_LARGEFILE
|
||||
NO_AUTH
|
||||
these are avilable to explictly disable compile time options which would
|
||||
otherwise be enabled automatically (HAVE_IPV6, >2Gb file sizes) or
|
||||
which are enabled by default in the distributed source tree. Building dnsmasq
|
||||
@@ -119,6 +134,8 @@ RESOLVFILE
|
||||
#define HAVE_DHCP6
|
||||
#define HAVE_TFTP
|
||||
#define HAVE_SCRIPT
|
||||
#define HAVE_AUTH
|
||||
#define HAVE_IPSET
|
||||
/* #define HAVE_LUASCRIPT */
|
||||
/* #define HAVE_BROKEN_RTC */
|
||||
/* #define HAVE_DBUS */
|
||||
@@ -126,7 +143,6 @@ RESOLVFILE
|
||||
/* #define HAVE_CONNTRACK */
|
||||
|
||||
|
||||
|
||||
/* Default locations for important system files. */
|
||||
|
||||
#ifndef LEASEFILE
|
||||
@@ -157,7 +173,13 @@ RESOLVFILE
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef RUNFILE
|
||||
# if defined(__ANDROID__)
|
||||
# define RUNFILE "/data/dnsmasq.pid"
|
||||
# else
|
||||
# define RUNFILE "/var/run/dnsmasq.pid"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* platform dependent options: these are determined automatically below
|
||||
|
||||
@@ -167,7 +189,7 @@ HAVE_SOLARIS_NETWORK
|
||||
define exactly one of these to alter interaction with kernel networking.
|
||||
|
||||
HAVE_GETOPT_LONG
|
||||
defined when GNU-sty;e getopt_long available.
|
||||
defined when GNU-style getopt_long available.
|
||||
|
||||
HAVE_ARC4RANDOM
|
||||
defined if arc4random() available to get better security from DNS spoofs
|
||||
@@ -257,12 +279,12 @@ HAVE_SOCKADDR_SA_LEN
|
||||
#if defined(INET6_ADDRSTRLEN) && defined(IPV6_V6ONLY)
|
||||
# define HAVE_IPV6
|
||||
# define ADDRSTRLEN INET6_ADDRSTRLEN
|
||||
#elif defined(INET_ADDRSTRLEN)
|
||||
#else
|
||||
# if !defined(INET_ADDRSTRLEN)
|
||||
# define INET_ADDRSTRLEN 16 /* 4*3 + 3 dots + NULL */
|
||||
# endif
|
||||
# undef HAVE_IPV6
|
||||
# define ADDRSTRLEN INET_ADDRSTRLEN
|
||||
#else
|
||||
# undef HAVE_IPV6
|
||||
# define ADDRSTRLEN 16 /* 4*3 + 3 dots + NULL */
|
||||
#endif
|
||||
|
||||
|
||||
@@ -301,6 +323,13 @@ HAVE_SOCKADDR_SA_LEN
|
||||
#define HAVE_SCRIPT
|
||||
#endif
|
||||
|
||||
#ifdef NO_AUTH
|
||||
#undef HAVE_AUTH
|
||||
#endif
|
||||
|
||||
#if defined(NO_IPSET) || !defined(HAVE_LINUX_NETWORK)
|
||||
#undef HAVE_IPSET
|
||||
#endif
|
||||
|
||||
/* Define a string indicating which options are in use.
|
||||
DNSMASQP_COMPILE_OPTS is only defined in dnsmasq.c */
|
||||
@@ -359,7 +388,15 @@ static char *compile_opts =
|
||||
#ifndef HAVE_CONNTRACK
|
||||
"no-"
|
||||
#endif
|
||||
"conntrack";
|
||||
"conntrack "
|
||||
#ifndef HAVE_IPSET
|
||||
"no-"
|
||||
#endif
|
||||
"ipset "
|
||||
#ifndef HAVE_AUTH
|
||||
"no-"
|
||||
#endif
|
||||
"auth";
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2013 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
|
||||
|
||||
445
src/dbus.c
445
src/dbus.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
#include <dbus/dbus.h>
|
||||
|
||||
const char* introspection_xml =
|
||||
const char* introspection_xml_template =
|
||||
"<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n"
|
||||
"\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
|
||||
"<node name=\"" DNSMASQ_PATH "\">\n"
|
||||
@@ -29,7 +29,7 @@ const char* introspection_xml =
|
||||
" <arg name=\"data\" direction=\"out\" type=\"s\"/>\n"
|
||||
" </method>\n"
|
||||
" </interface>\n"
|
||||
" <interface name=\"" DNSMASQ_SERVICE "\">\n"
|
||||
" <interface name=\"%s\">\n"
|
||||
" <method name=\"ClearCache\">\n"
|
||||
" </method>\n"
|
||||
" <method name=\"GetVersion\">\n"
|
||||
@@ -38,6 +38,12 @@ const char* introspection_xml =
|
||||
" <method name=\"SetServers\">\n"
|
||||
" <arg name=\"servers\" direction=\"in\" type=\"av\"/>\n"
|
||||
" </method>\n"
|
||||
" <method name=\"SetDomainServers\">\n"
|
||||
" <arg name=\"servers\" direction=\"in\" type=\"as\"/>\n"
|
||||
" </method>\n"
|
||||
" <method name=\"SetServersEx\">\n"
|
||||
" <arg name=\"servers\" direction=\"in\" type=\"aas\"/>\n"
|
||||
" </method>\n"
|
||||
" <signal name=\"DhcpLeaseAdded\">\n"
|
||||
" <arg name=\"ipaddr\" type=\"s\"/>\n"
|
||||
" <arg name=\"hwaddr\" type=\"s\"/>\n"
|
||||
@@ -56,6 +62,8 @@ const char* introspection_xml =
|
||||
" </interface>\n"
|
||||
"</node>\n";
|
||||
|
||||
static char *introspection_xml = NULL;
|
||||
|
||||
struct watch {
|
||||
DBusWatch *watch;
|
||||
struct watch *next;
|
||||
@@ -83,34 +91,135 @@ static dbus_bool_t add_watch(DBusWatch *watch, void *data)
|
||||
|
||||
static void remove_watch(DBusWatch *watch, void *data)
|
||||
{
|
||||
struct watch **up, *w;
|
||||
struct watch **up, *w, *tmp;
|
||||
|
||||
for (up = &(daemon->watches), w = daemon->watches; w; w = w->next)
|
||||
if (w->watch == watch)
|
||||
{
|
||||
*up = w->next;
|
||||
free(w);
|
||||
}
|
||||
else
|
||||
up = &(w->next);
|
||||
for (up = &(daemon->watches), w = daemon->watches; w; w = tmp)
|
||||
{
|
||||
tmp = w->next;
|
||||
if (w->watch == watch)
|
||||
{
|
||||
*up = tmp;
|
||||
free(w);
|
||||
}
|
||||
else
|
||||
up = &(w->next);
|
||||
}
|
||||
|
||||
w = data; /* no warning */
|
||||
}
|
||||
|
||||
static void dbus_read_servers(DBusMessage *message)
|
||||
static void add_update_server(union mysockaddr *addr,
|
||||
union mysockaddr *source_addr,
|
||||
const char *interface,
|
||||
const char *domain)
|
||||
{
|
||||
struct server *serv;
|
||||
|
||||
/* See if there is a suitable candidate, and unmark */
|
||||
for (serv = daemon->servers; serv; serv = serv->next)
|
||||
if ((serv->flags & SERV_FROM_DBUS) &&
|
||||
(serv->flags & SERV_MARK))
|
||||
{
|
||||
if (domain)
|
||||
{
|
||||
if (!(serv->flags & SERV_HAS_DOMAIN) || !hostname_isequal(domain, serv->domain))
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (serv->flags & SERV_HAS_DOMAIN)
|
||||
continue;
|
||||
}
|
||||
|
||||
serv->flags &= ~SERV_MARK;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (!serv && (serv = whine_malloc(sizeof (struct server))))
|
||||
{
|
||||
/* Not found, create a new one. */
|
||||
memset(serv, 0, sizeof(struct server));
|
||||
|
||||
if (domain && !(serv->domain = whine_malloc(strlen(domain)+1)))
|
||||
{
|
||||
free(serv);
|
||||
serv = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
serv->next = daemon->servers;
|
||||
daemon->servers = serv;
|
||||
serv->flags = SERV_FROM_DBUS;
|
||||
if (domain)
|
||||
{
|
||||
strcpy(serv->domain, domain);
|
||||
serv->flags |= SERV_HAS_DOMAIN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (serv)
|
||||
{
|
||||
if (interface)
|
||||
strcpy(serv->interface, interface);
|
||||
else
|
||||
serv->interface[0] = 0;
|
||||
|
||||
if (source_addr->in.sin_family == AF_INET &&
|
||||
addr->in.sin_addr.s_addr == 0 &&
|
||||
serv->domain)
|
||||
serv->flags |= SERV_NO_ADDR;
|
||||
else
|
||||
{
|
||||
serv->flags &= ~SERV_NO_ADDR;
|
||||
serv->addr = *addr;
|
||||
serv->source_addr = *source_addr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void mark_dbus(void)
|
||||
{
|
||||
struct server *serv;
|
||||
|
||||
/* mark everything from DBUS */
|
||||
for (serv = daemon->servers; serv; serv = serv->next)
|
||||
if (serv->flags & SERV_FROM_DBUS)
|
||||
serv->flags |= SERV_MARK;
|
||||
}
|
||||
|
||||
static void cleanup_dbus()
|
||||
{
|
||||
struct server *serv, *tmp, **up;
|
||||
|
||||
/* unlink and free anything still marked. */
|
||||
for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp)
|
||||
{
|
||||
tmp = serv->next;
|
||||
if (serv->flags & SERV_MARK)
|
||||
{
|
||||
server_gone(serv);
|
||||
*up = serv->next;
|
||||
if (serv->domain)
|
||||
free(serv->domain);
|
||||
free(serv);
|
||||
}
|
||||
else
|
||||
up = &serv->next;
|
||||
}
|
||||
}
|
||||
|
||||
static void dbus_read_servers(DBusMessage *message)
|
||||
{
|
||||
DBusMessageIter iter;
|
||||
union mysockaddr addr, source_addr;
|
||||
char *domain;
|
||||
|
||||
dbus_message_iter_init(message, &iter);
|
||||
|
||||
/* mark everything from DBUS */
|
||||
for (serv = daemon->servers; serv; serv = serv->next)
|
||||
if (serv->flags & SERV_FROM_DBUS)
|
||||
serv->flags |= SERV_MARK;
|
||||
|
||||
|
||||
mark_dbus();
|
||||
|
||||
while (1)
|
||||
{
|
||||
int skip = 0;
|
||||
@@ -169,6 +278,7 @@ static void dbus_read_servers(DBusMessage *message)
|
||||
/* At the end */
|
||||
break;
|
||||
|
||||
/* process each domain */
|
||||
do {
|
||||
if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING)
|
||||
{
|
||||
@@ -179,83 +289,187 @@ static void dbus_read_servers(DBusMessage *message)
|
||||
domain = NULL;
|
||||
|
||||
if (!skip)
|
||||
{
|
||||
/* See if this is already there, and unmark */
|
||||
for (serv = daemon->servers; serv; serv = serv->next)
|
||||
if ((serv->flags & SERV_FROM_DBUS) &&
|
||||
(serv->flags & SERV_MARK))
|
||||
{
|
||||
if (!(serv->flags & SERV_HAS_DOMAIN) && !domain)
|
||||
{
|
||||
serv->flags &= ~SERV_MARK;
|
||||
break;
|
||||
}
|
||||
if ((serv->flags & SERV_HAS_DOMAIN) &&
|
||||
domain &&
|
||||
hostname_isequal(domain, serv->domain))
|
||||
{
|
||||
serv->flags &= ~SERV_MARK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!serv && (serv = whine_malloc(sizeof (struct server))))
|
||||
{
|
||||
/* Not found, create a new one. */
|
||||
memset(serv, 0, sizeof(struct server));
|
||||
|
||||
if (domain)
|
||||
serv->domain = whine_malloc(strlen(domain)+1);
|
||||
|
||||
if (domain && !serv->domain)
|
||||
{
|
||||
free(serv);
|
||||
serv = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
serv->next = daemon->servers;
|
||||
daemon->servers = serv;
|
||||
serv->flags = SERV_FROM_DBUS;
|
||||
if (domain)
|
||||
{
|
||||
strcpy(serv->domain, domain);
|
||||
serv->flags |= SERV_HAS_DOMAIN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (serv)
|
||||
{
|
||||
if (source_addr.in.sin_family == AF_INET &&
|
||||
addr.in.sin_addr.s_addr == 0 &&
|
||||
serv->domain)
|
||||
serv->flags |= SERV_NO_ADDR;
|
||||
else
|
||||
{
|
||||
serv->flags &= ~SERV_NO_ADDR;
|
||||
serv->addr = addr;
|
||||
serv->source_addr = source_addr;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING);
|
||||
add_update_server(&addr, &source_addr, NULL, domain);
|
||||
|
||||
} while (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING);
|
||||
}
|
||||
|
||||
|
||||
/* unlink and free anything still marked. */
|
||||
for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp)
|
||||
cleanup_dbus();
|
||||
}
|
||||
|
||||
static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
|
||||
{
|
||||
DBusMessageIter iter, array_iter, string_iter;
|
||||
DBusMessage *error = NULL;
|
||||
const char *addr_err;
|
||||
char *dup = NULL;
|
||||
|
||||
my_syslog(LOG_INFO, _("setting upstream servers from DBus"));
|
||||
|
||||
if (!dbus_message_iter_init(message, &iter))
|
||||
{
|
||||
tmp = serv->next;
|
||||
if (serv->flags & SERV_MARK)
|
||||
{
|
||||
server_gone(serv);
|
||||
*up = serv->next;
|
||||
free(serv);
|
||||
}
|
||||
else
|
||||
up = &serv->next;
|
||||
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
|
||||
"Failed to initialize dbus message iter");
|
||||
}
|
||||
|
||||
/* check that the message contains an array of arrays */
|
||||
if ((dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) ||
|
||||
(dbus_message_iter_get_element_type(&iter) != (strings ? DBUS_TYPE_STRING : DBUS_TYPE_ARRAY)))
|
||||
{
|
||||
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
|
||||
strings ? "Expected array of string" : "Expected array of string arrays");
|
||||
}
|
||||
|
||||
mark_dbus();
|
||||
|
||||
/* array_iter points to each "as" element in the outer array */
|
||||
dbus_message_iter_recurse(&iter, &array_iter);
|
||||
while (dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_INVALID)
|
||||
{
|
||||
const char *str = NULL;
|
||||
union mysockaddr addr, source_addr;
|
||||
char interface[IF_NAMESIZE];
|
||||
char *str_addr, *str_domain = NULL;
|
||||
|
||||
if (strings)
|
||||
{
|
||||
dbus_message_iter_get_basic(&array_iter, &str);
|
||||
if (!str || !strlen (str))
|
||||
{
|
||||
error = dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
|
||||
"Empty string");
|
||||
break;
|
||||
}
|
||||
|
||||
/* dup the string because it gets modified during parsing */
|
||||
if (dup)
|
||||
free(dup);
|
||||
if (!(dup = str_domain = whine_malloc(strlen(str)+1)))
|
||||
break;
|
||||
|
||||
strcpy(str_domain, str);
|
||||
|
||||
/* point to address part of old string for error message */
|
||||
if ((str_addr = strrchr(str, '/')))
|
||||
str = str_addr+1;
|
||||
|
||||
if ((str_addr = strrchr(str_domain, '/')))
|
||||
{
|
||||
if (*str_domain != '/' || str_addr == str_domain)
|
||||
{
|
||||
error = dbus_message_new_error_printf(message,
|
||||
DBUS_ERROR_INVALID_ARGS,
|
||||
"No domain terminator '%s'",
|
||||
str);
|
||||
break;
|
||||
}
|
||||
*str_addr++ = 0;
|
||||
str_domain++;
|
||||
}
|
||||
else
|
||||
{
|
||||
str_addr = str_domain;
|
||||
str_domain = NULL;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
/* check the types of the struct and its elements */
|
||||
if ((dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_ARRAY) ||
|
||||
(dbus_message_iter_get_element_type(&array_iter) != DBUS_TYPE_STRING))
|
||||
{
|
||||
error = dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
|
||||
"Expected inner array of strings");
|
||||
break;
|
||||
}
|
||||
|
||||
/* string_iter points to each "s" element in the inner array */
|
||||
dbus_message_iter_recurse(&array_iter, &string_iter);
|
||||
if (dbus_message_iter_get_arg_type(&string_iter) != DBUS_TYPE_STRING)
|
||||
{
|
||||
/* no IP address given */
|
||||
error = dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
|
||||
"Expected IP address");
|
||||
break;
|
||||
}
|
||||
|
||||
dbus_message_iter_get_basic(&string_iter, &str);
|
||||
if (!str || !strlen (str))
|
||||
{
|
||||
error = dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
|
||||
"Empty IP address");
|
||||
break;
|
||||
}
|
||||
|
||||
/* dup the string because it gets modified during parsing */
|
||||
if (dup)
|
||||
free(dup);
|
||||
if (!(dup = str_addr = whine_malloc(strlen(str)+1)))
|
||||
break;
|
||||
|
||||
strcpy(str_addr, str);
|
||||
}
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
memset(&source_addr, 0, sizeof(source_addr));
|
||||
memset(&interface, 0, sizeof(interface));
|
||||
|
||||
/* parse the IP address */
|
||||
addr_err = parse_server(str_addr, &addr, &source_addr, (char *) &interface, NULL);
|
||||
|
||||
if (addr_err)
|
||||
{
|
||||
error = dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
|
||||
"Invalid IP address '%s': %s",
|
||||
str, addr_err);
|
||||
break;
|
||||
}
|
||||
|
||||
if (strings)
|
||||
{
|
||||
char *p;
|
||||
|
||||
do {
|
||||
if (str_domain)
|
||||
{
|
||||
if ((p = strchr(str_domain, '/')))
|
||||
*p++ = 0;
|
||||
}
|
||||
else
|
||||
p = NULL;
|
||||
|
||||
add_update_server(&addr, &source_addr, interface, str_domain);
|
||||
} while ((str_domain = p));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* jump past the address to the domain list (if any) */
|
||||
dbus_message_iter_next (&string_iter);
|
||||
|
||||
/* parse domains and add each server/domain pair to the list */
|
||||
do {
|
||||
str = NULL;
|
||||
if (dbus_message_iter_get_arg_type(&string_iter) == DBUS_TYPE_STRING)
|
||||
dbus_message_iter_get_basic(&string_iter, &str);
|
||||
dbus_message_iter_next (&string_iter);
|
||||
|
||||
add_update_server(&addr, &source_addr, interface, str);
|
||||
} while (dbus_message_iter_get_arg_type(&string_iter) == DBUS_TYPE_STRING);
|
||||
}
|
||||
|
||||
/* jump to next element in outer array */
|
||||
dbus_message_iter_next(&array_iter);
|
||||
}
|
||||
|
||||
cleanup_dbus();
|
||||
|
||||
if (dup)
|
||||
free(dup);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
DBusHandlerResult message_handler(DBusConnection *connection,
|
||||
@@ -263,23 +477,27 @@ DBusHandlerResult message_handler(DBusConnection *connection,
|
||||
void *user_data)
|
||||
{
|
||||
char *method = (char *)dbus_message_get_member(message);
|
||||
|
||||
DBusMessage *reply = NULL;
|
||||
|
||||
if (dbus_message_is_method_call(message, DBUS_INTERFACE_INTROSPECTABLE, "Introspect"))
|
||||
{
|
||||
DBusMessage *reply = dbus_message_new_method_return(message);
|
||||
|
||||
dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection_xml, DBUS_TYPE_INVALID);
|
||||
dbus_connection_send (connection, reply, NULL);
|
||||
dbus_message_unref (reply);
|
||||
/* string length: "%s" provides space for termination zero */
|
||||
if (!introspection_xml &&
|
||||
(introspection_xml = whine_malloc(strlen(introspection_xml_template) + strlen(daemon->dbus_name))))
|
||||
sprintf(introspection_xml, introspection_xml_template, daemon->dbus_name);
|
||||
|
||||
if (introspection_xml)
|
||||
{
|
||||
reply = dbus_message_new_method_return(message);
|
||||
dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection_xml, DBUS_TYPE_INVALID);
|
||||
}
|
||||
}
|
||||
else if (strcmp(method, "GetVersion") == 0)
|
||||
{
|
||||
char *v = VERSION;
|
||||
DBusMessage *reply = dbus_message_new_method_return(message);
|
||||
reply = dbus_message_new_method_return(message);
|
||||
|
||||
dbus_message_append_args(reply, DBUS_TYPE_STRING, &v, DBUS_TYPE_INVALID);
|
||||
dbus_connection_send (connection, reply, NULL);
|
||||
dbus_message_unref (reply);
|
||||
}
|
||||
else if (strcmp(method, "SetServers") == 0)
|
||||
{
|
||||
@@ -287,6 +505,16 @@ DBusHandlerResult message_handler(DBusConnection *connection,
|
||||
dbus_read_servers(message);
|
||||
check_servers();
|
||||
}
|
||||
else if (strcmp(method, "SetServersEx") == 0)
|
||||
{
|
||||
reply = dbus_read_servers_ex(message, 0);
|
||||
check_servers();
|
||||
}
|
||||
else if (strcmp(method, "SetDomainServers") == 0)
|
||||
{
|
||||
reply = dbus_read_servers_ex(message, 1);
|
||||
check_servers();
|
||||
}
|
||||
else if (strcmp(method, "ClearCache") == 0)
|
||||
clear_cache_and_reload(dnsmasq_time());
|
||||
else
|
||||
@@ -294,8 +522,17 @@ DBusHandlerResult message_handler(DBusConnection *connection,
|
||||
|
||||
method = user_data; /* no warning */
|
||||
|
||||
/* If no reply or no error, return nothing */
|
||||
if (!reply)
|
||||
reply = dbus_message_new_method_return(message);
|
||||
|
||||
if (reply)
|
||||
{
|
||||
dbus_connection_send (connection, reply, NULL);
|
||||
dbus_message_unref (reply);
|
||||
}
|
||||
|
||||
return (DBUS_HANDLER_RESULT_HANDLED);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -315,7 +552,7 @@ char *dbus_init(void)
|
||||
dbus_connection_set_watch_functions(connection, add_watch, remove_watch,
|
||||
NULL, NULL, NULL);
|
||||
dbus_error_init (&dbus_error);
|
||||
dbus_bus_request_name (connection, DNSMASQ_SERVICE, 0, &dbus_error);
|
||||
dbus_bus_request_name (connection, daemon->dbus_name, 0, &dbus_error);
|
||||
if (dbus_error_is_set (&dbus_error))
|
||||
return (char *)dbus_error.message;
|
||||
|
||||
@@ -325,7 +562,7 @@ char *dbus_init(void)
|
||||
|
||||
daemon->dbus = connection;
|
||||
|
||||
if ((message = dbus_message_new_signal(DNSMASQ_PATH, DNSMASQ_SERVICE, "Up")))
|
||||
if ((message = dbus_message_new_signal(DNSMASQ_PATH, daemon->dbus_name, "Up")))
|
||||
{
|
||||
dbus_connection_send(connection, message, NULL);
|
||||
dbus_message_unref(message);
|
||||
@@ -430,7 +667,7 @@ void emit_dbus_signal(int action, struct dhcp_lease *lease, char *hostname)
|
||||
else
|
||||
return;
|
||||
|
||||
if (!(message = dbus_message_new_signal(DNSMASQ_PATH, DNSMASQ_SERVICE, action_str)))
|
||||
if (!(message = dbus_message_new_signal(DNSMASQ_PATH, daemon->dbus_name, action_str)))
|
||||
return;
|
||||
|
||||
dbus_message_iter_init_append(message, &args);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2013 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
|
||||
@@ -91,6 +91,7 @@ struct dhcp_netid *option_filter(struct dhcp_netid *tags, struct dhcp_netid *con
|
||||
{
|
||||
struct dhcp_netid *tagif = run_tag_if(tags);
|
||||
struct dhcp_opt *opt;
|
||||
struct dhcp_opt *tmp;
|
||||
|
||||
/* flag options which are valid with the current tag set (sans context tags) */
|
||||
for (opt = opts; opt; opt = opt->next)
|
||||
@@ -111,6 +112,13 @@ struct dhcp_netid *option_filter(struct dhcp_netid *tags, struct dhcp_netid *con
|
||||
last_tag->next = tags;
|
||||
tagif = run_tag_if(context_tags);
|
||||
|
||||
/* reset stuff with tag:!<tag> which now matches. */
|
||||
for (opt = opts; opt; opt = opt->next)
|
||||
if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)) &&
|
||||
(opt->flags & DHOPT_TAGOK) &&
|
||||
!match_netid(opt->netid, tagif, 0))
|
||||
opt->flags &= ~DHOPT_TAGOK;
|
||||
|
||||
for (opt = opts; opt; opt = opt->next)
|
||||
if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925 | DHOPT_TAGOK)) &&
|
||||
match_netid(opt->netid, tagif, 0))
|
||||
@@ -128,7 +136,6 @@ struct dhcp_netid *option_filter(struct dhcp_netid *tags, struct dhcp_netid *con
|
||||
for (opt = opts; opt; opt = opt->next)
|
||||
if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925 | DHOPT_TAGOK)) && !opt->netid)
|
||||
{
|
||||
struct dhcp_opt *tmp;
|
||||
for (tmp = opts; tmp; tmp = tmp->next)
|
||||
if (tmp->opt == opt->opt && (tmp->flags & DHOPT_TAGOK))
|
||||
break;
|
||||
@@ -138,6 +145,13 @@ struct dhcp_netid *option_filter(struct dhcp_netid *tags, struct dhcp_netid *con
|
||||
my_syslog(MS_DHCP | LOG_WARNING, _("Ignoring duplicate dhcp-option %d"), tmp->opt);
|
||||
}
|
||||
|
||||
/* Finally, eliminate duplicate options later in the chain, and therefore earlier in the config file. */
|
||||
for (opt = opts; opt; opt = opt->next)
|
||||
if (opt->flags & DHOPT_TAGOK)
|
||||
for (tmp = opt->next; tmp; tmp = tmp->next)
|
||||
if (tmp->opt == opt->opt)
|
||||
tmp->flags &= ~DHOPT_TAGOK;
|
||||
|
||||
return tagif;
|
||||
}
|
||||
|
||||
@@ -239,40 +253,6 @@ int match_bytes(struct dhcp_opt *o, unsigned char *p, int len)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void check_dhcp_hosts(int fatal)
|
||||
{
|
||||
/* If the same IP appears in more than one host config, then DISCOVER
|
||||
for one of the hosts will get the address, but REQUEST will be NAKed,
|
||||
since the address is reserved by the other one -> protocol loop.
|
||||
Also check that FQDNs match the domain we are using. */
|
||||
|
||||
struct dhcp_config *configs, *cp;
|
||||
|
||||
for (configs = daemon->dhcp_conf; configs; configs = configs->next)
|
||||
{
|
||||
char *domain;
|
||||
|
||||
if ((configs->flags & DHOPT_BANK) || fatal)
|
||||
{
|
||||
for (cp = configs->next; cp; cp = cp->next)
|
||||
if ((configs->flags & cp->flags & CONFIG_ADDR) && configs->addr.s_addr == cp->addr.s_addr)
|
||||
{
|
||||
if (fatal)
|
||||
die(_("duplicate IP address %s in dhcp-config directive."),
|
||||
inet_ntoa(cp->addr), EC_BADCONF);
|
||||
else
|
||||
my_syslog(MS_DHCP | LOG_ERR, _("duplicate IP address %s in %s."),
|
||||
inet_ntoa(cp->addr), daemon->dhcp_hosts_file);
|
||||
configs->flags &= ~CONFIG_ADDR;
|
||||
}
|
||||
|
||||
/* split off domain part */
|
||||
if ((configs->flags & CONFIG_NAME) && (domain = strip_hostname(configs->hostname)))
|
||||
configs->domain = domain;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dhcp_update_configs(struct dhcp_config *configs)
|
||||
{
|
||||
/* Some people like to keep all static IP addresses in /etc/hosts.
|
||||
@@ -282,7 +262,7 @@ void dhcp_update_configs(struct dhcp_config *configs)
|
||||
in at most one dhcp-host. Since /etc/hosts can be re-read by SIGHUP,
|
||||
restore the status-quo ante first. */
|
||||
|
||||
struct dhcp_config *config;
|
||||
struct dhcp_config *config, *conf_tmp;
|
||||
struct crec *crec;
|
||||
int prot = AF_INET;
|
||||
|
||||
@@ -324,7 +304,8 @@ void dhcp_update_configs(struct dhcp_config *configs)
|
||||
config->hostname, daemon->addrbuff);
|
||||
}
|
||||
|
||||
if (prot == AF_INET && !config_find_by_address(configs, crec->addr.addr.addr.addr4))
|
||||
if (prot == AF_INET &&
|
||||
(!(conf_tmp = config_find_by_address(configs, crec->addr.addr.addr.addr4)) || conf_tmp == config))
|
||||
{
|
||||
config->addr = crec->addr.addr.addr.addr4;
|
||||
config->flags |= CONFIG_ADDR | CONFIG_ADDR_HOSTS;
|
||||
@@ -332,7 +313,8 @@ void dhcp_update_configs(struct dhcp_config *configs)
|
||||
}
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
if (prot == AF_INET6 && !config_find_by_address6(configs, &crec->addr.addr.addr.addr6, 128, 0))
|
||||
if (prot == AF_INET6 &&
|
||||
(!(conf_tmp = config_find_by_address6(configs, &crec->addr.addr.addr.addr6, 128, 0)) || conf_tmp == config))
|
||||
{
|
||||
memcpy(&config->addr6, &crec->addr.addr.addr.addr6, IN6ADDRSZ);
|
||||
config->flags |= CONFIG_ADDR6 | CONFIG_ADDR_HOSTS;
|
||||
@@ -358,90 +340,416 @@ void dhcp_update_configs(struct dhcp_config *configs)
|
||||
|
||||
}
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
static int join_multicast_worker(struct in6_addr *local, int prefix,
|
||||
int scope, int if_index, int dad, void *vparam)
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
void bindtodevice(int fd)
|
||||
{
|
||||
char ifrn_name[IFNAMSIZ];
|
||||
struct ipv6_mreq mreq;
|
||||
int fd, i, max = *((int *)vparam);
|
||||
struct dhcp_context *context;
|
||||
struct iname *tmp;
|
||||
|
||||
(void)prefix;
|
||||
(void)scope;
|
||||
(void)dad;
|
||||
/* If we are doing DHCP on exactly one interface, and running linux, do SO_BINDTODEVICE
|
||||
to that device. This is for the use case of (eg) OpenStack, which runs a new
|
||||
dnsmasq instance for each VLAN interface it creates. Without the BINDTODEVICE,
|
||||
individual processes don't always see the packets they should.
|
||||
SO_BINDTODEVICE is only available Linux. */
|
||||
|
||||
/* record which interfaces we join on, so that we do it at most one per
|
||||
interface, even when they have multiple addresses. Use outpacket
|
||||
as an array of int, since it's always allocated here and easy
|
||||
to expand for theoretical vast numbers of interfaces. */
|
||||
for (i = 0; i < max; i++)
|
||||
if (if_index == ((int *)daemon->outpacket.iov_base)[i])
|
||||
return 1;
|
||||
struct irec *iface, *found;
|
||||
|
||||
if ((fd = socket(PF_INET6, SOCK_DGRAM, 0)) == -1)
|
||||
return 0;
|
||||
for (found = NULL, iface = daemon->interfaces; iface; iface = iface->next)
|
||||
if (iface->dhcp_ok)
|
||||
{
|
||||
if (!found)
|
||||
found = iface;
|
||||
else if (strcmp(found->name, iface->name) != 0)
|
||||
{
|
||||
/* more than one. */
|
||||
found = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!indextoname(fd, if_index, ifrn_name))
|
||||
if (found)
|
||||
{
|
||||
close(fd);
|
||||
return 0;
|
||||
struct ifreq ifr;
|
||||
strcpy(ifr.ifr_name, found->name);
|
||||
/* only allowed by root. */
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr)) == -1 &&
|
||||
errno != EPERM)
|
||||
die(_("failed to set SO_BINDTODEVICE on DHCP socket: %s"), NULL, EC_BADNET);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct opttab_t {
|
||||
char *name;
|
||||
u16 val, size;
|
||||
} opttab[] = {
|
||||
{ "netmask", 1, OT_ADDR_LIST },
|
||||
{ "time-offset", 2, 4 },
|
||||
{ "router", 3, OT_ADDR_LIST },
|
||||
{ "dns-server", 6, OT_ADDR_LIST },
|
||||
{ "log-server", 7, OT_ADDR_LIST },
|
||||
{ "lpr-server", 9, OT_ADDR_LIST },
|
||||
{ "hostname", 12, OT_INTERNAL | OT_NAME },
|
||||
{ "boot-file-size", 13, 2 | OT_DEC },
|
||||
{ "domain-name", 15, OT_NAME },
|
||||
{ "swap-server", 16, OT_ADDR_LIST },
|
||||
{ "root-path", 17, OT_NAME },
|
||||
{ "extension-path", 18, OT_NAME },
|
||||
{ "ip-forward-enable", 19, 1 },
|
||||
{ "non-local-source-routing", 20, 1 },
|
||||
{ "policy-filter", 21, OT_ADDR_LIST },
|
||||
{ "max-datagram-reassembly", 22, 2 | OT_DEC },
|
||||
{ "default-ttl", 23, 1 | OT_DEC },
|
||||
{ "mtu", 26, 2 | OT_DEC },
|
||||
{ "all-subnets-local", 27, 1 },
|
||||
{ "broadcast", 28, OT_INTERNAL | OT_ADDR_LIST },
|
||||
{ "router-discovery", 31, 1 },
|
||||
{ "router-solicitation", 32, OT_ADDR_LIST },
|
||||
{ "static-route", 33, OT_ADDR_LIST },
|
||||
{ "trailer-encapsulation", 34, 1 },
|
||||
{ "arp-timeout", 35, 4 | OT_DEC },
|
||||
{ "ethernet-encap", 36, 1 },
|
||||
{ "tcp-ttl", 37, 1 },
|
||||
{ "tcp-keepalive", 38, 4 | OT_DEC },
|
||||
{ "nis-domain", 40, OT_NAME },
|
||||
{ "nis-server", 41, OT_ADDR_LIST },
|
||||
{ "ntp-server", 42, OT_ADDR_LIST },
|
||||
{ "vendor-encap", 43, OT_INTERNAL },
|
||||
{ "netbios-ns", 44, OT_ADDR_LIST },
|
||||
{ "netbios-dd", 45, OT_ADDR_LIST },
|
||||
{ "netbios-nodetype", 46, 1 },
|
||||
{ "netbios-scope", 47, 0 },
|
||||
{ "x-windows-fs", 48, OT_ADDR_LIST },
|
||||
{ "x-windows-dm", 49, OT_ADDR_LIST },
|
||||
{ "requested-address", 50, OT_INTERNAL | OT_ADDR_LIST },
|
||||
{ "lease-time", 51, OT_INTERNAL | OT_TIME },
|
||||
{ "option-overload", 52, OT_INTERNAL },
|
||||
{ "message-type", 53, OT_INTERNAL | OT_DEC },
|
||||
{ "server-identifier", 54, OT_INTERNAL | OT_ADDR_LIST },
|
||||
{ "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},
|
||||
{ "vendor-class", 60, 0 },
|
||||
{ "client-id", 61, OT_INTERNAL },
|
||||
{ "nis+-domain", 64, OT_NAME },
|
||||
{ "nis+-server", 65, OT_ADDR_LIST },
|
||||
{ "tftp-server", 66, OT_NAME },
|
||||
{ "bootfile-name", 67, OT_NAME },
|
||||
{ "mobile-ip-home", 68, OT_ADDR_LIST },
|
||||
{ "smtp-server", 69, OT_ADDR_LIST },
|
||||
{ "pop3-server", 70, OT_ADDR_LIST },
|
||||
{ "nntp-server", 71, OT_ADDR_LIST },
|
||||
{ "irc-server", 74, OT_ADDR_LIST },
|
||||
{ "user-class", 77, 0 },
|
||||
{ "FQDN", 81, OT_INTERNAL },
|
||||
{ "agent-id", 82, OT_INTERNAL },
|
||||
{ "client-arch", 93, 2 | OT_DEC },
|
||||
{ "client-interface-id", 94, 0 },
|
||||
{ "client-machine-id", 97, 0 },
|
||||
{ "subnet-select", 118, OT_INTERNAL },
|
||||
{ "domain-search", 119, OT_RFC1035_NAME },
|
||||
{ "sip-server", 120, 0 },
|
||||
{ "classless-static-route", 121, 0 },
|
||||
{ "vendor-id-encap", 125, 0 },
|
||||
{ "server-ip-address", 255, OT_ADDR_LIST }, /* special, internal only, sets siaddr */
|
||||
{ NULL, 0, 0 }
|
||||
};
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
static const struct opttab_t opttab6[] = {
|
||||
{ "client-id", 1, OT_INTERNAL },
|
||||
{ "server-id", 2, OT_INTERNAL },
|
||||
{ "ia-na", 3, OT_INTERNAL },
|
||||
{ "ia-ta", 4, OT_INTERNAL },
|
||||
{ "iaaddr", 5, OT_INTERNAL },
|
||||
{ "oro", 6, OT_INTERNAL },
|
||||
{ "preference", 7, OT_INTERNAL | OT_DEC },
|
||||
{ "unicast", 12, OT_INTERNAL },
|
||||
{ "status", 13, OT_INTERNAL },
|
||||
{ "rapid-commit", 14, OT_INTERNAL },
|
||||
{ "user-class", 15, OT_INTERNAL | OT_CSTRING },
|
||||
{ "vendor-class", 16, OT_INTERNAL | OT_CSTRING },
|
||||
{ "vendor-opts", 17, OT_INTERNAL },
|
||||
{ "sip-server-domain", 21, OT_RFC1035_NAME },
|
||||
{ "sip-server", 22, OT_ADDR_LIST },
|
||||
{ "dns-server", 23, OT_ADDR_LIST },
|
||||
{ "domain-search", 24, OT_RFC1035_NAME },
|
||||
{ "nis-server", 27, OT_ADDR_LIST },
|
||||
{ "nis+-server", 28, OT_ADDR_LIST },
|
||||
{ "nis-domain", 29, OT_RFC1035_NAME },
|
||||
{ "nis+-domain", 30, OT_RFC1035_NAME },
|
||||
{ "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 },
|
||||
{ "bootfile-url", 59, OT_NAME },
|
||||
{ "bootfile-param", 60, OT_CSTRING },
|
||||
{ NULL, 0, 0 }
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
void display_opts(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
printf(_("Known DHCP options:\n"));
|
||||
|
||||
for (i = 0; opttab[i].name; i++)
|
||||
if (!(opttab[i].size & OT_INTERNAL))
|
||||
printf("%3d %s\n", opttab[i].val, opttab[i].name);
|
||||
}
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
void display_opts6(void)
|
||||
{
|
||||
int i;
|
||||
printf(_("Known DHCPv6 options:\n"));
|
||||
|
||||
for (i = 0; opttab6[i].name; i++)
|
||||
if (!(opttab6[i].size & OT_INTERNAL))
|
||||
printf("%3d %s\n", opttab6[i].val, opttab6[i].name);
|
||||
}
|
||||
#endif
|
||||
|
||||
int lookup_dhcp_opt(int prot, char *name)
|
||||
{
|
||||
const struct opttab_t *t;
|
||||
int i;
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
if (prot == AF_INET6)
|
||||
t = opttab6;
|
||||
else
|
||||
#endif
|
||||
t = opttab;
|
||||
|
||||
for (i = 0; t[i].name; i++)
|
||||
if (strcasecmp(t[i].name, name) == 0)
|
||||
return t[i].val;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int lookup_dhcp_len(int prot, int val)
|
||||
{
|
||||
const struct opttab_t *t;
|
||||
int i;
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
if (prot == AF_INET6)
|
||||
t = opttab6;
|
||||
else
|
||||
#endif
|
||||
t = opttab;
|
||||
|
||||
for (i = 0; t[i].name; i++)
|
||||
if (val == t[i].val)
|
||||
return t[i].size & ~OT_DEC;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *option_string(int prot, unsigned int opt, unsigned char *val, int opt_len, char *buf, int buf_len)
|
||||
{
|
||||
int o, i, j, nodecode = 0;
|
||||
const struct opttab_t *ot = opttab;
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
if (prot == AF_INET6)
|
||||
ot = opttab6;
|
||||
#endif
|
||||
|
||||
for (o = 0; ot[o].name; o++)
|
||||
if (ot[o].val == opt)
|
||||
{
|
||||
if (buf)
|
||||
{
|
||||
memset(buf, 0, buf_len);
|
||||
|
||||
if (ot[o].size & OT_ADDR_LIST)
|
||||
{
|
||||
struct all_addr addr;
|
||||
int addr_len = INADDRSZ;
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
if (prot == AF_INET6)
|
||||
addr_len = IN6ADDRSZ;
|
||||
#endif
|
||||
for (buf[0]= 0, i = 0; i <= opt_len - addr_len; i += addr_len)
|
||||
{
|
||||
if (i != 0)
|
||||
strncat(buf, ", ", buf_len - strlen(buf));
|
||||
/* align */
|
||||
memcpy(&addr, &val[i], addr_len);
|
||||
inet_ntop(prot, &val[i], daemon->addrbuff, ADDRSTRLEN);
|
||||
strncat(buf, daemon->addrbuff, buf_len - strlen(buf));
|
||||
}
|
||||
}
|
||||
else if (ot[o].size & OT_NAME)
|
||||
for (i = 0, j = 0; i < opt_len && j < buf_len ; i++)
|
||||
{
|
||||
char c = val[i];
|
||||
if (isprint((int)c))
|
||||
buf[j++] = c;
|
||||
}
|
||||
#ifdef HAVE_DHCP6
|
||||
/* We don't handle compressed rfc1035 names, so no good in IPv4 land */
|
||||
else if ((ot[o].size & OT_RFC1035_NAME) && prot == AF_INET6)
|
||||
{
|
||||
i = 0, j = 0;
|
||||
while (i < opt_len && val[i] != 0)
|
||||
{
|
||||
int k, l = i + val[i] + 1;
|
||||
for (k = i + 1; k < opt_len && k < l && j < buf_len ; k++)
|
||||
{
|
||||
char c = val[k];
|
||||
if (isprint((int)c))
|
||||
buf[j++] = c;
|
||||
}
|
||||
i = l;
|
||||
if (val[i] != 0 && j < buf_len)
|
||||
buf[j++] = '.';
|
||||
}
|
||||
}
|
||||
else if ((ot[o].size & OT_CSTRING))
|
||||
{
|
||||
int k, len;
|
||||
unsigned char *p;
|
||||
|
||||
i = 0, j = 0;
|
||||
while (1)
|
||||
{
|
||||
p = &val[i];
|
||||
GETSHORT(len, p);
|
||||
for (k = 0; k < len && j < buf_len; k++)
|
||||
{
|
||||
char c = *p++;
|
||||
if (isprint((int)c))
|
||||
buf[j++] = c;
|
||||
}
|
||||
i += len +2;
|
||||
if (i >= opt_len)
|
||||
break;
|
||||
|
||||
if (j < buf_len)
|
||||
buf[j++] = ',';
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else if ((ot[o].size & (OT_DEC | OT_TIME)) && opt_len != 0)
|
||||
{
|
||||
unsigned int dec = 0;
|
||||
|
||||
for (i = 0; i < opt_len; i++)
|
||||
dec = (dec << 8) | val[i];
|
||||
|
||||
if (ot[o].size & OT_TIME)
|
||||
prettyprint_time(buf, dec);
|
||||
else
|
||||
sprintf(buf, "%u", dec);
|
||||
}
|
||||
else
|
||||
nodecode = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (opt_len != 0 && buf && (!ot[o].name || nodecode))
|
||||
{
|
||||
int trunc = 0;
|
||||
if (opt_len > 14)
|
||||
{
|
||||
trunc = 1;
|
||||
opt_len = 14;
|
||||
}
|
||||
print_mac(buf, val, opt_len);
|
||||
if (trunc)
|
||||
strncat(buf, "...", buf_len - strlen(buf));
|
||||
|
||||
|
||||
}
|
||||
|
||||
return ot[o].name ? ot[o].name : "";
|
||||
|
||||
}
|
||||
|
||||
void log_context(int family, struct dhcp_context *context)
|
||||
{
|
||||
/* Cannot use dhcp_buff* for RA contexts */
|
||||
|
||||
void *start = &context->start;
|
||||
void *end = &context->end;
|
||||
char *template = "", *p = daemon->namebuff;
|
||||
|
||||
*p = 0;
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
if (family == AF_INET6)
|
||||
{
|
||||
struct in6_addr subnet = context->start6;
|
||||
if (!(context->flags & CONTEXT_TEMPLATE))
|
||||
setaddr6part(&subnet, 0);
|
||||
inet_ntop(AF_INET6, &subnet, daemon->addrbuff, ADDRSTRLEN);
|
||||
start = &context->start6;
|
||||
end = &context->end6;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (family != AF_INET && (context->flags & CONTEXT_DEPRECATE))
|
||||
strcpy(daemon->namebuff, _(", prefix deprecated"));
|
||||
else
|
||||
{
|
||||
p += sprintf(p, _(", lease time "));
|
||||
prettyprint_time(p, context->lease_time);
|
||||
p += strlen(p);
|
||||
}
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
if (context->flags & CONTEXT_CONSTRUCTED)
|
||||
{
|
||||
char ifrn_name[IFNAMSIZ];
|
||||
|
||||
template = p;
|
||||
p += sprintf(p, ", ");
|
||||
|
||||
if (indextoname(daemon->doing_dhcp6 ? daemon->dhcp6fd : daemon->icmp6fd, context->if_index, ifrn_name))
|
||||
sprintf(p, "constructed for %s", ifrn_name);
|
||||
}
|
||||
else if (context->flags & CONTEXT_TEMPLATE)
|
||||
{
|
||||
template = p;
|
||||
p += sprintf(p, ", ");
|
||||
|
||||
sprintf(p, "template for %s", context->template_interface);
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((context->flags & CONTEXT_DHCP) || family == AF_INET)
|
||||
{
|
||||
inet_ntop(family, start, daemon->dhcp_buff, 256);
|
||||
inet_ntop(family, end, daemon->dhcp_buff3, 256);
|
||||
my_syslog(MS_DHCP | LOG_INFO,
|
||||
(context->flags & CONTEXT_RA_STATELESS) ?
|
||||
_("%s stateless on %s%.0s%.0s%s") :
|
||||
(context->flags & CONTEXT_STATIC) ?
|
||||
_("%s, static leases only on %.0s%s%s%.0s") :
|
||||
(context->flags & CONTEXT_PROXY) ?
|
||||
_("%s, proxy on subnet %.0s%s%.0s%.0s") :
|
||||
_("%s, IP range %s -- %s%s%.0s"),
|
||||
(family != AF_INET) ? "DHCPv6" : "DHCP",
|
||||
daemon->dhcp_buff, daemon->dhcp_buff3, daemon->namebuff, template);
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
/* Are we doing DHCP on this interface? */
|
||||
if (!iface_check(AF_INET6, (struct all_addr *)local, ifrn_name))
|
||||
return 1;
|
||||
|
||||
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
|
||||
if (tmp->name && (strcmp(tmp->name, ifrn_name) == 0))
|
||||
return 1;
|
||||
|
||||
/* weird libvirt-inspired access control */
|
||||
for (context = daemon->dhcp6; context; context = context->next)
|
||||
if (!context->interface || strcmp(context->interface, ifrn_name) == 0)
|
||||
break;
|
||||
|
||||
if (!context)
|
||||
return 1;
|
||||
|
||||
mreq.ipv6mr_interface = if_index;
|
||||
|
||||
inet_pton(AF_INET6, ALL_RELAY_AGENTS_AND_SERVERS, &mreq.ipv6mr_multiaddr);
|
||||
|
||||
if (daemon->dhcp6 &&
|
||||
setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
|
||||
return 0;
|
||||
|
||||
inet_pton(AF_INET6, ALL_SERVERS, &mreq.ipv6mr_multiaddr);
|
||||
|
||||
if (daemon->dhcp6 &&
|
||||
setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
|
||||
return 0;
|
||||
|
||||
inet_pton(AF_INET6, ALL_ROUTERS, &mreq.ipv6mr_multiaddr);
|
||||
|
||||
if (daemon->ra_contexts &&
|
||||
setsockopt(daemon->icmp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
|
||||
return 0;
|
||||
|
||||
expand_buf(&daemon->outpacket, (max+1) * sizeof(int));
|
||||
((int *)daemon->outpacket.iov_base)[max++] = if_index;
|
||||
|
||||
*((int *)vparam) = max;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void join_multicast(void)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
if (!iface_enumerate(AF_INET6, &count, join_multicast_worker))
|
||||
die(_("failed to join DHCPv6 multicast group: %s"), NULL, EC_BADNET);
|
||||
}
|
||||
#ifdef HAVE_DHCP6
|
||||
if (context->flags & CONTEXT_RA_NAME)
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("DHCPv4-derived IPv6 names on %s%s"), daemon->addrbuff, template);
|
||||
|
||||
if ((context->flags & CONTEXT_RA) || (option_bool(OPT_RA) && (context->flags & CONTEXT_DHCP) && family == AF_INET6))
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("router advertisement on %s%s"), daemon->addrbuff, template);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
54
src/dhcp.c
54
src/dhcp.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2013 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,9 +28,9 @@ struct match_param {
|
||||
struct in_addr netmask, broadcast, addr;
|
||||
};
|
||||
|
||||
static int complete_context(struct in_addr local, int if_index,
|
||||
static int complete_context(struct in_addr local, int if_index, char *label,
|
||||
struct in_addr netmask, struct in_addr broadcast, void *vparam);
|
||||
static int check_listen_addrs(struct in_addr local, int if_index,
|
||||
static int check_listen_addrs(struct in_addr local, int if_index, char *label,
|
||||
struct in_addr netmask, struct in_addr broadcast, void *vparam);
|
||||
|
||||
static int make_fd(int port)
|
||||
@@ -65,14 +65,22 @@ static int make_fd(int port)
|
||||
|
||||
/* When bind-interfaces is set, there might be more than one dnmsasq
|
||||
instance binding port 67. That's OK if they serve different networks.
|
||||
Need to set REUSEADDR to make this posible, or REUSEPORT on *BSD. */
|
||||
if (option_bool(OPT_NOWILD))
|
||||
Need to set REUSEADDR|REUSEPORT to make this posible.
|
||||
Handle the case that REUSEPORT is defined, but the kernel doesn't
|
||||
support it. This handles the introduction of REUSEPORT on Linux. */
|
||||
if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND))
|
||||
{
|
||||
int rc = -1, porterr = 0;
|
||||
|
||||
#ifdef SO_REUSEPORT
|
||||
int rc = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &oneopt, sizeof(oneopt));
|
||||
#else
|
||||
int rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt));
|
||||
if ((rc = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &oneopt, sizeof(oneopt))) == -1 &&
|
||||
errno != ENOPROTOOPT)
|
||||
porterr = 1;
|
||||
#endif
|
||||
|
||||
if (rc == -1 && !porterr)
|
||||
rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt));
|
||||
|
||||
if (rc == -1)
|
||||
die(_("failed to set SO_REUSE{ADDR|PORT} on DHCP socket: %s"), NULL, EC_BADNET);
|
||||
}
|
||||
@@ -116,9 +124,7 @@ void dhcp_init(void)
|
||||
|
||||
/* Make BPF raw send socket */
|
||||
init_bpf();
|
||||
#endif
|
||||
|
||||
check_dhcp_hosts(1);
|
||||
#endif
|
||||
}
|
||||
|
||||
void dhcp_packet(time_t now, int pxe_fd)
|
||||
@@ -254,17 +260,9 @@ void dhcp_packet(time_t now, int pxe_fd)
|
||||
}
|
||||
|
||||
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
|
||||
if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
|
||||
if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
|
||||
return;
|
||||
|
||||
/* weird libvirt-inspired access control */
|
||||
for (context = daemon->dhcp; context; context = context->next)
|
||||
if (!context->interface || strcmp(context->interface, ifr.ifr_name) == 0)
|
||||
break;
|
||||
|
||||
if (!context)
|
||||
return;
|
||||
|
||||
/* unlinked contexts are marked by context->current == context */
|
||||
for (context = daemon->dhcp; context; context = context->next)
|
||||
context->current = context;
|
||||
@@ -272,12 +270,12 @@ void dhcp_packet(time_t now, int pxe_fd)
|
||||
parm.current = NULL;
|
||||
parm.ind = iface_index;
|
||||
|
||||
if (!iface_check(AF_INET, (struct all_addr *)&iface_addr, ifr.ifr_name))
|
||||
if (!iface_check(AF_INET, (struct all_addr *)&iface_addr, ifr.ifr_name, NULL))
|
||||
{
|
||||
/* If we failed to match the primary address of the interface, see if we've got a --listen-address
|
||||
for a secondary */
|
||||
struct match_param match;
|
||||
|
||||
|
||||
match.matched = 0;
|
||||
match.ind = iface_index;
|
||||
|
||||
@@ -289,7 +287,7 @@ void dhcp_packet(time_t now, int pxe_fd)
|
||||
iface_addr = match.addr;
|
||||
/* make sure secondary address gets priority in case
|
||||
there is more than one address on the interface in the same subnet */
|
||||
complete_context(match.addr, iface_index, match.netmask, match.broadcast, &parm);
|
||||
complete_context(match.addr, iface_index, NULL, match.netmask, match.broadcast, &parm);
|
||||
}
|
||||
|
||||
if (!iface_enumerate(AF_INET, &parm, complete_context))
|
||||
@@ -299,7 +297,7 @@ void dhcp_packet(time_t now, int pxe_fd)
|
||||
iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz,
|
||||
now, unicast_dest, &is_inform, pxe_fd, iface_addr);
|
||||
lease_update_file(now);
|
||||
lease_update_dns();
|
||||
lease_update_dns(0);
|
||||
|
||||
if (iov.iov_len == 0)
|
||||
return;
|
||||
@@ -413,12 +411,14 @@ void dhcp_packet(time_t now, int pxe_fd)
|
||||
}
|
||||
|
||||
/* check against secondary interface addresses */
|
||||
static int check_listen_addrs(struct in_addr local, int if_index,
|
||||
static int check_listen_addrs(struct in_addr local, int if_index, char *label,
|
||||
struct in_addr netmask, struct in_addr broadcast, void *vparam)
|
||||
{
|
||||
struct match_param *param = vparam;
|
||||
struct iname *tmp;
|
||||
|
||||
(void) label;
|
||||
|
||||
if (if_index == param->ind)
|
||||
{
|
||||
for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
|
||||
@@ -446,11 +446,13 @@ static int check_listen_addrs(struct in_addr local, int if_index,
|
||||
|
||||
Note that the current chain may be superceded later for configured hosts or those coming via gateways. */
|
||||
|
||||
static int complete_context(struct in_addr local, int if_index,
|
||||
static int complete_context(struct in_addr local, int if_index, char *label,
|
||||
struct in_addr netmask, struct in_addr broadcast, void *vparam)
|
||||
{
|
||||
struct dhcp_context *context;
|
||||
struct iface_param *param = vparam;
|
||||
|
||||
(void)label;
|
||||
|
||||
for (context = daemon->dhcp; context; context = context->next)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -54,10 +54,16 @@
|
||||
#define OPTION6_RECONFIGURE_MSG 19
|
||||
#define OPTION6_RECONF_ACCEPT 20
|
||||
#define OPTION6_DNS_SERVER 23
|
||||
#define OPTION6_DOMAIN_SEARCH 24
|
||||
#define OPTION6_REMOTE_ID 37
|
||||
#define OPTION6_SUBSCRIBER_ID 38
|
||||
#define OPTION6_FQDN 39
|
||||
|
||||
/* replace this with the real number when allocated.
|
||||
defining this also enables the relevant code. */
|
||||
/* #define OPTION6_PREFIX_CLASS 99 */
|
||||
|
||||
|
||||
#define DHCP6SUCCESS 0
|
||||
#define DHCP6UNSPEC 1
|
||||
#define DHCP6NOADDRS 2
|
||||
|
||||
397
src/dhcp6.c
397
src/dhcp6.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -21,11 +21,12 @@
|
||||
struct iface_param {
|
||||
struct dhcp_context *current;
|
||||
struct in6_addr fallback;
|
||||
int ind;
|
||||
int ind, addr_match;
|
||||
};
|
||||
|
||||
static int complete_context6(struct in6_addr *local, int prefix,
|
||||
int scope, int if_index, int dad, void *vparam);
|
||||
int scope, int if_index, int flags,
|
||||
unsigned int preferred, unsigned int valid, void *vparam);
|
||||
|
||||
static int make_duid1(int index, unsigned int type, char *mac, size_t maclen, void *parm);
|
||||
|
||||
@@ -33,18 +34,42 @@ void dhcp6_init(void)
|
||||
{
|
||||
int fd;
|
||||
struct sockaddr_in6 saddr;
|
||||
#if defined(IP_TOS) && defined(IPTOS_CLASS_CS6)
|
||||
#if defined(IPV6_TCLASS) && defined(IPTOS_CLASS_CS6)
|
||||
int class = IPTOS_CLASS_CS6;
|
||||
#endif
|
||||
|
||||
int oneopt = 1;
|
||||
|
||||
if ((fd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP)) == -1 ||
|
||||
#if defined(IP_TOS) && defined(IPTOS_CLASS_CS6)
|
||||
#if defined(IPV6_TCLASS) && defined(IPTOS_CLASS_CS6)
|
||||
setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &class, sizeof(class)) == -1 ||
|
||||
#endif
|
||||
setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &oneopt, sizeof(oneopt)) == -1 ||
|
||||
!fix_fd(fd) ||
|
||||
!set_ipv6pktinfo(fd))
|
||||
die (_("cannot create DHCPv6 socket: %s"), NULL, EC_BADNET);
|
||||
|
||||
/* When bind-interfaces is set, there might be more than one dnmsasq
|
||||
instance binding port 547. That's OK if they serve different networks.
|
||||
Need to set REUSEADDR|REUSEPORT to make this posible.
|
||||
Handle the case that REUSEPORT is defined, but the kernel doesn't
|
||||
support it. This handles the introduction of REUSEPORT on Linux. */
|
||||
if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND))
|
||||
{
|
||||
int rc = -1, porterr = 0;
|
||||
|
||||
#ifdef SO_REUSEPORT
|
||||
if ((rc = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &oneopt, sizeof(oneopt))) == -1 &&
|
||||
errno != ENOPROTOOPT)
|
||||
porterr = 1;
|
||||
#endif
|
||||
|
||||
if (rc == -1 && !porterr)
|
||||
rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt));
|
||||
|
||||
if (rc == -1)
|
||||
die(_("failed to set SO_REUSE{ADDR|PORT} on DHCPv6 socket: %s"), NULL, EC_BADNET);
|
||||
}
|
||||
|
||||
memset(&saddr, 0, sizeof(saddr));
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
saddr.sin6_len = sizeof(struct sockaddr_in6);
|
||||
@@ -71,10 +96,10 @@ void dhcp6_packet(time_t now)
|
||||
char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
|
||||
} control_u;
|
||||
struct sockaddr_in6 from;
|
||||
struct all_addr dest;
|
||||
ssize_t sz;
|
||||
struct ifreq ifr;
|
||||
struct iname *tmp;
|
||||
unsigned short port;
|
||||
|
||||
msg.msg_control = control_u.control6;
|
||||
msg.msg_controllen = sizeof(control_u);
|
||||
@@ -84,7 +109,7 @@ void dhcp6_packet(time_t now)
|
||||
msg.msg_iov = &daemon->dhcp_packet;
|
||||
msg.msg_iovlen = 1;
|
||||
|
||||
if ((sz = recv_dhcp_packet(daemon->dhcp6fd, &msg)) == -1 || sz <= 4)
|
||||
if ((sz = recv_dhcp_packet(daemon->dhcp6fd, &msg)) == -1)
|
||||
return;
|
||||
|
||||
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
|
||||
@@ -97,68 +122,95 @@ void dhcp6_packet(time_t now)
|
||||
p.c = CMSG_DATA(cmptr);
|
||||
|
||||
if_index = p.p->ipi6_ifindex;
|
||||
dest.addr.addr6 = p.p->ipi6_addr;
|
||||
}
|
||||
|
||||
if (!indextoname(daemon->dhcp6fd, if_index, ifr.ifr_name))
|
||||
return;
|
||||
|
||||
if (!iface_check(AF_INET6, (struct all_addr *)&dest, ifr.ifr_name))
|
||||
return;
|
||||
|
||||
for (tmp = daemon->if_except; tmp; tmp = tmp->next)
|
||||
if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
|
||||
return;
|
||||
|
||||
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
|
||||
if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
|
||||
if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
|
||||
return;
|
||||
|
||||
/* weird libvirt-inspired access control */
|
||||
for (context = daemon->dhcp6; context; context = context->next)
|
||||
if (!context->interface || strcmp(context->interface, ifr.ifr_name) == 0)
|
||||
break;
|
||||
|
||||
if (!context)
|
||||
return;
|
||||
|
||||
/* unlinked contexts are marked by context->current == context */
|
||||
for (context = daemon->dhcp6; context; context = context->next)
|
||||
{
|
||||
context->current = context;
|
||||
memset(&context->local6, 0, IN6ADDRSZ);
|
||||
}
|
||||
|
||||
parm.current = NULL;
|
||||
parm.ind = if_index;
|
||||
parm.addr_match = 0;
|
||||
memset(&parm.fallback, 0, IN6ADDRSZ);
|
||||
|
||||
for (context = daemon->dhcp6; context; context = context->next)
|
||||
if (IN6_IS_ADDR_UNSPECIFIED(&context->start6) && context->prefix == 0)
|
||||
{
|
||||
/* wildcard context for DHCP-stateless only */
|
||||
parm.current = context;
|
||||
context->current = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* unlinked contexts are marked by context->current == context */
|
||||
context->current = context;
|
||||
memset(&context->local6, 0, IN6ADDRSZ);
|
||||
}
|
||||
|
||||
if (!iface_enumerate(AF_INET6, &parm, complete_context6))
|
||||
return;
|
||||
|
||||
if (daemon->if_names || daemon->if_addrs)
|
||||
{
|
||||
|
||||
for (tmp = daemon->if_names; tmp; tmp = tmp->next)
|
||||
if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
|
||||
break;
|
||||
|
||||
if (!tmp && !parm.addr_match)
|
||||
return;
|
||||
}
|
||||
|
||||
lease_prune(NULL, now); /* lose any expired leases */
|
||||
|
||||
sz = dhcp6_reply(parm.current, if_index, ifr.ifr_name, &parm.fallback,
|
||||
sz, IN6_IS_ADDR_MULTICAST(&from.sin6_addr), now);
|
||||
port = dhcp6_reply(parm.current, if_index, ifr.ifr_name, &parm.fallback,
|
||||
sz, IN6_IS_ADDR_MULTICAST(&from.sin6_addr), now);
|
||||
|
||||
lease_update_file(now);
|
||||
lease_update_dns();
|
||||
lease_update_dns(0);
|
||||
|
||||
if (sz != 0)
|
||||
while (sendto(daemon->dhcp6fd, daemon->outpacket.iov_base, sz, 0, (struct sockaddr *)&from, sizeof(from)) == -1 &&
|
||||
/* The port in the source address of the original request should
|
||||
be correct, but at least once client sends from the server port,
|
||||
so we explicitly send to the client port to a client, and the
|
||||
server port to a relay. */
|
||||
if (port != 0)
|
||||
{
|
||||
from.sin6_port = htons(port);
|
||||
while (sendto(daemon->dhcp6fd, daemon->outpacket.iov_base, save_counter(0),
|
||||
0, (struct sockaddr *)&from, sizeof(from)) == -1 &&
|
||||
retry_send());
|
||||
}
|
||||
}
|
||||
|
||||
static int complete_context6(struct in6_addr *local, int prefix,
|
||||
int scope, int if_index, int dad, void *vparam)
|
||||
int scope, int if_index, int flags, unsigned int preferred,
|
||||
unsigned int valid, void *vparam)
|
||||
{
|
||||
struct dhcp_context *context;
|
||||
struct iface_param *param = vparam;
|
||||
|
||||
struct iname *tmp;
|
||||
|
||||
(void)scope; /* warning */
|
||||
(void)dad;
|
||||
|
||||
if (if_index == param->ind &&
|
||||
!IN6_IS_ADDR_LOOPBACK(local) &&
|
||||
!IN6_IS_ADDR_LINKLOCAL(local) &&
|
||||
!IN6_IS_ADDR_MULTICAST(local))
|
||||
{
|
||||
/* if we have --listen-address config, see if the
|
||||
arrival interface has a matching address. */
|
||||
for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
|
||||
if (tmp->addr.sa.sa_family == AF_INET6 &&
|
||||
IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, local))
|
||||
param->addr_match = 1;
|
||||
|
||||
/* Determine a globally address on the arrival interface, even
|
||||
if we have no matching dhcp-context, because we're only
|
||||
allocating on remote subnets via relays. This
|
||||
@@ -167,16 +219,40 @@ static int complete_context6(struct in6_addr *local, int prefix,
|
||||
|
||||
for (context = daemon->dhcp6; context; context = context->next)
|
||||
{
|
||||
if (prefix == context->prefix &&
|
||||
if ((context->flags & CONTEXT_DHCP) &&
|
||||
!(context->flags & CONTEXT_TEMPLATE) &&
|
||||
prefix == context->prefix &&
|
||||
is_same_net6(local, &context->start6, prefix) &&
|
||||
is_same_net6(local, &context->end6, prefix))
|
||||
{
|
||||
|
||||
|
||||
/* link it onto the current chain if we've not seen it before */
|
||||
if (context->current == context)
|
||||
{
|
||||
context->current = param->current;
|
||||
param->current = context;
|
||||
struct dhcp_context *tmp, **up;
|
||||
|
||||
/* use interface values only for contructed contexts */
|
||||
if (!(context->flags & CONTEXT_CONSTRUCTED))
|
||||
preferred = valid = 0xffffffff;
|
||||
else if (flags & IFACE_DEPRECATED)
|
||||
preferred = 0;
|
||||
|
||||
if (context->flags & CONTEXT_DEPRECATE)
|
||||
preferred = 0;
|
||||
|
||||
/* order chain, longest preferred time first */
|
||||
for (up = ¶m->current, tmp = param->current; tmp; tmp = tmp->current)
|
||||
if (tmp->preferred <= preferred)
|
||||
break;
|
||||
else
|
||||
up = &tmp->current;
|
||||
|
||||
context->current = *up;
|
||||
*up = context;
|
||||
context->local6 = *local;
|
||||
context->preferred = preferred;
|
||||
context->valid = valid;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -197,8 +273,8 @@ struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int address6_allocate(struct dhcp_context *context, unsigned char *clid, int clid_len,
|
||||
int serial, struct dhcp_netid *netids, struct in6_addr *ans)
|
||||
struct dhcp_context *address6_allocate(struct dhcp_context *context, unsigned char *clid, int clid_len,
|
||||
int iaid, int serial, struct dhcp_netid *netids, int plain_range, struct in6_addr *ans)
|
||||
{
|
||||
/* Find a free address: exclude anything in use and anything allocated to
|
||||
a particular hwaddr/clientid/hostname in our configuration.
|
||||
@@ -215,12 +291,12 @@ int address6_allocate(struct dhcp_context *context, unsigned char *clid, int cl
|
||||
|
||||
/* hash hwaddr: use the SDBM hashing algorithm. This works
|
||||
for MAC addresses, let's see how it manages with client-ids! */
|
||||
for (j = 0, i = 0; i < clid_len; i++)
|
||||
for (j = iaid, i = 0; i < clid_len; i++)
|
||||
j += clid[i] + (j << 6) + (j << 16) - j;
|
||||
|
||||
for (pass = 0; pass <= 1; pass++)
|
||||
for (pass = 0; pass <= plain_range ? 1 : 0; pass++)
|
||||
for (c = context; c; c = c->current)
|
||||
if (c->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
|
||||
if (c->flags & (CONTEXT_DEPRECATE | CONTEXT_STATIC | CONTEXT_RA_STATELESS | CONTEXT_USED))
|
||||
continue;
|
||||
else if (!match_netid(c->filter, netids, pass))
|
||||
continue;
|
||||
@@ -230,7 +306,7 @@ int address6_allocate(struct dhcp_context *context, unsigned char *clid, int cl
|
||||
/* 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 + serial) % (1 + addr6part(&c->end6) - addr6part(&c->start6)));
|
||||
start = addr6part(&c->start6) + ((j + c->addr_epoch) % (1 + addr6part(&c->end6) - addr6part(&c->start6)));
|
||||
|
||||
/* iterate until we find a free address. */
|
||||
addr = start;
|
||||
@@ -247,7 +323,7 @@ int address6_allocate(struct dhcp_context *context, unsigned char *clid, int cl
|
||||
{
|
||||
*ans = c->start6;
|
||||
setaddr6part (ans, addr);
|
||||
return 1;
|
||||
return c;
|
||||
}
|
||||
|
||||
addr++;
|
||||
@@ -257,13 +333,15 @@ int address6_allocate(struct dhcp_context *context, unsigned char *clid, int cl
|
||||
|
||||
} while (addr != start);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* can dynamically allocate addr */
|
||||
struct dhcp_context *address6_available(struct dhcp_context *context,
|
||||
struct in6_addr *taddr,
|
||||
struct dhcp_netid *netids)
|
||||
struct dhcp_netid *netids,
|
||||
int plain_range)
|
||||
{
|
||||
u64 start, end, addr = addr6part(taddr);
|
||||
struct dhcp_context *tmp;
|
||||
@@ -273,65 +351,66 @@ struct dhcp_context *address6_available(struct dhcp_context *context,
|
||||
start = addr6part(&tmp->start6);
|
||||
end = addr6part(&tmp->end6);
|
||||
|
||||
if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY)) &&
|
||||
is_same_net6(&context->start6, taddr, context->prefix) &&
|
||||
is_same_net6(&context->end6, taddr, context->prefix) &&
|
||||
if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_RA_STATELESS)) &&
|
||||
is_same_net6(&tmp->start6, taddr, tmp->prefix) &&
|
||||
is_same_net6(&tmp->end6, taddr, tmp->prefix) &&
|
||||
addr >= start &&
|
||||
addr <= end &&
|
||||
match_netid(tmp->filter, netids, 1))
|
||||
match_netid(tmp->filter, netids, plain_range))
|
||||
return tmp;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct dhcp_context *narrow_context6(struct dhcp_context *context,
|
||||
struct in6_addr *taddr,
|
||||
struct dhcp_netid *netids)
|
||||
/* address OK if configured */
|
||||
struct dhcp_context *address6_valid(struct dhcp_context *context,
|
||||
struct in6_addr *taddr,
|
||||
struct dhcp_netid *netids,
|
||||
int plain_range)
|
||||
{
|
||||
/* We start of with a set of possible contexts, all on the current physical interface.
|
||||
These are chained on ->current.
|
||||
Here we have an address, and return the actual context correponding to that
|
||||
address. Note that none may fit, if the address came a dhcp-host and is outside
|
||||
any dhcp-range. In that case we return a static range if possible, or failing that,
|
||||
any context on the correct subnet. (If there's more than one, this is a dodgy
|
||||
configuration: maybe there should be a warning.) */
|
||||
|
||||
struct dhcp_context *tmp;
|
||||
|
||||
for (tmp = context; tmp; tmp = tmp->current)
|
||||
if (is_same_net6(&tmp->start6, taddr, tmp->prefix) &&
|
||||
match_netid(tmp->filter, netids, plain_range))
|
||||
return tmp;
|
||||
|
||||
if (!(tmp = address6_available(context, taddr, netids)))
|
||||
{
|
||||
for (tmp = context; tmp; tmp = tmp->current)
|
||||
if (match_netid(tmp->filter, netids, 1) &&
|
||||
is_same_net6(taddr, &tmp->start6, tmp->prefix) &&
|
||||
(tmp->flags & CONTEXT_STATIC))
|
||||
break;
|
||||
|
||||
if (!tmp)
|
||||
for (tmp = context; tmp; tmp = tmp->current)
|
||||
if (match_netid(tmp->filter, netids, 1) &&
|
||||
is_same_net6(taddr, &tmp->start6, tmp->prefix) &&
|
||||
!(tmp->flags & CONTEXT_PROXY))
|
||||
break;
|
||||
}
|
||||
|
||||
/* Only one context allowed now */
|
||||
if (tmp)
|
||||
tmp->current = NULL;
|
||||
|
||||
return tmp;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int is_addr_in_context6(struct dhcp_context *context, struct dhcp_config *config)
|
||||
int config_valid(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr)
|
||||
{
|
||||
if (!context) /* called via find_config() from lease_update_from_configs() */
|
||||
return 1;
|
||||
if (!(config->flags & CONFIG_ADDR6))
|
||||
if (!config || !(config->flags & CONFIG_ADDR6))
|
||||
return 0;
|
||||
|
||||
if ((config->flags & CONFIG_WILDCARD) && context->prefix == 64)
|
||||
{
|
||||
*addr = context->start6;
|
||||
setaddr6part(addr, addr6part(&config->addr6));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (is_same_net6(&context->start6, &config->addr6, context->prefix))
|
||||
{
|
||||
*addr = config->addr6;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int is_config_in_context6(struct dhcp_context *context, struct dhcp_config *config)
|
||||
{
|
||||
if (!(config->flags & CONFIG_ADDR6) ||
|
||||
(config->flags & CONFIG_WILDCARD))
|
||||
|
||||
return 1;
|
||||
|
||||
for (; context; context = context->current)
|
||||
if (is_same_net6(&config->addr6, &context->start6, context->prefix))
|
||||
return 1;
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -349,7 +428,7 @@ struct dhcp_config *find_config6(struct dhcp_config *configs,
|
||||
{
|
||||
if (config->clid_len == duid_len &&
|
||||
memcmp(config->clid, duid, duid_len) == 0 &&
|
||||
is_addr_in_context6(context, config))
|
||||
is_config_in_context6(context, config))
|
||||
return config;
|
||||
}
|
||||
|
||||
@@ -357,7 +436,7 @@ struct dhcp_config *find_config6(struct dhcp_config *configs,
|
||||
for (config = configs; config; config = config->next)
|
||||
if ((config->flags & CONFIG_NAME) &&
|
||||
hostname_isequal(config->hostname, hostname) &&
|
||||
is_addr_in_context6(context, config))
|
||||
is_config_in_context6(context, config))
|
||||
return config;
|
||||
|
||||
return NULL;
|
||||
@@ -417,6 +496,140 @@ static int make_duid1(int index, unsigned int type, char *mac, size_t maclen, vo
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct cparam {
|
||||
time_t now;
|
||||
int newone, newname;
|
||||
};
|
||||
|
||||
static int construct_worker(struct in6_addr *local, int prefix,
|
||||
int scope, int if_index, int flags,
|
||||
int preferred, int valid, void *vparam)
|
||||
{
|
||||
char ifrn_name[IFNAMSIZ];
|
||||
struct in6_addr start6, end6;
|
||||
struct dhcp_context *template, *context;
|
||||
|
||||
(void)scope;
|
||||
(void)flags;
|
||||
(void)valid;
|
||||
(void)preferred;
|
||||
|
||||
struct cparam *param = vparam;
|
||||
|
||||
if (IN6_IS_ADDR_LOOPBACK(local) ||
|
||||
IN6_IS_ADDR_LINKLOCAL(local) ||
|
||||
IN6_IS_ADDR_MULTICAST(local))
|
||||
return 1;
|
||||
|
||||
if (!indextoname(daemon->doing_dhcp6 ? daemon->dhcp6fd : daemon->icmp6fd, if_index, ifrn_name))
|
||||
return 0;
|
||||
|
||||
for (template = daemon->dhcp6; template; template = template->next)
|
||||
if (!(template->flags & CONTEXT_TEMPLATE))
|
||||
{
|
||||
/* non-template entries, just fill in interface and local addresses */
|
||||
if (prefix == template->prefix &&
|
||||
is_same_net6(local, &template->start6, prefix) &&
|
||||
is_same_net6(local, &template->end6, prefix))
|
||||
{
|
||||
template->if_index = if_index;
|
||||
template->local6 = *local;
|
||||
}
|
||||
|
||||
}
|
||||
else if (addr6part(local) == addr6part(&template->start6) && wildcard_match(template->template_interface, ifrn_name))
|
||||
{
|
||||
start6 = *local;
|
||||
setaddr6part(&start6, addr6part(&template->start6));
|
||||
end6 = *local;
|
||||
setaddr6part(&end6, addr6part(&template->end6));
|
||||
|
||||
for (context = daemon->dhcp6; context; context = context->next)
|
||||
if ((context->flags & CONTEXT_CONSTRUCTED) &&
|
||||
IN6_ARE_ADDR_EQUAL(&start6, &context->start6) &&
|
||||
IN6_ARE_ADDR_EQUAL(&end6, &context->end6))
|
||||
{
|
||||
context->flags &= ~CONTEXT_GC;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!context && (context = whine_malloc(sizeof (struct dhcp_context))))
|
||||
{
|
||||
*context = *template;
|
||||
context->start6 = start6;
|
||||
context->end6 = end6;
|
||||
context->flags &= ~CONTEXT_TEMPLATE;
|
||||
context->flags |= CONTEXT_CONSTRUCTED;
|
||||
context->if_index = if_index;
|
||||
context->local6 = *local;
|
||||
|
||||
context->next = daemon->dhcp6;
|
||||
daemon->dhcp6 = context;
|
||||
|
||||
ra_start_unsolicted(param->now, context);
|
||||
/* we created a new one, need to call
|
||||
lease_update_file to get periodic functions called */
|
||||
param->newone = 1;
|
||||
|
||||
/* Will need to add new putative SLAAC addresses to existing leases */
|
||||
if (context->flags & CONTEXT_RA_NAME)
|
||||
param->newname = 1;
|
||||
|
||||
log_context(AF_INET6, context);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void dhcp_construct_contexts(time_t now)
|
||||
{
|
||||
struct dhcp_context *tmp, *context, **up;
|
||||
struct cparam param;
|
||||
param.newone = 0;
|
||||
param.newname = 0;
|
||||
param.now = now;
|
||||
|
||||
for (context = daemon->dhcp6; context; context = context->next)
|
||||
{
|
||||
context->if_index = 0;
|
||||
if (context->flags & CONTEXT_CONSTRUCTED)
|
||||
context->flags |= CONTEXT_GC;
|
||||
}
|
||||
|
||||
iface_enumerate(AF_INET6, ¶m, construct_worker);
|
||||
|
||||
for (up = &daemon->dhcp6, context = daemon->dhcp6; context; context = tmp)
|
||||
{
|
||||
tmp = context->next;
|
||||
|
||||
if (context->flags & CONTEXT_GC)
|
||||
{
|
||||
*up = context->next;
|
||||
param.newone = 1; /* include deletion */
|
||||
if (context->flags & CONTEXT_RA_NAME)
|
||||
param.newname = 1;
|
||||
free(context);
|
||||
}
|
||||
else
|
||||
up = &context->next;
|
||||
}
|
||||
|
||||
if (param.newone)
|
||||
{
|
||||
if (daemon->dhcp || daemon->doing_dhcp6)
|
||||
{
|
||||
if (param.newname)
|
||||
lease_update_slaac(now);
|
||||
lease_update_file(now);
|
||||
}
|
||||
else
|
||||
/* Not doing DHCP, so no lease system, manage alarms for ra only */
|
||||
send_alarm(periodic_ra(now), now);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2013 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,6 +52,7 @@
|
||||
#define T_OPT 41
|
||||
#define T_TKEY 249
|
||||
#define T_TSIG 250
|
||||
#define T_AXFR 252
|
||||
#define T_MAILB 253
|
||||
#define T_ANY 255
|
||||
|
||||
|
||||
484
src/dnsmasq.c
484
src/dnsmasq.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -51,6 +51,7 @@ int main (int argc, char **argv)
|
||||
cap_user_header_t hdr = NULL;
|
||||
cap_user_data_t data = NULL;
|
||||
#endif
|
||||
struct dhcp_context *context;
|
||||
|
||||
#ifdef LOCALEDIR
|
||||
setlocale(LC_ALL, "");
|
||||
@@ -84,6 +85,7 @@ int main (int argc, char **argv)
|
||||
|
||||
daemon->addrbuff = safe_malloc(ADDRSTRLEN);
|
||||
|
||||
|
||||
#ifdef HAVE_DHCP
|
||||
if (!daemon->lease_file)
|
||||
{
|
||||
@@ -114,10 +116,18 @@ int main (int argc, char **argv)
|
||||
set_option_bool(OPT_NOWILD);
|
||||
}
|
||||
# endif
|
||||
|
||||
/* -- bind-dynamic not supported on !Linux, fall back to --bind-interfaces */
|
||||
if (option_bool(OPT_CLEVERBIND))
|
||||
{
|
||||
bind_fallback = 1;
|
||||
set_option_bool(OPT_NOWILD);
|
||||
reset_option_bool(OPT_CLEVERBIND);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef HAVE_TFTP
|
||||
if (daemon->tftp_unlimited || daemon->tftp_interfaces)
|
||||
if (option_bool(OPT_TFTP))
|
||||
die(_("TFTP server not available: set HAVE_TFTP in src/config.h"), NULL, EC_BADCONF);
|
||||
#endif
|
||||
|
||||
@@ -139,86 +149,117 @@ int main (int argc, char **argv)
|
||||
die(_("asychronous logging is not available under Android"), NULL, EC_BADCONF);
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_AUTH
|
||||
if (daemon->authserver)
|
||||
die(_("authoritative DNS not available: set HAVE_AUTH in src/config.h"), NULL, EC_BADCONF);
|
||||
#endif
|
||||
|
||||
rand_init();
|
||||
|
||||
now = dnsmasq_time();
|
||||
|
||||
/* Create a serial at startup if not configured. */
|
||||
if (daemon->authinterface && daemon->soa_sn == 0)
|
||||
#ifdef HAVE_BROKEN_RTC
|
||||
die(_("zone serial must be configured in --auth-soa"), NULL, EC_BADCONF);
|
||||
#else
|
||||
daemon->soa_sn = now;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DHCP
|
||||
if (daemon->dhcp || daemon->dhcp6)
|
||||
{
|
||||
{
|
||||
|
||||
# ifdef HAVE_DHCP6
|
||||
if (daemon->dhcp6)
|
||||
{
|
||||
daemon->doing_ra = option_bool(OPT_RA);
|
||||
|
||||
for (context = daemon->dhcp6; context; context = context->next)
|
||||
{
|
||||
if (context->flags & CONTEXT_DHCP)
|
||||
daemon->doing_dhcp6 = 1;
|
||||
if (context->flags & CONTEXT_RA)
|
||||
daemon->doing_ra = 1;
|
||||
#ifndef HAVE_LINUX_NETWORK
|
||||
if (context->flags & CONTEXT_TEMPLATE)
|
||||
die (_("dhcp-range constructor not available on this platform"), NULL, EC_BADCONF);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
# endif
|
||||
|
||||
/* 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.*/
|
||||
dhcp_common_init();
|
||||
lease_init(now);
|
||||
|
||||
if (daemon->dhcp || daemon->doing_dhcp6)
|
||||
{
|
||||
dhcp_common_init();
|
||||
lease_init(now);
|
||||
}
|
||||
|
||||
if (daemon->dhcp)
|
||||
dhcp_init();
|
||||
}
|
||||
|
||||
|
||||
# ifdef HAVE_DHCP6
|
||||
/* Start RA subsystem if --enable-ra OR dhcp-range=<subnet>, ra-only */
|
||||
if (daemon->ra_contexts || option_bool(OPT_RA))
|
||||
{
|
||||
/* link the DHCP6 contexts to the ra-only ones so we can traverse them all
|
||||
from ->ra_contexts, but only the non-ra-onlies from ->dhcp6 */
|
||||
struct dhcp_context *context;
|
||||
if (daemon->doing_ra)
|
||||
ra_init(now);
|
||||
|
||||
if (!daemon->ra_contexts)
|
||||
daemon->ra_contexts = daemon->dhcp6;
|
||||
else
|
||||
{
|
||||
for (context = daemon->ra_contexts; context->next; context = context->next);
|
||||
context->next = daemon->dhcp6;
|
||||
}
|
||||
ra_init(now);
|
||||
if (daemon->doing_dhcp6)
|
||||
dhcp6_init();
|
||||
# endif
|
||||
}
|
||||
|
||||
if (daemon->dhcp6)
|
||||
dhcp6_init();
|
||||
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_IPSET
|
||||
if (daemon->ipsets)
|
||||
ipset_init();
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
/* After lease_init */
|
||||
netlink_init();
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
/* after netlink_init */
|
||||
if (daemon->ra_contexts || daemon->dhcp6)
|
||||
join_multicast();
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DHCP
|
||||
/* after netlink_init */
|
||||
if (daemon->dhcp || daemon->dhcp6)
|
||||
lease_find_interfaces(now);
|
||||
|
||||
if (option_bool(OPT_NOWILD) && option_bool(OPT_CLEVERBIND))
|
||||
die(_("cannot set --bind-interfaces and --bind-dynamic"), NULL, EC_BADCONF);
|
||||
#endif
|
||||
|
||||
if (!enumerate_interfaces())
|
||||
die(_("failed to find list of interfaces: %s"), NULL, EC_MISC);
|
||||
|
||||
if (option_bool(OPT_NOWILD))
|
||||
if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND))
|
||||
{
|
||||
create_bound_listeners(1);
|
||||
|
||||
if (!option_bool(OPT_CLEVERBIND))
|
||||
for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next)
|
||||
if (if_tmp->name && !if_tmp->used)
|
||||
die(_("unknown interface %s"), if_tmp->name, EC_BADNET);
|
||||
|
||||
for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next)
|
||||
if (if_tmp->name && !if_tmp->used)
|
||||
die(_("unknown interface %s"), if_tmp->name, EC_BADNET);
|
||||
|
||||
for (if_tmp = daemon->if_addrs; if_tmp; if_tmp = if_tmp->next)
|
||||
if (!if_tmp->used)
|
||||
{
|
||||
prettyprint_addr(&if_tmp->addr, daemon->namebuff);
|
||||
die(_("no interface with address %s"), daemon->namebuff, EC_BADNET);
|
||||
}
|
||||
#if defined(HAVE_LINUX_NETWORK) && defined(HAVE_DHCP)
|
||||
/* after enumerate_interfaces() */
|
||||
if (daemon->dhcp)
|
||||
{
|
||||
bindtodevice(daemon->dhcpfd);
|
||||
if (daemon->enable_pxe)
|
||||
bindtodevice(daemon->pxefd);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_LINUX_NETWORK) && defined(HAVE_DHCP6)
|
||||
if (daemon->dhcp6)
|
||||
bindtodevice(daemon->dhcp6fd);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
create_wildcard_listeners();
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
/* after enumerate_interfaces() */
|
||||
if (daemon->doing_dhcp6 || daemon->doing_ra)
|
||||
join_multicast(1);
|
||||
#endif
|
||||
|
||||
if (daemon->port != 0)
|
||||
cache_init();
|
||||
@@ -363,15 +404,48 @@ int main (int argc, char **argv)
|
||||
/* write pidfile _after_ forking ! */
|
||||
if (daemon->runfile)
|
||||
{
|
||||
FILE *pidfile;
|
||||
int fd, err = 0;
|
||||
|
||||
sprintf(daemon->namebuff, "%d\n", (int) getpid());
|
||||
|
||||
/* Explanation: Some installations of dnsmasq (eg Debian/Ubuntu) locate the pid-file
|
||||
in a directory which is writable by the non-privileged user that dnsmasq runs as. This
|
||||
allows the daemon to delete the file as part of its shutdown. This is a security hole to the
|
||||
extent that an attacker running as the unprivileged user could replace the pidfile with a
|
||||
symlink, and have the target of that symlink overwritten as root next time dnsmasq starts.
|
||||
|
||||
The folowing code first deletes any existing file, and then opens it with the O_EXCL flag,
|
||||
ensuring that the open() fails should there be any existing file (because the unlink() failed,
|
||||
or an attacker exploited the race between unlink() and open()). This ensures that no symlink
|
||||
attack can succeed.
|
||||
|
||||
Any compromise of the non-privileged user still theoretically allows the pid-file to be
|
||||
replaced whilst dnsmasq is running. The worst that could allow is that the usual
|
||||
"shutdown dnsmasq" shell command could be tricked into stopping any other process.
|
||||
|
||||
Note that if dnsmasq is started as non-root (eg for testing) it silently ignores
|
||||
failure to write the pid-file.
|
||||
*/
|
||||
|
||||
unlink(daemon->runfile);
|
||||
|
||||
/* only complain if started as root */
|
||||
if ((pidfile = fopen(daemon->runfile, "w")))
|
||||
if ((fd = open(daemon->runfile, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH)) == -1)
|
||||
{
|
||||
fprintf(pidfile, "%d\n", (int) getpid());
|
||||
fclose(pidfile);
|
||||
/* only complain if started as root */
|
||||
if (getuid() == 0)
|
||||
err = 1;
|
||||
}
|
||||
else if (getuid() == 0)
|
||||
else
|
||||
{
|
||||
if (!read_write(fd, (unsigned char *)daemon->namebuff, strlen(daemon->namebuff), 0))
|
||||
err = 1;
|
||||
|
||||
while (!err && close(fd) == -1)
|
||||
if (!retry_send())
|
||||
err = 1;
|
||||
}
|
||||
|
||||
if (err)
|
||||
{
|
||||
send_event(err_pipe[1], EVENT_PIDFILE, errno, daemon->runfile);
|
||||
_exit(0);
|
||||
@@ -417,8 +491,9 @@ int main (int argc, char **argv)
|
||||
#if defined(HAVE_LINUX_NETWORK)
|
||||
/* On linux, we keep CAP_NETADMIN (for ARP-injection) and
|
||||
CAP_NET_RAW (for icmp) if we're doing dhcp. If we have yet to bind
|
||||
ports because of DAD, we need CAP_NET_BIND_SERVICE too. */
|
||||
if (is_dad_listeners())
|
||||
ports because of DAD, or we're doing it dynamically,
|
||||
we need CAP_NET_BIND_SERVICE too. */
|
||||
if (is_dad_listeners() || option_bool(OPT_CLEVERBIND))
|
||||
data->effective = data->permitted = data->inheritable =
|
||||
(1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) |
|
||||
(1 << CAP_SETUID) | (1 << CAP_NET_BIND_SERVICE);
|
||||
@@ -466,7 +541,7 @@ int main (int argc, char **argv)
|
||||
}
|
||||
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
if (is_dad_listeners())
|
||||
if (is_dad_listeners() || option_bool(OPT_CLEVERBIND))
|
||||
data->effective = data->permitted =
|
||||
(1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) | (1 << CAP_NET_BIND_SERVICE);
|
||||
else
|
||||
@@ -490,6 +565,34 @@ int main (int argc, char **argv)
|
||||
prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TFTP
|
||||
if (option_bool(OPT_TFTP))
|
||||
{
|
||||
DIR *dir;
|
||||
struct tftp_prefix *p;
|
||||
|
||||
if (daemon->tftp_prefix)
|
||||
{
|
||||
if (!((dir = opendir(daemon->tftp_prefix))))
|
||||
{
|
||||
send_event(err_pipe[1], EVENT_TFTP_ERR, errno, daemon->tftp_prefix);
|
||||
_exit(0);
|
||||
}
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
for (p = daemon->if_prefix; p; p = p->next)
|
||||
{
|
||||
if (!((dir = opendir(p->prefix))))
|
||||
{
|
||||
send_event(err_pipe[1], EVENT_TFTP_ERR, errno, p->prefix);
|
||||
_exit(0);
|
||||
}
|
||||
closedir(dir);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (daemon->port == 0)
|
||||
my_syslog(LOG_INFO, _("started, version %s DNS disabled"), VERSION);
|
||||
else if (daemon->cachesize != 0)
|
||||
@@ -532,67 +635,30 @@ int main (int argc, char **argv)
|
||||
|
||||
if (daemon->max_logs != 0)
|
||||
my_syslog(LOG_INFO, _("asynchronous logging enabled, queue limit is %d messages"), daemon->max_logs);
|
||||
|
||||
if (daemon->ra_contexts)
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("IPv6 router advertisement enabled"));
|
||||
|
||||
|
||||
#ifdef HAVE_DHCP
|
||||
if (daemon->dhcp || daemon->dhcp6)
|
||||
{
|
||||
struct dhcp_context *dhcp_tmp;
|
||||
int family = AF_INET;
|
||||
dhcp_tmp = daemon->dhcp;
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
again:
|
||||
#endif
|
||||
for (; dhcp_tmp; dhcp_tmp = dhcp_tmp->next)
|
||||
{
|
||||
void *start = &dhcp_tmp->start;
|
||||
void *end = &dhcp_tmp->end;
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
if (family == AF_INET6)
|
||||
{
|
||||
start = &dhcp_tmp->start6;
|
||||
end = &dhcp_tmp->end6;
|
||||
}
|
||||
#endif
|
||||
|
||||
prettyprint_time(daemon->dhcp_buff2, dhcp_tmp->lease_time);
|
||||
inet_ntop(family, start, daemon->dhcp_buff, 256);
|
||||
inet_ntop(family, end, daemon->dhcp_buff3, 256);
|
||||
my_syslog(MS_DHCP | LOG_INFO,
|
||||
(dhcp_tmp->flags & CONTEXT_STATIC) ?
|
||||
_("DHCP, static leases only on %.0s%s, lease time %s") :
|
||||
(dhcp_tmp->flags & CONTEXT_RA_NAME) ?
|
||||
_("router advertisement with DHCPv4-derived names on %.0s%s, lifetime %s") :
|
||||
(dhcp_tmp->flags & CONTEXT_RA_ONLY) ?
|
||||
_("router advertisement only on %.0s%s, lifetime %s") :
|
||||
(dhcp_tmp->flags & CONTEXT_PROXY) ?
|
||||
_("DHCP, proxy on subnet %.0s%s%.0s") :
|
||||
_("DHCP, IP range %s -- %s, lease time %s"),
|
||||
daemon->dhcp_buff, daemon->dhcp_buff3, daemon->dhcp_buff2);
|
||||
}
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
if (family == AF_INET)
|
||||
{
|
||||
family = AF_INET6;
|
||||
if (daemon->ra_contexts)
|
||||
dhcp_tmp = daemon->ra_contexts;
|
||||
else
|
||||
dhcp_tmp = daemon->dhcp6;
|
||||
goto again;
|
||||
}
|
||||
#endif
|
||||
for (context = daemon->dhcp; context; context = context->next)
|
||||
log_context(AF_INET, context);
|
||||
|
||||
}
|
||||
#endif
|
||||
# ifdef HAVE_DHCP6
|
||||
for (context = daemon->dhcp6; context; context = context->next)
|
||||
log_context(AF_INET6, context);
|
||||
|
||||
if (daemon->doing_dhcp6 || daemon->doing_ra)
|
||||
dhcp_construct_contexts(now);
|
||||
|
||||
if (option_bool(OPT_RA))
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("IPv6 router advertisement enabled"));
|
||||
# endif
|
||||
|
||||
/* after dhcp_contruct_contexts */
|
||||
if (daemon->dhcp || daemon->doing_dhcp6)
|
||||
lease_find_interfaces(now);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TFTP
|
||||
if (daemon->tftp_unlimited || daemon->tftp_interfaces)
|
||||
if (option_bool(OPT_TFTP))
|
||||
{
|
||||
#ifdef FD_SETSIZE
|
||||
if (FD_SETSIZE < (unsigned)max_fd)
|
||||
@@ -696,16 +762,16 @@ int main (int argc, char **argv)
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
if (daemon->dhcp6)
|
||||
if (daemon->doing_dhcp6)
|
||||
{
|
||||
FD_SET(daemon->dhcp6fd, &rset);
|
||||
bump_maxfd(daemon->dhcp6fd, &maxfd);
|
||||
|
||||
if (daemon->ra_contexts)
|
||||
{
|
||||
FD_SET(daemon->icmp6fd, &rset);
|
||||
bump_maxfd(daemon->icmp6fd, &maxfd);
|
||||
}
|
||||
}
|
||||
|
||||
if (daemon->doing_ra)
|
||||
{
|
||||
FD_SET(daemon->icmp6fd, &rset);
|
||||
bump_maxfd(daemon->icmp6fd, &maxfd);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -721,6 +787,10 @@ int main (int argc, char **argv)
|
||||
# ifdef HAVE_SCRIPT
|
||||
while (helper_buf_empty() && do_script_run(now));
|
||||
|
||||
# ifdef HAVE_TFTP
|
||||
while (helper_buf_empty() && do_tftp_script_run());
|
||||
# endif
|
||||
|
||||
if (!helper_buf_empty())
|
||||
{
|
||||
FD_SET(daemon->helperfd, &wset);
|
||||
@@ -729,6 +799,11 @@ int main (int argc, char **argv)
|
||||
# else
|
||||
/* need this for other side-effects */
|
||||
while (do_script_run(now));
|
||||
|
||||
# ifdef HAVE_TFTP
|
||||
while (do_tftp_script_run());
|
||||
# endif
|
||||
|
||||
# endif
|
||||
#endif
|
||||
|
||||
@@ -757,7 +832,7 @@ int main (int argc, char **argv)
|
||||
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
if (FD_ISSET(daemon->netlinkfd, &rset))
|
||||
netlink_multicast();
|
||||
netlink_multicast(now);
|
||||
#endif
|
||||
|
||||
/* Check for changes to resolv files once per second max. */
|
||||
@@ -805,14 +880,11 @@ int main (int argc, char **argv)
|
||||
}
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
if (daemon->dhcp6)
|
||||
{
|
||||
if (FD_ISSET(daemon->dhcp6fd, &rset))
|
||||
dhcp6_packet(now);
|
||||
if (daemon->doing_dhcp6 && FD_ISSET(daemon->dhcp6fd, &rset))
|
||||
dhcp6_packet(now);
|
||||
|
||||
if (daemon->ra_contexts && FD_ISSET(daemon->icmp6fd, &rset))
|
||||
icmp6_packet();
|
||||
}
|
||||
if (daemon->doing_ra && FD_ISSET(daemon->icmp6fd, &rset))
|
||||
icmp6_packet(now);
|
||||
#endif
|
||||
|
||||
# ifdef HAVE_SCRIPT
|
||||
@@ -864,9 +936,17 @@ static void sig_handler(int sig)
|
||||
}
|
||||
}
|
||||
|
||||
void send_alarm(void)
|
||||
/* now == 0 -> queue immediate callback */
|
||||
void send_alarm(time_t event, time_t now)
|
||||
{
|
||||
send_event(pipewrite, EVENT_ALARM, 0, NULL);
|
||||
if (now == 0 || event != 0)
|
||||
{
|
||||
/* alarm(0) or alarm(-ve) doesn't do what we want.... */
|
||||
if ((now == 0 || difftime(event, now) <= 0.0))
|
||||
send_event(pipewrite, EVENT_ALARM, 0, NULL);
|
||||
else
|
||||
alarm((unsigned)difftime(event, now));
|
||||
}
|
||||
}
|
||||
|
||||
void send_event(int fd, int event, int data, char *msg)
|
||||
@@ -946,6 +1026,9 @@ static void fatal_event(struct event_desc *ev, char *msg)
|
||||
|
||||
case EVENT_LUA_ERR:
|
||||
die(_("failed to load Lua script: %s"), msg, EC_MISC);
|
||||
|
||||
case EVENT_TFTP_ERR:
|
||||
die(_("TFTP directory %s inaccessible: %s"), msg, EC_FILE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -981,20 +1064,15 @@ static void async_event(int pipe, time_t now)
|
||||
|
||||
case EVENT_ALARM:
|
||||
#ifdef HAVE_DHCP
|
||||
if (daemon->dhcp || daemon->dhcp6)
|
||||
if (daemon->dhcp || daemon->doing_dhcp6)
|
||||
{
|
||||
lease_prune(NULL, now);
|
||||
lease_update_file(now);
|
||||
}
|
||||
#ifdef HAVE_DHCP6
|
||||
else if (daemon->ra_contexts)
|
||||
{
|
||||
/* Not doing DHCP, so no lease system, manage
|
||||
alarms for ra only */
|
||||
time_t next_event = periodic_ra(now);
|
||||
if (next_event != 0)
|
||||
alarm((unsigned)difftime(next_event, now));
|
||||
}
|
||||
else if (daemon->doing_ra)
|
||||
/* Not doing DHCP, so no lease system, manage alarms for ra only */
|
||||
send_alarm(periodic_ra(now), now);
|
||||
#endif
|
||||
#endif
|
||||
break;
|
||||
@@ -1149,26 +1227,21 @@ void clear_cache_and_reload(time_t now)
|
||||
cache_reload();
|
||||
|
||||
#ifdef HAVE_DHCP
|
||||
if (daemon->dhcp || daemon->dhcp6)
|
||||
if (daemon->dhcp || daemon->doing_dhcp6)
|
||||
{
|
||||
if (option_bool(OPT_ETHERS))
|
||||
dhcp_read_ethers();
|
||||
reread_dhcp();
|
||||
dhcp_update_configs(daemon->dhcp_conf);
|
||||
check_dhcp_hosts(0);
|
||||
lease_update_from_configs();
|
||||
lease_update_file(now);
|
||||
lease_update_dns();
|
||||
lease_update_dns(1);
|
||||
}
|
||||
#ifdef HAVE_DHCP6
|
||||
else if (daemon->ra_contexts)
|
||||
{
|
||||
/* Not doing DHCP, so no lease system, manage
|
||||
alarms for ra only */
|
||||
time_t next_event = periodic_ra(now);
|
||||
if (next_event != 0)
|
||||
alarm((unsigned)difftime(next_event, now));
|
||||
}
|
||||
else if (daemon->doing_ra)
|
||||
/* Not doing DHCP, so no lease system, manage
|
||||
alarms for ra only */
|
||||
send_alarm(periodic_ra(now), now);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
@@ -1269,36 +1342,74 @@ static void check_dns_listeners(fd_set *set, time_t now)
|
||||
|
||||
if (listener->tcpfd != -1 && FD_ISSET(listener->tcpfd, set))
|
||||
{
|
||||
int confd;
|
||||
int confd, client_ok = 1;
|
||||
struct irec *iface = NULL;
|
||||
pid_t p;
|
||||
|
||||
while((confd = accept(listener->tcpfd, NULL, NULL)) == -1 && errno == EINTR);
|
||||
union mysockaddr tcp_addr;
|
||||
socklen_t tcp_len = sizeof(union mysockaddr);
|
||||
|
||||
while ((confd = accept(listener->tcpfd, NULL, NULL)) == -1 && errno == EINTR);
|
||||
|
||||
if (confd == -1)
|
||||
continue;
|
||||
|
||||
if (option_bool(OPT_NOWILD))
|
||||
iface = listener->iface;
|
||||
else
|
||||
|
||||
if (getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) == -1)
|
||||
{
|
||||
union mysockaddr tcp_addr;
|
||||
socklen_t tcp_len = sizeof(union mysockaddr);
|
||||
/* Check for allowed interfaces when binding the wildcard address:
|
||||
we do this by looking for an interface with the same address as
|
||||
the local address of the TCP connection, then looking to see if that's
|
||||
an allowed interface. As a side effect, we get the netmask of the
|
||||
interface too, for localisation. */
|
||||
|
||||
/* interface may be new since startup */
|
||||
if (enumerate_interfaces() &&
|
||||
getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) != -1)
|
||||
for (iface = daemon->interfaces; iface; iface = iface->next)
|
||||
if (sockaddr_isequal(&iface->addr, &tcp_addr))
|
||||
break;
|
||||
close(confd);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!iface)
|
||||
|
||||
if (option_bool(OPT_NOWILD))
|
||||
iface = listener->iface; /* May be NULL */
|
||||
else
|
||||
{
|
||||
int if_index;
|
||||
char intr_name[IF_NAMESIZE];
|
||||
|
||||
/* In full wildcard mode, need to refresh interface list.
|
||||
This happens automagically in CLEVERBIND */
|
||||
if (!option_bool(OPT_CLEVERBIND))
|
||||
enumerate_interfaces();
|
||||
|
||||
/* if we can find the arrival interface, check it's one that's allowed */
|
||||
if ((if_index = tcp_interface(confd, tcp_addr.sa.sa_family)) != 0 &&
|
||||
indextoname(listener->tcpfd, if_index, intr_name))
|
||||
{
|
||||
struct all_addr addr;
|
||||
addr.addr.addr4 = tcp_addr.in.sin_addr;
|
||||
#ifdef HAVE_IPV6
|
||||
if (tcp_addr.sa.sa_family == AF_INET6)
|
||||
addr.addr.addr6 = tcp_addr.in6.sin6_addr;
|
||||
#endif
|
||||
|
||||
for (iface = daemon->interfaces; iface; iface = iface->next)
|
||||
if (iface->index == if_index)
|
||||
break;
|
||||
|
||||
if (!iface && !loopback_exception(listener->tcpfd, tcp_addr.sa.sa_family, &addr, intr_name))
|
||||
client_ok = 0;
|
||||
}
|
||||
|
||||
if (option_bool(OPT_CLEVERBIND))
|
||||
iface = listener->iface; /* May be NULL */
|
||||
else
|
||||
{
|
||||
/* Check for allowed interfaces when binding the wildcard address:
|
||||
we do this by looking for an interface with the same address as
|
||||
the local address of the TCP connection, then looking to see if that's
|
||||
an allowed interface. As a side effect, we get the netmask of the
|
||||
interface too, for localisation. */
|
||||
|
||||
for (iface = daemon->interfaces; iface; iface = iface->next)
|
||||
if (sockaddr_isequal(&iface->addr, &tcp_addr))
|
||||
break;
|
||||
|
||||
if (!iface)
|
||||
client_ok = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!client_ok)
|
||||
{
|
||||
shutdown(confd, SHUT_RDWR);
|
||||
close(confd);
|
||||
@@ -1324,7 +1435,20 @@ static void check_dns_listeners(fd_set *set, time_t now)
|
||||
unsigned char *buff;
|
||||
struct server *s;
|
||||
int flags;
|
||||
|
||||
struct in_addr netmask;
|
||||
int auth_dns;
|
||||
|
||||
if (iface)
|
||||
{
|
||||
netmask = iface->netmask;
|
||||
auth_dns = iface->dns_auth;
|
||||
}
|
||||
else
|
||||
{
|
||||
netmask.s_addr = 0;
|
||||
auth_dns = 0;
|
||||
}
|
||||
|
||||
#ifndef NO_FORK
|
||||
/* Arrange for SIGALARM after CHILD_LIFETIME seconds to
|
||||
terminate the process. */
|
||||
@@ -1342,7 +1466,7 @@ static void check_dns_listeners(fd_set *set, time_t now)
|
||||
if ((flags = fcntl(confd, F_GETFL, 0)) != -1)
|
||||
fcntl(confd, F_SETFL, flags & ~O_NONBLOCK);
|
||||
|
||||
buff = tcp_request(confd, now, &iface->addr, iface->netmask);
|
||||
buff = tcp_request(confd, now, &tcp_addr, netmask, auth_dns);
|
||||
|
||||
shutdown(confd, SHUT_RDWR);
|
||||
close(confd);
|
||||
@@ -1455,7 +1579,7 @@ int icmp_ping(struct in_addr addr)
|
||||
set_log_writer(&wset, &maxfd);
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
if (daemon->ra_contexts)
|
||||
if (daemon->doing_ra)
|
||||
{
|
||||
FD_SET(daemon->icmp6fd, &rset);
|
||||
bump_maxfd(daemon->icmp6fd, &maxfd);
|
||||
@@ -1474,8 +1598,8 @@ int icmp_ping(struct in_addr addr)
|
||||
check_dns_listeners(&rset, now);
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
if (daemon->ra_contexts && FD_ISSET(daemon->icmp6fd, &rset))
|
||||
icmp6_packet();
|
||||
if (daemon->doing_ra && FD_ISSET(daemon->icmp6fd, &rset))
|
||||
icmp6_packet(now);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TFTP
|
||||
|
||||
297
src/dnsmasq.h
297
src/dnsmasq.h
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2013 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-2012 Simon Kelley"
|
||||
#define COPYRIGHT "Copyright (c) 2000-2013 Simon Kelley"
|
||||
|
||||
#ifndef NO_LARGEFILE
|
||||
/* Ensure we can use files >2GB (log files may grow this big) */
|
||||
@@ -159,6 +159,7 @@ struct event_desc {
|
||||
#define EVENT_LOG_ERR 17
|
||||
#define EVENT_FORK_ERR 18
|
||||
#define EVENT_LUA_ERR 19
|
||||
#define EVENT_TFTP_ERR 20
|
||||
|
||||
/* Exit codes. */
|
||||
#define EC_GOOD 0
|
||||
@@ -217,7 +218,10 @@ struct event_desc {
|
||||
#define OPT_CONNTRACK 35
|
||||
#define OPT_FQDN_UPDATE 36
|
||||
#define OPT_RA 37
|
||||
#define OPT_LAST 38
|
||||
#define OPT_TFTP_LC 38
|
||||
#define OPT_CLEVERBIND 39
|
||||
#define OPT_TFTP 40
|
||||
#define OPT_LAST 41
|
||||
|
||||
/* 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. */
|
||||
@@ -274,6 +278,32 @@ struct cname {
|
||||
struct cname *next;
|
||||
};
|
||||
|
||||
struct auth_zone {
|
||||
char *domain;
|
||||
struct subnet {
|
||||
int is6, prefixlen;
|
||||
struct in_addr addr4;
|
||||
#ifdef HAVE_IPV6
|
||||
struct in6_addr addr6;
|
||||
#endif
|
||||
struct subnet *next;
|
||||
} *subnet;
|
||||
struct auth_zone *next;
|
||||
};
|
||||
|
||||
|
||||
struct host_record {
|
||||
struct name_list {
|
||||
char *name;
|
||||
struct name_list *next;
|
||||
} *names;
|
||||
struct in_addr addr;
|
||||
#ifdef HAVE_IPV6
|
||||
struct in6_addr addr6;
|
||||
#endif
|
||||
struct host_record *next;
|
||||
};
|
||||
|
||||
struct interface_name {
|
||||
char *name; /* domain name */
|
||||
char *intr; /* interface name */
|
||||
@@ -285,17 +315,30 @@ union bigname {
|
||||
union bigname *next; /* freelist */
|
||||
};
|
||||
|
||||
struct keydata {
|
||||
struct keydata *next;
|
||||
unsigned char key[KEYBLOCK_LEN];
|
||||
};
|
||||
|
||||
struct crec {
|
||||
struct crec *next, *prev, *hash_next;
|
||||
time_t ttd; /* time to die */
|
||||
int uid;
|
||||
/* union is 16 bytes when doing IPv6, 8 bytes on 32 bit machines without IPv6 */
|
||||
union {
|
||||
struct all_addr addr;
|
||||
struct {
|
||||
struct crec *cache;
|
||||
int uid;
|
||||
} cname;
|
||||
struct {
|
||||
struct keydata *keydata;
|
||||
unsigned char algo;
|
||||
unsigned char digest; /* DS only */
|
||||
unsigned short flags_or_keyid; /* flags for DNSKEY, keyid for DS */
|
||||
} key;
|
||||
} addr;
|
||||
time_t ttd; /* time to die */
|
||||
/* used as keylen if F_DS or F_DNSKEY, index to source for F_HOSTS */
|
||||
int uid;
|
||||
unsigned short flags;
|
||||
union {
|
||||
char sname[SMALLDNAME];
|
||||
@@ -316,14 +359,23 @@ struct crec {
|
||||
#define F_BIGNAME (1u<<9)
|
||||
#define F_NXDOMAIN (1u<<10)
|
||||
#define F_CNAME (1u<<11)
|
||||
#define F_NOERR (1u<<12)
|
||||
#define F_DNSKEY (1u<<12)
|
||||
#define F_CONFIG (1u<<13)
|
||||
#define F_DS (1u<<14)
|
||||
#define F_DNSSECOK (1u<<15)
|
||||
|
||||
/* below here are only valid as args to log_query: cache
|
||||
entries are limited to 16 bits */
|
||||
#define F_UPSTREAM (1u<<16)
|
||||
#define F_RRNAME (1u<<17)
|
||||
#define F_SERVER (1u<<18)
|
||||
#define F_QUERY (1u<<19)
|
||||
#define F_NOERR (1u<<20)
|
||||
#define F_AUTH (1u<<21)
|
||||
|
||||
/* composites */
|
||||
#define F_TYPE (F_IPV4 | F_IPV6 | F_DNSKEY | F_DS) /* Only one may be set */
|
||||
|
||||
|
||||
|
||||
/* struct sockaddr is not large enough to hold any address,
|
||||
@@ -337,6 +389,11 @@ union mysockaddr {
|
||||
#endif
|
||||
};
|
||||
|
||||
/* bits in flag param to IPv6 callbacks from iface_enumerate() */
|
||||
#define IFACE_TENTATIVE 1
|
||||
#define IFACE_DEPRECATED 2
|
||||
|
||||
|
||||
#define SERV_FROM_RESOLV 1 /* 1 for servers from resolv, 0 for command line. */
|
||||
#define SERV_NO_ADDR 2 /* no server, this domain is local only */
|
||||
#define SERV_LITERAL_ADDRESS 4 /* addr is the answer, not the server */
|
||||
@@ -373,17 +430,23 @@ struct server {
|
||||
struct server *next;
|
||||
};
|
||||
|
||||
struct ipsets {
|
||||
char **sets;
|
||||
char *domain;
|
||||
struct ipsets *next;
|
||||
};
|
||||
|
||||
struct irec {
|
||||
union mysockaddr addr;
|
||||
struct in_addr netmask; /* only valid for IPv4 */
|
||||
int tftp_ok, mtu, done, dad;
|
||||
char *name;
|
||||
int tftp_ok, dhcp_ok, mtu, done, dad, dns_auth, index, multicast_done;
|
||||
char *name;
|
||||
struct irec *next;
|
||||
};
|
||||
|
||||
struct listener {
|
||||
int fd, tcpfd, tftpfd, family;
|
||||
struct irec *iface; /* only valid for non-wildcard */
|
||||
struct irec *iface; /* only sometimes valid for non-wildcard */
|
||||
struct listener *next;
|
||||
};
|
||||
|
||||
@@ -391,7 +454,7 @@ struct listener {
|
||||
struct iname {
|
||||
char *name;
|
||||
union mysockaddr addr;
|
||||
int isloop, used;
|
||||
int used;
|
||||
struct iname *next;
|
||||
};
|
||||
|
||||
@@ -432,11 +495,21 @@ struct frec {
|
||||
struct frec *next;
|
||||
};
|
||||
|
||||
/* flags in top of length field for DHCP-option tables */
|
||||
#define OT_ADDR_LIST 0x8000
|
||||
#define OT_RFC1035_NAME 0x4000
|
||||
#define OT_INTERNAL 0x2000
|
||||
#define OT_NAME 0x1000
|
||||
#define OT_CSTRING 0x0800
|
||||
#define OT_DEC 0x0400
|
||||
#define OT_TIME 0x0200
|
||||
|
||||
/* actions in the daemon->helper RPC */
|
||||
#define ACTION_DEL 1
|
||||
#define ACTION_OLD_HOSTNAME 2
|
||||
#define ACTION_OLD 3
|
||||
#define ACTION_ADD 4
|
||||
#define ACTION_TFTP 5
|
||||
|
||||
#define LEASE_NEW 1 /* newly created */
|
||||
#define LEASE_CHANGED 2 /* modified */
|
||||
@@ -445,6 +518,7 @@ struct frec {
|
||||
#define LEASE_USED 16 /* used this DHCPv6 transaction */
|
||||
#define LEASE_NA 32 /* IPv6 no-temporary lease */
|
||||
#define LEASE_TA 64 /* IPv6 temporary lease */
|
||||
#define LEASE_HAVE_HWADDR 128 /* Have set hwaddress */
|
||||
|
||||
struct dhcp_lease {
|
||||
int clid_len; /* length of client identifier */
|
||||
@@ -462,6 +536,15 @@ struct dhcp_lease {
|
||||
unsigned char *extradata;
|
||||
unsigned int extradata_len, extradata_size;
|
||||
int last_interface;
|
||||
#ifdef HAVE_DHCP6
|
||||
struct slaac_address {
|
||||
struct in6_addr addr, local;
|
||||
time_t ping_time;
|
||||
int backoff; /* zero -> confirmed */
|
||||
struct slaac_address *next;
|
||||
} *slaac_address;
|
||||
int vendorclass_count;
|
||||
#endif
|
||||
struct dhcp_lease *next;
|
||||
};
|
||||
|
||||
@@ -517,6 +600,7 @@ struct dhcp_config {
|
||||
#define CONFIG_DECLINED 1024 /* address declined by client */
|
||||
#define CONFIG_BANK 2048 /* from dhcp hosts file */
|
||||
#define CONFIG_ADDR6 4096
|
||||
#define CONFIG_WILDCARD 8192
|
||||
|
||||
struct dhcp_opt {
|
||||
int opt, len, flags;
|
||||
@@ -589,14 +673,22 @@ struct dhcp_bridge {
|
||||
};
|
||||
|
||||
struct cond_domain {
|
||||
char *domain;
|
||||
char *domain, *prefix;
|
||||
struct in_addr start, end;
|
||||
#ifdef HAVE_IPV6
|
||||
struct in6_addr start6, end6;
|
||||
#endif
|
||||
int is6;
|
||||
struct cond_domain *next;
|
||||
};
|
||||
|
||||
#ifdef OPTION6_PREFIX_CLASS
|
||||
struct prefix_class {
|
||||
int class;
|
||||
struct dhcp_netid tag;
|
||||
struct prefix_class *next;
|
||||
};
|
||||
#endif
|
||||
|
||||
struct dhcp_context {
|
||||
unsigned int lease_time, addr_epoch;
|
||||
@@ -606,22 +698,32 @@ struct dhcp_context {
|
||||
#ifdef HAVE_DHCP6
|
||||
struct in6_addr start6, end6; /* range of available addresses */
|
||||
struct in6_addr local6;
|
||||
int prefix;
|
||||
time_t ra_time;
|
||||
int prefix, if_index;
|
||||
unsigned int valid, preferred;
|
||||
time_t ra_time, ra_short_period_start;
|
||||
char *template_interface;
|
||||
#endif
|
||||
int flags;
|
||||
char *interface;
|
||||
struct dhcp_netid netid, *filter;
|
||||
struct dhcp_context *next, *current;
|
||||
};
|
||||
|
||||
#define CONTEXT_STATIC 1
|
||||
#define CONTEXT_NETMASK 2
|
||||
#define CONTEXT_BRDCAST 4
|
||||
#define CONTEXT_PROXY 8
|
||||
#define CONTEXT_RA_ONLY 16
|
||||
#define CONTEXT_RA_DONE 32
|
||||
#define CONTEXT_RA_NAME 64
|
||||
#define CONTEXT_STATIC 1
|
||||
#define CONTEXT_NETMASK 2
|
||||
#define CONTEXT_BRDCAST 4
|
||||
#define CONTEXT_PROXY 8
|
||||
#define CONTEXT_RA_ONLY 16
|
||||
#define CONTEXT_RA_DONE 32
|
||||
#define CONTEXT_RA_NAME 64
|
||||
#define CONTEXT_RA_STATELESS 128
|
||||
#define CONTEXT_DHCP 256
|
||||
#define CONTEXT_DEPRECATE 512
|
||||
#define CONTEXT_TEMPLATE 1024 /* create contexts using addresses */
|
||||
#define CONTEXT_CONSTRUCTED 2048
|
||||
#define CONTEXT_GC 4096
|
||||
#define CONTEXT_RA 8192
|
||||
#define CONTEXT_CONF_USED 16384
|
||||
#define CONTEXT_USED 32768
|
||||
|
||||
struct ping_result {
|
||||
struct in_addr addr;
|
||||
@@ -630,13 +732,6 @@ struct ping_result {
|
||||
struct ping_result *next;
|
||||
};
|
||||
|
||||
struct subnet_map {
|
||||
int iface;
|
||||
struct in6_addr subnet;
|
||||
struct subnet_map *next;
|
||||
};
|
||||
|
||||
|
||||
struct tftp_file {
|
||||
int refcount, fd;
|
||||
off_t size;
|
||||
@@ -662,11 +757,6 @@ struct addr_list {
|
||||
struct addr_list *next;
|
||||
};
|
||||
|
||||
struct interface_list {
|
||||
char *interface;
|
||||
struct interface_list *next;
|
||||
};
|
||||
|
||||
struct tftp_prefix {
|
||||
char *interface;
|
||||
char *prefix;
|
||||
@@ -684,30 +774,36 @@ extern struct daemon {
|
||||
time_t last_resolv;
|
||||
struct mx_srv_record *mxnames;
|
||||
struct naptr *naptr;
|
||||
struct txt_record *txt;
|
||||
struct txt_record *txt, *rr;
|
||||
struct ptr_record *ptr;
|
||||
struct host_record *host_records, *host_records_tail;
|
||||
struct cname *cnames;
|
||||
struct auth_zone *auth_zones;
|
||||
struct interface_name *int_names;
|
||||
char *mxtarget;
|
||||
char *lease_file;
|
||||
char *username, *groupname, *scriptuser;
|
||||
char *luascript;
|
||||
char *authserver, *hostmaster;
|
||||
struct iname *authinterface;
|
||||
struct name_list *secondary_forward_server;
|
||||
int group_set, osport;
|
||||
char *domain_suffix;
|
||||
struct cond_domain *cond_domain;
|
||||
struct cond_domain *cond_domain, *synth_domains;
|
||||
char *runfile;
|
||||
char *lease_change_command;
|
||||
struct iname *if_names, *if_addrs, *if_except, *dhcp_except;
|
||||
struct iname *if_names, *if_addrs, *if_except, *dhcp_except, *auth_peers;
|
||||
struct bogus_addr *bogus_addr;
|
||||
struct server *servers;
|
||||
struct ipsets *ipsets;
|
||||
int log_fac; /* log facility */
|
||||
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;
|
||||
unsigned long local_ttl, neg_ttl, max_ttl, max_cache_ttl, auth_ttl;
|
||||
struct hostsfile *addn_hosts;
|
||||
struct dhcp_context *dhcp, *dhcp6, *ra_contexts;
|
||||
struct dhcp_context *dhcp, *dhcp6;
|
||||
struct dhcp_config *dhcp_conf;
|
||||
struct dhcp_opt *dhcp_opts, *dhcp_match, *dhcp_opts6, *dhcp_match6;
|
||||
struct dhcp_vendor *dhcp_vendors;
|
||||
@@ -718,6 +814,7 @@ extern struct daemon {
|
||||
struct addr_list *override_relays;
|
||||
int override;
|
||||
int enable_pxe;
|
||||
int doing_ra, doing_dhcp6;
|
||||
struct dhcp_netid_list *dhcp_ignore, *dhcp_ignore_names, *dhcp_gen_names;
|
||||
struct dhcp_netid_list *force_broadcast, *bootp_dynamic;
|
||||
struct hostsfile *dhcp_hosts_file, *dhcp_opts_file;
|
||||
@@ -729,10 +826,13 @@ extern struct daemon {
|
||||
unsigned short edns_pktsz;
|
||||
char *tftp_prefix;
|
||||
struct tftp_prefix *if_prefix; /* per-interface TFTP prefixes */
|
||||
struct interface_list *tftp_interfaces; /* interfaces for limited TFTP service */
|
||||
int tftp_unlimited;
|
||||
unsigned int duid_enterprise, duid_config_len;
|
||||
unsigned char *duid_config;
|
||||
char *dbus_name;
|
||||
unsigned long soa_sn, soa_refresh, soa_retry, soa_expiry;
|
||||
#ifdef OPTION6_PREFIX_CLASS
|
||||
struct prefix_class *prefix_classes;
|
||||
#endif
|
||||
|
||||
/* globally used stuff for DNS */
|
||||
char *packet; /* packet buffer */
|
||||
@@ -779,7 +879,7 @@ extern struct daemon {
|
||||
#endif
|
||||
|
||||
/* TFTP stuff */
|
||||
struct tftp_transfer *tftp_trans;
|
||||
struct tftp_transfer *tftp_trans, *tftp_done_trans;
|
||||
|
||||
/* utility string buffer, hold max sized IP address as string */
|
||||
char *addrbuff;
|
||||
@@ -790,7 +890,7 @@ extern struct daemon {
|
||||
void cache_init(void);
|
||||
void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg);
|
||||
char *record_source(int index);
|
||||
void querystr(char *str, unsigned short type);
|
||||
void querystr(char *desc, char *str, unsigned short type);
|
||||
struct crec *cache_find_by_addr(struct crec *crecp,
|
||||
struct all_addr *addr, time_t now,
|
||||
unsigned short prot);
|
||||
@@ -806,10 +906,19 @@ struct in_addr a_record_from_hosts(char *name, time_t now);
|
||||
void cache_unhash_dhcp(void);
|
||||
void dump_cache(time_t now);
|
||||
char *cache_get_name(struct crec *crecp);
|
||||
struct crec *cache_enumerate(int init);
|
||||
#ifdef HAVE_DNSSEC
|
||||
struct keydata *keydata_alloc(char *data, size_t len);
|
||||
void keydata_free(struct keydata *blocks);
|
||||
#endif
|
||||
|
||||
/* domain.c */
|
||||
char *get_domain(struct in_addr addr);
|
||||
#ifdef HAVE_IPV6
|
||||
char *get_domain6(struct in6_addr *addr);
|
||||
#endif
|
||||
int is_name_synthetic(int flags, char *name, struct all_addr *addr);
|
||||
int is_rev_synth(int flag, struct all_addr *addr, char *name);
|
||||
|
||||
/* rfc1035.c */
|
||||
unsigned int extract_request(struct dns_header *header, size_t qlen,
|
||||
@@ -818,7 +927,8 @@ size_t setup_reply(struct dns_header *header, size_t qlen,
|
||||
struct all_addr *addrp, unsigned int flags,
|
||||
unsigned long local_ttl);
|
||||
int extract_addresses(struct dns_header *header, size_t qlen, char *namebuff,
|
||||
time_t now, int is_sign, int checkrebind, int checking_disabled);
|
||||
time_t now, char **ipsets, int is_sign, int checkrebind,
|
||||
int checking_disabled);
|
||||
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 check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
|
||||
@@ -830,6 +940,18 @@ 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);
|
||||
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, ...);
|
||||
unsigned char *skip_questions(struct dns_header *header, size_t plen);
|
||||
int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
|
||||
char *name, int isExtract, int extrabytes);
|
||||
int in_arpa_name_2_addr(char *namein, struct all_addr *addrp);
|
||||
|
||||
/* 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);
|
||||
#endif
|
||||
|
||||
/* util.c */
|
||||
void rand_init(void);
|
||||
@@ -842,7 +964,7 @@ void safe_pipe(int *fd, int read_noblock);
|
||||
void *whine_malloc(size_t size);
|
||||
int sa_len(union mysockaddr *addr);
|
||||
int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2);
|
||||
int hostname_isequal(char *a, char *b);
|
||||
int hostname_isequal(const char *a, const char *b);
|
||||
time_t dnsmasq_time(void);
|
||||
int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask);
|
||||
#ifdef HAVE_IPV6
|
||||
@@ -862,6 +984,8 @@ 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);
|
||||
|
||||
/* log.c */
|
||||
void die(char *message, char *arg1, int exit_code);
|
||||
int log_start(struct passwd *ent_pw, int errfd);
|
||||
@@ -877,16 +1001,19 @@ char *option_string(int prot, unsigned int opt, unsigned char *val,
|
||||
int opt_len, char *buf, int buf_len);
|
||||
void reread_dhcp(void);
|
||||
void set_option_bool(unsigned int opt);
|
||||
void reset_option_bool(unsigned int opt);
|
||||
struct hostsfile *expand_filelist(struct hostsfile *list);
|
||||
char *parse_server(char *arg, union mysockaddr *addr,
|
||||
union mysockaddr *source_addr, char *interface, int *flags);
|
||||
|
||||
/* forward.c */
|
||||
void reply_query(int fd, int family, time_t now);
|
||||
void receive_query(struct listener *listen, time_t now);
|
||||
unsigned char *tcp_request(int confd, time_t now,
|
||||
union mysockaddr *local_addr, struct in_addr netmask);
|
||||
union mysockaddr *local_addr, struct in_addr netmask, int auth_dns);
|
||||
void server_gone(struct server *server);
|
||||
struct frec *get_new_frec(time_t now, int *wait);
|
||||
void send_from(int fd, int nowild, char *packet, size_t len,
|
||||
int send_from(int fd, int nowild, char *packet, size_t len,
|
||||
union mysockaddr *to, struct all_addr *source,
|
||||
unsigned int iface);
|
||||
|
||||
@@ -901,12 +1028,18 @@ int enumerate_interfaces();
|
||||
void create_wildcard_listeners(void);
|
||||
void create_bound_listeners(int die);
|
||||
int is_dad_listeners(void);
|
||||
int iface_check(int family, struct all_addr *addr, char *name);
|
||||
int iface_check(int family, struct all_addr *addr, char *name, int *auth_dns);
|
||||
int loopback_exception(int fd, int family, struct all_addr *addr, char *name);
|
||||
int label_exception(int index, int family, struct all_addr *addr);
|
||||
int fix_fd(int fd);
|
||||
int tcp_interface(int fd, int af);
|
||||
struct in_addr get_ifaddr(char *intr);
|
||||
#ifdef HAVE_IPV6
|
||||
int set_ipv6pktinfo(int fd);
|
||||
#endif
|
||||
#ifdef HAVE_DHCP6
|
||||
void join_multicast(int dienow);
|
||||
#endif
|
||||
|
||||
/* dhcp.c */
|
||||
#ifdef HAVE_DHCP
|
||||
@@ -935,21 +1068,25 @@ char *host_from_dns(struct in_addr addr);
|
||||
/* lease.c */
|
||||
#ifdef HAVE_DHCP
|
||||
void lease_update_file(time_t now);
|
||||
void lease_update_dns();
|
||||
void lease_update_dns(int force);
|
||||
void lease_init(time_t now);
|
||||
struct dhcp_lease *lease4_allocate(struct in_addr addr);
|
||||
#ifdef HAVE_DHCP6
|
||||
struct dhcp_lease *lease6_allocate(struct in6_addr *addrp, int lease_type);
|
||||
struct dhcp_lease *lease6_find(unsigned char *clid, int clid_len,
|
||||
int lease_type, int iaid, struct in6_addr *addr);
|
||||
void lease6_reset(void);
|
||||
struct dhcp_lease *lease6_find_by_client(struct dhcp_lease *first, int lease_type, unsigned char *clid, int clid_len, int iaid);
|
||||
struct dhcp_lease *lease6_find_by_addr(struct in6_addr *net, int prefix, u64 addr);
|
||||
u64 lease_find_max_addr6(struct dhcp_context *context);
|
||||
void lease_ping_reply(struct in6_addr *sender, unsigned char *packet, char *interface);
|
||||
void lease_update_slaac(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);
|
||||
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_expires(struct dhcp_lease *lease, unsigned int len, time_t now);
|
||||
void lease_set_interface(struct dhcp_lease *lease, int interface);
|
||||
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,
|
||||
unsigned char *clid, int clid_len);
|
||||
struct dhcp_lease *lease_find_by_addr(struct in_addr addr);
|
||||
@@ -978,7 +1115,7 @@ unsigned char *extended_hwaddr(int hwtype, int hwlen, unsigned char *hwaddr,
|
||||
int make_icmp_sock(void);
|
||||
int icmp_ping(struct in_addr addr);
|
||||
#endif
|
||||
void send_alarm(void);
|
||||
void send_alarm(time_t event, time_t now);
|
||||
void send_event(int fd, int event, int data, char *msg);
|
||||
void clear_cache_and_reload(time_t now);
|
||||
void poll_resolv(int force, int do_reload, time_t now);
|
||||
@@ -986,7 +1123,7 @@ void poll_resolv(int force, int do_reload, time_t now);
|
||||
/* netlink.c */
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
void netlink_init(void);
|
||||
void netlink_multicast(void);
|
||||
void netlink_multicast(time_t now);
|
||||
#endif
|
||||
|
||||
/* bpf.c */
|
||||
@@ -1009,12 +1146,21 @@ void emit_dbus_signal(int action, struct dhcp_lease *lease, char *hostname);
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* ipset.c */
|
||||
#ifdef HAVE_IPSET
|
||||
void ipset_init(void);
|
||||
int add_to_ipset(const char *setname, const struct all_addr *ipaddr, int flags, int remove);
|
||||
#endif
|
||||
|
||||
/* helper.c */
|
||||
#if defined(HAVE_DHCP) && !defined(NO_FORK)
|
||||
#if defined(HAVE_SCRIPT)
|
||||
int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd);
|
||||
void helper_write(void);
|
||||
void queue_script(int action, struct dhcp_lease *lease,
|
||||
char *hostname, time_t now);
|
||||
#ifdef HAVE_TFTP
|
||||
void queue_tftp(off_t file_len, char *filename, union mysockaddr *peer);
|
||||
#endif
|
||||
int helper_buf_empty(void);
|
||||
#endif
|
||||
|
||||
@@ -1022,6 +1168,7 @@ int helper_buf_empty(void);
|
||||
#ifdef HAVE_TFTP
|
||||
void tftp_request(struct listener *listen, time_t now);
|
||||
void check_tftp_listeners(fd_set *rset, time_t now);
|
||||
int do_tftp_script_run(void);
|
||||
#endif
|
||||
|
||||
/* conntrack.c */
|
||||
@@ -1034,14 +1181,17 @@ int get_incoming_mark(union mysockaddr *peer_addr, struct all_addr *local_addr,
|
||||
#ifdef HAVE_DHCP6
|
||||
void dhcp6_init(void);
|
||||
void dhcp6_packet(time_t now);
|
||||
int address6_allocate(struct dhcp_context *context, unsigned char *clid, int clid_len,
|
||||
int serial, struct dhcp_netid *netids, struct in6_addr *ans);
|
||||
struct dhcp_context *address6_allocate(struct dhcp_context *context, unsigned char *clid, int clid_len,
|
||||
int iaid, int serial, struct dhcp_netid *netids, int plain_range, struct in6_addr *ans);
|
||||
int config_valid(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr);
|
||||
struct dhcp_context *address6_available(struct dhcp_context *context,
|
||||
struct in6_addr *taddr,
|
||||
struct dhcp_netid *netids);
|
||||
struct dhcp_context *narrow_context6(struct dhcp_context *context,
|
||||
struct in6_addr *taddr,
|
||||
struct dhcp_netid *netids);
|
||||
struct dhcp_netid *netids,
|
||||
int plain_range);
|
||||
struct dhcp_context *address6_valid(struct dhcp_context *context,
|
||||
struct in6_addr *taddr,
|
||||
struct dhcp_netid *netids,
|
||||
int plain_range);
|
||||
struct dhcp_config *find_config6(struct dhcp_config *configs,
|
||||
struct dhcp_context *context,
|
||||
unsigned char *duid, int duid_len,
|
||||
@@ -1049,12 +1199,13 @@ struct dhcp_config *find_config6(struct dhcp_config *configs,
|
||||
struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct in6_addr *net,
|
||||
int prefix, u64 addr);
|
||||
void make_duid(time_t now);
|
||||
void dhcp_construct_contexts(time_t now);
|
||||
#endif
|
||||
|
||||
/* rfc3315.c */
|
||||
#ifdef HAVE_DHCP6
|
||||
size_t dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name,
|
||||
struct in6_addr *fallback, size_t sz, int is_multicast, time_t now);
|
||||
unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name,
|
||||
struct in6_addr *fallback, size_t sz, int is_multicast, time_t now);
|
||||
#endif
|
||||
|
||||
/* dhcp-common.c */
|
||||
@@ -1069,10 +1220,18 @@ char *strip_hostname(char *hostname);
|
||||
void log_tags(struct dhcp_netid *netid, u32 xid);
|
||||
int match_bytes(struct dhcp_opt *o, unsigned char *p, int len);
|
||||
void dhcp_update_configs(struct dhcp_config *configs);
|
||||
void check_dhcp_hosts(int fatal);
|
||||
void display_opts(void);
|
||||
int lookup_dhcp_opt(int prot, char *name);
|
||||
int lookup_dhcp_len(int prot, int val);
|
||||
char *option_string(int prot, unsigned int opt, unsigned char *val,
|
||||
int opt_len, char *buf, int buf_len);
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
void bindtodevice(int fd);
|
||||
#endif
|
||||
# ifdef HAVE_DHCP6
|
||||
void join_multicast(void);
|
||||
void display_opts6(void);
|
||||
# endif
|
||||
void log_context(int family, struct dhcp_context *context);
|
||||
#endif
|
||||
|
||||
/* outpacket.c */
|
||||
@@ -1091,8 +1250,14 @@ void put_opt6_string(char *s);
|
||||
/* radv.c */
|
||||
#ifdef HAVE_DHCP6
|
||||
void ra_init(time_t now);
|
||||
void icmp6_packet(void);
|
||||
void icmp6_packet(time_t now);
|
||||
time_t periodic_ra(time_t now);
|
||||
void ra_start_unsolicted(time_t now);
|
||||
struct subnet_map *build_subnet_map(void);
|
||||
void ra_start_unsolicted(time_t now, struct dhcp_context *context);
|
||||
#endif
|
||||
|
||||
/* slaac.c */
|
||||
#ifdef HAVE_DHCP6
|
||||
void slaac_add_addrs(struct dhcp_lease *lease, time_t now, int force);
|
||||
time_t periodic_slaac(time_t now, struct dhcp_lease *leases);
|
||||
void slaac_ping_reply(struct in6_addr *sender, unsigned char *packet, char *interface, struct dhcp_lease *leases);
|
||||
#endif
|
||||
|
||||
232
src/domain.c
Normal file
232
src/domain.c
Normal file
@@ -0,0 +1,232 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 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"
|
||||
|
||||
|
||||
static struct cond_domain *search_domain(struct in_addr addr, struct cond_domain *c);
|
||||
#ifdef HAVE_IPV6
|
||||
static struct cond_domain *search_domain6(struct in6_addr *addr, struct cond_domain *c);
|
||||
#endif
|
||||
|
||||
|
||||
int is_name_synthetic(int flags, char *name, struct all_addr *addr)
|
||||
{
|
||||
char *p;
|
||||
struct cond_domain *c = NULL;
|
||||
int prot = AF_INET;
|
||||
|
||||
#ifdef HAVE_IPV6
|
||||
if (flags & F_IPV6)
|
||||
prot = AF_INET6;
|
||||
#endif
|
||||
|
||||
for (c = daemon->synth_domains; c; c = c->next)
|
||||
{
|
||||
int found = 0;
|
||||
char *tail, *pref;
|
||||
|
||||
for (tail = name, pref = c->prefix; *tail != 0 && pref && *pref != 0; tail++, pref++)
|
||||
{
|
||||
unsigned int c1 = (unsigned char) *pref;
|
||||
unsigned int c2 = (unsigned char) *tail;
|
||||
|
||||
if (c1 >= 'A' && c1 <= 'Z')
|
||||
c1 += 'a' - 'A';
|
||||
if (c2 >= 'A' && c2 <= 'Z')
|
||||
c2 += 'a' - 'A';
|
||||
|
||||
if (c1 != c2)
|
||||
break;
|
||||
}
|
||||
|
||||
if (pref && *pref != 0)
|
||||
continue; /* prefix match fail */
|
||||
|
||||
/* NB, must not alter name if we return zero */
|
||||
for (p = tail; *p; p++)
|
||||
{
|
||||
char c = *p;
|
||||
|
||||
if ((c >='0' && c <= '9') || c == '-')
|
||||
continue;
|
||||
|
||||
#ifdef HAVE_IPV6
|
||||
if (prot == AF_INET6 && ((c >='A' && c <= 'F') || (c >='a' && c <= 'f')))
|
||||
continue;
|
||||
#endif
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (*p != '.')
|
||||
continue;
|
||||
|
||||
*p = 0;
|
||||
|
||||
/* swap . or : for - */
|
||||
for (p = tail; *p; p++)
|
||||
if (*p == '-')
|
||||
{
|
||||
if (prot == AF_INET)
|
||||
*p = '.';
|
||||
#ifdef HAVE_IPV6
|
||||
else
|
||||
*p = ':';
|
||||
#endif
|
||||
}
|
||||
|
||||
if (hostname_isequal(c->domain, p+1) && inet_pton(prot, tail, addr))
|
||||
{
|
||||
if (prot == AF_INET)
|
||||
{
|
||||
if (!c->is6 &&
|
||||
ntohl(addr->addr.addr4.s_addr) >= ntohl(c->start.s_addr) &&
|
||||
ntohl(addr->addr.addr4.s_addr) <= ntohl(c->end.s_addr))
|
||||
found = 1;
|
||||
}
|
||||
#ifdef HAVE_IPV6
|
||||
else
|
||||
{
|
||||
u64 addrpart = addr6part(&addr->addr.addr6);
|
||||
|
||||
if (c->is6 &&
|
||||
is_same_net6(&addr->addr.addr6, &c->start6, 64) &&
|
||||
addrpart >= addr6part(&c->start6) &&
|
||||
addrpart <= addr6part(&c->end6))
|
||||
found = 1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* restore name */
|
||||
for (p = tail; *p; p++)
|
||||
if (*p == '.' || *p == ':')
|
||||
*p = '-';
|
||||
|
||||
*p = '.';
|
||||
|
||||
if (found)
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int is_rev_synth(int flag, struct all_addr *addr, char *name)
|
||||
{
|
||||
struct cond_domain *c;
|
||||
|
||||
if (flag & F_IPV4 && (c = search_domain(addr->addr.addr4, daemon->synth_domains)))
|
||||
{
|
||||
char *p;
|
||||
|
||||
*name = 0;
|
||||
if (c->prefix)
|
||||
strncpy(name, c->prefix, MAXDNAME - ADDRSTRLEN);
|
||||
|
||||
inet_ntop(AF_INET, &addr->addr.addr4, name + strlen(name), ADDRSTRLEN);
|
||||
for (p = name; *p; p++)
|
||||
if (*p == '.')
|
||||
*p = '-';
|
||||
|
||||
strncat(name, ".", MAXDNAME);
|
||||
strncat(name, c->domain, MAXDNAME);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef HAVE_IPV6
|
||||
if (flag & F_IPV6 && (c = search_domain6(&addr->addr.addr6, daemon->synth_domains)))
|
||||
{
|
||||
char *p;
|
||||
|
||||
*name = 0;
|
||||
if (c->prefix)
|
||||
strncpy(name, c->prefix, MAXDNAME - ADDRSTRLEN);
|
||||
|
||||
inet_ntop(AF_INET6, &addr->addr.addr6, name + strlen(name), ADDRSTRLEN);
|
||||
|
||||
/* IPv6 presentation address can start with ":", but valid domain names
|
||||
cannot start with "-" so prepend a zero in that case. */
|
||||
if (!c->prefix && *name == ':')
|
||||
{
|
||||
*name = '0';
|
||||
inet_ntop(AF_INET6, &addr->addr.addr6, name+1, ADDRSTRLEN);
|
||||
}
|
||||
|
||||
for (p = name; *p; p++)
|
||||
if (*p == ':')
|
||||
*p = '-';
|
||||
|
||||
strncat(name, ".", MAXDNAME);
|
||||
strncat(name, c->domain, MAXDNAME);
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static struct cond_domain *search_domain(struct in_addr addr, struct cond_domain *c)
|
||||
{
|
||||
for (; c; c = c->next)
|
||||
if (!c->is6 &&
|
||||
ntohl(addr.s_addr) >= ntohl(c->start.s_addr) &&
|
||||
ntohl(addr.s_addr) <= ntohl(c->end.s_addr))
|
||||
return c;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *get_domain(struct in_addr addr)
|
||||
{
|
||||
struct cond_domain *c;
|
||||
|
||||
if ((c = search_domain(addr, daemon->cond_domain)))
|
||||
return c->domain;
|
||||
|
||||
return daemon->domain_suffix;
|
||||
}
|
||||
|
||||
#ifdef HAVE_IPV6
|
||||
static struct cond_domain *search_domain6(struct in6_addr *addr, struct cond_domain *c)
|
||||
{
|
||||
u64 addrpart = addr6part(addr);
|
||||
|
||||
for (; c; c = c->next)
|
||||
if (c->is6 &&
|
||||
is_same_net6(addr, &c->start6, 64) &&
|
||||
addrpart >= addr6part(&c->start6) &&
|
||||
addrpart <= addr6part(&c->end6))
|
||||
return c;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *get_domain6(struct in6_addr *addr)
|
||||
{
|
||||
struct cond_domain *c;
|
||||
|
||||
if ((c = search_domain6(addr, daemon->cond_domain)))
|
||||
return c->domain;
|
||||
|
||||
return daemon->domain_suffix;
|
||||
}
|
||||
#endif
|
||||
393
src/forward.c
393
src/forward.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -26,9 +26,9 @@ static struct randfd *allocate_rfd(int family);
|
||||
|
||||
/* Send a UDP packet with its source address set as "source"
|
||||
unless nowild is true, when we just send it with the kernel default */
|
||||
void send_from(int fd, int nowild, char *packet, size_t len,
|
||||
union mysockaddr *to, struct all_addr *source,
|
||||
unsigned int iface)
|
||||
int send_from(int fd, int nowild, char *packet, size_t len,
|
||||
union mysockaddr *to, struct all_addr *source,
|
||||
unsigned int iface)
|
||||
{
|
||||
struct msghdr msg;
|
||||
struct iovec iov[1];
|
||||
@@ -95,23 +95,20 @@ void send_from(int fd, int nowild, char *packet, size_t len,
|
||||
#endif
|
||||
}
|
||||
|
||||
retry:
|
||||
if (sendmsg(fd, &msg, 0) == -1)
|
||||
while (sendmsg(fd, &msg, 0) == -1)
|
||||
{
|
||||
/* certain Linux kernels seem to object to setting the source address in the IPv6 stack
|
||||
by returning EINVAL from sendmsg. In that case, try again without setting the
|
||||
source address, since it will nearly alway be correct anyway. IPv6 stinks. */
|
||||
if (errno == EINVAL && msg.msg_controllen)
|
||||
{
|
||||
msg.msg_controllen = 0;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
if (retry_send())
|
||||
goto retry;
|
||||
continue;
|
||||
|
||||
/* If interface is still in DAD, EINVAL results - ignore that. */
|
||||
if (errno == EINVAL)
|
||||
break;
|
||||
|
||||
my_syslog(LOG_ERR, _("failed to send packet: %s"), strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static unsigned int search_servers(time_t now, struct all_addr **addrpp,
|
||||
@@ -331,8 +328,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
struct server *firstsentto = start;
|
||||
int forwarded = 0;
|
||||
|
||||
if (udpaddr && option_bool(OPT_ADD_MAC))
|
||||
plen = add_mac(header, plen, ((char *) header) + PACKETSZ, udpaddr);
|
||||
if (option_bool(OPT_ADD_MAC))
|
||||
plen = add_mac(header, plen, ((char *) header) + PACKETSZ, &forward->source);
|
||||
|
||||
while (1)
|
||||
{
|
||||
@@ -375,7 +372,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
if (option_bool(OPT_CONNTRACK))
|
||||
{
|
||||
unsigned int mark;
|
||||
if (get_incoming_mark(udpaddr, dst_addr, 0, &mark))
|
||||
if (get_incoming_mark(&forward->source, &forward->dest, 0, &mark))
|
||||
setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
|
||||
}
|
||||
#endif
|
||||
@@ -432,7 +429,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
if (udpfd != -1)
|
||||
{
|
||||
plen = setup_reply(header, plen, addrp, flags, daemon->local_ttl);
|
||||
send_from(udpfd, option_bool(OPT_NOWILD), (char *)header, plen, udpaddr, dst_addr, dst_iface);
|
||||
send_from(udpfd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND), (char *)header, plen, udpaddr, dst_addr, dst_iface);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -442,9 +439,28 @@ static size_t process_reply(struct dns_header *header, time_t now,
|
||||
struct server *server, size_t n, int check_rebind, int checking_disabled)
|
||||
{
|
||||
unsigned char *pheader, *sizep;
|
||||
char **sets = 0;
|
||||
int munged = 0, is_sign;
|
||||
size_t plen;
|
||||
|
||||
#ifdef HAVE_IPSET
|
||||
/* Similar algorithm to search_servers. */
|
||||
struct ipsets *ipset_pos;
|
||||
unsigned int namelen = strlen(daemon->namebuff);
|
||||
unsigned int matchlen = 0;
|
||||
for (ipset_pos = daemon->ipsets; ipset_pos; ipset_pos = ipset_pos->next)
|
||||
{
|
||||
unsigned int domainlen = strlen(ipset_pos->domain);
|
||||
char *matchstart = daemon->namebuff + namelen - domainlen;
|
||||
if (namelen >= domainlen && hostname_isequal(matchstart, ipset_pos->domain) &&
|
||||
(domainlen == 0 || namelen == domainlen || *(matchstart - 1) == '.' ) &&
|
||||
domainlen >= matchlen) {
|
||||
matchlen = domainlen;
|
||||
sets = ipset_pos->sets;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* If upstream is advertising a larger UDP packet size
|
||||
than we allow, trim it so that we don't get overlarge
|
||||
requests for the client. We can't do this for signed packets. */
|
||||
@@ -497,7 +513,7 @@ static size_t process_reply(struct dns_header *header, time_t now,
|
||||
SET_RCODE(header, NOERROR);
|
||||
}
|
||||
|
||||
if (extract_addresses(header, n, daemon->namebuff, now, is_sign, check_rebind, checking_disabled))
|
||||
if (extract_addresses(header, n, daemon->namebuff, now, sets, is_sign, check_rebind, checking_disabled))
|
||||
{
|
||||
my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff);
|
||||
munged = 1;
|
||||
@@ -620,7 +636,7 @@ void reply_query(int fd, int family, time_t now)
|
||||
{
|
||||
header->id = htons(forward->orig_id);
|
||||
header->hb4 |= HB4_RA; /* recursion if available */
|
||||
send_from(forward->fd, option_bool(OPT_NOWILD), daemon->packet, nn,
|
||||
send_from(forward->fd, option_bool(OPT_NOWILD) || option_bool (OPT_CLEVERBIND), daemon->packet, nn,
|
||||
&forward->source, &forward->dest, forward->iface);
|
||||
}
|
||||
free_frec(forward); /* cancel */
|
||||
@@ -638,6 +654,7 @@ void receive_query(struct listener *listen, time_t now)
|
||||
size_t m;
|
||||
ssize_t n;
|
||||
int if_index = 0;
|
||||
int auth_dns = 0;
|
||||
struct iovec iov[1];
|
||||
struct msghdr msg;
|
||||
struct cmsghdr *cmptr;
|
||||
@@ -660,17 +677,20 @@ void receive_query(struct listener *listen, time_t now)
|
||||
/* packet buffer overwritten */
|
||||
daemon->srv_save = NULL;
|
||||
|
||||
if (listen->family == AF_INET && option_bool(OPT_NOWILD))
|
||||
dst_addr_4.s_addr = 0;
|
||||
netmask.s_addr = 0;
|
||||
|
||||
if (option_bool(OPT_NOWILD) && listen->iface)
|
||||
{
|
||||
dst_addr_4 = listen->iface->addr.in.sin_addr;
|
||||
netmask = listen->iface->netmask;
|
||||
auth_dns = listen->iface->dns_auth;
|
||||
|
||||
if (listen->family == AF_INET)
|
||||
{
|
||||
dst_addr_4 = listen->iface->addr.in.sin_addr;
|
||||
netmask = listen->iface->netmask;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dst_addr_4.s_addr = 0;
|
||||
netmask.s_addr = 0;
|
||||
}
|
||||
|
||||
|
||||
iov[0].iov_base = daemon->packet;
|
||||
iov[0].iov_len = daemon->edns_pktsz;
|
||||
|
||||
@@ -762,10 +782,18 @@ void receive_query(struct listener *listen, time_t now)
|
||||
|
||||
/* enforce available interface configuration */
|
||||
|
||||
if (!indextoname(listen->fd, if_index, ifr.ifr_name) ||
|
||||
!iface_check(listen->family, &dst_addr, ifr.ifr_name))
|
||||
if (!indextoname(listen->fd, if_index, ifr.ifr_name))
|
||||
return;
|
||||
|
||||
if (!iface_check(listen->family, &dst_addr, ifr.ifr_name, &auth_dns))
|
||||
{
|
||||
if (!option_bool(OPT_CLEVERBIND))
|
||||
enumerate_interfaces();
|
||||
if (!loopback_exception(listen->fd, listen->family, &dst_addr, ifr.ifr_name) &&
|
||||
!label_exception(if_index, listen->family, &dst_addr))
|
||||
return;
|
||||
}
|
||||
|
||||
if (listen->family == AF_INET && option_bool(OPT_LOCALISE))
|
||||
{
|
||||
struct irec *iface;
|
||||
@@ -779,7 +807,7 @@ void receive_query(struct listener *listen, time_t now)
|
||||
break;
|
||||
|
||||
/* interface may be new */
|
||||
if (!iface)
|
||||
if (!iface && !option_bool(OPT_CLEVERBIND))
|
||||
enumerate_interfaces();
|
||||
|
||||
for (iface = daemon->interfaces; iface; iface = iface->next)
|
||||
@@ -799,7 +827,7 @@ void receive_query(struct listener *listen, time_t now)
|
||||
{
|
||||
char types[20];
|
||||
|
||||
querystr(types, type);
|
||||
querystr(auth_dns ? "auth" : "query", types, type);
|
||||
|
||||
if (listen->family == AF_INET)
|
||||
log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
|
||||
@@ -811,19 +839,32 @@ void receive_query(struct listener *listen, time_t now)
|
||||
#endif
|
||||
}
|
||||
|
||||
m = answer_request (header, ((char *) header) + PACKETSZ, (size_t)n,
|
||||
dst_addr_4, netmask, now);
|
||||
if (m >= 1)
|
||||
#ifdef HAVE_AUTH
|
||||
if (auth_dns)
|
||||
{
|
||||
send_from(listen->fd, option_bool(OPT_NOWILD), (char *)header,
|
||||
m, &source_addr, &dst_addr, if_index);
|
||||
daemon->local_answer++;
|
||||
m = answer_auth(header, ((char *) header) + PACKETSZ, (size_t)n, now, &source_addr);
|
||||
if (m >= 1)
|
||||
send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND),
|
||||
(char *)header, m, &source_addr, &dst_addr, if_index);
|
||||
}
|
||||
else if (forward_query(listen->fd, &source_addr, &dst_addr, if_index,
|
||||
header, (size_t)n, now, NULL))
|
||||
daemon->queries_forwarded++;
|
||||
else
|
||||
daemon->local_answer++;
|
||||
#endif
|
||||
{
|
||||
m = answer_request(header, ((char *) header) + PACKETSZ, (size_t)n,
|
||||
dst_addr_4, netmask, now);
|
||||
|
||||
if (m >= 1)
|
||||
{
|
||||
send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND),
|
||||
(char *)header, m, &source_addr, &dst_addr, if_index);
|
||||
daemon->local_answer++;
|
||||
}
|
||||
else if (forward_query(listen->fd, &source_addr, &dst_addr, if_index,
|
||||
header, (size_t)n, now, NULL))
|
||||
daemon->queries_forwarded++;
|
||||
else
|
||||
daemon->local_answer++;
|
||||
}
|
||||
}
|
||||
|
||||
/* The daemon forks before calling this: it should deal with one connection,
|
||||
@@ -831,17 +872,21 @@ void receive_query(struct listener *listen, time_t now)
|
||||
about resources for debug mode, when the fork is suppressed: that's
|
||||
done by the caller. */
|
||||
unsigned char *tcp_request(int confd, time_t now,
|
||||
union mysockaddr *local_addr, struct in_addr netmask)
|
||||
union mysockaddr *local_addr, struct in_addr netmask, int auth_dns)
|
||||
{
|
||||
size_t size = 0;
|
||||
int norebind = 0;
|
||||
int checking_disabled;
|
||||
size_t m;
|
||||
unsigned short qtype, gotname;
|
||||
unsigned short qtype;
|
||||
unsigned int gotname;
|
||||
unsigned char c1, c2;
|
||||
/* Max TCP packet + slop */
|
||||
unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ);
|
||||
struct dns_header *header;
|
||||
/* Max TCP packet + slop + size */
|
||||
unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ + sizeof(u16));
|
||||
unsigned char *payload = &packet[2];
|
||||
/* largest field in header is 16-bits, so this is still sufficiently aligned */
|
||||
struct dns_header *header = (struct dns_header *)payload;
|
||||
u16 *length = (u16 *)packet;
|
||||
struct server *last_server;
|
||||
struct in_addr dst_addr_4;
|
||||
union mysockaddr peer_addr;
|
||||
@@ -855,14 +900,12 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
if (!packet ||
|
||||
!read_write(confd, &c1, 1, 1) || !read_write(confd, &c2, 1, 1) ||
|
||||
!(size = c1 << 8 | c2) ||
|
||||
!read_write(confd, packet, size, 1))
|
||||
!read_write(confd, payload, size, 1))
|
||||
return packet;
|
||||
|
||||
if (size < (int)sizeof(struct dns_header))
|
||||
continue;
|
||||
|
||||
header = (struct dns_header *)packet;
|
||||
|
||||
/* save state of "cd" flag in query */
|
||||
checking_disabled = header->hb4 & HB4_CD;
|
||||
|
||||
@@ -873,7 +916,7 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
{
|
||||
char types[20];
|
||||
|
||||
querystr(types, qtype);
|
||||
querystr(auth_dns ? "auth" : "query", types, qtype);
|
||||
|
||||
if (peer_addr.sa.sa_family == AF_INET)
|
||||
log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
|
||||
@@ -890,144 +933,146 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
else
|
||||
dst_addr_4.s_addr = 0;
|
||||
|
||||
/* m > 0 if answered from cache */
|
||||
m = answer_request(header, ((char *) header) + 65536, (unsigned int)size,
|
||||
dst_addr_4, netmask, now);
|
||||
|
||||
/* Do this by steam now we're not in the select() loop */
|
||||
check_log_writer(NULL);
|
||||
|
||||
if (m == 0)
|
||||
#ifdef HAVE_AUTH
|
||||
if (auth_dns)
|
||||
m = answer_auth(header, ((char *) header) + 65536, (size_t)size, now, &peer_addr);
|
||||
else
|
||||
#endif
|
||||
{
|
||||
unsigned int flags = 0;
|
||||
struct all_addr *addrp = NULL;
|
||||
int type = 0;
|
||||
char *domain = NULL;
|
||||
|
||||
if (option_bool(OPT_ADD_MAC))
|
||||
size = add_mac(header, size, ((char *) header) + 65536, &peer_addr);
|
||||
|
||||
if (gotname)
|
||||
flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);
|
||||
/* m > 0 if answered from cache */
|
||||
m = answer_request(header, ((char *) header) + 65536, (size_t)size,
|
||||
dst_addr_4, netmask, now);
|
||||
|
||||
if (type != 0 || option_bool(OPT_ORDER) || !daemon->last_server)
|
||||
last_server = daemon->servers;
|
||||
else
|
||||
last_server = daemon->last_server;
|
||||
|
||||
if (!flags && last_server)
|
||||
/* Do this by steam now we're not in the select() loop */
|
||||
check_log_writer(NULL);
|
||||
|
||||
if (m == 0)
|
||||
{
|
||||
struct server *firstsendto = NULL;
|
||||
unsigned int crc = questions_crc(header, (unsigned int)size, daemon->namebuff);
|
||||
|
||||
/* Loop round available servers until we succeed in connecting to one.
|
||||
Note that this code subtley ensures that consecutive queries on this connection
|
||||
which can go to the same server, do so. */
|
||||
while (1)
|
||||
{
|
||||
if (!firstsendto)
|
||||
firstsendto = last_server;
|
||||
else
|
||||
{
|
||||
if (!(last_server = last_server->next))
|
||||
last_server = daemon->servers;
|
||||
|
||||
if (last_server == firstsendto)
|
||||
break;
|
||||
}
|
||||
unsigned int flags = 0;
|
||||
struct all_addr *addrp = NULL;
|
||||
int type = 0;
|
||||
char *domain = NULL;
|
||||
|
||||
/* server for wrong domain */
|
||||
if (type != (last_server->flags & SERV_TYPE) ||
|
||||
(type == SERV_HAS_DOMAIN && !hostname_isequal(domain, last_server->domain)))
|
||||
continue;
|
||||
|
||||
if (last_server->tcpfd == -1)
|
||||
if (option_bool(OPT_ADD_MAC))
|
||||
size = add_mac(header, size, ((char *) header) + 65536, &peer_addr);
|
||||
|
||||
if (gotname)
|
||||
flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);
|
||||
|
||||
if (type != 0 || option_bool(OPT_ORDER) || !daemon->last_server)
|
||||
last_server = daemon->servers;
|
||||
else
|
||||
last_server = daemon->last_server;
|
||||
|
||||
if (!flags && last_server)
|
||||
{
|
||||
struct server *firstsendto = NULL;
|
||||
unsigned int crc = questions_crc(header, (unsigned int)size, daemon->namebuff);
|
||||
|
||||
/* Loop round available servers until we succeed in connecting to one.
|
||||
Note that this code subtley ensures that consecutive queries on this connection
|
||||
which can go to the same server, do so. */
|
||||
while (1)
|
||||
{
|
||||
if ((last_server->tcpfd = socket(last_server->addr.sa.sa_family, SOCK_STREAM, 0)) == -1)
|
||||
if (!firstsendto)
|
||||
firstsendto = last_server;
|
||||
else
|
||||
{
|
||||
if (!(last_server = last_server->next))
|
||||
last_server = daemon->servers;
|
||||
|
||||
if (last_server == firstsendto)
|
||||
break;
|
||||
}
|
||||
|
||||
/* server for wrong domain */
|
||||
if (type != (last_server->flags & SERV_TYPE) ||
|
||||
(type == SERV_HAS_DOMAIN && !hostname_isequal(domain, last_server->domain)))
|
||||
continue;
|
||||
|
||||
if ((!local_bind(last_server->tcpfd, &last_server->source_addr, last_server->interface, 1) ||
|
||||
connect(last_server->tcpfd, &last_server->addr.sa, sa_len(&last_server->addr)) == -1))
|
||||
if (last_server->tcpfd == -1)
|
||||
{
|
||||
if ((last_server->tcpfd = socket(last_server->addr.sa.sa_family, SOCK_STREAM, 0)) == -1)
|
||||
continue;
|
||||
|
||||
if ((!local_bind(last_server->tcpfd, &last_server->source_addr, last_server->interface, 1) ||
|
||||
connect(last_server->tcpfd, &last_server->addr.sa, sa_len(&last_server->addr)) == -1))
|
||||
{
|
||||
close(last_server->tcpfd);
|
||||
last_server->tcpfd = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef HAVE_CONNTRACK
|
||||
/* Copy connection mark of incoming query to outgoing connection. */
|
||||
if (option_bool(OPT_CONNTRACK))
|
||||
{
|
||||
unsigned int mark;
|
||||
struct all_addr local;
|
||||
#ifdef HAVE_IPV6
|
||||
if (local_addr->sa.sa_family == AF_INET6)
|
||||
local.addr.addr6 = local_addr->in6.sin6_addr;
|
||||
else
|
||||
#endif
|
||||
local.addr.addr4 = local_addr->in.sin_addr;
|
||||
|
||||
if (get_incoming_mark(&peer_addr, &local, 1, &mark))
|
||||
setsockopt(last_server->tcpfd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
*length = htons(size);
|
||||
|
||||
if (!read_write(last_server->tcpfd, packet, size + sizeof(u16), 0) ||
|
||||
!read_write(last_server->tcpfd, &c1, 1, 1) ||
|
||||
!read_write(last_server->tcpfd, &c2, 1, 1))
|
||||
{
|
||||
close(last_server->tcpfd);
|
||||
last_server->tcpfd = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef HAVE_CONNTRACK
|
||||
/* Copy connection mark of incoming query to outgoing connection. */
|
||||
if (option_bool(OPT_CONNTRACK))
|
||||
{
|
||||
unsigned int mark;
|
||||
struct all_addr local;
|
||||
#ifdef HAVE_IPV6
|
||||
if (local_addr->sa.sa_family == AF_INET6)
|
||||
local.addr.addr6 = local_addr->in6.sin6_addr;
|
||||
else
|
||||
#endif
|
||||
local.addr.addr4 = local_addr->in.sin_addr;
|
||||
|
||||
if (get_incoming_mark(&peer_addr, &local, 1, &mark))
|
||||
setsockopt(last_server->tcpfd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
c1 = size >> 8;
|
||||
c2 = size;
|
||||
|
||||
if (!read_write(last_server->tcpfd, &c1, 1, 0) ||
|
||||
!read_write(last_server->tcpfd, &c2, 1, 0) ||
|
||||
!read_write(last_server->tcpfd, packet, size, 0) ||
|
||||
!read_write(last_server->tcpfd, &c1, 1, 1) ||
|
||||
!read_write(last_server->tcpfd, &c2, 1, 1))
|
||||
{
|
||||
close(last_server->tcpfd);
|
||||
last_server->tcpfd = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
m = (c1 << 8) | c2;
|
||||
if (!read_write(last_server->tcpfd, packet, m, 1))
|
||||
return packet;
|
||||
|
||||
if (!gotname)
|
||||
strcpy(daemon->namebuff, "query");
|
||||
if (last_server->addr.sa.sa_family == AF_INET)
|
||||
log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff,
|
||||
(struct all_addr *)&last_server->addr.in.sin_addr, NULL);
|
||||
}
|
||||
|
||||
m = (c1 << 8) | c2;
|
||||
if (!read_write(last_server->tcpfd, payload, m, 1))
|
||||
return packet;
|
||||
|
||||
if (!gotname)
|
||||
strcpy(daemon->namebuff, "query");
|
||||
if (last_server->addr.sa.sa_family == AF_INET)
|
||||
log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff,
|
||||
(struct all_addr *)&last_server->addr.in.sin_addr, NULL);
|
||||
#ifdef HAVE_IPV6
|
||||
else
|
||||
log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff,
|
||||
(struct all_addr *)&last_server->addr.in6.sin6_addr, NULL);
|
||||
else
|
||||
log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff,
|
||||
(struct all_addr *)&last_server->addr.in6.sin6_addr, NULL);
|
||||
#endif
|
||||
|
||||
/* There's no point in updating the cache, since this process will exit and
|
||||
lose the information after a few queries. We make this call for the alias and
|
||||
bogus-nxdomain side-effects. */
|
||||
/* If the crc of the question section doesn't match the crc we sent, then
|
||||
someone might be attempting to insert bogus values into the cache by
|
||||
sending replies containing questions and bogus answers. */
|
||||
if (crc == questions_crc(header, (unsigned int)m, daemon->namebuff))
|
||||
m = process_reply(header, now, last_server, (unsigned int)m,
|
||||
option_bool(OPT_NO_REBIND) && !norebind, checking_disabled);
|
||||
|
||||
break;
|
||||
|
||||
/* There's no point in updating the cache, since this process will exit and
|
||||
lose the information after a few queries. We make this call for the alias and
|
||||
bogus-nxdomain side-effects. */
|
||||
/* If the crc of the question section doesn't match the crc we sent, then
|
||||
someone might be attempting to insert bogus values into the cache by
|
||||
sending replies containing questions and bogus answers. */
|
||||
if (crc == questions_crc(header, (unsigned int)m, daemon->namebuff))
|
||||
m = process_reply(header, now, last_server, (unsigned int)m,
|
||||
option_bool(OPT_NO_REBIND) && !norebind, checking_disabled);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* In case of local answer or no connections made. */
|
||||
if (m == 0)
|
||||
m = setup_reply(header, (unsigned int)size, addrp, flags, daemon->local_ttl);
|
||||
}
|
||||
|
||||
/* In case of local answer or no connections made. */
|
||||
if (m == 0)
|
||||
m = setup_reply(header, (unsigned int)size, addrp, flags, daemon->local_ttl);
|
||||
}
|
||||
|
||||
|
||||
check_log_writer(NULL);
|
||||
|
||||
c1 = m>>8;
|
||||
c2 = m;
|
||||
if (!read_write(confd, &c1, 1, 0) ||
|
||||
!read_write(confd, &c2, 1, 0) ||
|
||||
!read_write(confd, packet, m, 0))
|
||||
*length = htons(m);
|
||||
|
||||
if (m == 0 || !read_write(confd, packet, m + sizeof(u16), 0))
|
||||
return packet;
|
||||
}
|
||||
}
|
||||
|
||||
507
src/helper.c
507
src/helper.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -34,10 +34,15 @@ static void my_setenv(const char *name, const char *value, int *error);
|
||||
static unsigned char *grab_extradata(unsigned char *buf, unsigned char *end, char *env, int *err);
|
||||
|
||||
#ifdef HAVE_LUASCRIPT
|
||||
#define LUA_COMPAT_ALL
|
||||
#include <lua.h>
|
||||
#include <lualib.h>
|
||||
#include <lauxlib.h>
|
||||
|
||||
#ifndef lua_open
|
||||
#define lua_open() luaL_newstate()
|
||||
#endif
|
||||
|
||||
lua_State *lua;
|
||||
|
||||
static unsigned char *grab_extradata_lua(unsigned char *buf, unsigned char *end, char *field);
|
||||
@@ -192,18 +197,24 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
|
||||
#endif
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
|
||||
is6 = !!(data.flags & (LEASE_TA | LEASE_NA));
|
||||
|
||||
if (data.action == ACTION_DEL)
|
||||
action_str = "del";
|
||||
else if (data.action == ACTION_ADD)
|
||||
action_str = "add";
|
||||
else if (data.action == ACTION_OLD || data.action == ACTION_OLD_HOSTNAME)
|
||||
action_str = "old";
|
||||
else if (data.action == ACTION_TFTP)
|
||||
{
|
||||
action_str = "tftp";
|
||||
is6 = (data.flags != AF_INET);
|
||||
}
|
||||
else
|
||||
continue;
|
||||
|
||||
is6 = !!(data.flags & (LEASE_TA | LEASE_NA));
|
||||
|
||||
|
||||
if (!is6)
|
||||
{
|
||||
/* stringify MAC into dhcp_buff */
|
||||
@@ -218,13 +229,6 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
|
||||
}
|
||||
}
|
||||
|
||||
/* expiry or length into dhcp_buff2 */
|
||||
#ifdef HAVE_BROKEN_RTC
|
||||
sprintf(daemon->dhcp_buff2, "%u", data.length);
|
||||
#else
|
||||
sprintf(daemon->dhcp_buff2, "%lu", (unsigned long)data.expires);
|
||||
#endif
|
||||
|
||||
/* supplied data may just exceed normal buffer (unlikely) */
|
||||
if ((data.hostname_len + data.ed_len + data.clid_len) > MAXDNAME &&
|
||||
!(alloc_buff = buf = malloc(data.hostname_len + data.ed_len + data.clid_len)))
|
||||
@@ -271,13 +275,16 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
|
||||
char *dot;
|
||||
hostname = (char *)buf;
|
||||
hostname[data.hostname_len - 1] = 0;
|
||||
if (!legal_hostname(hostname))
|
||||
hostname = NULL;
|
||||
else if ((dot = strchr(hostname, '.')))
|
||||
if (data.action != ACTION_TFTP)
|
||||
{
|
||||
domain = dot+1;
|
||||
*dot = 0;
|
||||
}
|
||||
if (!legal_hostname(hostname))
|
||||
hostname = NULL;
|
||||
else if ((dot = strchr(hostname, '.')))
|
||||
{
|
||||
domain = dot+1;
|
||||
*dot = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extradata = buf + data.hostname_len;
|
||||
@@ -289,116 +296,144 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
|
||||
inet_ntop(AF_INET6, &data.hwaddr, daemon->addrbuff, ADDRSTRLEN);
|
||||
#endif
|
||||
|
||||
/* file length */
|
||||
if (data.action == ACTION_TFTP)
|
||||
sprintf(daemon->dhcp_buff, "%u", data.hwaddr_len);
|
||||
|
||||
#ifdef HAVE_LUASCRIPT
|
||||
if (daemon->luascript)
|
||||
{
|
||||
lua_getglobal(lua, "lease"); /* function to call */
|
||||
lua_pushstring(lua, action_str); /* arg1 - action */
|
||||
lua_newtable(lua); /* arg2 - data table */
|
||||
|
||||
if (is6)
|
||||
if (data.action == ACTION_TFTP)
|
||||
{
|
||||
lua_pushstring(lua, daemon->dhcp_buff);
|
||||
lua_setfield(lua, -2, "client_duid");
|
||||
lua_pushstring(lua, daemon->packet);
|
||||
lua_setfield(lua, -2, "server_duid");
|
||||
lua_pushstring(lua, daemon->dhcp_buff3);
|
||||
lua_setfield(lua, -2, "iaid");
|
||||
lua_getglobal(lua, "tftp");
|
||||
if (lua_type(lua, -1) != LUA_TFUNCTION)
|
||||
lua_pop(lua, 1); /* tftp 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, "destination_address");
|
||||
lua_pushstring(lua, hostname);
|
||||
lua_setfield(lua, -2, "file_name");
|
||||
lua_pushstring(lua, daemon->dhcp_buff);
|
||||
lua_setfield(lua, -2, "file_size");
|
||||
lua_call(lua, 2, 0); /* pass 2 values, expect 0 */
|
||||
}
|
||||
}
|
||||
|
||||
if (!is6 && data.clid_len != 0)
|
||||
{
|
||||
lua_pushstring(lua, daemon->packet);
|
||||
lua_setfield(lua, -2, "client_id");
|
||||
}
|
||||
|
||||
if (strlen(data.interface) != 0)
|
||||
{
|
||||
lua_pushstring(lua, data.interface);
|
||||
lua_setfield(lua, -2, "interface");
|
||||
}
|
||||
|
||||
#ifdef HAVE_BROKEN_RTC
|
||||
lua_pushnumber(lua, data.length);
|
||||
lua_setfield(lua, -2, "lease_length");
|
||||
#else
|
||||
lua_pushnumber(lua, data.expires);
|
||||
lua_setfield(lua, -2, "lease_expires");
|
||||
#endif
|
||||
|
||||
if (hostname)
|
||||
{
|
||||
lua_pushstring(lua, hostname);
|
||||
lua_setfield(lua, -2, "hostname");
|
||||
}
|
||||
|
||||
if (domain)
|
||||
{
|
||||
lua_pushstring(lua, domain);
|
||||
lua_setfield(lua, -2, "domain");
|
||||
}
|
||||
|
||||
end = extradata + data.ed_len;
|
||||
buf = extradata;
|
||||
|
||||
if (!is6)
|
||||
buf = grab_extradata_lua(buf, end, "vendor_class");
|
||||
#ifdef HAVE_DHCP6
|
||||
else
|
||||
for (i = 0; i < data.hwaddr_len; i++)
|
||||
{
|
||||
sprintf(daemon->dhcp_buff2, "vendor_class%i", i);
|
||||
buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
|
||||
}
|
||||
{
|
||||
lua_getglobal(lua, "lease"); /* function to call */
|
||||
lua_pushstring(lua, action_str); /* arg1 - action */
|
||||
lua_newtable(lua); /* arg2 - data table */
|
||||
|
||||
if (is6)
|
||||
{
|
||||
lua_pushstring(lua, daemon->dhcp_buff);
|
||||
lua_setfield(lua, -2, "client_duid");
|
||||
lua_pushstring(lua, daemon->packet);
|
||||
lua_setfield(lua, -2, "server_duid");
|
||||
lua_pushstring(lua, daemon->dhcp_buff3);
|
||||
lua_setfield(lua, -2, "iaid");
|
||||
}
|
||||
|
||||
if (!is6 && data.clid_len != 0)
|
||||
{
|
||||
lua_pushstring(lua, daemon->packet);
|
||||
lua_setfield(lua, -2, "client_id");
|
||||
}
|
||||
|
||||
if (strlen(data.interface) != 0)
|
||||
{
|
||||
lua_pushstring(lua, data.interface);
|
||||
lua_setfield(lua, -2, "interface");
|
||||
}
|
||||
|
||||
#ifdef HAVE_BROKEN_RTC
|
||||
lua_pushnumber(lua, data.length);
|
||||
lua_setfield(lua, -2, "lease_length");
|
||||
#else
|
||||
lua_pushnumber(lua, data.expires);
|
||||
lua_setfield(lua, -2, "lease_expires");
|
||||
#endif
|
||||
|
||||
buf = grab_extradata_lua(buf, end, "supplied_hostname");
|
||||
|
||||
if (!is6)
|
||||
{
|
||||
buf = grab_extradata_lua(buf, end, "cpewan_oui");
|
||||
buf = grab_extradata_lua(buf, end, "cpewan_serial");
|
||||
buf = grab_extradata_lua(buf, end, "cpewan_class");
|
||||
|
||||
if (hostname)
|
||||
{
|
||||
lua_pushstring(lua, hostname);
|
||||
lua_setfield(lua, -2, "hostname");
|
||||
}
|
||||
|
||||
if (domain)
|
||||
{
|
||||
lua_pushstring(lua, domain);
|
||||
lua_setfield(lua, -2, "domain");
|
||||
}
|
||||
|
||||
end = extradata + data.ed_len;
|
||||
buf = extradata;
|
||||
|
||||
if (!is6)
|
||||
buf = grab_extradata_lua(buf, end, "vendor_class");
|
||||
#ifdef HAVE_DHCP6
|
||||
else
|
||||
for (i = 0; i < data.hwaddr_len; i++)
|
||||
{
|
||||
sprintf(daemon->dhcp_buff2, "vendor_class%i", i);
|
||||
buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
|
||||
}
|
||||
#endif
|
||||
|
||||
buf = grab_extradata_lua(buf, end, "supplied_hostname");
|
||||
|
||||
if (!is6)
|
||||
{
|
||||
buf = grab_extradata_lua(buf, end, "cpewan_oui");
|
||||
buf = grab_extradata_lua(buf, end, "cpewan_serial");
|
||||
buf = grab_extradata_lua(buf, end, "cpewan_class");
|
||||
buf = grab_extradata_lua(buf, end, "circuit_id");
|
||||
buf = grab_extradata_lua(buf, end, "subscriber_id");
|
||||
buf = grab_extradata_lua(buf, end, "remote_id");
|
||||
}
|
||||
|
||||
buf = grab_extradata_lua(buf, end, "tags");
|
||||
|
||||
if (is6)
|
||||
buf = grab_extradata_lua(buf, end, "relay_address");
|
||||
else if (data.giaddr.s_addr != 0)
|
||||
{
|
||||
lua_pushstring(lua, inet_ntoa(data.giaddr));
|
||||
lua_setfield(lua, -2, "relay_address");
|
||||
}
|
||||
|
||||
for (i = 0; buf; i++)
|
||||
{
|
||||
sprintf(daemon->dhcp_buff2, "user_class%i", i);
|
||||
buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
|
||||
}
|
||||
|
||||
if (data.action != ACTION_DEL && data.remaining_time != 0)
|
||||
{
|
||||
lua_pushnumber(lua, data.remaining_time);
|
||||
lua_setfield(lua, -2, "time_remaining");
|
||||
}
|
||||
|
||||
if (data.action == ACTION_OLD_HOSTNAME && hostname)
|
||||
{
|
||||
lua_pushstring(lua, hostname);
|
||||
lua_setfield(lua, -2, "old_hostname");
|
||||
}
|
||||
|
||||
if (!is6)
|
||||
{
|
||||
lua_pushstring(lua, daemon->dhcp_buff);
|
||||
lua_setfield(lua, -2, "mac_address");
|
||||
}
|
||||
|
||||
lua_pushstring(lua, daemon->addrbuff);
|
||||
lua_setfield(lua, -2, "ip_address");
|
||||
|
||||
lua_call(lua, 2, 0); /* pass 2 values, expect 0 */
|
||||
}
|
||||
|
||||
buf = grab_extradata_lua(buf, end, "tags");
|
||||
|
||||
if (is6)
|
||||
buf = grab_extradata_lua(buf, end, "relay_address");
|
||||
else if (data.giaddr.s_addr != 0)
|
||||
{
|
||||
lua_pushstring(lua, inet_ntoa(data.giaddr));
|
||||
lua_setfield(lua, -2, "relay_address");
|
||||
}
|
||||
|
||||
for (i = 0; buf; i++)
|
||||
{
|
||||
sprintf(daemon->dhcp_buff2, "user_class%i", i);
|
||||
buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
|
||||
}
|
||||
|
||||
if (data.action != ACTION_DEL && data.remaining_time != 0)
|
||||
{
|
||||
lua_pushnumber(lua, data.remaining_time);
|
||||
lua_setfield(lua, -2, "time_remaining");
|
||||
}
|
||||
|
||||
if (data.action == ACTION_OLD_HOSTNAME && hostname)
|
||||
{
|
||||
lua_pushstring(lua, hostname);
|
||||
lua_setfield(lua, -2, "old_hostname");
|
||||
}
|
||||
|
||||
if (!is6)
|
||||
{
|
||||
lua_pushstring(lua, daemon->dhcp_buff);
|
||||
lua_setfield(lua, -2, "mac_address");
|
||||
}
|
||||
|
||||
lua_pushstring(lua, daemon->addrbuff);
|
||||
lua_setfield(lua, -2, "ip_address");
|
||||
|
||||
lua_call(lua, 2, 0); /* pass 2 values, expect 0 */
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -439,81 +474,92 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is6)
|
||||
if (data.action != ACTION_TFTP)
|
||||
{
|
||||
my_setenv("DNSMASQ_IAID", daemon->dhcp_buff3, &err);
|
||||
my_setenv("DNSMASQ_SERVER_DUID", daemon->packet, &err);
|
||||
}
|
||||
|
||||
if (!is6 && data.clid_len != 0)
|
||||
my_setenv("DNSMASQ_CLIENT_ID", daemon->packet, &err);
|
||||
|
||||
if (strlen(data.interface) != 0)
|
||||
my_setenv("DNSMASQ_INTERFACE", data.interface, &err);
|
||||
|
||||
#ifdef HAVE_BROKEN_RTC
|
||||
my_setenv("DNSMASQ_LEASE_LENGTH", daemon->dhcp_buff2, &err);
|
||||
#else
|
||||
my_setenv("DNSMASQ_LEASE_EXPIRES", daemon->dhcp_buff2, &err);
|
||||
#endif
|
||||
|
||||
if (domain)
|
||||
my_setenv("DNSMASQ_DOMAIN", domain, &err);
|
||||
|
||||
end = extradata + data.ed_len;
|
||||
buf = extradata;
|
||||
|
||||
if (!is6)
|
||||
buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS", &err);
|
||||
#ifdef HAVE_DHCP6
|
||||
else
|
||||
{
|
||||
if (data.hwaddr_len != 0)
|
||||
if (is6)
|
||||
{
|
||||
buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS_ID", &err);
|
||||
for (i = 0; i < data.hwaddr_len - 1; i++)
|
||||
my_setenv("DNSMASQ_IAID", daemon->dhcp_buff3, &err);
|
||||
my_setenv("DNSMASQ_SERVER_DUID", daemon->packet, &err);
|
||||
}
|
||||
|
||||
if (!is6 && data.clid_len != 0)
|
||||
my_setenv("DNSMASQ_CLIENT_ID", daemon->packet, &err);
|
||||
|
||||
if (strlen(data.interface) != 0)
|
||||
my_setenv("DNSMASQ_INTERFACE", data.interface, &err);
|
||||
|
||||
#ifdef HAVE_BROKEN_RTC
|
||||
sprintf(daemon->dhcp_buff2, "%u", data.length);
|
||||
my_setenv("DNSMASQ_LEASE_LENGTH", daemon->dhcp_buff2, &err);
|
||||
#else
|
||||
sprintf(daemon->dhcp_buff2, "%lu", (unsigned long)data.expires);
|
||||
my_setenv("DNSMASQ_LEASE_EXPIRES", daemon->dhcp_buff2, &err);
|
||||
#endif
|
||||
|
||||
if (domain)
|
||||
my_setenv("DNSMASQ_DOMAIN", domain, &err);
|
||||
|
||||
end = extradata + data.ed_len;
|
||||
buf = extradata;
|
||||
|
||||
if (!is6)
|
||||
buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS", &err);
|
||||
#ifdef HAVE_DHCP6
|
||||
else
|
||||
{
|
||||
if (data.hwaddr_len != 0)
|
||||
{
|
||||
sprintf(daemon->dhcp_buff2, "DNSMASQ_VENDOR_CLASS%i", i);
|
||||
buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
|
||||
buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS_ID", &err);
|
||||
for (i = 0; i < data.hwaddr_len - 1; i++)
|
||||
{
|
||||
sprintf(daemon->dhcp_buff2, "DNSMASQ_VENDOR_CLASS%i", i);
|
||||
buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
buf = grab_extradata(buf, end, "DNSMASQ_SUPPLIED_HOSTNAME", &err);
|
||||
|
||||
if (!is6)
|
||||
{
|
||||
buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_OUI", &err);
|
||||
buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_SERIAL", &err);
|
||||
buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_CLASS", &err);
|
||||
buf = grab_extradata(buf, end, "DNSMASQ_CIRCUIT_ID", &err);
|
||||
buf = grab_extradata(buf, end, "DNSMASQ_SUBSCRIBER_ID", &err);
|
||||
buf = grab_extradata(buf, end, "DNSMASQ_REMOTE_ID", &err);
|
||||
}
|
||||
|
||||
buf = grab_extradata(buf, end, "DNSMASQ_TAGS", &err);
|
||||
|
||||
buf = grab_extradata(buf, end, "DNSMASQ_SUPPLIED_HOSTNAME", &err);
|
||||
|
||||
if (!is6)
|
||||
{
|
||||
buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_OUI", &err);
|
||||
buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_SERIAL", &err);
|
||||
buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_CLASS", &err);
|
||||
}
|
||||
|
||||
buf = grab_extradata(buf, end, "DNSMASQ_TAGS", &err);
|
||||
|
||||
if (is6)
|
||||
buf = grab_extradata(buf, end, "DNSMASQ_RELAY_ADDRESS", &err);
|
||||
else if (data.giaddr.s_addr != 0)
|
||||
my_setenv("DNSMASQ_RELAY_ADDRESS", inet_ntoa(data.giaddr), &err);
|
||||
|
||||
for (i = 0; buf; i++)
|
||||
{
|
||||
sprintf(daemon->dhcp_buff2, "DNSMASQ_USER_CLASS%i", i);
|
||||
buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
|
||||
}
|
||||
|
||||
if (data.action != ACTION_DEL && data.remaining_time != 0)
|
||||
{
|
||||
sprintf(daemon->dhcp_buff2, "%u", data.remaining_time);
|
||||
my_setenv("DNSMASQ_TIME_REMAINING", daemon->dhcp_buff2, &err);
|
||||
}
|
||||
|
||||
if (data.action == ACTION_OLD_HOSTNAME && hostname)
|
||||
{
|
||||
my_setenv("DNSMASQ_OLD_HOSTNAME", hostname, &err);
|
||||
hostname = NULL;
|
||||
if (is6)
|
||||
buf = grab_extradata(buf, end, "DNSMASQ_RELAY_ADDRESS", &err);
|
||||
else if (data.giaddr.s_addr != 0)
|
||||
my_setenv("DNSMASQ_RELAY_ADDRESS", inet_ntoa(data.giaddr), &err);
|
||||
|
||||
for (i = 0; buf; i++)
|
||||
{
|
||||
sprintf(daemon->dhcp_buff2, "DNSMASQ_USER_CLASS%i", i);
|
||||
buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
|
||||
}
|
||||
|
||||
if (data.action != ACTION_DEL && data.remaining_time != 0)
|
||||
{
|
||||
sprintf(daemon->dhcp_buff2, "%u", data.remaining_time);
|
||||
my_setenv("DNSMASQ_TIME_REMAINING", daemon->dhcp_buff2, &err);
|
||||
}
|
||||
|
||||
if (data.action == ACTION_OLD_HOSTNAME && hostname)
|
||||
{
|
||||
my_setenv("DNSMASQ_OLD_HOSTNAME", hostname, &err);
|
||||
hostname = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (option_bool(OPT_LOG_OPTS))
|
||||
my_setenv("DNSMASQ_LOG_DHCP", "1", &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);
|
||||
@@ -584,32 +630,8 @@ static unsigned char *grab_extradata_lua(unsigned char *buf, unsigned char *end,
|
||||
}
|
||||
#endif
|
||||
|
||||
/* pack up lease data into a buffer */
|
||||
void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t now)
|
||||
static void buff_alloc(size_t size)
|
||||
{
|
||||
unsigned char *p;
|
||||
size_t size;
|
||||
unsigned int hostname_len = 0, clid_len = 0, ed_len = 0;
|
||||
int fd = daemon->dhcpfd;
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
if (!daemon->dhcp)
|
||||
fd = daemon->dhcp6fd;
|
||||
#endif
|
||||
|
||||
/* no script */
|
||||
if (daemon->helperfd == -1)
|
||||
return;
|
||||
|
||||
if (lease->extradata)
|
||||
ed_len = lease->extradata_len;
|
||||
if (lease->clid)
|
||||
clid_len = lease->clid_len;
|
||||
if (hostname)
|
||||
hostname_len = strlen(hostname) + 1;
|
||||
|
||||
size = sizeof(struct script_data) + clid_len + ed_len + hostname_len;
|
||||
|
||||
if (size > buf_size)
|
||||
{
|
||||
struct script_data *new;
|
||||
@@ -625,10 +647,42 @@ void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t n
|
||||
buf = new;
|
||||
buf_size = size;
|
||||
}
|
||||
}
|
||||
|
||||
/* pack up lease data into a buffer */
|
||||
void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t now)
|
||||
{
|
||||
unsigned char *p;
|
||||
unsigned int hostname_len = 0, clid_len = 0, ed_len = 0;
|
||||
int fd = daemon->dhcpfd;
|
||||
#ifdef HAVE_DHCP6
|
||||
int is6 = !!(lease->flags & (LEASE_TA | LEASE_NA));
|
||||
|
||||
if (!daemon->dhcp)
|
||||
fd = daemon->dhcp6fd;
|
||||
#endif
|
||||
|
||||
/* no script */
|
||||
if (daemon->helperfd == -1)
|
||||
return;
|
||||
|
||||
if (lease->extradata)
|
||||
ed_len = lease->extradata_len;
|
||||
if (lease->clid)
|
||||
clid_len = lease->clid_len;
|
||||
if (hostname)
|
||||
hostname_len = strlen(hostname) + 1;
|
||||
|
||||
buff_alloc(sizeof(struct script_data) + clid_len + ed_len + hostname_len);
|
||||
|
||||
buf->action = action;
|
||||
buf->flags = lease->flags;
|
||||
buf->hwaddr_len = lease->hwaddr_len;
|
||||
#ifdef HAVE_DHCP6
|
||||
if (is6)
|
||||
buf->hwaddr_len = lease->vendorclass_count;
|
||||
else
|
||||
#endif
|
||||
buf->hwaddr_len = lease->hwaddr_len;
|
||||
buf->hwaddr_type = lease->hwaddr_type;
|
||||
buf->clid_len = clid_len;
|
||||
buf->ed_len = ed_len;
|
||||
@@ -669,6 +723,37 @@ void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t n
|
||||
bytes_in_buf = p - (unsigned char *)buf;
|
||||
}
|
||||
|
||||
#ifdef HAVE_TFTP
|
||||
/* This nastily re-uses DHCP-fields for TFTP stuff */
|
||||
void queue_tftp(off_t file_len, char *filename, union mysockaddr *peer)
|
||||
{
|
||||
unsigned int filename_len;
|
||||
|
||||
/* no script */
|
||||
if (daemon->helperfd == -1)
|
||||
return;
|
||||
|
||||
filename_len = strlen(filename) + 1;
|
||||
buff_alloc(sizeof(struct script_data) + filename_len);
|
||||
memset(buf, 0, sizeof(struct script_data));
|
||||
|
||||
buf->action = ACTION_TFTP;
|
||||
buf->hostname_len = filename_len;
|
||||
buf->hwaddr_len = file_len;
|
||||
|
||||
if ((buf->flags = peer->sa.sa_family) == AF_INET)
|
||||
buf->addr = peer->in.sin_addr;
|
||||
#ifdef HAVE_IPV6
|
||||
else
|
||||
memcpy(buf->hwaddr, &peer->in6.sin6_addr, IN6ADDRSZ);
|
||||
#endif
|
||||
|
||||
memcpy((unsigned char *)(buf+1), filename, filename_len);
|
||||
|
||||
bytes_in_buf = sizeof(struct script_data) + filename_len;
|
||||
}
|
||||
#endif
|
||||
|
||||
int helper_buf_empty(void)
|
||||
{
|
||||
return bytes_in_buf == 0;
|
||||
|
||||
229
src/ipset.c
Normal file
229
src/ipset.c
Normal file
@@ -0,0 +1,229 @@
|
||||
/* ipset.c is Copyright (c) 2013 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 dated June, 1991, or
|
||||
(at your option) version 3 dated 29 June, 2007.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
#ifdef HAVE_IPSET
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/netlink.h>
|
||||
|
||||
/* We want to be able to compile against old header files
|
||||
Kernel version is handled at run-time. */
|
||||
|
||||
#define NFNL_SUBSYS_IPSET 6
|
||||
|
||||
#define IPSET_ATTR_DATA 7
|
||||
#define IPSET_ATTR_IP 1
|
||||
#define IPSET_ATTR_IPADDR_IPV4 1
|
||||
#define IPSET_ATTR_IPADDR_IPV6 2
|
||||
#define IPSET_ATTR_PROTOCOL 1
|
||||
#define IPSET_ATTR_SETNAME 2
|
||||
#define IPSET_CMD_ADD 9
|
||||
#define IPSET_CMD_DEL 10
|
||||
#define IPSET_MAXNAMELEN 32
|
||||
#define IPSET_PROTOCOL 6
|
||||
|
||||
#ifndef NFNETLINK_V0
|
||||
#define NFNETLINK_V0 0
|
||||
#endif
|
||||
|
||||
#ifndef NLA_F_NESTED
|
||||
#define NLA_F_NESTED (1 << 15)
|
||||
#endif
|
||||
|
||||
#ifndef NLA_F_NET_BYTEORDER
|
||||
#define NLA_F_NET_BYTEORDER (1 << 14)
|
||||
#endif
|
||||
|
||||
struct my_nlattr {
|
||||
__u16 nla_len;
|
||||
__u16 nla_type;
|
||||
};
|
||||
|
||||
struct my_nfgenmsg {
|
||||
__u8 nfgen_family; /* AF_xxx */
|
||||
__u8 version; /* nfnetlink version */
|
||||
__be16 res_id; /* resource id */
|
||||
};
|
||||
|
||||
|
||||
/* data structure size in here is fixed */
|
||||
#define BUFF_SZ 256
|
||||
|
||||
#define NL_ALIGN(len) (((len)+3) & ~(3))
|
||||
static const struct sockaddr_nl snl = { .nl_family = AF_NETLINK };
|
||||
static int ipset_sock, old_kernel;
|
||||
static char *buffer;
|
||||
|
||||
static inline void add_attr(struct nlmsghdr *nlh, uint16_t type, size_t len, const void *data)
|
||||
{
|
||||
struct my_nlattr *attr = (void *)nlh + NL_ALIGN(nlh->nlmsg_len);
|
||||
uint16_t payload_len = NL_ALIGN(sizeof(struct my_nlattr)) + len;
|
||||
attr->nla_type = type;
|
||||
attr->nla_len = payload_len;
|
||||
memcpy((void *)attr + NL_ALIGN(sizeof(struct my_nlattr)), data, len);
|
||||
nlh->nlmsg_len += NL_ALIGN(payload_len);
|
||||
}
|
||||
|
||||
void ipset_init(void)
|
||||
{
|
||||
struct utsname utsname;
|
||||
int version;
|
||||
char *split;
|
||||
|
||||
if (uname(&utsname) < 0)
|
||||
die(_("failed to find kernel version: %s"), NULL, EC_MISC);
|
||||
|
||||
split = strtok(utsname.release, ".");
|
||||
version = (split ? atoi(split) : 0);
|
||||
split = strtok(NULL, ".");
|
||||
version = version * 256 + (split ? atoi(split) : 0);
|
||||
split = strtok(NULL, ".");
|
||||
version = version * 256 + (split ? atoi(split) : 0);
|
||||
old_kernel = (version < KERNEL_VERSION(2,6,32));
|
||||
|
||||
if (old_kernel && (ipset_sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) != -1)
|
||||
return;
|
||||
|
||||
if (!old_kernel &&
|
||||
(buffer = safe_malloc(BUFF_SZ)) &&
|
||||
(ipset_sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_NETFILTER)) != -1 &&
|
||||
(bind(ipset_sock, (struct sockaddr *)&snl, sizeof(snl)) != -1))
|
||||
return;
|
||||
|
||||
die (_("failed to create IPset control socket: %s"), NULL, EC_MISC);
|
||||
}
|
||||
|
||||
static int new_add_to_ipset(const char *setname, const struct all_addr *ipaddr, int af, int remove)
|
||||
{
|
||||
struct nlmsghdr *nlh;
|
||||
struct my_nfgenmsg *nfg;
|
||||
struct my_nlattr *nested[2];
|
||||
uint8_t proto;
|
||||
int addrsz = INADDRSZ;
|
||||
ssize_t rc;
|
||||
|
||||
#ifdef HAVE_IPV6
|
||||
if (af == AF_INET6)
|
||||
addrsz = IN6ADDRSZ;
|
||||
#endif
|
||||
|
||||
if (strlen(setname) >= IPSET_MAXNAMELEN)
|
||||
{
|
||||
errno = ENAMETOOLONG;
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(buffer, 0, BUFF_SZ);
|
||||
|
||||
nlh = (struct nlmsghdr *)buffer;
|
||||
nlh->nlmsg_len = NL_ALIGN(sizeof(struct nlmsghdr));
|
||||
nlh->nlmsg_type = (remove ? IPSET_CMD_DEL : IPSET_CMD_ADD) | (NFNL_SUBSYS_IPSET << 8);
|
||||
nlh->nlmsg_flags = NLM_F_REQUEST;
|
||||
|
||||
nfg = (struct my_nfgenmsg *)(buffer + nlh->nlmsg_len);
|
||||
nlh->nlmsg_len += NL_ALIGN(sizeof(struct my_nfgenmsg));
|
||||
nfg->nfgen_family = af;
|
||||
nfg->version = NFNETLINK_V0;
|
||||
nfg->res_id = htons(0);
|
||||
|
||||
proto = IPSET_PROTOCOL;
|
||||
add_attr(nlh, IPSET_ATTR_PROTOCOL, sizeof(proto), &proto);
|
||||
add_attr(nlh, IPSET_ATTR_SETNAME, strlen(setname) + 1, setname);
|
||||
nested[0] = (struct my_nlattr *)(buffer + NL_ALIGN(nlh->nlmsg_len));
|
||||
nlh->nlmsg_len += NL_ALIGN(sizeof(struct my_nlattr));
|
||||
nested[0]->nla_type = NLA_F_NESTED | IPSET_ATTR_DATA;
|
||||
nested[1] = (struct my_nlattr *)(buffer + NL_ALIGN(nlh->nlmsg_len));
|
||||
nlh->nlmsg_len += NL_ALIGN(sizeof(struct my_nlattr));
|
||||
nested[1]->nla_type = NLA_F_NESTED | IPSET_ATTR_IP;
|
||||
add_attr(nlh,
|
||||
(af == AF_INET ? IPSET_ATTR_IPADDR_IPV4 : IPSET_ATTR_IPADDR_IPV6) | NLA_F_NET_BYTEORDER,
|
||||
addrsz, &ipaddr->addr);
|
||||
nested[1]->nla_len = (void *)buffer + NL_ALIGN(nlh->nlmsg_len) - (void *)nested[1];
|
||||
nested[0]->nla_len = (void *)buffer + NL_ALIGN(nlh->nlmsg_len) - (void *)nested[0];
|
||||
|
||||
while ((rc = sendto(ipset_sock, buffer, nlh->nlmsg_len, 0,
|
||||
(struct sockaddr *)&snl, sizeof(snl))) == -1 && retry_send());
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static int old_add_to_ipset(const char *setname, const struct all_addr *ipaddr, int remove)
|
||||
{
|
||||
socklen_t size;
|
||||
struct ip_set_req_adt_get {
|
||||
unsigned op;
|
||||
unsigned version;
|
||||
union {
|
||||
char name[IPSET_MAXNAMELEN];
|
||||
uint16_t index;
|
||||
} set;
|
||||
char typename[IPSET_MAXNAMELEN];
|
||||
} req_adt_get;
|
||||
struct ip_set_req_adt {
|
||||
unsigned op;
|
||||
uint16_t index;
|
||||
uint32_t ip;
|
||||
} req_adt;
|
||||
|
||||
if (strlen(setname) >= sizeof(req_adt_get.set.name))
|
||||
{
|
||||
errno = ENAMETOOLONG;
|
||||
return -1;
|
||||
}
|
||||
|
||||
req_adt_get.op = 0x10;
|
||||
req_adt_get.version = 3;
|
||||
strcpy(req_adt_get.set.name, setname);
|
||||
size = sizeof(req_adt_get);
|
||||
if (getsockopt(ipset_sock, SOL_IP, 83, &req_adt_get, &size) < 0)
|
||||
return -1;
|
||||
req_adt.op = remove ? 0x102 : 0x101;
|
||||
req_adt.index = req_adt_get.set.index;
|
||||
req_adt.ip = ntohl(ipaddr->addr.addr4.s_addr);
|
||||
if (setsockopt(ipset_sock, SOL_IP, 83, &req_adt, sizeof(req_adt)) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int add_to_ipset(const char *setname, const struct all_addr *ipaddr, int flags, int remove)
|
||||
{
|
||||
int af = AF_INET;
|
||||
|
||||
#ifdef HAVE_IPV6
|
||||
if (flags & F_IPV6)
|
||||
{
|
||||
af = AF_INET6;
|
||||
/* old method only supports IPv4 */
|
||||
if (old_kernel)
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
return old_kernel ? old_add_to_ipset(setname, ipaddr, remove) : new_add_to_ipset(setname, ipaddr, af, remove);
|
||||
}
|
||||
|
||||
#endif
|
||||
233
src/lease.c
233
src/lease.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2013 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
|
||||
@@ -97,7 +97,8 @@ void lease_init(time_t now)
|
||||
if (hw_type == 0 && hw_len != 0)
|
||||
hw_type = ARPHRD_ETHER;
|
||||
|
||||
lease_set_hwaddr(lease, (unsigned char *)daemon->dhcp_buff2, (unsigned char *)daemon->packet, hw_len, hw_type, clid_len);
|
||||
lease_set_hwaddr(lease, (unsigned char *)daemon->dhcp_buff2, (unsigned char *)daemon->packet,
|
||||
hw_len, hw_type, clid_len, now, 0);
|
||||
|
||||
if (strcmp(daemon->dhcp_buff, "*") != 0)
|
||||
lease_set_hostname(lease, daemon->dhcp_buff, 0, get_domain(lease->addr), NULL);
|
||||
@@ -114,11 +115,11 @@ void lease_init(time_t now)
|
||||
s++;
|
||||
}
|
||||
|
||||
hw_type = atoi(s);
|
||||
hw_type = strtoul(s, NULL, 10);
|
||||
|
||||
if ((lease = lease6_allocate(&addr.addr.addr6, lease_type)))
|
||||
{
|
||||
lease_set_hwaddr(lease, NULL, (unsigned char *)daemon->packet, 0, hw_type, clid_len);
|
||||
lease_set_hwaddr(lease, NULL, (unsigned char *)daemon->packet, 0, hw_type, clid_len, now, 0);
|
||||
|
||||
if (strcmp(daemon->dhcp_buff, "*") != 0)
|
||||
lease_set_hostname(lease, daemon->dhcp_buff, 0, get_domain6((struct in6_addr *)lease->hwaddr), NULL);
|
||||
@@ -306,9 +307,23 @@ void lease_update_file(time_t now)
|
||||
next_event = 0;
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
/* do timed RAs and determine when the next is */
|
||||
if (daemon->ra_contexts)
|
||||
next_event = periodic_ra(now);
|
||||
/* do timed RAs and determine when the next is, also pings to potential SLAAC addresses */
|
||||
if (daemon->doing_ra)
|
||||
{
|
||||
time_t event;
|
||||
|
||||
if ((event = periodic_slaac(now, leases)) != 0)
|
||||
{
|
||||
if (next_event == 0 || difftime(next_event, event) > 0.0)
|
||||
next_event = event;
|
||||
}
|
||||
|
||||
if ((event = periodic_ra(now)) != 0)
|
||||
{
|
||||
if (next_event == 0 || difftime(next_event, event) > 0.0)
|
||||
next_event = event;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
for (lease = leases; lease; lease = lease->next)
|
||||
@@ -326,44 +341,67 @@ void lease_update_file(time_t now)
|
||||
(unsigned int)difftime(next_event, now));
|
||||
}
|
||||
|
||||
if (next_event != 0)
|
||||
alarm((unsigned)difftime(next_event, now));
|
||||
send_alarm(next_event, now);
|
||||
}
|
||||
|
||||
|
||||
static int find_interface_v4(struct in_addr local, int if_index,
|
||||
static int find_interface_v4(struct in_addr local, int if_index, char *label,
|
||||
struct in_addr netmask, struct in_addr broadcast, void *vparam)
|
||||
{
|
||||
struct dhcp_lease *lease;
|
||||
|
||||
(void) label;
|
||||
(void) broadcast;
|
||||
(void) vparam;
|
||||
|
||||
for (lease = leases; lease; lease = lease->next)
|
||||
if (!(lease->flags & (LEASE_TA | LEASE_NA)))
|
||||
if (is_same_net(local, lease->addr, netmask))
|
||||
lease->last_interface = if_index;
|
||||
lease_set_interface(lease, if_index, *((time_t *)vparam));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
static int find_interface_v6(struct in6_addr *local, int prefix,
|
||||
int scope, int if_index, int dad, void *vparam)
|
||||
int scope, int if_index, int flags,
|
||||
int preferred, int valid, void *vparam)
|
||||
{
|
||||
struct dhcp_lease *lease;
|
||||
|
||||
(void) scope;
|
||||
(void) vparam;
|
||||
(void)dad;
|
||||
(void)scope;
|
||||
(void)flags;
|
||||
(void)preferred;
|
||||
(void)valid;
|
||||
|
||||
for (lease = leases; lease; lease = lease->next)
|
||||
if ((lease->flags & (LEASE_TA | LEASE_NA)))
|
||||
if (is_same_net6(local, (struct in6_addr *)&lease->hwaddr, prefix))
|
||||
lease->last_interface = if_index;
|
||||
lease_set_interface(lease, if_index, *((time_t *)vparam));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void lease_ping_reply(struct in6_addr *sender, unsigned char *packet, char *interface)
|
||||
{
|
||||
/* We may be doing RA but not DHCPv4, in which case the lease
|
||||
database may not exist and we have nothing to do anyway */
|
||||
if (daemon->dhcp)
|
||||
slaac_ping_reply(sender, packet, interface, leases);
|
||||
}
|
||||
|
||||
void lease_update_slaac(time_t now)
|
||||
{
|
||||
/* Called when we contruct a new RA-names context, to add putative
|
||||
new SLAAC addresses to existing leases. */
|
||||
|
||||
struct dhcp_lease *lease;
|
||||
|
||||
if (daemon->dhcp)
|
||||
for (lease = leases; lease; lease = lease->next)
|
||||
slaac_add_addrs(lease, now, 0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -373,9 +411,9 @@ static int find_interface_v6(struct in6_addr *local, int prefix,
|
||||
start-time. */
|
||||
void lease_find_interfaces(time_t now)
|
||||
{
|
||||
iface_enumerate(AF_INET, NULL, find_interface_v4);
|
||||
iface_enumerate(AF_INET, &now, find_interface_v4);
|
||||
#ifdef HAVE_DHCP6
|
||||
iface_enumerate(AF_INET6, NULL, find_interface_v6);
|
||||
iface_enumerate(AF_INET6, &now, find_interface_v6);
|
||||
|
||||
/* If we're not doing DHCPv6, and there are not v6 leases, don't add the DUID to the database */
|
||||
if (!daemon->duid && daemon->dhcp6)
|
||||
@@ -388,14 +426,15 @@ void lease_find_interfaces(time_t now)
|
||||
|
||||
|
||||
|
||||
void lease_update_dns(void)
|
||||
void lease_update_dns(int force)
|
||||
{
|
||||
struct dhcp_lease *lease;
|
||||
|
||||
if (daemon->port != 0 && dns_dirty)
|
||||
if (daemon->port != 0 && (dns_dirty || force))
|
||||
{
|
||||
#ifdef HAVE_DHCP6
|
||||
struct subnet_map *subnets = build_subnet_map();
|
||||
#ifndef HAVE_BROKEN_RTC
|
||||
/* force transfer to authoritative secondaries */
|
||||
daemon->soa_sn++;
|
||||
#endif
|
||||
|
||||
cache_unhash_dhcp();
|
||||
@@ -409,41 +448,15 @@ void lease_update_dns(void)
|
||||
prot = AF_INET6;
|
||||
else if (lease->hostname || lease->fqdn)
|
||||
{
|
||||
struct subnet_map *map;
|
||||
for (map = subnets; map; map = map->next)
|
||||
if (lease->last_interface == map->iface)
|
||||
struct slaac_address *slaac;
|
||||
|
||||
for (slaac = lease->slaac_address; slaac; slaac = slaac->next)
|
||||
if (slaac->backoff == 0)
|
||||
{
|
||||
struct in6_addr addr = map->subnet;
|
||||
if (lease->hwaddr_len == 6 &&
|
||||
(lease->hwaddr_type == ARPHRD_ETHER || lease->hwaddr_type == ARPHRD_IEEE802))
|
||||
{
|
||||
/* convert MAC address to EUI-64 */
|
||||
memcpy(&addr.s6_addr[8], lease->hwaddr, 3);
|
||||
memcpy(&addr.s6_addr[13], &lease->hwaddr[3], 3);
|
||||
addr.s6_addr[11] = 0xff;
|
||||
addr.s6_addr[12] = 0xfe;
|
||||
}
|
||||
#if defined(ARPHRD_EUI64)
|
||||
else if (lease->hwaddr_len == 8 &&
|
||||
lease->hwaddr_type == ARPHRD_EUI64)
|
||||
memcpy(&addr.s6_addr[8], lease->hwaddr, 8);
|
||||
#endif
|
||||
#if defined(ARPHRD_IEEE1394) && defined(ARPHRD_EUI64)
|
||||
else if (lease->clid_len == 9 &&
|
||||
lease->clid[0] == ARPHRD_EUI64 &&
|
||||
lease->hwaddr_type == ARPHRD_IEEE1394)
|
||||
/* firewire has EUI-64 identifier as clid */
|
||||
memcpy(&addr.s6_addr[8], &lease->clid[1], 8);
|
||||
#endif
|
||||
else
|
||||
continue;
|
||||
|
||||
addr.s6_addr[8] ^= 0x02;
|
||||
|
||||
if (lease->fqdn)
|
||||
cache_add_dhcp_entry(lease->fqdn, AF_INET6, (struct all_addr *)&addr, lease->expires);
|
||||
cache_add_dhcp_entry(lease->fqdn, AF_INET6, (struct all_addr *)&slaac->addr, lease->expires);
|
||||
if (!option_bool(OPT_DHCP_FQDN) && lease->hostname)
|
||||
cache_add_dhcp_entry(lease->hostname, AF_INET6, (struct all_addr *)&addr, lease->expires);
|
||||
cache_add_dhcp_entry(lease->hostname, AF_INET6, (struct all_addr *)&slaac->addr, lease->expires);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -543,8 +556,7 @@ struct dhcp_lease *lease_find_by_addr(struct in_addr addr)
|
||||
}
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
/* addr or clid may be NULL for "don't care, both NULL resets "USED" flags both
|
||||
set activates USED check */
|
||||
/* find address for {CLID, IAID, address} */
|
||||
struct dhcp_lease *lease6_find(unsigned char *clid, int clid_len,
|
||||
int lease_type, int iaid, struct in6_addr *addr)
|
||||
{
|
||||
@@ -555,24 +567,51 @@ struct dhcp_lease *lease6_find(unsigned char *clid, int clid_len,
|
||||
if (!(lease->flags & lease_type) || lease->hwaddr_type != iaid)
|
||||
continue;
|
||||
|
||||
if (clid && addr && (lease->flags & LEASE_USED))
|
||||
if (memcmp(lease->hwaddr, addr, IN6ADDRSZ) != 0)
|
||||
continue;
|
||||
|
||||
if (addr && memcmp(lease->hwaddr, addr, IN6ADDRSZ) != 0)
|
||||
continue;
|
||||
|
||||
if (clid &&
|
||||
(clid_len != lease->clid_len ||
|
||||
if ((clid_len != lease->clid_len ||
|
||||
memcmp(clid, lease->clid, clid_len) != 0))
|
||||
continue;
|
||||
|
||||
if (clid || addr)
|
||||
{
|
||||
lease->flags |= LEASE_USED;
|
||||
return lease;
|
||||
}
|
||||
else
|
||||
lease->flags &= ~LEASE_USED;
|
||||
return lease;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* reset "USED flags */
|
||||
void lease6_reset(void)
|
||||
{
|
||||
struct dhcp_lease *lease;
|
||||
|
||||
for (lease = leases; lease; lease = lease->next)
|
||||
lease->flags &= ~LEASE_USED;
|
||||
}
|
||||
|
||||
/* enumerate all leases belonging to {CLID, IAID} */
|
||||
struct dhcp_lease *lease6_find_by_client(struct dhcp_lease *first, int lease_type, unsigned char *clid, int clid_len, int iaid)
|
||||
{
|
||||
struct dhcp_lease *lease;
|
||||
|
||||
if (!first)
|
||||
first = leases;
|
||||
else
|
||||
first = first->next;
|
||||
|
||||
for (lease = first; lease; lease = lease->next)
|
||||
{
|
||||
if (lease->flags & LEASE_USED)
|
||||
continue;
|
||||
|
||||
if (!(lease->flags & lease_type) || lease->hwaddr_type != iaid)
|
||||
continue;
|
||||
|
||||
if ((clid_len != lease->clid_len ||
|
||||
memcmp(clid, lease->clid, clid_len) != 0))
|
||||
continue;
|
||||
|
||||
return lease;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
@@ -665,8 +704,11 @@ static struct dhcp_lease *lease_allocate(void)
|
||||
struct dhcp_lease *lease4_allocate(struct in_addr addr)
|
||||
{
|
||||
struct dhcp_lease *lease = lease_allocate();
|
||||
lease->addr = addr;
|
||||
lease->hwaddr_len = 256; /* illegal value */
|
||||
if (lease)
|
||||
{
|
||||
lease->addr = addr;
|
||||
lease->hwaddr_len = 256; /* illegal value */
|
||||
}
|
||||
|
||||
return lease;
|
||||
}
|
||||
@@ -675,8 +717,12 @@ struct dhcp_lease *lease4_allocate(struct in_addr addr)
|
||||
struct dhcp_lease *lease6_allocate(struct in6_addr *addrp, int lease_type)
|
||||
{
|
||||
struct dhcp_lease *lease = lease_allocate();
|
||||
memcpy(lease->hwaddr, addrp, sizeof(*addrp)) ;
|
||||
lease->flags |= lease_type;
|
||||
|
||||
if (lease)
|
||||
{
|
||||
memcpy(lease->hwaddr, addrp, sizeof(*addrp)) ;
|
||||
lease->flags |= lease_type;
|
||||
}
|
||||
|
||||
return lease;
|
||||
}
|
||||
@@ -713,8 +759,16 @@ void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now)
|
||||
}
|
||||
|
||||
void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr,
|
||||
unsigned char *clid, int hw_len, int hw_type, int clid_len)
|
||||
unsigned char *clid, int hw_len, int hw_type, int clid_len,
|
||||
time_t now, int force)
|
||||
{
|
||||
#ifdef HAVE_DHCP6
|
||||
int change = force;
|
||||
lease->flags |= LEASE_HAVE_HWADDR;
|
||||
#endif
|
||||
|
||||
(void)force;
|
||||
|
||||
if (hw_len != lease->hwaddr_len ||
|
||||
hw_type != lease->hwaddr_type ||
|
||||
(hw_len != 0 && memcmp(lease->hwaddr, hwaddr, hw_len) != 0))
|
||||
@@ -725,6 +779,9 @@ void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr,
|
||||
lease->hwaddr_type = hw_type;
|
||||
lease->flags |= LEASE_CHANGED;
|
||||
file_dirty = 1; /* run script on change */
|
||||
#ifdef HAVE_DHCP6
|
||||
change = 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* only update clid when one is available, stops packets
|
||||
@@ -742,17 +799,27 @@ void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr,
|
||||
free(lease->clid);
|
||||
if (!(lease->clid = whine_malloc(clid_len)))
|
||||
return;
|
||||
#ifdef HAVE_DHCP6
|
||||
change = 1;
|
||||
#endif
|
||||
}
|
||||
else if (memcmp(lease->clid, clid, clid_len) != 0)
|
||||
{
|
||||
lease->flags |= LEASE_AUX_CHANGED;
|
||||
file_dirty = 1;
|
||||
#ifdef HAVE_DHCP6
|
||||
change = 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
lease->clid_len = clid_len;
|
||||
memcpy(lease->clid, clid, clid_len);
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
if (change)
|
||||
slaac_add_addrs(lease, now, force);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void kill_name(struct dhcp_lease *lease)
|
||||
@@ -870,13 +937,17 @@ void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth, char *do
|
||||
lease->flags |= LEASE_CHANGED; /* run script on change */
|
||||
}
|
||||
|
||||
void lease_set_interface(struct dhcp_lease *lease, int interface)
|
||||
void lease_set_interface(struct dhcp_lease *lease, int interface, time_t now)
|
||||
{
|
||||
if (lease->last_interface == interface)
|
||||
return;
|
||||
|
||||
lease->last_interface = interface;
|
||||
lease->flags |= LEASE_CHANGED;
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
slaac_add_addrs(lease, now, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
void rerun_scripts(void)
|
||||
@@ -919,6 +990,14 @@ int do_script_run(time_t now)
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef HAVE_DHCP6
|
||||
struct slaac_address *slaac, *tmp;
|
||||
for (slaac = lease->slaac_address; slaac; slaac = tmp)
|
||||
{
|
||||
tmp = slaac->next;
|
||||
free(slaac);
|
||||
}
|
||||
#endif
|
||||
kill_name(lease);
|
||||
#ifdef HAVE_SCRIPT
|
||||
queue_script(ACTION_DEL, lease, lease->old_hostname, now);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2013 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
|
||||
|
||||
196
src/netlink.c
196
src/netlink.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2013 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
|
||||
@@ -38,8 +38,8 @@
|
||||
static struct iovec iov;
|
||||
static u32 netlink_pid;
|
||||
|
||||
static void nl_err(struct nlmsghdr *h);
|
||||
static void nl_routechange(struct nlmsghdr *h);
|
||||
static int nl_async(struct nlmsghdr *h);
|
||||
static void nl_newaddress(time_t now);
|
||||
|
||||
void netlink_init(void)
|
||||
{
|
||||
@@ -49,10 +49,17 @@ void netlink_init(void)
|
||||
addr.nl_family = AF_NETLINK;
|
||||
addr.nl_pad = 0;
|
||||
addr.nl_pid = 0; /* autobind */
|
||||
#ifdef HAVE_IPV6
|
||||
addr.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE;
|
||||
#else
|
||||
addr.nl_groups = RTMGRP_IPV4_ROUTE;
|
||||
if (option_bool(OPT_CLEVERBIND))
|
||||
addr.nl_groups |= RTMGRP_IPV4_IFADDR;
|
||||
#ifdef HAVE_IPV6
|
||||
addr.nl_groups |= RTMGRP_IPV6_ROUTE;
|
||||
if (option_bool(OPT_CLEVERBIND))
|
||||
addr.nl_groups |= RTMGRP_IPV6_IFADDR;
|
||||
#endif
|
||||
#ifdef HAVE_DHCP6
|
||||
if (daemon->doing_ra || daemon->doing_dhcp6)
|
||||
addr.nl_groups |= RTMGRP_IPV6_IFADDR;
|
||||
#endif
|
||||
|
||||
/* May not be able to have permission to set multicast groups don't die in that case */
|
||||
@@ -136,7 +143,7 @@ int iface_enumerate(int family, void *parm, int (*callback)())
|
||||
struct nlmsghdr *h;
|
||||
ssize_t len;
|
||||
static unsigned int seq = 0;
|
||||
int callback_ok = 1;
|
||||
int callback_ok = 1, newaddr = 0;
|
||||
|
||||
struct {
|
||||
struct nlmsghdr nlh;
|
||||
@@ -182,12 +189,21 @@ int iface_enumerate(int family, void *parm, int (*callback)())
|
||||
}
|
||||
|
||||
for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len))
|
||||
if (h->nlmsg_seq != seq || h->nlmsg_pid != netlink_pid)
|
||||
nl_routechange(h); /* May be multicast arriving async */
|
||||
else if (h->nlmsg_type == NLMSG_ERROR)
|
||||
nl_err(h);
|
||||
if (h->nlmsg_seq != seq || h->nlmsg_pid != netlink_pid || h->nlmsg_type == NLMSG_ERROR)
|
||||
{
|
||||
/* May be multicast arriving async */
|
||||
if (nl_async(h))
|
||||
newaddr = 1;
|
||||
}
|
||||
else if (h->nlmsg_type == NLMSG_DONE)
|
||||
return callback_ok;
|
||||
{
|
||||
/* handle async new interface address arrivals, these have to be done
|
||||
after we complete as we're not re-entrant */
|
||||
if (newaddr)
|
||||
nl_newaddress(dnsmasq_time());
|
||||
|
||||
return callback_ok;
|
||||
}
|
||||
else if (h->nlmsg_type == RTM_NEWADDR && family != AF_UNSPEC && family != AF_LOCAL)
|
||||
{
|
||||
struct ifaddrmsg *ifa = NLMSG_DATA(h);
|
||||
@@ -199,7 +215,8 @@ int iface_enumerate(int family, void *parm, int (*callback)())
|
||||
if (ifa->ifa_family == AF_INET)
|
||||
{
|
||||
struct in_addr netmask, addr, broadcast;
|
||||
|
||||
char *label = NULL;
|
||||
|
||||
netmask.s_addr = htonl(0xffffffff << (32 - ifa->ifa_prefixlen));
|
||||
addr.s_addr = 0;
|
||||
broadcast.s_addr = 0;
|
||||
@@ -210,29 +227,46 @@ int iface_enumerate(int family, void *parm, int (*callback)())
|
||||
addr = *((struct in_addr *)(rta+1));
|
||||
else if (rta->rta_type == IFA_BROADCAST)
|
||||
broadcast = *((struct in_addr *)(rta+1));
|
||||
else if (rta->rta_type == IFA_LABEL)
|
||||
label = RTA_DATA(rta);
|
||||
|
||||
rta = RTA_NEXT(rta, len1);
|
||||
}
|
||||
|
||||
if (addr.s_addr && callback_ok)
|
||||
if (!((*callback)(addr, ifa->ifa_index, netmask, broadcast, parm)))
|
||||
if (!((*callback)(addr, ifa->ifa_index, label, netmask, broadcast, parm)))
|
||||
callback_ok = 0;
|
||||
}
|
||||
#ifdef HAVE_IPV6
|
||||
else if (ifa->ifa_family == AF_INET6)
|
||||
{
|
||||
struct in6_addr *addrp = NULL;
|
||||
u32 valid = 0, preferred = 0;
|
||||
int flags = 0;
|
||||
|
||||
while (RTA_OK(rta, len1))
|
||||
{
|
||||
if (rta->rta_type == IFA_ADDRESS)
|
||||
addrp = ((struct in6_addr *)(rta+1));
|
||||
|
||||
else if (rta->rta_type == IFA_CACHEINFO)
|
||||
{
|
||||
struct ifa_cacheinfo *ifc = (struct ifa_cacheinfo *)(rta+1);
|
||||
preferred = ifc->ifa_prefered;
|
||||
valid = ifc->ifa_valid;
|
||||
}
|
||||
rta = RTA_NEXT(rta, len1);
|
||||
}
|
||||
|
||||
if (ifa->ifa_flags & IFA_F_TENTATIVE)
|
||||
flags |= IFACE_TENTATIVE;
|
||||
|
||||
if (ifa->ifa_flags & IFA_F_DEPRECATED)
|
||||
flags |= IFACE_DEPRECATED;
|
||||
|
||||
if (addrp && callback_ok)
|
||||
if (!((*callback)(addrp, (int)(ifa->ifa_prefixlen), (int)(ifa->ifa_scope),
|
||||
(int)(ifa->ifa_index), (int)(ifa->ifa_flags & IFA_F_TENTATIVE), parm)))
|
||||
(int)(ifa->ifa_index), flags,
|
||||
(int) preferred, (int)valid, parm)))
|
||||
callback_ok = 0;
|
||||
}
|
||||
#endif
|
||||
@@ -291,11 +325,11 @@ int iface_enumerate(int family, void *parm, int (*callback)())
|
||||
}
|
||||
}
|
||||
|
||||
void netlink_multicast(void)
|
||||
void netlink_multicast(time_t now)
|
||||
{
|
||||
ssize_t len;
|
||||
struct nlmsghdr *h;
|
||||
int flags;
|
||||
int flags, newaddr = 0;
|
||||
|
||||
/* don't risk blocking reading netlink messages here. */
|
||||
if ((flags = fcntl(daemon->netlinkfd, F_GETFL)) == -1 ||
|
||||
@@ -303,70 +337,84 @@ void netlink_multicast(void)
|
||||
return;
|
||||
|
||||
if ((len = netlink_recv()) != -1)
|
||||
{
|
||||
for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len))
|
||||
if (h->nlmsg_type == NLMSG_ERROR)
|
||||
nl_err(h);
|
||||
else
|
||||
nl_routechange(h);
|
||||
}
|
||||
|
||||
/* restore non-blocking status */
|
||||
fcntl(daemon->netlinkfd, F_SETFL, flags);
|
||||
}
|
||||
|
||||
static void nl_err(struct nlmsghdr *h)
|
||||
{
|
||||
struct nlmsgerr *err = NLMSG_DATA(h);
|
||||
for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len))
|
||||
if (nl_async(h))
|
||||
newaddr = 1;
|
||||
|
||||
if (err->error != 0)
|
||||
my_syslog(LOG_ERR, _("netlink returns error: %s"), strerror(-(err->error)));
|
||||
/* restore non-blocking status */
|
||||
fcntl(daemon->netlinkfd, F_SETFL, flags);
|
||||
|
||||
if (newaddr)
|
||||
nl_newaddress(now);
|
||||
}
|
||||
|
||||
/* We arrange to receive netlink multicast messages whenever the network route is added.
|
||||
If this happens and we still have a DNS packet in the buffer, we re-send it.
|
||||
This helps on DoD links, where frequently the packet which triggers dialling is
|
||||
a DNS query, which then gets lost. By re-sending, we can avoid the lookup
|
||||
failing. Note that we only accept these messages from the kernel (pid == 0) */
|
||||
static void nl_routechange(struct nlmsghdr *h)
|
||||
static int nl_async(struct nlmsghdr *h)
|
||||
{
|
||||
if (h->nlmsg_pid == 0 && h->nlmsg_type == RTM_NEWROUTE)
|
||||
if (h->nlmsg_type == NLMSG_ERROR)
|
||||
{
|
||||
struct rtmsg *rtm = NLMSG_DATA(h);
|
||||
int fd;
|
||||
|
||||
if (rtm->rtm_type != RTN_UNICAST || rtm->rtm_scope != RT_SCOPE_LINK)
|
||||
return;
|
||||
|
||||
/* Force re-reading resolv file right now, for luck. */
|
||||
daemon->last_resolv = 0;
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
/* force RAs to sync new network and pick up new interfaces. */
|
||||
if (daemon->ra_contexts)
|
||||
{
|
||||
ra_start_unsolicted(dnsmasq_time());
|
||||
/* cause lease_update_file to run after we return, in case we were called from
|
||||
iface_enumerate and can't re-enter it now */
|
||||
send_alarm();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (daemon->srv_save)
|
||||
{
|
||||
if (daemon->srv_save->sfd)
|
||||
fd = daemon->srv_save->sfd->fd;
|
||||
else if (daemon->rfd_save && daemon->rfd_save->refcount != 0)
|
||||
fd = daemon->rfd_save->fd;
|
||||
else
|
||||
return;
|
||||
|
||||
while(sendto(fd, daemon->packet, daemon->packet_len, 0,
|
||||
&daemon->srv_save->addr.sa, sa_len(&daemon->srv_save->addr)) == -1 && retry_send());
|
||||
}
|
||||
struct nlmsgerr *err = NLMSG_DATA(h);
|
||||
if (err->error != 0)
|
||||
my_syslog(LOG_ERR, _("netlink returns error: %s"), strerror(-(err->error)));
|
||||
return 0;
|
||||
}
|
||||
else if (h->nlmsg_pid == 0 && h->nlmsg_type == RTM_NEWROUTE)
|
||||
{
|
||||
/* We arrange to receive netlink multicast messages whenever the network route is added.
|
||||
If this happens and we still have a DNS packet in the buffer, we re-send it.
|
||||
This helps on DoD links, where frequently the packet which triggers dialling is
|
||||
a DNS query, which then gets lost. By re-sending, we can avoid the lookup
|
||||
failing. */
|
||||
struct rtmsg *rtm = NLMSG_DATA(h);
|
||||
|
||||
if (rtm->rtm_type == RTN_UNICAST && rtm->rtm_scope == RT_SCOPE_LINK)
|
||||
{
|
||||
/* Force re-reading resolv file right now, for luck. */
|
||||
daemon->last_resolv = 0;
|
||||
|
||||
if (daemon->srv_save)
|
||||
{
|
||||
int fd;
|
||||
|
||||
if (daemon->srv_save->sfd)
|
||||
fd = daemon->srv_save->sfd->fd;
|
||||
else if (daemon->rfd_save && daemon->rfd_save->refcount != 0)
|
||||
fd = daemon->rfd_save->fd;
|
||||
else
|
||||
return 0;
|
||||
|
||||
while(sendto(fd, daemon->packet, daemon->packet_len, 0,
|
||||
&daemon->srv_save->addr.sa, sa_len(&daemon->srv_save->addr)) == -1 && retry_send());
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
else if (h->nlmsg_type == RTM_NEWADDR || h->nlmsg_type == RTM_DELADDR)
|
||||
return 1; /* clever bind mode - rescan */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nl_newaddress(time_t now)
|
||||
{
|
||||
if (option_bool(OPT_CLEVERBIND) || daemon->doing_dhcp6 || daemon->doing_ra)
|
||||
enumerate_interfaces();
|
||||
|
||||
if (option_bool(OPT_CLEVERBIND))
|
||||
create_bound_listeners(0);
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
if (daemon->doing_dhcp6 || daemon->doing_ra)
|
||||
{
|
||||
join_multicast(0);
|
||||
dhcp_construct_contexts(now);
|
||||
}
|
||||
|
||||
if (daemon->doing_dhcp6)
|
||||
lease_find_interfaces(now);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
422
src/network.c
422
src/network.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2013 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
|
||||
@@ -107,7 +107,7 @@ int indextoname(int fd, int index, char *name)
|
||||
|
||||
#endif
|
||||
|
||||
int iface_check(int family, struct all_addr *addr, char *name)
|
||||
int iface_check(int family, struct all_addr *addr, char *name, int *auth)
|
||||
{
|
||||
struct iname *tmp;
|
||||
int ret = 1;
|
||||
@@ -115,57 +115,127 @@ int iface_check(int family, struct all_addr *addr, char *name)
|
||||
/* Note: have to check all and not bail out early, so that we set the
|
||||
"used" flags. */
|
||||
|
||||
if (auth)
|
||||
*auth = 0;
|
||||
|
||||
if (daemon->if_names || daemon->if_addrs)
|
||||
{
|
||||
#ifdef HAVE_DHCP
|
||||
struct dhcp_context *range;
|
||||
#endif
|
||||
|
||||
ret = 0;
|
||||
|
||||
#ifdef HAVE_DHCP
|
||||
for (range = daemon->dhcp; range; range = range->next)
|
||||
if (range->interface && strcmp(range->interface, name) == 0)
|
||||
ret = 1;
|
||||
#endif
|
||||
|
||||
for (tmp = daemon->if_names; tmp; tmp = tmp->next)
|
||||
if (tmp->name && (strcmp(tmp->name, name) == 0))
|
||||
if (tmp->name && wildcard_match(tmp->name, name))
|
||||
ret = tmp->used = 1;
|
||||
|
||||
for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
|
||||
if (tmp->addr.sa.sa_family == family)
|
||||
{
|
||||
if (family == AF_INET &&
|
||||
tmp->addr.in.sin_addr.s_addr == addr->addr.addr4.s_addr)
|
||||
ret = tmp->used = 1;
|
||||
if (addr)
|
||||
for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
|
||||
if (tmp->addr.sa.sa_family == family)
|
||||
{
|
||||
if (family == AF_INET &&
|
||||
tmp->addr.in.sin_addr.s_addr == addr->addr.addr4.s_addr)
|
||||
ret = tmp->used = 1;
|
||||
#ifdef HAVE_IPV6
|
||||
else if (family == AF_INET6 &&
|
||||
IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr,
|
||||
&addr->addr.addr6))
|
||||
ret = tmp->used = 1;
|
||||
else if (family == AF_INET6 &&
|
||||
IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr,
|
||||
&addr->addr.addr6))
|
||||
ret = tmp->used = 1;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (tmp = daemon->if_except; tmp; tmp = tmp->next)
|
||||
if (tmp->name && (strcmp(tmp->name, name) == 0))
|
||||
if (tmp->name && wildcard_match(tmp->name, name))
|
||||
ret = 0;
|
||||
|
||||
|
||||
for (tmp = daemon->authinterface; tmp; tmp = tmp->next)
|
||||
if (tmp->name)
|
||||
{
|
||||
if (strcmp(tmp->name, name) == 0)
|
||||
break;
|
||||
}
|
||||
else if (addr && tmp->addr.sa.sa_family == AF_INET && family == AF_INET &&
|
||||
tmp->addr.in.sin_addr.s_addr == addr->addr.addr4.s_addr)
|
||||
break;
|
||||
#ifdef HAVE_IPV6
|
||||
else if (addr && tmp->addr.sa.sa_family == AF_INET6 && family == AF_INET6 &&
|
||||
IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, &addr->addr.addr6))
|
||||
break;
|
||||
#endif
|
||||
|
||||
if (tmp && auth)
|
||||
{
|
||||
*auth = 1;
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int iface_allowed(struct irec **irecp, int if_index,
|
||||
|
||||
|
||||
/* Fix for problem that the kernel sometimes reports the loopback inerface as the
|
||||
arrival interface when a packet originates locally, even when sent to address of
|
||||
an interface other than the loopback. Accept packet if it arrived via a loopback
|
||||
interface, even when we're not accepting packets that way, as long as the destination
|
||||
address is one we're believing. Interface list must be up-to-date before calling. */
|
||||
int loopback_exception(int fd, int family, struct all_addr *addr, char *name)
|
||||
{
|
||||
struct ifreq ifr;
|
||||
struct irec *iface;
|
||||
|
||||
strncpy(ifr.ifr_name, name, IF_NAMESIZE);
|
||||
if (ioctl(fd, SIOCGIFFLAGS, &ifr) != -1 &&
|
||||
ifr.ifr_flags & IFF_LOOPBACK)
|
||||
{
|
||||
for (iface = daemon->interfaces; iface; iface = iface->next)
|
||||
if (iface->addr.sa.sa_family == family)
|
||||
{
|
||||
if (family == AF_INET)
|
||||
{
|
||||
if (iface->addr.in.sin_addr.s_addr == addr->addr.addr4.s_addr)
|
||||
return 1;
|
||||
}
|
||||
#ifdef HAVE_IPV6
|
||||
else if (IN6_ARE_ADDR_EQUAL(&iface->addr.in6.sin6_addr, &addr->addr.addr6))
|
||||
return 1;
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If we're configured with something like --interface=eth0:0 then we'll listen correctly
|
||||
on the relevant address, but the name of the arrival interface, derived from the
|
||||
index won't match the config. Check that we found an interface address for the arrival
|
||||
interface: daemon->interfaces must be up-to-date. */
|
||||
int label_exception(int index, int family, struct all_addr *addr)
|
||||
{
|
||||
struct irec *iface;
|
||||
|
||||
/* labels only supported on IPv4 addresses. */
|
||||
if (family != AF_INET)
|
||||
return 0;
|
||||
|
||||
for (iface = daemon->interfaces; iface; iface = iface->next)
|
||||
if (iface->index == index && iface->addr.sa.sa_family == AF_INET &&
|
||||
iface->addr.in.sin_addr.s_addr == addr->addr.addr4.s_addr)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iface_allowed(struct irec **irecp, int if_index, char *label,
|
||||
union mysockaddr *addr, struct in_addr netmask, int dad)
|
||||
{
|
||||
struct irec *iface;
|
||||
int fd, mtu = 0, loopback;
|
||||
struct ifreq ifr;
|
||||
int tftp_ok = daemon->tftp_unlimited;
|
||||
int tftp_ok = !!option_bool(OPT_TFTP);
|
||||
int dhcp_ok = 1;
|
||||
int auth_dns = 0;
|
||||
#ifdef HAVE_DHCP
|
||||
struct iname *tmp;
|
||||
#endif
|
||||
struct interface_list *ir = NULL;
|
||||
|
||||
/* check whether the interface IP has been added already
|
||||
we call this routine multiple times. */
|
||||
@@ -190,7 +260,10 @@ static int iface_allowed(struct irec **irecp, int if_index,
|
||||
}
|
||||
|
||||
loopback = ifr.ifr_flags & IFF_LOOPBACK;
|
||||
|
||||
|
||||
if (loopback)
|
||||
dhcp_ok = 0;
|
||||
|
||||
if (ioctl(fd, SIOCGIFMTU, &ifr) != -1)
|
||||
mtu = ifr.ifr_mtu;
|
||||
|
||||
@@ -203,65 +276,72 @@ static int iface_allowed(struct irec **irecp, int if_index,
|
||||
struct iname *lo;
|
||||
for (lo = daemon->if_names; lo; lo = lo->next)
|
||||
if (lo->name && strcmp(lo->name, ifr.ifr_name) == 0)
|
||||
{
|
||||
lo->isloop = 1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
if (!lo &&
|
||||
(lo = whine_malloc(sizeof(struct iname))) &&
|
||||
(lo->name = whine_malloc(strlen(ifr.ifr_name)+1)))
|
||||
if (!lo && (lo = whine_malloc(sizeof(struct iname))))
|
||||
{
|
||||
strcpy(lo->name, ifr.ifr_name);
|
||||
lo->isloop = lo->used = 1;
|
||||
lo->next = daemon->if_names;
|
||||
daemon->if_names = lo;
|
||||
if ((lo->name = whine_malloc(strlen(ifr.ifr_name)+1)))
|
||||
{
|
||||
strcpy(lo->name, ifr.ifr_name);
|
||||
lo->used = 1;
|
||||
lo->next = daemon->if_names;
|
||||
daemon->if_names = lo;
|
||||
}
|
||||
else
|
||||
free(lo);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_TFTP
|
||||
/* implement wierd TFTP service rules */
|
||||
for (ir = daemon->tftp_interfaces; ir; ir = ir->next)
|
||||
if (strcmp(ir->interface, ifr.ifr_name) == 0)
|
||||
{
|
||||
tftp_ok = 1;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!ir)
|
||||
{
|
||||
if (addr->sa.sa_family == AF_INET &&
|
||||
!iface_check(AF_INET, (struct all_addr *)&addr->in.sin_addr, ifr.ifr_name))
|
||||
return 1;
|
||||
|
||||
#ifdef HAVE_DHCP
|
||||
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
|
||||
if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
|
||||
tftp_ok = 0;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_IPV6
|
||||
if (addr->sa.sa_family == AF_INET6 &&
|
||||
!iface_check(AF_INET6, (struct all_addr *)&addr->in6.sin6_addr, ifr.ifr_name))
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
if (!label)
|
||||
label = ifr.ifr_name;
|
||||
|
||||
if (addr->sa.sa_family == AF_INET &&
|
||||
!iface_check(AF_INET, (struct all_addr *)&addr->in.sin_addr, label, &auth_dns))
|
||||
return 1;
|
||||
|
||||
#ifdef HAVE_IPV6
|
||||
if (addr->sa.sa_family == AF_INET6 &&
|
||||
!iface_check(AF_INET6, (struct all_addr *)&addr->in6.sin6_addr, label, &auth_dns))
|
||||
return 1;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DHCP
|
||||
/* No DHCP where we're doing auth DNS. */
|
||||
if (auth_dns)
|
||||
{
|
||||
tftp_ok = 0;
|
||||
dhcp_ok = 0;
|
||||
}
|
||||
else
|
||||
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
|
||||
if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
|
||||
{
|
||||
tftp_ok = 0;
|
||||
dhcp_ok = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* add to list */
|
||||
if ((iface = whine_malloc(sizeof(struct irec))))
|
||||
{
|
||||
iface->addr = *addr;
|
||||
iface->netmask = netmask;
|
||||
iface->tftp_ok = tftp_ok;
|
||||
iface->dhcp_ok = dhcp_ok;
|
||||
iface->dns_auth = auth_dns;
|
||||
iface->mtu = mtu;
|
||||
iface->dad = dad;
|
||||
iface->done = 0;
|
||||
iface->done = iface->multicast_done = 0;
|
||||
iface->index = if_index;
|
||||
if ((iface->name = whine_malloc(strlen(ifr.ifr_name)+1)))
|
||||
strcpy(iface->name, ifr.ifr_name);
|
||||
iface->next = *irecp;
|
||||
*irecp = iface;
|
||||
return 1;
|
||||
{
|
||||
strcpy(iface->name, ifr.ifr_name);
|
||||
iface->next = *irecp;
|
||||
*irecp = iface;
|
||||
return 1;
|
||||
}
|
||||
free(iface);
|
||||
|
||||
}
|
||||
|
||||
errno = ENOMEM;
|
||||
@@ -270,7 +350,8 @@ static int iface_allowed(struct irec **irecp, int if_index,
|
||||
|
||||
#ifdef HAVE_IPV6
|
||||
static int iface_allowed_v6(struct in6_addr *local, int prefix,
|
||||
int scope, int if_index, int dad, void *vparam)
|
||||
int scope, int if_index, int flags,
|
||||
int preferred, int valid, void *vparam)
|
||||
{
|
||||
union mysockaddr addr;
|
||||
struct in_addr netmask; /* dummy */
|
||||
@@ -278,6 +359,8 @@ static int iface_allowed_v6(struct in6_addr *local, int prefix,
|
||||
|
||||
(void)prefix; /* warning */
|
||||
(void)scope; /* warning */
|
||||
(void)preferred;
|
||||
(void)valid;
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
@@ -288,11 +371,11 @@ static int iface_allowed_v6(struct in6_addr *local, int prefix,
|
||||
addr.in6.sin6_port = htons(daemon->port);
|
||||
addr.in6.sin6_scope_id = if_index;
|
||||
|
||||
return iface_allowed((struct irec **)vparam, if_index, &addr, netmask, dad);
|
||||
return iface_allowed((struct irec **)vparam, if_index, NULL, &addr, netmask, !!(flags & IFACE_TENTATIVE));
|
||||
}
|
||||
#endif
|
||||
|
||||
static int iface_allowed_v4(struct in_addr local, int if_index,
|
||||
static int iface_allowed_v4(struct in_addr local, int if_index, char *label,
|
||||
struct in_addr netmask, struct in_addr broadcast, void *vparam)
|
||||
{
|
||||
union mysockaddr addr;
|
||||
@@ -306,7 +389,7 @@ static int iface_allowed_v4(struct in_addr local, int if_index,
|
||||
addr.in.sin_addr = local;
|
||||
addr.in.sin_port = htons(daemon->port);
|
||||
|
||||
return iface_allowed((struct irec **)vparam, if_index, &addr, netmask, 0);
|
||||
return iface_allowed((struct irec **)vparam, if_index, label, &addr, netmask, 0);
|
||||
}
|
||||
|
||||
int enumerate_interfaces(void)
|
||||
@@ -339,6 +422,7 @@ static int make_sock(union mysockaddr *addr, int type, int dienow)
|
||||
if ((fd = socket(family, type, 0)) == -1)
|
||||
{
|
||||
int port;
|
||||
char *s;
|
||||
|
||||
/* No error if the kernel just doesn't support this IP flavour */
|
||||
if (errno == EPROTONOSUPPORT ||
|
||||
@@ -347,18 +431,27 @@ static int make_sock(union mysockaddr *addr, int type, int dienow)
|
||||
return -1;
|
||||
|
||||
err:
|
||||
port = prettyprint_addr(addr, daemon->addrbuff);
|
||||
if (!option_bool(OPT_NOWILD) && !option_bool(OPT_CLEVERBIND))
|
||||
sprintf(daemon->addrbuff, "port %d", port);
|
||||
s = _("failed to create listening socket for %s: %s");
|
||||
|
||||
if (fd != -1)
|
||||
close (fd);
|
||||
|
||||
if (dienow)
|
||||
{
|
||||
port = prettyprint_addr(addr, daemon->namebuff);
|
||||
if (!option_bool(OPT_NOWILD))
|
||||
sprintf(daemon->namebuff, "port %d", port);
|
||||
die(_("failed to create listening socket for %s: %s"),
|
||||
daemon->namebuff, EC_BADNET);
|
||||
|
||||
/* failure to bind addresses given by --listen-address at this point
|
||||
is OK if we're doing bind-dynamic */
|
||||
if (!option_bool(OPT_CLEVERBIND))
|
||||
die(s, daemon->addrbuff, EC_BADNET);
|
||||
}
|
||||
else
|
||||
my_syslog(LOG_WARNING, s, daemon->addrbuff, strerror(errno));
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 || !fix_fd(fd))
|
||||
goto err;
|
||||
|
||||
@@ -425,6 +518,77 @@ int set_ipv6pktinfo(int fd)
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* Find the interface on which a TCP connection arrived, if possible, or zero otherwise. */
|
||||
int tcp_interface(int fd, int af)
|
||||
{
|
||||
int if_index = 0;
|
||||
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
int opt = 1;
|
||||
struct cmsghdr *cmptr;
|
||||
struct msghdr msg;
|
||||
|
||||
/* use mshdr do that the CMSDG_* macros are available */
|
||||
msg.msg_control = daemon->packet;
|
||||
msg.msg_controllen = daemon->packet_buff_sz;
|
||||
|
||||
/* we overwrote the buffer... */
|
||||
daemon->srv_save = NULL;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
#ifdef HAVE_IPV6
|
||||
else
|
||||
{
|
||||
/* Only the RFC-2292 API has the ability to find the interface for TCP connections,
|
||||
it was removed in RFC-3542 !!!!
|
||||
|
||||
Fortunately, Linux kept the 2292 ABI when it moved to 3542. The following code always
|
||||
uses the old ABI, and should work with pre- and post-3542 kernel headers */
|
||||
|
||||
#ifdef IPV6_2292PKTOPTIONS
|
||||
# define PKTOPTIONS IPV6_2292PKTOPTIONS
|
||||
#else
|
||||
# define PKTOPTIONS IPV6_PKTOPTIONS
|
||||
#endif
|
||||
|
||||
if (set_ipv6pktinfo(fd) &&
|
||||
getsockopt(fd, IPPROTO_IPV6, 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_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
|
||||
{
|
||||
union {
|
||||
unsigned char *c;
|
||||
struct in6_pktinfo *p;
|
||||
} p;
|
||||
p.c = CMSG_DATA(cmptr);
|
||||
|
||||
if_index = p.p->ipi6_ifindex;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* IPV6 */
|
||||
#endif /* Linux */
|
||||
|
||||
return if_index;
|
||||
}
|
||||
|
||||
static struct listener *create_listeners(union mysockaddr *addr, int do_tftp, int dienow)
|
||||
{
|
||||
@@ -476,8 +640,7 @@ static struct listener *create_listeners(union mysockaddr *addr, int do_tftp, in
|
||||
void create_wildcard_listeners(void)
|
||||
{
|
||||
union mysockaddr addr;
|
||||
struct listener *l;
|
||||
int tftp_enabled = daemon->tftp_unlimited || daemon->tftp_interfaces;
|
||||
struct listener *l, *l6;
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
@@ -487,7 +650,7 @@ void create_wildcard_listeners(void)
|
||||
addr.in.sin_addr.s_addr = INADDR_ANY;
|
||||
addr.in.sin_port = htons(daemon->port);
|
||||
|
||||
l = create_listeners(&addr, tftp_enabled, 1);
|
||||
l = create_listeners(&addr, !!option_bool(OPT_TFTP), 1);
|
||||
|
||||
#ifdef HAVE_IPV6
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
@@ -497,11 +660,12 @@ void create_wildcard_listeners(void)
|
||||
addr.in6.sin6_family = AF_INET6;
|
||||
addr.in6.sin6_addr = in6addr_any;
|
||||
addr.in6.sin6_port = htons(daemon->port);
|
||||
|
||||
|
||||
l6 = create_listeners(&addr, !!option_bool(OPT_TFTP), 1);
|
||||
if (l)
|
||||
l->next = create_listeners(&addr, tftp_enabled, 1);
|
||||
l->next = l6;
|
||||
else
|
||||
l = create_listeners(&addr, tftp_enabled, 1);
|
||||
l = l6;
|
||||
#endif
|
||||
|
||||
daemon->listeners = l;
|
||||
@@ -511,6 +675,7 @@ void create_bound_listeners(int dienow)
|
||||
{
|
||||
struct listener *new;
|
||||
struct irec *iface;
|
||||
struct iname *if_tmp;
|
||||
|
||||
for (iface = daemon->interfaces; iface; iface = iface->next)
|
||||
if (!iface->done && !iface->dad &&
|
||||
@@ -521,6 +686,26 @@ void create_bound_listeners(int dienow)
|
||||
daemon->listeners = new;
|
||||
iface->done = 1;
|
||||
}
|
||||
|
||||
/* Check for --listen-address options that haven't been used because there's
|
||||
no interface with a matching address. These may be valid: eg it's possible
|
||||
to listen on 127.0.1.1 even if the loopback interface is 127.0.0.1
|
||||
|
||||
If the address isn't valid the bind() will fail and we'll die()
|
||||
(except in bind-dynamic mode, when we'll complain but keep trying.)
|
||||
|
||||
The resulting listeners have the ->iface field NULL, and this has to be
|
||||
handled by the DNS and TFTP code. It disables --localise-queries processing
|
||||
(no netmask) and some MTU login the tftp code. */
|
||||
|
||||
for (if_tmp = daemon->if_addrs; if_tmp; if_tmp = if_tmp->next)
|
||||
if (!if_tmp->used &&
|
||||
(new = create_listeners(&if_tmp->addr, !!option_bool(OPT_TFTP), dienow)))
|
||||
{
|
||||
new->iface = NULL;
|
||||
new->next = daemon->listeners;
|
||||
daemon->listeners = new;
|
||||
}
|
||||
}
|
||||
|
||||
int is_dad_listeners(void)
|
||||
@@ -534,6 +719,61 @@ int is_dad_listeners(void)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
void join_multicast(int dienow)
|
||||
{
|
||||
struct irec *iface, *tmp;
|
||||
|
||||
for (iface = daemon->interfaces; iface; iface = iface->next)
|
||||
if (iface->addr.sa.sa_family == AF_INET6 && iface->dhcp_ok && !iface->multicast_done)
|
||||
{
|
||||
/* There's an irec per address but we only want to join for multicast
|
||||
once per interface. Weed out duplicates. */
|
||||
for (tmp = daemon->interfaces; tmp; tmp = tmp->next)
|
||||
if (tmp->multicast_done && tmp->index == iface->index)
|
||||
break;
|
||||
|
||||
iface->multicast_done = 1;
|
||||
|
||||
if (!tmp)
|
||||
{
|
||||
struct ipv6_mreq mreq;
|
||||
int err = 0;
|
||||
|
||||
mreq.ipv6mr_interface = iface->index;
|
||||
|
||||
inet_pton(AF_INET6, ALL_RELAY_AGENTS_AND_SERVERS, &mreq.ipv6mr_multiaddr);
|
||||
|
||||
if (daemon->doing_dhcp6 &&
|
||||
setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
|
||||
err = 1;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
if (err)
|
||||
{
|
||||
char *s = _("interface %s failed to join DHCPv6 multicast group: %s");
|
||||
if (dienow)
|
||||
die(s, iface->name, EC_BADNET);
|
||||
else
|
||||
my_syslog(LOG_ERR, s, iface->name, strerror(errno));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* return a UDP socket bound to a random port, have to cope with straying into
|
||||
occupied port nos and reserved ones. */
|
||||
int random_sock(int family)
|
||||
|
||||
1763
src/option.c
1763
src/option.c
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2013 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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2013 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,9 +14,16 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define ALL_HOSTS "FF02::1"
|
||||
#define ALL_NODES "FF02::1"
|
||||
#define ALL_ROUTERS "FF02::2"
|
||||
|
||||
struct ping_packet {
|
||||
u8 type, code;
|
||||
u16 checksum;
|
||||
u16 identifier;
|
||||
u16 sequence_no;
|
||||
};
|
||||
|
||||
struct ra_packet {
|
||||
u8 type, code;
|
||||
u16 checksum;
|
||||
@@ -32,13 +39,11 @@ struct prefix_opt {
|
||||
struct in6_addr prefix;
|
||||
};
|
||||
|
||||
#define ICMP6_ROUTER_SOLICIT 133
|
||||
#define ICMP6_ROUTER_ADVERT 134
|
||||
|
||||
#define ICMP6_OPT_SOURCE_MAC 1
|
||||
#define ICMP6_OPT_PREFIX 3
|
||||
#define ICMP6_OPT_MTU 5
|
||||
#define ICMP6_OPT_RDNSS 25
|
||||
#define ICMP6_OPT_DNSSL 31
|
||||
|
||||
|
||||
|
||||
|
||||
537
src/radv.c
537
src/radv.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2013 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,45 +27,56 @@
|
||||
#include <netinet/icmp6.h>
|
||||
|
||||
struct ra_param {
|
||||
int ind, managed, found_context, first;
|
||||
time_t now;
|
||||
int ind, managed, other, found_context, first;
|
||||
char *if_name;
|
||||
struct in6_addr link_local;
|
||||
struct dhcp_netid *tags;
|
||||
struct in6_addr link_local, link_global;
|
||||
unsigned int pref_time;
|
||||
};
|
||||
|
||||
struct search_param {
|
||||
time_t now; int iface;
|
||||
};
|
||||
|
||||
static void send_ra(int iface, char *iface_name, struct in6_addr *dest);
|
||||
static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *dest);
|
||||
static int add_prefixes(struct in6_addr *local, int prefix,
|
||||
int scope, int if_index, int dad, void *vparam);
|
||||
int scope, int if_index, int flags,
|
||||
unsigned int preferred, unsigned int valid, void *vparam);
|
||||
static int iface_search(struct in6_addr *local, int prefix,
|
||||
int scope, int if_index, int dad, void *vparam);
|
||||
int scope, int if_index, int flags,
|
||||
int prefered, int valid, void *vparam);
|
||||
static int add_lla(int index, unsigned int type, char *mac, size_t maclen, void *parm);
|
||||
|
||||
static int hop_limit;
|
||||
static time_t ra_short_period_start;
|
||||
|
||||
void ra_init(time_t now)
|
||||
{
|
||||
struct icmp6_filter filter;
|
||||
int fd;
|
||||
#if defined(IP_TOS) && defined(IPTOS_CLASS_CS6)
|
||||
#if defined(IPV6_TCLASS) && defined(IPTOS_CLASS_CS6)
|
||||
int class = IPTOS_CLASS_CS6;
|
||||
#endif
|
||||
int val = 255; /* radvd uses this value */
|
||||
socklen_t len = sizeof(int);
|
||||
|
||||
struct dhcp_context *context;
|
||||
|
||||
/* ensure this is around even if we're not doing DHCPv6 */
|
||||
expand_buf(&daemon->outpacket, sizeof(struct dhcp_packet));
|
||||
|
||||
|
||||
/* See if we're guessing SLAAC addresses, if so we need to recieve ping replies */
|
||||
for (context = daemon->dhcp6; context; context = context->next)
|
||||
if ((context->flags & CONTEXT_RA_NAME))
|
||||
break;
|
||||
|
||||
ICMP6_FILTER_SETBLOCKALL(&filter);
|
||||
ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filter);
|
||||
ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter);
|
||||
if (context)
|
||||
ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filter);
|
||||
|
||||
if ((fd = socket(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) == -1 ||
|
||||
getsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hop_limit, &len) ||
|
||||
#if defined(IP_TOS) && defined(IPTOS_CLASS_CS6)
|
||||
#if defined(IPV6_TCLASS) && defined(IPTOS_CLASS_CS6)
|
||||
setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &class, sizeof(class)) == -1 ||
|
||||
#endif
|
||||
!fix_fd(fd) ||
|
||||
@@ -77,27 +88,29 @@ void ra_init(time_t now)
|
||||
|
||||
daemon->icmp6fd = fd;
|
||||
|
||||
ra_start_unsolicted(now);
|
||||
ra_start_unsolicted(now, NULL);
|
||||
}
|
||||
|
||||
void ra_start_unsolicted(time_t now)
|
||||
void ra_start_unsolicted(time_t now, struct dhcp_context *context)
|
||||
{
|
||||
struct dhcp_context *context;
|
||||
|
||||
/* init timers so that we do ra's for all soon. some ra_times will end up zeroed
|
||||
/* init timers so that we do ra's for some/all soon. some ra_times will end up zeroed
|
||||
if it's not appropriate to advertise those contexts.
|
||||
This gets re-called on a netlink route-change to re-do the advertisement
|
||||
and pick up new interfaces */
|
||||
|
||||
/* range 0 - 5 */
|
||||
for (context = daemon->ra_contexts; context; context = context->next)
|
||||
context->ra_time = now + (rand16()/13000);
|
||||
|
||||
/* re-do frequently for a minute or so, in case the first gets lost. */
|
||||
ra_short_period_start = now;
|
||||
|
||||
if (context)
|
||||
context->ra_short_period_start = context->ra_time = now;
|
||||
else
|
||||
for (context = daemon->dhcp6; context; context = context->next)
|
||||
if (!(context->flags & CONTEXT_TEMPLATE))
|
||||
{
|
||||
context->ra_time = now + (rand16()/13000); /* range 0 - 5 */
|
||||
/* re-do frequently for a minute or so, in case the first gets lost. */
|
||||
context->ra_short_period_start = now;
|
||||
}
|
||||
}
|
||||
|
||||
void icmp6_packet(void)
|
||||
void icmp6_packet(time_t now)
|
||||
{
|
||||
char interface[IF_NAMESIZE+1];
|
||||
ssize_t sz;
|
||||
@@ -109,10 +122,8 @@ void icmp6_packet(void)
|
||||
char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
|
||||
} control_u;
|
||||
struct sockaddr_in6 from;
|
||||
unsigned char *p;
|
||||
char *mac = "";
|
||||
unsigned char *packet;
|
||||
struct iname *tmp;
|
||||
struct dhcp_context *context;
|
||||
|
||||
/* Note: use outpacket for input buffer */
|
||||
msg.msg_control = control_u.control6;
|
||||
@@ -125,6 +136,8 @@ void icmp6_packet(void)
|
||||
|
||||
if ((sz = recv_dhcp_packet(daemon->icmp6fd, &msg)) == -1 || sz < 8)
|
||||
return;
|
||||
|
||||
packet = (unsigned char *)daemon->outpacket.iov_base;
|
||||
|
||||
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
|
||||
if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
|
||||
@@ -141,94 +154,168 @@ void icmp6_packet(void)
|
||||
if (!indextoname(daemon->icmp6fd, if_index, interface))
|
||||
return;
|
||||
|
||||
if (!iface_check(AF_LOCAL, NULL, interface))
|
||||
if (!iface_check(AF_LOCAL, NULL, interface, NULL))
|
||||
return;
|
||||
|
||||
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
|
||||
if (tmp->name && (strcmp(tmp->name, interface) == 0))
|
||||
if (tmp->name && wildcard_match(tmp->name, interface))
|
||||
return;
|
||||
|
||||
/* weird libvirt-inspired access control */
|
||||
for (context = daemon->dhcp6; context; context = context->next)
|
||||
if (!context->interface || strcmp(context->interface, interface) == 0)
|
||||
break;
|
||||
|
||||
if (!context)
|
||||
return;
|
||||
|
||||
p = (unsigned char *)daemon->outpacket.iov_base;
|
||||
|
||||
if (p[0] != ICMP6_ROUTER_SOLICIT || p[1] != 0)
|
||||
if (packet[1] != 0)
|
||||
return;
|
||||
|
||||
/* look for link-layer address option for logging */
|
||||
if (sz >= 16 && p[8] == ICMP6_OPT_SOURCE_MAC && (p[9] * 8) + 8 <= sz)
|
||||
if (packet[0] == ICMP6_ECHO_REPLY)
|
||||
lease_ping_reply(&from.sin6_addr, packet, interface);
|
||||
else if (packet[0] == ND_ROUTER_SOLICIT)
|
||||
{
|
||||
print_mac(daemon->namebuff, &p[10], (p[9] * 8) - 2);
|
||||
mac = daemon->namebuff;
|
||||
char *mac = "";
|
||||
|
||||
/* look for link-layer address option for logging */
|
||||
if (sz >= 16 && packet[8] == ICMP6_OPT_SOURCE_MAC && (packet[9] * 8) + 8 <= sz)
|
||||
{
|
||||
print_mac(daemon->namebuff, &packet[10], (packet[9] * 8) - 2);
|
||||
mac = daemon->namebuff;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
my_syslog(MS_DHCP | LOG_INFO, "RTR-SOLICIT(%s) %s", interface, mac);
|
||||
|
||||
send_ra(if_index, interface, &from.sin6_addr);
|
||||
}
|
||||
|
||||
static void send_ra(int iface, char *iface_name, struct in6_addr *dest)
|
||||
static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *dest)
|
||||
{
|
||||
struct ra_packet *ra;
|
||||
struct ra_param parm;
|
||||
struct ifreq ifr;
|
||||
struct sockaddr_in6 addr;
|
||||
struct dhcp_context *context;
|
||||
|
||||
struct dhcp_netid iface_id;
|
||||
struct dhcp_opt *opt_cfg;
|
||||
int done_dns = 0;
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
FILE *f;
|
||||
#endif
|
||||
|
||||
save_counter(0);
|
||||
ra = expand(sizeof(struct ra_packet));
|
||||
|
||||
ra->type = ICMP6_ROUTER_ADVERT;
|
||||
ra->type = ND_ROUTER_ADVERT;
|
||||
ra->code = 0;
|
||||
ra->hop_limit = hop_limit;
|
||||
ra->flags = 0;
|
||||
ra->lifetime = htons(1800); /* AdvDefaultLifetime*/
|
||||
ra->flags = 0x00;
|
||||
ra->lifetime = htons(RA_INTERVAL * 3); /* AdvDefaultLifetime * 3 */
|
||||
ra->reachable_time = 0;
|
||||
ra->retrans_time = 0;
|
||||
|
||||
parm.ind = iface;
|
||||
parm.managed = 0;
|
||||
parm.other = 0;
|
||||
parm.found_context = 0;
|
||||
parm.if_name = iface_name;
|
||||
parm.first = 1;
|
||||
|
||||
for (context = daemon->ra_contexts; context; context = context->next)
|
||||
context->flags &= ~CONTEXT_RA_DONE;
|
||||
parm.now = now;
|
||||
parm.pref_time = 0;
|
||||
|
||||
/* set tag with name == interface */
|
||||
iface_id.net = iface_name;
|
||||
iface_id.next = NULL;
|
||||
parm.tags = &iface_id;
|
||||
|
||||
for (context = daemon->dhcp6; context; context = context->next)
|
||||
{
|
||||
context->flags &= ~CONTEXT_RA_DONE;
|
||||
context->netid.next = &context->netid;
|
||||
}
|
||||
|
||||
if (!iface_enumerate(AF_INET6, &parm, add_prefixes) ||
|
||||
!parm.found_context)
|
||||
return;
|
||||
|
||||
strncpy(ifr.ifr_name, iface_name, IF_NAMESIZE);
|
||||
|
||||
if (ioctl(daemon->icmp6fd, SIOCGIFMTU, &ifr) != -1)
|
||||
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
/* Note that IPv6 MTU is not necessarilly the same as the IPv4 MTU
|
||||
available from SIOCGIFMTU */
|
||||
sprintf(daemon->namebuff, "/proc/sys/net/ipv6/conf/%s/mtu", iface_name);
|
||||
if ((f = fopen(daemon->namebuff, "r")))
|
||||
{
|
||||
put_opt6_char(ICMP6_OPT_MTU);
|
||||
put_opt6_char(1);
|
||||
put_opt6_short(0);
|
||||
put_opt6_long(ifr.ifr_mtu);
|
||||
if (fgets(daemon->namebuff, MAXDNAME, f))
|
||||
{
|
||||
put_opt6_char(ICMP6_OPT_MTU);
|
||||
put_opt6_char(1);
|
||||
put_opt6_short(0);
|
||||
put_opt6_long(atoi(daemon->namebuff));
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
#endif
|
||||
|
||||
iface_enumerate(AF_LOCAL, &iface, add_lla);
|
||||
|
||||
/* RDNSS, RFC 6106, use relevant DHCP6 options */
|
||||
(void)option_filter(parm.tags, NULL, daemon->dhcp_opts6);
|
||||
|
||||
/* RDNSS, RFC 6106 */
|
||||
put_opt6_char(ICMP6_OPT_RDNSS);
|
||||
put_opt6_char(3);
|
||||
put_opt6_short(0);
|
||||
put_opt6_long(1800); /* lifetime - twice RA retransmit */
|
||||
put_opt6(&parm.link_local, IN6ADDRSZ);
|
||||
for (opt_cfg = daemon->dhcp_opts6; opt_cfg; opt_cfg = opt_cfg->next)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* netids match and not encapsulated? */
|
||||
if (!(opt_cfg->flags & DHOPT_TAGOK))
|
||||
continue;
|
||||
|
||||
if (opt_cfg->opt == OPTION6_DNS_SERVER)
|
||||
{
|
||||
struct in6_addr *a = (struct in6_addr *)opt_cfg->val;
|
||||
|
||||
done_dns = 1;
|
||||
if (opt_cfg->len == 0)
|
||||
continue;
|
||||
|
||||
put_opt6_char(ICMP6_OPT_RDNSS);
|
||||
put_opt6_char((opt_cfg->len/8) + 1);
|
||||
put_opt6_short(0);
|
||||
put_opt6_long(RA_INTERVAL * 2); /* lifetime - twice RA retransmit */
|
||||
/* zero means "self" */
|
||||
for (i = 0; i < opt_cfg->len; i += IN6ADDRSZ, a++)
|
||||
if (IN6_IS_ADDR_UNSPECIFIED(a))
|
||||
put_opt6(&parm.link_global, IN6ADDRSZ);
|
||||
else
|
||||
put_opt6(a, IN6ADDRSZ);
|
||||
}
|
||||
|
||||
if (opt_cfg->opt == OPTION6_DOMAIN_SEARCH && opt_cfg->len != 0)
|
||||
{
|
||||
int len = ((opt_cfg->len+7)/8);
|
||||
|
||||
put_opt6_char(ICMP6_OPT_DNSSL);
|
||||
put_opt6_char(len + 1);
|
||||
put_opt6_short(0);
|
||||
put_opt6_long(1800); /* lifetime - twice RA retransmit */
|
||||
put_opt6(opt_cfg->val, opt_cfg->len);
|
||||
|
||||
/* pad */
|
||||
for (i = opt_cfg->len; i < len * 8; i++)
|
||||
put_opt6_char(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (daemon->port == NAMESERVER_PORT && !done_dns)
|
||||
{
|
||||
/* default == us, as long as we are supplying DNS service. */
|
||||
put_opt6_char(ICMP6_OPT_RDNSS);
|
||||
put_opt6_char(3);
|
||||
put_opt6_short(0);
|
||||
put_opt6_long(RA_INTERVAL * 2); /* lifetime - twice RA retransmit */
|
||||
put_opt6(&parm.link_global, IN6ADDRSZ);
|
||||
}
|
||||
|
||||
/* set managed bits unless we're providing only RA on this link */
|
||||
if (parm.managed)
|
||||
ra->flags = 0xc0;
|
||||
|
||||
ra->flags |= 0x80; /* M flag, managed, */
|
||||
if (parm.other)
|
||||
ra->flags |= 0x40; /* O flag, other */
|
||||
|
||||
/* decide where we're sending */
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
@@ -238,13 +325,13 @@ static void send_ra(int iface, char *iface_name, struct in6_addr *dest)
|
||||
addr.sin6_port = htons(IPPROTO_ICMPV6);
|
||||
if (dest)
|
||||
{
|
||||
memcpy(&addr.sin6_addr, dest, sizeof(struct in6_addr));
|
||||
addr.sin6_addr = *dest;
|
||||
if (IN6_IS_ADDR_LINKLOCAL(dest) ||
|
||||
IN6_IS_ADDR_MC_LINKLOCAL(dest))
|
||||
addr.sin6_scope_id = iface;
|
||||
}
|
||||
else
|
||||
inet_pton(AF_INET6, ALL_HOSTS, &addr.sin6_addr);
|
||||
inet_pton(AF_INET6, ALL_NODES, &addr.sin6_addr);
|
||||
|
||||
send_from(daemon->icmp6fd, 0, daemon->outpacket.iov_base, save_counter(0),
|
||||
(union mysockaddr *)&addr, (struct all_addr *)&parm.link_local, iface);
|
||||
@@ -252,81 +339,134 @@ static void send_ra(int iface, char *iface_name, struct in6_addr *dest)
|
||||
}
|
||||
|
||||
static int add_prefixes(struct in6_addr *local, int prefix,
|
||||
int scope, int if_index, int dad, void *vparam)
|
||||
int scope, int if_index, int flags,
|
||||
unsigned int preferred, unsigned int valid, void *vparam)
|
||||
{
|
||||
struct dhcp_context *context, *tmp;
|
||||
struct ra_param *param = vparam;
|
||||
struct prefix_opt *opt;
|
||||
|
||||
(void)scope; /* warning */
|
||||
(void)dad;
|
||||
|
||||
|
||||
if (if_index == param->ind)
|
||||
{
|
||||
if (IN6_IS_ADDR_LINKLOCAL(local))
|
||||
param->link_local = *local;
|
||||
else if (!IN6_IS_ADDR_LOOPBACK(local) &&
|
||||
!IN6_IS_ADDR_LINKLOCAL(local) &&
|
||||
!IN6_IS_ADDR_MULTICAST(local))
|
||||
{
|
||||
for (context = daemon->ra_contexts; context; context = context->next)
|
||||
if (prefix == context->prefix &&
|
||||
int do_prefix = 0;
|
||||
int do_slaac = 0;
|
||||
int deprecate = 0;
|
||||
int constructed = 0;
|
||||
unsigned int time = 0xffffffff;
|
||||
struct dhcp_context *context;
|
||||
|
||||
for (context = daemon->dhcp6; context; context = context->next)
|
||||
if (!(context->flags & CONTEXT_TEMPLATE) &&
|
||||
prefix == context->prefix &&
|
||||
is_same_net6(local, &context->start6, prefix) &&
|
||||
is_same_net6(local, &context->end6, prefix))
|
||||
{
|
||||
if (!(context->flags & CONTEXT_RA_ONLY))
|
||||
if ((context->flags &
|
||||
(CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS)))
|
||||
{
|
||||
do_slaac = 1;
|
||||
if (context->flags & CONTEXT_DHCP)
|
||||
{
|
||||
param->other = 1;
|
||||
if (!(context->flags & CONTEXT_RA_STATELESS))
|
||||
param->managed = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* don't do RA for non-ra-only unless --enable-ra is set */
|
||||
if (!option_bool(OPT_RA))
|
||||
continue;
|
||||
param->managed = 1;
|
||||
param->other = 1;
|
||||
}
|
||||
|
||||
if (context->flags & CONTEXT_RA_DONE)
|
||||
continue;
|
||||
|
||||
/* subsequent prefixes on the same interface don't need timers */
|
||||
if (!param->first)
|
||||
context->ra_time = 0;
|
||||
param->first = 0;
|
||||
param->found_context = 1;
|
||||
context->flags |= CONTEXT_RA_DONE;
|
||||
|
||||
/* mark this subnet and duplicates: as done. */
|
||||
for (tmp = context->next; tmp; tmp = tmp->next)
|
||||
if (tmp->prefix == prefix &&
|
||||
is_same_net6(local, &tmp->start6, prefix) &&
|
||||
is_same_net6(local, &tmp->end6, prefix))
|
||||
{
|
||||
tmp->flags |= CONTEXT_RA_DONE;
|
||||
context->ra_time = 0;
|
||||
}
|
||||
|
||||
if ((opt = expand(sizeof(struct prefix_opt))))
|
||||
/* find floor time, don't reduce below RA interval. */
|
||||
if (time > context->lease_time)
|
||||
{
|
||||
u64 addrpart = addr6part(&context->start6);
|
||||
u64 mask = (prefix == 64) ? (u64)-1LL : (1LLU << (128 - prefix)) - 1LLU;
|
||||
unsigned int time = context->lease_time;
|
||||
|
||||
/* lifetimes must be min 2 hrs, by RFC 2462 */
|
||||
if (time < 7200)
|
||||
time = 7200;
|
||||
|
||||
opt->type = ICMP6_OPT_PREFIX;
|
||||
opt->len = 4;
|
||||
opt->prefix_len = prefix;
|
||||
/* autonomous only is we're not doing dhcp */
|
||||
opt->flags = (context->flags & CONTEXT_RA_ONLY) ? 0xc0 : 0x00;
|
||||
opt->valid_lifetime = opt->preferred_lifetime = htonl(time);
|
||||
opt->reserved = 0;
|
||||
|
||||
opt->prefix = context->start6;
|
||||
setaddr6part(&opt->prefix, addrpart & ~mask);
|
||||
|
||||
inet_ntop(AF_INET6, &opt->prefix, daemon->addrbuff, ADDRSTRLEN);
|
||||
my_syslog(MS_DHCP | LOG_INFO, "RTR-ADVERT(%s) %s", param->if_name, daemon->addrbuff);
|
||||
time = context->lease_time;
|
||||
if (time < ((unsigned int)RA_INTERVAL))
|
||||
time = RA_INTERVAL;
|
||||
}
|
||||
|
||||
if (context->flags & CONTEXT_DEPRECATE)
|
||||
deprecate = 1;
|
||||
|
||||
if (context->flags & CONTEXT_CONSTRUCTED)
|
||||
constructed = 1;
|
||||
|
||||
|
||||
/* collect dhcp-range tags */
|
||||
if (context->netid.next == &context->netid && context->netid.net)
|
||||
{
|
||||
context->netid.next = param->tags;
|
||||
param->tags = &context->netid;
|
||||
}
|
||||
|
||||
/* subsequent prefixes on the same interface
|
||||
and subsequent instances of this prefix don't need timers.
|
||||
Be careful not to find the same prefix twice with different
|
||||
addresses. */
|
||||
if (!(context->flags & CONTEXT_RA_DONE))
|
||||
{
|
||||
if (!param->first)
|
||||
context->ra_time = 0;
|
||||
context->flags |= CONTEXT_RA_DONE;
|
||||
do_prefix = 1;
|
||||
}
|
||||
|
||||
param->first = 0;
|
||||
param->found_context = 1;
|
||||
}
|
||||
|
||||
/* configured time is ceiling */
|
||||
if (!constructed || valid > time)
|
||||
valid = time;
|
||||
|
||||
if (flags & IFACE_DEPRECATED)
|
||||
preferred = 0;
|
||||
|
||||
if (deprecate)
|
||||
time = 0;
|
||||
|
||||
/* configured time is ceiling */
|
||||
if (!constructed || preferred > time)
|
||||
preferred = time;
|
||||
|
||||
if (preferred > param->pref_time)
|
||||
{
|
||||
param->pref_time = preferred;
|
||||
param->link_global = *local;
|
||||
}
|
||||
|
||||
if (do_prefix)
|
||||
{
|
||||
struct prefix_opt *opt;
|
||||
|
||||
if ((opt = expand(sizeof(struct prefix_opt))))
|
||||
{
|
||||
/* zero net part of address */
|
||||
setaddr6part(local, addr6part(local) & ~((prefix == 64) ? (u64)-1LL : (1LLU << (128 - prefix)) - 1LLU));
|
||||
|
||||
opt->type = ICMP6_OPT_PREFIX;
|
||||
opt->len = 4;
|
||||
opt->prefix_len = prefix;
|
||||
/* autonomous only if we're not doing dhcp, always set "on-link" */
|
||||
opt->flags = do_slaac ? 0xC0 : 0x80;
|
||||
opt->valid_lifetime = htonl(valid);
|
||||
opt->preferred_lifetime = htonl(preferred);
|
||||
opt->reserved = 0;
|
||||
opt->prefix = *local;
|
||||
|
||||
inet_ntop(AF_INET6, local, daemon->addrbuff, ADDRSTRLEN);
|
||||
my_syslog(MS_DHCP | LOG_INFO, "RTR-ADVERT(%s) %s", param->if_name, daemon->addrbuff);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
@@ -361,18 +501,19 @@ time_t periodic_ra(time_t now)
|
||||
char interface[IF_NAMESIZE+1];
|
||||
|
||||
param.now = now;
|
||||
param.iface = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
/* find overdue events, and time of first future event */
|
||||
for (next_event = 0, context = daemon->ra_contexts; context; context = context->next)
|
||||
for (next_event = 0, context = daemon->dhcp6; context; context = context->next)
|
||||
if (context->ra_time != 0)
|
||||
{
|
||||
if (difftime(context->ra_time, now) < 0.0)
|
||||
if (difftime(context->ra_time, now) <= 0.0)
|
||||
break; /* overdue */
|
||||
|
||||
if (next_event == 0 || difftime(next_event, context->ra_time + 2) > 0.0)
|
||||
next_event = context->ra_time + 2;
|
||||
if (next_event == 0 || difftime(next_event, context->ra_time) > 0.0)
|
||||
next_event = context->ra_time;
|
||||
}
|
||||
|
||||
/* none overdue */
|
||||
@@ -386,113 +527,67 @@ time_t periodic_ra(time_t now)
|
||||
ever be able to send ra's and satistfy it. */
|
||||
if (iface_enumerate(AF_INET6, ¶m, iface_search))
|
||||
context->ra_time = 0;
|
||||
else if (indextoname(daemon->icmp6fd, param.iface, interface))
|
||||
send_ra(param.iface, interface, NULL);
|
||||
}
|
||||
|
||||
else if (param.iface != 0 &&
|
||||
indextoname(daemon->icmp6fd, param.iface, interface) &&
|
||||
iface_check(AF_LOCAL, NULL, interface, NULL))
|
||||
{
|
||||
struct iname *tmp;
|
||||
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
|
||||
if (tmp->name && wildcard_match(tmp->name, interface))
|
||||
break;
|
||||
if (!tmp)
|
||||
send_ra(now, param.iface, interface, NULL);
|
||||
}
|
||||
}
|
||||
return next_event;
|
||||
}
|
||||
|
||||
|
||||
static int iface_search(struct in6_addr *local, int prefix,
|
||||
int scope, int if_index, int dad, void *vparam)
|
||||
int scope, int if_index, int flags,
|
||||
int preferred, int valid, void *vparam)
|
||||
{
|
||||
struct search_param *param = vparam;
|
||||
struct dhcp_context *context;
|
||||
|
||||
(void)scope;
|
||||
(void)dad;
|
||||
(void)preferred;
|
||||
(void)valid;
|
||||
|
||||
for (context = daemon->ra_contexts; context; context = context->next)
|
||||
if (prefix == context->prefix &&
|
||||
for (context = daemon->dhcp6; context; context = context->next)
|
||||
if (!(context->flags & CONTEXT_TEMPLATE) &&
|
||||
prefix == context->prefix &&
|
||||
is_same_net6(local, &context->start6, prefix) &&
|
||||
is_same_net6(local, &context->end6, prefix))
|
||||
if (context->ra_time != 0 && difftime(context->ra_time, param->now) < 0.0)
|
||||
{
|
||||
/* found an interface that's overdue for RA determine new
|
||||
timeout value and zap other contexts on the same interface
|
||||
so they don't timeout independently .*/
|
||||
is_same_net6(local, &context->end6, prefix) &&
|
||||
context->ra_time != 0 &&
|
||||
difftime(context->ra_time, param->now) <= 0.0)
|
||||
{
|
||||
/* found an interface that's overdue for RA determine new
|
||||
timeout value and arrange for RA to be sent unless interface is
|
||||
still doing DAD.*/
|
||||
|
||||
if (!(flags & IFACE_TENTATIVE))
|
||||
param->iface = if_index;
|
||||
|
||||
if (difftime(param->now, ra_short_period_start) < 60.0)
|
||||
/* range 5 - 20 */
|
||||
context->ra_time = param->now + 5 + (rand16()/4400);
|
||||
else
|
||||
/* range 450 - 600 */
|
||||
context->ra_time = param->now + 450 + (rand16()/440);
|
||||
|
||||
return 0; /* found, abort */
|
||||
}
|
||||
|
||||
if (difftime(param->now, context->ra_short_period_start) < 60.0)
|
||||
/* range 5 - 20 */
|
||||
context->ra_time = param->now + 5 + (rand16()/4400);
|
||||
else
|
||||
/* range 3/4 - 1 times RA_INTERVAL */
|
||||
context->ra_time = param->now + (3 * RA_INTERVAL)/4 + ((RA_INTERVAL * (unsigned int)rand16()) >> 18);
|
||||
|
||||
/* zero timers for other contexts on the same subnet, so they don't timeout
|
||||
independently */
|
||||
for (context = context->next; context; context = context->next)
|
||||
if (prefix == context->prefix &&
|
||||
is_same_net6(local, &context->start6, prefix) &&
|
||||
is_same_net6(local, &context->end6, prefix))
|
||||
context->ra_time = 0;
|
||||
|
||||
return 0; /* found, abort */
|
||||
}
|
||||
|
||||
return 1; /* keep searching */
|
||||
}
|
||||
|
||||
static int add_subnet(struct in6_addr *local, int prefix,
|
||||
int scope, int if_index, int dad, void *vparam)
|
||||
{
|
||||
struct dhcp_context *context;
|
||||
struct subnet_map **subnets = vparam;
|
||||
struct subnet_map *map;
|
||||
|
||||
(void)scope;
|
||||
(void)dad;
|
||||
|
||||
for (context = daemon->ra_contexts; context; context = context->next)
|
||||
if ((context->flags & CONTEXT_RA_NAME) &&
|
||||
prefix == context->prefix &&
|
||||
is_same_net6(local, &context->start6, prefix) &&
|
||||
is_same_net6(local, &context->end6, prefix))
|
||||
{
|
||||
for (map = *subnets; map; map = map->next)
|
||||
if (map->iface == 0 ||
|
||||
(map->iface == if_index && is_same_net6(local, &map->subnet, prefix)))
|
||||
break;
|
||||
|
||||
/* It's there already */
|
||||
if (map && map->iface != 0)
|
||||
continue;
|
||||
|
||||
if (!map && (map = whine_malloc(sizeof(struct subnet_map))))
|
||||
{
|
||||
map->next = *subnets;
|
||||
*subnets = map;
|
||||
}
|
||||
|
||||
if (map)
|
||||
{
|
||||
map->iface = if_index;
|
||||
map->subnet = *local;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Build a map from ra-names subnets to corresponding interfaces. This
|
||||
is used to go from DHCPv4 leases to SLAAC addresses,
|
||||
interface->IPv6-subnet, IPv6-subnet + MAC address -> SLAAC.
|
||||
*/
|
||||
struct subnet_map *build_subnet_map(void)
|
||||
{
|
||||
struct subnet_map *map;
|
||||
struct dhcp_context *context;
|
||||
static struct subnet_map *subnets = NULL;
|
||||
|
||||
for (context = daemon->ra_contexts; context; context = context->next)
|
||||
if ((context->flags & CONTEXT_RA_NAME))
|
||||
break;
|
||||
|
||||
/* no ra-names, no need to go further. */
|
||||
if (!context)
|
||||
return NULL;
|
||||
|
||||
/* mark unused */
|
||||
for (map = subnets; map; map = map->next)
|
||||
map->iface = 0;
|
||||
|
||||
if (iface_enumerate(AF_INET6, &subnets, add_subnet))
|
||||
return subnets;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
166
src/rfc1035.c
166
src/rfc1035.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2013 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,10 +16,6 @@
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
static int add_resource_record(struct dns_header *header, char *limit, int *truncp,
|
||||
unsigned int nameoffset, unsigned char **pp,
|
||||
unsigned long ttl, unsigned int *offset, unsigned short type,
|
||||
unsigned short class, char *format, ...);
|
||||
|
||||
#define CHECK_LEN(header, pp, plen, len) \
|
||||
((size_t)((pp) - (unsigned char *)(header) + (len)) <= (plen))
|
||||
@@ -27,8 +23,8 @@ static int add_resource_record(struct dns_header *header, char *limit, int *trun
|
||||
#define ADD_RDLEN(header, pp, plen, len) \
|
||||
(!CHECK_LEN(header, pp, plen, len) ? 0 : (((pp) += (len)), 1))
|
||||
|
||||
static int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
|
||||
char *name, int isExtract, int extrabytes)
|
||||
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;
|
||||
@@ -173,7 +169,7 @@ static int extract_name(struct dns_header *header, size_t plen, unsigned char **
|
||||
|
||||
/* Max size of input string (for IPv6) is 75 chars.) */
|
||||
#define MAXARPANAME 75
|
||||
static int in_arpa_name_2_addr(char *namein, struct all_addr *addrp)
|
||||
int in_arpa_name_2_addr(char *namein, struct all_addr *addrp)
|
||||
{
|
||||
int j;
|
||||
char name[MAXARPANAME+1], *cp1;
|
||||
@@ -333,7 +329,7 @@ static unsigned char *skip_name(unsigned char *ansp, struct dns_header *header,
|
||||
return ansp;
|
||||
}
|
||||
|
||||
static unsigned char *skip_questions(struct dns_header *header, size_t plen)
|
||||
unsigned char *skip_questions(struct dns_header *header, size_t plen)
|
||||
{
|
||||
int q;
|
||||
unsigned char *ansp = (unsigned char *)(header+1);
|
||||
@@ -699,15 +695,17 @@ static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *
|
||||
unsigned char *p2 = p1;
|
||||
/* make counted string zero-term and sanitise */
|
||||
for (i = 0; i < len; i++)
|
||||
if (isprint(*(p2+1)))
|
||||
{
|
||||
*p2 = *(p2+1);
|
||||
p2++;
|
||||
}
|
||||
{
|
||||
if (!isprint((int)*(p2+1)))
|
||||
break;
|
||||
|
||||
*p2 = *(p2+1);
|
||||
p2++;
|
||||
}
|
||||
*p2 = 0;
|
||||
my_syslog(LOG_INFO, "reply %s is %s", name, p1);
|
||||
/* restore */
|
||||
memmove(p1 + 1, p1, len);
|
||||
memmove(p1 + 1, p1, i);
|
||||
*p1 = len;
|
||||
p1 += len+1;
|
||||
}
|
||||
@@ -779,13 +777,18 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name)
|
||||
expired and cleaned out that way.
|
||||
Return 1 if we reject an address because it look like part of dns-rebinding attack. */
|
||||
int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t now,
|
||||
int is_sign, int check_rebind, int checking_disabled)
|
||||
char **ipsets, int is_sign, int check_rebind, int checking_disabled)
|
||||
{
|
||||
unsigned char *p, *p1, *endrr, *namep;
|
||||
int i, j, qtype, qclass, aqtype, aqclass, ardlen, res, searched_soa = 0;
|
||||
unsigned long ttl = 0;
|
||||
struct all_addr addr;
|
||||
|
||||
#ifdef HAVE_IPSET
|
||||
char **ipsets_cur;
|
||||
#else
|
||||
(void)ipsets; /* unused */
|
||||
#endif
|
||||
|
||||
cache_start_insert();
|
||||
|
||||
/* find_soa is needed for dns_doctor and logging side-effects, so don't call it lazily if there are any. */
|
||||
@@ -936,10 +939,14 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
if (!cname_count--)
|
||||
return 0; /* looped CNAMES */
|
||||
newc = cache_insert(name, NULL, now, attl, F_CNAME | F_FORWARD);
|
||||
if (newc && cpp)
|
||||
if (newc)
|
||||
{
|
||||
cpp->addr.cname.cache = newc;
|
||||
cpp->addr.cname.uid = newc->uid;
|
||||
newc->addr.cname.cache = NULL;
|
||||
if (cpp)
|
||||
{
|
||||
cpp->addr.cname.cache = newc;
|
||||
cpp->addr.cname.uid = newc->uid;
|
||||
}
|
||||
}
|
||||
|
||||
cpp = newc;
|
||||
@@ -964,6 +971,15 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
(flags & F_IPV4) &&
|
||||
private_net(addr.addr.addr4, !option_bool(OPT_LOCAL_REBIND)))
|
||||
return 1;
|
||||
|
||||
#ifdef HAVE_IPSET
|
||||
if (ipsets && (flags & (F_IPV4 | F_IPV6)))
|
||||
{
|
||||
ipsets_cur = ipsets;
|
||||
while (*ipsets_cur)
|
||||
add_to_ipset(*ipsets_cur++, &addr, flags, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
newc = cache_insert(name, &addr, now, attl, flags | F_FORWARD);
|
||||
if (newc && cpp)
|
||||
@@ -1003,10 +1019,16 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
}
|
||||
}
|
||||
|
||||
/* Don't put stuff from a truncated packet into the cache,
|
||||
also don't cache replies where DNSSEC validation was turned off, either
|
||||
the upstream server told us so, or the original query specified it. */
|
||||
if (!(header->hb3 & HB3_TC) && !(header->hb4 & HB4_CD) && !checking_disabled)
|
||||
/* Don't put stuff from a truncated packet into the cache.
|
||||
Don't cache replies where DNSSEC validation was turned off, either
|
||||
the upstream server told us so, or the original query specified it.
|
||||
Don't cache replies from non-recursive nameservers, since we may get a
|
||||
reply containing a CNAME but not its target, even though the target
|
||||
does exist. */
|
||||
if (!(header->hb3 & HB3_TC) &&
|
||||
!(header->hb4 & HB4_CD) &&
|
||||
(header->hb4 & HB4_RA) &&
|
||||
!checking_disabled)
|
||||
cache_end_insert();
|
||||
|
||||
return 0;
|
||||
@@ -1177,8 +1199,8 @@ int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_resource_record(struct dns_header *header, char *limit, int *truncp, unsigned int nameoffset, unsigned char **pp,
|
||||
unsigned long ttl, unsigned int *offset, unsigned short type, unsigned short class, char *format, ...)
|
||||
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, ...)
|
||||
{
|
||||
va_list ap;
|
||||
unsigned char *sav, *p = *pp;
|
||||
@@ -1189,8 +1211,26 @@ static int add_resource_record(struct dns_header *header, char *limit, int *trun
|
||||
|
||||
if (truncp && *truncp)
|
||||
return 0;
|
||||
|
||||
va_start(ap, format); /* make ap point to 1st unamed argument */
|
||||
|
||||
if (nameoffset > 0)
|
||||
{
|
||||
PUTSHORT(nameoffset | 0xc000, p);
|
||||
}
|
||||
else
|
||||
{
|
||||
char *name = va_arg(ap, char *);
|
||||
if (name)
|
||||
p = do_rfc1035_name(p, name);
|
||||
if (nameoffset < 0)
|
||||
{
|
||||
PUTSHORT(-nameoffset | 0xc000, p);
|
||||
}
|
||||
else
|
||||
*p++ = 0;
|
||||
}
|
||||
|
||||
PUTSHORT(nameoffset | 0xc000, p);
|
||||
PUTSHORT(type, p);
|
||||
PUTSHORT(class, p);
|
||||
PUTLONG(ttl, p); /* TTL */
|
||||
@@ -1198,8 +1238,6 @@ static int add_resource_record(struct dns_header *header, char *limit, int *trun
|
||||
sav = p; /* Save pointer to RDLength field */
|
||||
PUTSHORT(0, p); /* Placeholder RDLength */
|
||||
|
||||
va_start(ap, format); /* make ap point to 1st unamed argument */
|
||||
|
||||
for (; *format; format++)
|
||||
switch (*format)
|
||||
{
|
||||
@@ -1238,7 +1276,8 @@ static int add_resource_record(struct dns_header *header, char *limit, int *trun
|
||||
case 't':
|
||||
usval = va_arg(ap, int);
|
||||
sval = va_arg(ap, char *);
|
||||
memcpy(p, sval, usval);
|
||||
if (usval != 0)
|
||||
memcpy(p, sval, usval);
|
||||
p += usval;
|
||||
break;
|
||||
|
||||
@@ -1294,7 +1333,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
unsigned char *p, *ansp, *pheader;
|
||||
int qtype, qclass;
|
||||
struct all_addr addr;
|
||||
unsigned int nameoffset;
|
||||
int nameoffset;
|
||||
unsigned short flag;
|
||||
int q, ans, anscount = 0, addncount = 0;
|
||||
int dryrun = 0, sec_reqd = 0;
|
||||
@@ -1381,6 +1420,22 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
|
||||
if (qclass == C_IN)
|
||||
{
|
||||
struct txt_record *t;
|
||||
|
||||
for (t = daemon->rr; t; t = t->next)
|
||||
if ((t->class == qtype || qtype == T_ANY) && hostname_isequal(name, t->name))
|
||||
{
|
||||
ans = 1;
|
||||
if (!dryrun)
|
||||
{
|
||||
log_query(F_CONFIG | F_RRNAME, name, NULL, "<RR>");
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
daemon->local_ttl, NULL,
|
||||
t->class, C_IN, "t", t->len, t->txt))
|
||||
anscount ++;
|
||||
}
|
||||
}
|
||||
|
||||
if (qtype == T_PTR || qtype == T_ANY)
|
||||
{
|
||||
/* see if it's w.z.y.z.in-addr.arpa format */
|
||||
@@ -1462,6 +1517,19 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
}
|
||||
}
|
||||
} while ((crecp = cache_find_by_addr(crecp, &addr, now, is_arpa)));
|
||||
else if (is_rev_synth(is_arpa, &addr, name))
|
||||
{
|
||||
ans = 1;
|
||||
if (!dryrun)
|
||||
{
|
||||
log_query(F_CONFIG | F_REVERSE | is_arpa, name, &addr, NULL);
|
||||
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
daemon->local_ttl, NULL,
|
||||
T_PTR, C_IN, "d", name))
|
||||
anscount++;
|
||||
}
|
||||
}
|
||||
else if (is_arpa == F_IPV4 &&
|
||||
option_bool(OPT_BOGUSPRIV) &&
|
||||
private_net(addr.addr.addr4, 1))
|
||||
@@ -1632,6 +1700,34 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
}
|
||||
} while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME)));
|
||||
}
|
||||
else if (is_name_synthetic(flag, name, &addr))
|
||||
{
|
||||
ans = 1;
|
||||
if (!dryrun)
|
||||
{
|
||||
log_query(F_FORWARD | F_CONFIG | flag, name, &addr, NULL);
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
daemon->local_ttl, NULL, type, C_IN, type == T_A ? "4" : "6", &addr))
|
||||
anscount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (qtype == T_CNAME || qtype == T_ANY)
|
||||
{
|
||||
if ((crecp = cache_find_by_name(NULL, name, now, F_CNAME)) &&
|
||||
(qtype == T_CNAME || (crecp->flags & (F_HOSTS | F_DHCP))))
|
||||
{
|
||||
ans = 1;
|
||||
if (!dryrun)
|
||||
{
|
||||
log_query(crecp->flags, name, NULL, record_source(crecp->uid));
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
crec_ttl(crecp, now), &nameoffset,
|
||||
T_CNAME, C_IN, "d", cache_get_name(crecp->addr.cname.cache)))
|
||||
anscount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (qtype == T_MX || qtype == T_ANY)
|
||||
@@ -1643,7 +1739,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
ans = found = 1;
|
||||
if (!dryrun)
|
||||
{
|
||||
unsigned int offset;
|
||||
int offset;
|
||||
log_query(F_CONFIG | F_RRNAME, name, NULL, "<MX>");
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl,
|
||||
&offset, T_MX, C_IN, "sd", rec->weight, rec->target))
|
||||
@@ -1681,7 +1777,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
found = ans = 1;
|
||||
if (!dryrun)
|
||||
{
|
||||
unsigned int offset;
|
||||
int offset;
|
||||
log_query(F_CONFIG | F_RRNAME, name, NULL, "<SRV>");
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl,
|
||||
&offset, T_SRV, C_IN, "sssd",
|
||||
@@ -1811,7 +1907,3 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
return ansp - (unsigned char *)header;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
375
src/rfc2131.c
375
src/rfc2131.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -39,6 +39,7 @@ static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt
|
||||
static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize);
|
||||
static size_t dhcp_packet_size(struct dhcp_packet *mess, unsigned char *agent_id, unsigned char *real_end);
|
||||
static void clear_packet(struct dhcp_packet *mess, unsigned char *end);
|
||||
static int in_list(unsigned char *list, int opt);
|
||||
static void do_options(struct dhcp_context *context,
|
||||
struct dhcp_packet *mess,
|
||||
unsigned char *real_end,
|
||||
@@ -354,6 +355,117 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
ntohl(mess->xid), daemon->namebuff, inet_ntoa(context_tmp->end));
|
||||
}
|
||||
}
|
||||
|
||||
/* dhcp-match. If we have hex-and-wildcards, look for a left-anchored match.
|
||||
Otherwise assume the option is an array, and look for a matching element.
|
||||
If no data given, existance of the option is enough. This code handles
|
||||
rfc3925 V-I classes too. */
|
||||
for (o = daemon->dhcp_match; o; o = o->next)
|
||||
{
|
||||
unsigned int len, elen, match = 0;
|
||||
size_t offset, o2;
|
||||
|
||||
if (o->flags & DHOPT_RFC3925)
|
||||
{
|
||||
if (!(opt = option_find(mess, sz, OPTION_VENDOR_IDENT, 5)))
|
||||
continue;
|
||||
|
||||
for (offset = 0; offset < (option_len(opt) - 5u); offset += len + 5)
|
||||
{
|
||||
len = option_uint(opt, offset + 4 , 1);
|
||||
/* Need to take care that bad data can't run us off the end of the packet */
|
||||
if ((offset + len + 5 <= (option_len(opt))) &&
|
||||
(option_uint(opt, offset, 4) == (unsigned int)o->u.encap))
|
||||
for (o2 = offset + 5; o2 < offset + len + 5; o2 += elen + 1)
|
||||
{
|
||||
elen = option_uint(opt, o2, 1);
|
||||
if ((o2 + elen + 1 <= option_len(opt)) &&
|
||||
(match = match_bytes(o, option_ptr(opt, o2 + 1), elen)))
|
||||
break;
|
||||
}
|
||||
if (match)
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(opt = option_find(mess, sz, o->opt, 1)))
|
||||
continue;
|
||||
|
||||
match = match_bytes(o, option_ptr(opt, 0), option_len(opt));
|
||||
}
|
||||
|
||||
if (match)
|
||||
{
|
||||
o->netid->next = netid;
|
||||
netid = o->netid;
|
||||
}
|
||||
}
|
||||
|
||||
/* user-class options are, according to RFC3004, supposed to contain
|
||||
a set of counted strings. Here we check that this is so (by seeing
|
||||
if the counts are consistent with the overall option length) and if
|
||||
so zero the counts so that we don't get spurious matches between
|
||||
the vendor string and the counts. If the lengths don't add up, we
|
||||
assume that the option is a single string and non RFC3004 compliant
|
||||
and just do the substring match. dhclient provides these broken options.
|
||||
The code, later, which sends user-class data to the lease-change script
|
||||
relies on the transformation done here.
|
||||
*/
|
||||
|
||||
if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
|
||||
{
|
||||
unsigned char *ucp = option_ptr(opt, 0);
|
||||
int tmp, j;
|
||||
for (j = 0; j < option_len(opt); j += ucp[j] + 1);
|
||||
if (j == option_len(opt))
|
||||
for (j = 0; j < option_len(opt); j = tmp)
|
||||
{
|
||||
tmp = j + ucp[j] + 1;
|
||||
ucp[j] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
|
||||
{
|
||||
int mopt;
|
||||
|
||||
if (vendor->match_type == MATCH_VENDOR)
|
||||
mopt = OPTION_VENDOR_ID;
|
||||
else if (vendor->match_type == MATCH_USER)
|
||||
mopt = OPTION_USER_CLASS;
|
||||
else
|
||||
continue;
|
||||
|
||||
if ((opt = option_find(mess, sz, mopt, 1)))
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i <= (option_len(opt) - vendor->len); i++)
|
||||
if (memcmp(vendor->data, option_ptr(opt, i), vendor->len) == 0)
|
||||
{
|
||||
vendor->netid.next = netid;
|
||||
netid = &vendor->netid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* mark vendor-encapsulated options which match the client-supplied vendor class,
|
||||
save client-supplied vendor class */
|
||||
if ((opt = option_find(mess, sz, OPTION_VENDOR_ID, 1)))
|
||||
{
|
||||
memcpy(daemon->dhcp_buff3, option_ptr(opt, 0), option_len(opt));
|
||||
vendor_class_len = option_len(opt);
|
||||
}
|
||||
match_vendor_opts(opt, daemon->dhcp_opts);
|
||||
|
||||
if (option_bool(OPT_LOG_OPTS))
|
||||
{
|
||||
if (sanitise(opt, daemon->namebuff))
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("%u vendor class: %s"), ntohl(mess->xid), daemon->namebuff);
|
||||
if (sanitise(option_find(mess, sz, OPTION_USER_CLASS, 1), daemon->namebuff))
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("%u user class: %s"), ntohl(mess->xid), daemon->namebuff);
|
||||
}
|
||||
|
||||
mess->op = BOOTREPLY;
|
||||
|
||||
@@ -483,18 +595,18 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
{
|
||||
logaddr = &mess->yiaddr;
|
||||
|
||||
lease_set_hwaddr(lease, mess->chaddr, NULL, mess->hlen, mess->htype, 0);
|
||||
lease_set_hwaddr(lease, mess->chaddr, NULL, mess->hlen, mess->htype, 0, now, 1);
|
||||
if (hostname)
|
||||
lease_set_hostname(lease, hostname, 1, get_domain(lease->addr), domain);
|
||||
/* infinite lease unless nailed in dhcp-host line. */
|
||||
lease_set_expires(lease,
|
||||
have_config(config, CONFIG_TIME) ? config->lease_time : 0xffffffff,
|
||||
now);
|
||||
lease_set_interface(lease, int_index);
|
||||
lease_set_interface(lease, int_index, now);
|
||||
|
||||
clear_packet(mess, end);
|
||||
do_options(context, mess, end, NULL, hostname, get_domain(mess->yiaddr),
|
||||
netid, subnet_addr, 0, 0, 0, NULL, 0, now);
|
||||
netid, subnet_addr, 0, 0, -1, NULL, vendor_class_len, now);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -515,11 +627,22 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
op += 3;
|
||||
pp = op;
|
||||
|
||||
/* Always force update, since the client has no way to do it itself. */
|
||||
if (!option_bool(OPT_FQDN_UPDATE) && !(fqdn_flags & 0x01))
|
||||
fqdn_flags |= 0x03;
|
||||
|
||||
fqdn_flags &= ~0x08;
|
||||
/* NB, the following always sets at least one bit */
|
||||
if (option_bool(OPT_FQDN_UPDATE))
|
||||
{
|
||||
if (fqdn_flags & 0x01)
|
||||
{
|
||||
fqdn_flags |= 0x02; /* set O */
|
||||
fqdn_flags &= ~0x01; /* clear S */
|
||||
}
|
||||
fqdn_flags |= 0x08; /* set N */
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(fqdn_flags & 0x01))
|
||||
fqdn_flags |= 0x03; /* set S and O */
|
||||
fqdn_flags &= ~0x08; /* clear N */
|
||||
}
|
||||
|
||||
if (fqdn_flags & 0x04)
|
||||
while (*op != 0 && ((op + (*op) + 1) - pp) < len)
|
||||
@@ -610,119 +733,8 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
}
|
||||
}
|
||||
|
||||
/* dhcp-match. If we have hex-and-wildcards, look for a left-anchored match.
|
||||
Otherwise assume the option is an array, and look for a matching element.
|
||||
If no data given, existance of the option is enough. This code handles
|
||||
rfc3925 V-I classes too. */
|
||||
for (o = daemon->dhcp_match; o; o = o->next)
|
||||
{
|
||||
unsigned int len, elen, match = 0;
|
||||
size_t offset, o2;
|
||||
|
||||
if (o->flags & DHOPT_RFC3925)
|
||||
{
|
||||
if (!(opt = option_find(mess, sz, OPTION_VENDOR_IDENT, 5)))
|
||||
continue;
|
||||
|
||||
for (offset = 0; offset < (option_len(opt) - 5u); offset += len + 5)
|
||||
{
|
||||
len = option_uint(opt, offset + 4 , 1);
|
||||
/* Need to take care that bad data can't run us off the end of the packet */
|
||||
if ((offset + len + 5 <= (option_len(opt))) &&
|
||||
(option_uint(opt, offset, 4) == (unsigned int)o->u.encap))
|
||||
for (o2 = offset + 5; o2 < offset + len + 5; o2 += elen + 1)
|
||||
{
|
||||
elen = option_uint(opt, o2, 1);
|
||||
if ((o2 + elen + 1 <= option_len(opt)) &&
|
||||
(match = match_bytes(o, option_ptr(opt, o2 + 1), elen)))
|
||||
break;
|
||||
}
|
||||
if (match)
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(opt = option_find(mess, sz, o->opt, 1)))
|
||||
continue;
|
||||
|
||||
match = match_bytes(o, option_ptr(opt, 0), option_len(opt));
|
||||
}
|
||||
|
||||
if (match)
|
||||
{
|
||||
o->netid->next = netid;
|
||||
netid = o->netid;
|
||||
}
|
||||
}
|
||||
|
||||
/* user-class options are, according to RFC3004, supposed to contain
|
||||
a set of counted strings. Here we check that this is so (by seeing
|
||||
if the counts are consistent with the overall option length) and if
|
||||
so zero the counts so that we don't get spurious matches between
|
||||
the vendor string and the counts. If the lengths don't add up, we
|
||||
assume that the option is a single string and non RFC3004 compliant
|
||||
and just do the substring match. dhclient provides these broken options.
|
||||
The code, later, which sends user-class data to the lease-change script
|
||||
relies on the transformation done here.
|
||||
*/
|
||||
|
||||
if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
|
||||
{
|
||||
unsigned char *ucp = option_ptr(opt, 0);
|
||||
int tmp, j;
|
||||
for (j = 0; j < option_len(opt); j += ucp[j] + 1);
|
||||
if (j == option_len(opt))
|
||||
for (j = 0; j < option_len(opt); j = tmp)
|
||||
{
|
||||
tmp = j + ucp[j] + 1;
|
||||
ucp[j] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
|
||||
{
|
||||
int mopt;
|
||||
|
||||
if (vendor->match_type == MATCH_VENDOR)
|
||||
mopt = OPTION_VENDOR_ID;
|
||||
else if (vendor->match_type == MATCH_USER)
|
||||
mopt = OPTION_USER_CLASS;
|
||||
else
|
||||
continue;
|
||||
|
||||
if ((opt = option_find(mess, sz, mopt, 1)))
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i <= (option_len(opt) - vendor->len); i++)
|
||||
if (memcmp(vendor->data, option_ptr(opt, i), vendor->len) == 0)
|
||||
{
|
||||
vendor->netid.next = netid;
|
||||
netid = &vendor->netid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* mark vendor-encapsulated options which match the client-supplied vendor class,
|
||||
save client-supplied vendor class */
|
||||
if ((opt = option_find(mess, sz, OPTION_VENDOR_ID, 1)))
|
||||
{
|
||||
memcpy(daemon->dhcp_buff3, option_ptr(opt, 0), option_len(opt));
|
||||
vendor_class_len = option_len(opt);
|
||||
}
|
||||
match_vendor_opts(opt, daemon->dhcp_opts);
|
||||
|
||||
if (option_bool(OPT_LOG_OPTS))
|
||||
{
|
||||
if (sanitise(opt, daemon->namebuff))
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("%u vendor class: %s"), ntohl(mess->xid), daemon->namebuff);
|
||||
if (sanitise(option_find(mess, sz, OPTION_USER_CLASS, 1), daemon->namebuff))
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("%u user class: %s"), ntohl(mess->xid), daemon->namebuff);
|
||||
}
|
||||
|
||||
tagif_netid = run_tag_if(netid);
|
||||
|
||||
|
||||
/* if all the netids in the ignore list are present, ignore this client */
|
||||
for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
|
||||
if (match_netid(id_list->list, tagif_netid, 0))
|
||||
@@ -1222,51 +1234,69 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
|
||||
log_tags(tagif_netid, ntohl(mess->xid));
|
||||
|
||||
#ifdef HAVE_SCRIPT
|
||||
if (do_classes && daemon->lease_change_command)
|
||||
if (do_classes)
|
||||
{
|
||||
struct dhcp_netid *n;
|
||||
|
||||
if (mess->giaddr.s_addr)
|
||||
lease->giaddr = mess->giaddr;
|
||||
|
||||
/* pick up INIT-REBOOT events. */
|
||||
lease->flags |= LEASE_CHANGED;
|
||||
free(lease->extradata);
|
||||
lease->extradata = NULL;
|
||||
lease->extradata_size = lease->extradata_len = 0;
|
||||
|
||||
add_extradata_opt(lease, option_find(mess, sz, OPTION_VENDOR_ID, 1));
|
||||
add_extradata_opt(lease, option_find(mess, sz, OPTION_HOSTNAME, 1));
|
||||
add_extradata_opt(lease, oui);
|
||||
add_extradata_opt(lease, serial);
|
||||
add_extradata_opt(lease, class);
|
||||
|
||||
/* space-concat tag set */
|
||||
if (!tagif_netid)
|
||||
add_extradata_opt(lease, NULL);
|
||||
else
|
||||
for (n = tagif_netid; n; n = n->next)
|
||||
{
|
||||
struct dhcp_netid *n1;
|
||||
/* kill dupes */
|
||||
for (n1 = n->next; n1; n1 = n1->next)
|
||||
if (strcmp(n->net, n1->net) == 0)
|
||||
break;
|
||||
if (!n1)
|
||||
lease_add_extradata(lease, (unsigned char *)n->net, strlen(n->net), n->next ? ' ' : 0);
|
||||
}
|
||||
|
||||
if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
|
||||
#ifdef HAVE_SCRIPT
|
||||
if (daemon->lease_change_command)
|
||||
{
|
||||
int len = option_len(opt);
|
||||
unsigned char *ucp = option_ptr(opt, 0);
|
||||
/* 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);
|
||||
struct dhcp_netid *n;
|
||||
|
||||
if (mess->giaddr.s_addr)
|
||||
lease->giaddr = mess->giaddr;
|
||||
|
||||
free(lease->extradata);
|
||||
lease->extradata = NULL;
|
||||
lease->extradata_size = lease->extradata_len = 0;
|
||||
|
||||
add_extradata_opt(lease, option_find(mess, sz, OPTION_VENDOR_ID, 1));
|
||||
add_extradata_opt(lease, option_find(mess, sz, OPTION_HOSTNAME, 1));
|
||||
add_extradata_opt(lease, oui);
|
||||
add_extradata_opt(lease, serial);
|
||||
add_extradata_opt(lease, class);
|
||||
|
||||
if ((opt = option_find(mess, sz, OPTION_AGENT_ID, 1)))
|
||||
{
|
||||
add_extradata_opt(lease, option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_CIRCUIT_ID, 1));
|
||||
add_extradata_opt(lease, option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_SUBSCR_ID, 1));
|
||||
add_extradata_opt(lease, option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_REMOTE_ID, 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
add_extradata_opt(lease, NULL);
|
||||
add_extradata_opt(lease, NULL);
|
||||
add_extradata_opt(lease, NULL);
|
||||
}
|
||||
|
||||
/* space-concat tag set */
|
||||
if (!tagif_netid)
|
||||
add_extradata_opt(lease, NULL);
|
||||
else
|
||||
for (n = tagif_netid; n; n = n->next)
|
||||
{
|
||||
struct dhcp_netid *n1;
|
||||
/* kill dupes */
|
||||
for (n1 = n->next; n1; n1 = n1->next)
|
||||
if (strcmp(n->net, n1->net) == 0)
|
||||
break;
|
||||
if (!n1)
|
||||
lease_add_extradata(lease, (unsigned char *)n->net, strlen(n->net), n->next ? ' ' : 0);
|
||||
}
|
||||
|
||||
if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
|
||||
{
|
||||
int len = option_len(opt);
|
||||
unsigned char *ucp = option_ptr(opt, 0);
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!hostname_auth && (client_hostname = host_from_dns(mess->yiaddr)))
|
||||
{
|
||||
@@ -1276,7 +1306,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
}
|
||||
|
||||
time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
|
||||
lease_set_hwaddr(lease, mess->chaddr, clid, mess->hlen, mess->htype, clid_len);
|
||||
lease_set_hwaddr(lease, mess->chaddr, clid, mess->hlen, mess->htype, clid_len, now, do_classes);
|
||||
|
||||
/* if all the netids in the ignore_name list are present, ignore client-supplied name */
|
||||
if (!hostname_auth)
|
||||
@@ -1310,7 +1340,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
lease_set_hostname(lease, hostname, hostname_auth, get_domain(lease->addr), domain);
|
||||
|
||||
lease_set_expires(lease, time, now);
|
||||
lease_set_interface(lease, int_index);
|
||||
lease_set_interface(lease, int_index, now);
|
||||
|
||||
if (override.s_addr != 0)
|
||||
lease->override = override;
|
||||
@@ -1370,6 +1400,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
|
||||
if (lease)
|
||||
{
|
||||
lease_set_interface(lease, int_index, now);
|
||||
if (override.s_addr != 0)
|
||||
lease->override = override;
|
||||
else
|
||||
@@ -1379,15 +1410,19 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
clear_packet(mess, end);
|
||||
option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
|
||||
option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
|
||||
|
||||
if (lease)
|
||||
|
||||
/* RFC 2131 says that DHCPINFORM shouldn't include lease-time parameters, but
|
||||
we supply a utility which makes DHCPINFORM requests to get this information.
|
||||
Only include lease time if OPTION_LEASE_TIME is in the parameter request list,
|
||||
which won't be true for ordinary clients, but will be true for the
|
||||
dhcp_lease_time utility. */
|
||||
if (lease && in_list(req_options, OPTION_LEASE_TIME))
|
||||
{
|
||||
if (lease->expires == 0)
|
||||
time = 0xffffffff;
|
||||
else
|
||||
time = (unsigned int)difftime(lease->expires, now);
|
||||
option_put(mess, end, OPTION_LEASE_TIME, 4, time);
|
||||
lease_set_interface(lease, int_index);
|
||||
}
|
||||
|
||||
do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr),
|
||||
@@ -1731,7 +1766,7 @@ static unsigned char *free_space(struct dhcp_packet *mess, unsigned char *end, i
|
||||
if (overload[2] & 2)
|
||||
{
|
||||
p = dhcp_skip_opts(mess->sname);
|
||||
if (p + len + 3 >= mess->sname + sizeof(mess->file))
|
||||
if (p + len + 3 >= mess->sname + sizeof(mess->sname))
|
||||
p = NULL;
|
||||
}
|
||||
}
|
||||
@@ -2095,7 +2130,8 @@ static void do_options(struct dhcp_context *context,
|
||||
struct dhcp_netid_list *id_list;
|
||||
|
||||
/* filter options based on tags, those we want get DHOPT_TAGOK bit set */
|
||||
context->netid.next = NULL;
|
||||
if (context)
|
||||
context->netid.next = NULL;
|
||||
tagif = option_filter(netid, context && context->netid.net ? &context->netid : NULL, config_opts);
|
||||
|
||||
/* logging */
|
||||
@@ -2225,7 +2261,8 @@ static void do_options(struct dhcp_context *context,
|
||||
!option_find2(OPTION_ROUTER))
|
||||
option_put(mess, end, OPTION_ROUTER, INADDRSZ, ntohl(context->router.s_addr));
|
||||
|
||||
if (in_list(req_options, OPTION_DNSSERVER) &&
|
||||
if (daemon->port == NAMESERVER_PORT &&
|
||||
in_list(req_options, OPTION_DNSSERVER) &&
|
||||
!option_find2(OPTION_DNSSERVER))
|
||||
option_put(mess, end, OPTION_DNSSERVER, INADDRSZ, ntohl(context->local.s_addr));
|
||||
}
|
||||
@@ -2255,7 +2292,7 @@ static void do_options(struct dhcp_context *context,
|
||||
|
||||
if ((p = free_space(mess, end, OPTION_CLIENT_FQDN, len)))
|
||||
{
|
||||
*(p++) = fqdn_flags;
|
||||
*(p++) = fqdn_flags & 0x0f; /* MBZ bits to zero */
|
||||
*(p++) = 255;
|
||||
*(p++) = 255;
|
||||
|
||||
|
||||
1615
src/rfc3315.c
1615
src/rfc3315.c
File diff suppressed because it is too large
Load Diff
207
src/slaac.c
Normal file
207
src/slaac.c
Normal file
@@ -0,0 +1,207 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 dated June, 1991, or
|
||||
(at your option) version 3 dated 29 June, 2007.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
|
||||
#include <netinet/icmp6.h>
|
||||
|
||||
static int ping_id = 0;
|
||||
|
||||
void slaac_add_addrs(struct dhcp_lease *lease, time_t now, int force)
|
||||
{
|
||||
struct slaac_address *slaac, *old, **up;
|
||||
struct dhcp_context *context;
|
||||
int dns_dirty = 0;
|
||||
|
||||
if (!(lease->flags & LEASE_HAVE_HWADDR) ||
|
||||
(lease->flags & (LEASE_TA | LEASE_NA)) ||
|
||||
lease->last_interface == 0 ||
|
||||
!lease->hostname)
|
||||
return ;
|
||||
|
||||
old = lease->slaac_address;
|
||||
lease->slaac_address = NULL;
|
||||
|
||||
for (context = daemon->dhcp6; context; context = context->next)
|
||||
if ((context->flags & CONTEXT_RA_NAME) && lease->last_interface == context->if_index)
|
||||
{
|
||||
struct in6_addr addr = context->start6;
|
||||
if (lease->hwaddr_len == 6 &&
|
||||
(lease->hwaddr_type == ARPHRD_ETHER || lease->hwaddr_type == ARPHRD_IEEE802))
|
||||
{
|
||||
/* convert MAC address to EUI-64 */
|
||||
memcpy(&addr.s6_addr[8], lease->hwaddr, 3);
|
||||
memcpy(&addr.s6_addr[13], &lease->hwaddr[3], 3);
|
||||
addr.s6_addr[11] = 0xff;
|
||||
addr.s6_addr[12] = 0xfe;
|
||||
}
|
||||
#if defined(ARPHRD_EUI64)
|
||||
else if (lease->hwaddr_len == 8 &&
|
||||
lease->hwaddr_type == ARPHRD_EUI64)
|
||||
memcpy(&addr.s6_addr[8], lease->hwaddr, 8);
|
||||
#endif
|
||||
#if defined(ARPHRD_IEEE1394) && defined(ARPHRD_EUI64)
|
||||
else if (lease->clid_len == 9 &&
|
||||
lease->clid[0] == ARPHRD_EUI64 &&
|
||||
lease->hwaddr_type == ARPHRD_IEEE1394)
|
||||
/* firewire has EUI-64 identifier as clid */
|
||||
memcpy(&addr.s6_addr[8], &lease->clid[1], 8);
|
||||
#endif
|
||||
else
|
||||
continue;
|
||||
|
||||
addr.s6_addr[8] ^= 0x02;
|
||||
|
||||
/* check if we already have this one */
|
||||
for (up = &old, slaac = old; slaac; slaac = slaac->next)
|
||||
{
|
||||
if (IN6_ARE_ADDR_EQUAL(&addr, &slaac->addr))
|
||||
{
|
||||
*up = slaac->next;
|
||||
/* recheck when DHCPv4 goes through init-reboot */
|
||||
if (force)
|
||||
{
|
||||
slaac->ping_time = now;
|
||||
slaac->backoff = 1;
|
||||
dns_dirty = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
up = &slaac->next;
|
||||
}
|
||||
|
||||
/* No, make new one */
|
||||
if (!slaac && (slaac = whine_malloc(sizeof(struct slaac_address))))
|
||||
{
|
||||
slaac->ping_time = now;
|
||||
slaac->backoff = 1;
|
||||
slaac->addr = addr;
|
||||
slaac->local = context->local6;
|
||||
/* Do RA's to prod it */
|
||||
ra_start_unsolicted(now, context);
|
||||
}
|
||||
|
||||
if (slaac)
|
||||
{
|
||||
slaac->next = lease->slaac_address;
|
||||
lease->slaac_address = slaac;
|
||||
}
|
||||
}
|
||||
|
||||
if (old || dns_dirty)
|
||||
lease_update_dns(1);
|
||||
|
||||
/* Free any no reused */
|
||||
for (; old; old = slaac)
|
||||
{
|
||||
slaac = old->next;
|
||||
free(old);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
time_t periodic_slaac(time_t now, struct dhcp_lease *leases)
|
||||
{
|
||||
struct dhcp_context *context;
|
||||
struct dhcp_lease *lease;
|
||||
struct slaac_address *slaac;
|
||||
time_t next_event = 0;
|
||||
|
||||
for (context = daemon->dhcp6; context; context = context->next)
|
||||
if ((context->flags & CONTEXT_RA_NAME))
|
||||
break;
|
||||
|
||||
/* nothing configured */
|
||||
if (!context)
|
||||
return 0;
|
||||
|
||||
while (ping_id == 0)
|
||||
ping_id = rand16();
|
||||
|
||||
for (lease = leases; lease; lease = lease->next)
|
||||
for (slaac = lease->slaac_address; slaac; slaac = slaac->next)
|
||||
{
|
||||
/* confirmed or given up? */
|
||||
if (slaac->backoff == 0 || slaac->ping_time == 0)
|
||||
continue;
|
||||
|
||||
if (difftime(slaac->ping_time, now) <= 0.0)
|
||||
{
|
||||
struct ping_packet *ping;
|
||||
struct sockaddr_in6 addr;
|
||||
|
||||
save_counter(0);
|
||||
ping = expand(sizeof(struct ping_packet));
|
||||
ping->type = ICMP6_ECHO_REQUEST;
|
||||
ping->code = 0;
|
||||
ping->identifier = ping_id;
|
||||
ping->sequence_no = slaac->backoff;
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
addr.sin6_len = sizeof(struct sockaddr_in6);
|
||||
#endif
|
||||
addr.sin6_family = AF_INET6;
|
||||
addr.sin6_port = htons(IPPROTO_ICMPV6);
|
||||
addr.sin6_addr = slaac->addr;
|
||||
|
||||
if (sendto(daemon->icmp6fd, daemon->outpacket.iov_base, save_counter(0), 0,
|
||||
(struct sockaddr *)&addr, sizeof(addr)) == -1 &&
|
||||
errno == EHOSTUNREACH)
|
||||
slaac->ping_time = 0; /* Give up */
|
||||
else
|
||||
{
|
||||
slaac->ping_time += (1 << (slaac->backoff - 1)) + (rand16()/21785); /* 0 - 3 */
|
||||
if (slaac->backoff > 4)
|
||||
slaac->ping_time += rand16()/4000; /* 0 - 15 */
|
||||
if (slaac->backoff < 12)
|
||||
slaac->backoff++;
|
||||
}
|
||||
}
|
||||
|
||||
if (slaac->ping_time != 0 &&
|
||||
(next_event == 0 || difftime(next_event, slaac->ping_time) >= 0.0))
|
||||
next_event = slaac->ping_time;
|
||||
}
|
||||
|
||||
return next_event;
|
||||
}
|
||||
|
||||
|
||||
void slaac_ping_reply(struct in6_addr *sender, unsigned char *packet, char *interface, struct dhcp_lease *leases)
|
||||
{
|
||||
struct dhcp_lease *lease;
|
||||
struct slaac_address *slaac;
|
||||
struct ping_packet *ping = (struct ping_packet *)packet;
|
||||
int gotone = 0;
|
||||
|
||||
if (ping->identifier == ping_id)
|
||||
for (lease = leases; lease; lease = lease->next)
|
||||
for (slaac = lease->slaac_address; slaac; slaac = slaac->next)
|
||||
if (slaac->backoff != 0 && IN6_ARE_ADDR_EQUAL(sender, &slaac->addr))
|
||||
{
|
||||
slaac->backoff = 0;
|
||||
gotone = 1;
|
||||
inet_ntop(AF_INET6, sender, daemon->addrbuff, ADDRSTRLEN);
|
||||
my_syslog(MS_DHCP | LOG_INFO, "SLAAC-CONFIRM(%s) %s %s", interface, daemon->addrbuff, lease->hostname);
|
||||
}
|
||||
|
||||
lease_update_dns(gotone);
|
||||
}
|
||||
|
||||
#endif
|
||||
189
src/tftp.c
189
src/tftp.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2013 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,12 +18,13 @@
|
||||
|
||||
#ifdef HAVE_TFTP
|
||||
|
||||
static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, int special);
|
||||
static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix);
|
||||
static void free_transfer(struct tftp_transfer *transfer);
|
||||
static ssize_t tftp_err(int err, char *packet, char *mess, char *file);
|
||||
static ssize_t tftp_err_oops(char *packet, char *file);
|
||||
static ssize_t get_block(char *packet, struct tftp_transfer *transfer);
|
||||
static char *next(char **p, char *end);
|
||||
static void sanitise(char *buf);
|
||||
|
||||
#define OP_RRQ 1
|
||||
#define OP_WRQ 2
|
||||
@@ -47,7 +48,7 @@ void tftp_request(struct listener *listen, time_t now)
|
||||
struct msghdr msg;
|
||||
struct iovec iov;
|
||||
struct ifreq ifr;
|
||||
int is_err = 1, if_index = 0, mtu = 0, special = 0;
|
||||
int is_err = 1, if_index = 0, mtu = 0;
|
||||
#ifdef HAVE_DHCP
|
||||
struct iname *tmp;
|
||||
#endif
|
||||
@@ -57,10 +58,10 @@ void tftp_request(struct listener *listen, time_t now)
|
||||
int mtuflag = IP_PMTUDISC_DONT;
|
||||
#endif
|
||||
char namebuff[IF_NAMESIZE];
|
||||
char *name;
|
||||
char *name = NULL;
|
||||
char *prefix = daemon->tftp_prefix;
|
||||
struct tftp_prefix *pref;
|
||||
struct interface_list *ir;
|
||||
struct all_addr addra;
|
||||
|
||||
union {
|
||||
struct cmsghdr align; /* this ensures alignment */
|
||||
@@ -95,15 +96,24 @@ void tftp_request(struct listener *listen, time_t now)
|
||||
|
||||
if (option_bool(OPT_NOWILD))
|
||||
{
|
||||
addr = listen->iface->addr;
|
||||
mtu = listen->iface->mtu;
|
||||
name = listen->iface->name;
|
||||
if (listen->iface)
|
||||
{
|
||||
addr = listen->iface->addr;
|
||||
mtu = listen->iface->mtu;
|
||||
name = listen->iface->name;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* we're listening on an address that doesn't appear on an interface,
|
||||
ask the kernel what the socket is bound to */
|
||||
socklen_t tcp_len = sizeof(union mysockaddr);
|
||||
if (getsockname(listen->tftpfd, (struct sockaddr *)&addr, &tcp_len) == -1)
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
struct cmsghdr *cmptr;
|
||||
int check;
|
||||
struct interface_list *ir;
|
||||
|
||||
if (msg.msg_controllen < sizeof(struct cmsghdr))
|
||||
return;
|
||||
@@ -180,46 +190,42 @@ void tftp_request(struct listener *listen, time_t now)
|
||||
return;
|
||||
|
||||
name = namebuff;
|
||||
|
||||
addra.addr.addr4 = addr.in.sin_addr;
|
||||
|
||||
#ifdef HAVE_IPV6
|
||||
if (listen->family == AF_INET6)
|
||||
check = iface_check(AF_INET6, (struct all_addr *)&addr.in6.sin6_addr, name);
|
||||
else
|
||||
addra.addr.addr6 = addr.in6.sin6_addr;
|
||||
#endif
|
||||
check = iface_check(AF_INET, (struct all_addr *)&addr.in.sin_addr, name);
|
||||
|
||||
/* wierd TFTP service override */
|
||||
for (ir = daemon->tftp_interfaces; ir; ir = ir->next)
|
||||
if (strcmp(ir->interface, name) == 0)
|
||||
break;
|
||||
|
||||
if (!ir)
|
||||
if (!iface_check(listen->family, &addra, name, NULL))
|
||||
{
|
||||
if (!daemon->tftp_unlimited || !check)
|
||||
if (!option_bool(OPT_CLEVERBIND))
|
||||
enumerate_interfaces();
|
||||
if (!loopback_exception(listen->tftpfd, listen->family, &addra, name) &&
|
||||
!label_exception(if_index, listen->family, &addra) )
|
||||
return;
|
||||
|
||||
#ifdef HAVE_DHCP
|
||||
/* allowed interfaces are the same as for DHCP */
|
||||
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
|
||||
if (tmp->name && (strcmp(tmp->name, name) == 0))
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_DHCP
|
||||
/* allowed interfaces are the same as for DHCP */
|
||||
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
|
||||
if (tmp->name && wildcard_match(tmp->name, name))
|
||||
return;
|
||||
#endif
|
||||
|
||||
strncpy(ifr.ifr_name, name, IF_NAMESIZE);
|
||||
if (ioctl(listen->tftpfd, SIOCGIFMTU, &ifr) != -1)
|
||||
mtu = ifr.ifr_mtu;
|
||||
}
|
||||
|
||||
/* check for per-interface prefix */
|
||||
for (pref = daemon->if_prefix; pref; pref = pref->next)
|
||||
if (strcmp(pref->interface, name) == 0)
|
||||
prefix = pref->prefix;
|
||||
|
||||
/* wierd TFTP interfaces disable special options. */
|
||||
for (ir = daemon->tftp_interfaces; ir; ir = ir->next)
|
||||
if (strcmp(ir->interface, name) == 0)
|
||||
special = 1;
|
||||
if (name)
|
||||
{
|
||||
/* check for per-interface prefix */
|
||||
for (pref = daemon->if_prefix; pref; pref = pref->next)
|
||||
if (strcmp(pref->interface, name) == 0)
|
||||
prefix = pref->prefix;
|
||||
}
|
||||
|
||||
if (listen->family == AF_INET)
|
||||
{
|
||||
@@ -297,7 +303,10 @@ void tftp_request(struct listener *listen, time_t now)
|
||||
!(filename = next(&p, end)) ||
|
||||
!(mode = next(&p, end)) ||
|
||||
(strcasecmp(mode, "octet") != 0 && strcasecmp(mode, "netascii") != 0))
|
||||
len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), daemon->addrbuff);
|
||||
{
|
||||
len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), daemon->addrbuff);
|
||||
is_err = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (strcasecmp(mode, "netascii") == 0)
|
||||
@@ -307,8 +316,7 @@ void tftp_request(struct listener *listen, time_t now)
|
||||
{
|
||||
if (strcasecmp(opt, "blksize") == 0)
|
||||
{
|
||||
if ((opt = next(&p, end)) &&
|
||||
(special || !option_bool(OPT_TFTP_NOBLOCK)))
|
||||
if ((opt = next(&p, end)) && !option_bool(OPT_TFTP_NOBLOCK))
|
||||
{
|
||||
transfer->blocksize = atoi(opt);
|
||||
if (transfer->blocksize < 1)
|
||||
@@ -330,9 +338,12 @@ void tftp_request(struct listener *listen, time_t now)
|
||||
}
|
||||
|
||||
/* cope with backslashes from windows boxen. */
|
||||
while ((p = strchr(filename, '\\')))
|
||||
*p = '/';
|
||||
|
||||
for (p = filename; *p; p++)
|
||||
if (*p == '\\')
|
||||
*p = '/';
|
||||
else if (option_bool(OPT_TFTP_LC))
|
||||
*p = tolower(*p);
|
||||
|
||||
strcpy(daemon->namebuff, "/");
|
||||
if (prefix)
|
||||
{
|
||||
@@ -342,7 +353,7 @@ void tftp_request(struct listener *listen, time_t now)
|
||||
if (prefix[strlen(prefix)-1] != '/')
|
||||
strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));
|
||||
|
||||
if (!special && option_bool(OPT_TFTP_APREF))
|
||||
if (option_bool(OPT_TFTP_APREF))
|
||||
{
|
||||
size_t oldlen = strlen(daemon->namebuff);
|
||||
struct stat statbuf;
|
||||
@@ -369,7 +380,7 @@ void tftp_request(struct listener *listen, time_t now)
|
||||
strncat(daemon->namebuff, filename, (MAXDNAME-1) - strlen(daemon->namebuff));
|
||||
|
||||
/* check permissions and open file */
|
||||
if ((transfer->file = check_tftp_fileperm(&len, prefix, special)))
|
||||
if ((transfer->file = check_tftp_fileperm(&len, prefix)))
|
||||
{
|
||||
if ((len = get_block(packet, transfer)) == -1)
|
||||
len = tftp_err_oops(packet, daemon->namebuff);
|
||||
@@ -379,7 +390,7 @@ void tftp_request(struct listener *listen, time_t now)
|
||||
}
|
||||
|
||||
while (sendto(transfer->sockfd, packet, len, 0,
|
||||
(struct sockaddr *)&peer, sizeof(peer)) == -1 && errno == EINTR);
|
||||
(struct sockaddr *)&peer, sa_len(&peer)) == -1 && errno == EINTR);
|
||||
|
||||
if (is_err)
|
||||
free_transfer(transfer);
|
||||
@@ -390,7 +401,7 @@ void tftp_request(struct listener *listen, time_t now)
|
||||
}
|
||||
}
|
||||
|
||||
static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, int special)
|
||||
static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix)
|
||||
{
|
||||
char *packet = daemon->packet, *namebuff = daemon->namebuff;
|
||||
struct tftp_file *file;
|
||||
@@ -427,7 +438,7 @@ static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, int spe
|
||||
goto perm;
|
||||
}
|
||||
/* in secure mode, must be owned by user running dnsmasq */
|
||||
else if (!special && option_bool(OPT_TFTP_SECURE) && uid != statbuf.st_uid)
|
||||
else if (option_bool(OPT_TFTP_SECURE) && uid != statbuf.st_uid)
|
||||
goto perm;
|
||||
|
||||
/* If we're doing many tranfers from the same file, only
|
||||
@@ -487,12 +498,12 @@ void check_tftp_listeners(fd_set *rset, time_t now)
|
||||
{
|
||||
tmp = transfer->next;
|
||||
|
||||
prettyprint_addr(&transfer->peer, daemon->addrbuff);
|
||||
|
||||
if (FD_ISSET(transfer->sockfd, rset))
|
||||
{
|
||||
/* we overwrote the buffer... */
|
||||
daemon->srv_save = NULL;
|
||||
|
||||
prettyprint_addr(&transfer->peer, daemon->addrbuff);
|
||||
|
||||
if ((len = recv(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0)) >= (ssize_t)sizeof(struct ack))
|
||||
{
|
||||
@@ -514,14 +525,8 @@ void check_tftp_listeners(fd_set *rset, time_t now)
|
||||
if (!err)
|
||||
err = "";
|
||||
else
|
||||
{
|
||||
unsigned char *q, *r;
|
||||
for (q = r = (unsigned char *)err; *r; r++)
|
||||
if (isprint(*r))
|
||||
*(q++) = *r;
|
||||
*q = 0;
|
||||
}
|
||||
|
||||
sanitise(err);
|
||||
|
||||
my_syslog(MS_TFTP | LOG_ERR, _("error %d %s received from %s"),
|
||||
(int)ntohs(mess->block), err,
|
||||
daemon->addrbuff);
|
||||
@@ -548,30 +553,33 @@ void check_tftp_listeners(fd_set *rset, time_t now)
|
||||
len = tftp_err_oops(daemon->packet, transfer->file->filename);
|
||||
endcon = 1;
|
||||
}
|
||||
else if (++transfer->backoff > 5)
|
||||
/* don't complain about timeout when we're awaiting the last
|
||||
ACK, some clients never send it */
|
||||
else if (++transfer->backoff > 5 && len != 0)
|
||||
{
|
||||
/* don't complain about timeout when we're awaiting the last
|
||||
ACK, some clients never send it */
|
||||
if (len != 0)
|
||||
{
|
||||
my_syslog(MS_TFTP | LOG_ERR, _("failed sending %s to %s"),
|
||||
transfer->file->filename, daemon->addrbuff);
|
||||
len = 0;
|
||||
endcon = 1;
|
||||
}
|
||||
endcon = 1;
|
||||
len = 0;
|
||||
}
|
||||
|
||||
|
||||
if (len != 0)
|
||||
while(sendto(transfer->sockfd, daemon->packet, len, 0,
|
||||
(struct sockaddr *)&transfer->peer, sizeof(transfer->peer)) == -1 && errno == EINTR);
|
||||
(struct sockaddr *)&transfer->peer, sa_len(&transfer->peer)) == -1 && errno == EINTR);
|
||||
|
||||
if (endcon || len == 0)
|
||||
{
|
||||
if (!endcon)
|
||||
my_syslog(MS_TFTP | LOG_INFO, _("sent %s to %s"), transfer->file->filename, daemon->addrbuff);
|
||||
strcpy(daemon->namebuff, transfer->file->filename);
|
||||
sanitise(daemon->namebuff);
|
||||
my_syslog(MS_TFTP | LOG_INFO, endcon ? _("failed sending %s to %s") : _("sent %s to %s"), daemon->namebuff, daemon->addrbuff);
|
||||
/* unlink */
|
||||
*up = tmp;
|
||||
free_transfer(transfer);
|
||||
if (endcon)
|
||||
free_transfer(transfer);
|
||||
else
|
||||
{
|
||||
/* put on queue to be sent to script and deleted */
|
||||
transfer->next = daemon->tftp_done_trans;
|
||||
daemon->tftp_done_trans = transfer;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -605,6 +613,16 @@ static char *next(char **p, char *end)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void sanitise(char *buf)
|
||||
{
|
||||
unsigned char *q, *r;
|
||||
for (q = r = (unsigned char *)buf; *r; r++)
|
||||
if (isprint((int)*r))
|
||||
*(q++) = *r;
|
||||
*q = 0;
|
||||
|
||||
}
|
||||
|
||||
static ssize_t tftp_err(int err, char *packet, char *message, char *file)
|
||||
{
|
||||
struct errmess {
|
||||
@@ -613,7 +631,9 @@ static ssize_t tftp_err(int err, char *packet, char *message, char *file)
|
||||
} *mess = (struct errmess *)packet;
|
||||
ssize_t ret = 4;
|
||||
char *errstr = strerror(errno);
|
||||
|
||||
|
||||
sanitise(file);
|
||||
|
||||
mess->op = htons(OP_ERR);
|
||||
mess->err = htons(err);
|
||||
ret += (snprintf(mess->message, 500, message, file, errstr) + 1);
|
||||
@@ -624,7 +644,9 @@ static ssize_t tftp_err(int err, char *packet, char *message, char *file)
|
||||
|
||||
static ssize_t tftp_err_oops(char *packet, char *file)
|
||||
{
|
||||
return tftp_err(ERR_NOTDEF, packet, _("cannot read %s: %s"), file);
|
||||
/* May have >1 refs to file, so potentially mangle a copy of the name */
|
||||
strcpy(daemon->namebuff, file);
|
||||
return tftp_err(ERR_NOTDEF, packet, _("cannot read %s: %s"), daemon->namebuff);
|
||||
}
|
||||
|
||||
/* return -1 for error, zero for done. */
|
||||
@@ -709,4 +731,21 @@ static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int do_tftp_script_run(void)
|
||||
{
|
||||
struct tftp_transfer *transfer;
|
||||
|
||||
if ((transfer = daemon->tftp_done_trans))
|
||||
{
|
||||
daemon->tftp_done_trans = transfer->next;
|
||||
#ifdef HAVE_SCRIPT
|
||||
queue_tftp(transfer->file->size, transfer->file->filename, &transfer->peer);
|
||||
#endif
|
||||
free_transfer(transfer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
64
src/util.c
64
src/util.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2013 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
|
||||
@@ -142,19 +142,23 @@ static int check_name(char *in)
|
||||
int legal_hostname(char *name)
|
||||
{
|
||||
char c;
|
||||
int first;
|
||||
|
||||
if (!check_name(name))
|
||||
return 0;
|
||||
|
||||
for (; (c = *name); name++)
|
||||
for (first = 1; (c = *name); name++, first = 0)
|
||||
/* check for legal char a-z A-Z 0-9 - _ . */
|
||||
{
|
||||
if ((c >= 'A' && c <= 'Z') ||
|
||||
(c >= 'a' && c <= 'z') ||
|
||||
(c >= '0' && c <= '9') ||
|
||||
c == '-' || c == '_')
|
||||
(c >= 'a' && c <= 'z'))
|
||||
continue;
|
||||
|
||||
|
||||
if (!first &&
|
||||
((c >= '0' && c <= '9') ||
|
||||
c == '-' || c == '_'))
|
||||
continue;
|
||||
|
||||
/* end of hostname part */
|
||||
if (c == '.')
|
||||
return 1;
|
||||
@@ -280,7 +284,7 @@ int sa_len(union mysockaddr *addr)
|
||||
}
|
||||
|
||||
/* don't use strcasecmp and friends here - they may be messed up by LOCALE */
|
||||
int hostname_isequal(char *a, char *b)
|
||||
int hostname_isequal(const char *a, const char *b)
|
||||
{
|
||||
unsigned int c1, c2;
|
||||
|
||||
@@ -330,7 +334,7 @@ int is_same_net6(struct in6_addr *a, struct in6_addr *b, int prefixlen)
|
||||
return 0;
|
||||
|
||||
if (pfbits == 0 ||
|
||||
(a->s6_addr[pfbytes] >> (8 - pfbits) != b->s6_addr[pfbytes] >> (8 - pfbits)))
|
||||
(a->s6_addr[pfbytes] >> (8 - pfbits) == b->s6_addr[pfbytes] >> (8 - pfbits)))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
@@ -426,7 +430,7 @@ int parse_hex(char *in, unsigned char *out, int maxlen,
|
||||
|
||||
while (maxlen == -1 || i < maxlen)
|
||||
{
|
||||
for (r = in; *r != 0 && *r != ':' && *r != '-'; r++)
|
||||
for (r = in; *r != 0 && *r != ':' && *r != '-' && *r != ' '; r++)
|
||||
if (*r != '*' && !isxdigit((unsigned char)*r))
|
||||
return -1;
|
||||
|
||||
@@ -444,12 +448,29 @@ int parse_hex(char *in, unsigned char *out, int maxlen,
|
||||
else
|
||||
{
|
||||
*r = 0;
|
||||
mask = mask << 1;
|
||||
if (strcmp(in, "*") == 0)
|
||||
mask |= 1;
|
||||
{
|
||||
mask = (mask << 1) | 1;
|
||||
i++;
|
||||
}
|
||||
else
|
||||
out[i] = strtol(in, NULL, 16);
|
||||
i++;
|
||||
{
|
||||
int j, bytes = (1 + (r - in))/2;
|
||||
for (j = 0; j < bytes; j++)
|
||||
{
|
||||
char sav;
|
||||
if (j < bytes - 1)
|
||||
{
|
||||
sav = in[(j+1)*2];
|
||||
in[(j+1)*2] = 0;
|
||||
}
|
||||
out[i] = strtol(&in[j*2], NULL, 16);
|
||||
mask = mask << 1;
|
||||
i++;
|
||||
if (j < bytes - 1)
|
||||
in[(j+1)*2] = sav;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
in = r+1;
|
||||
@@ -564,3 +585,20 @@ int read_write(int fd, unsigned char *packet, int size, int rw)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Basically match a string value against a wildcard pattern. */
|
||||
int wildcard_match(const char* wildcard, const char* match)
|
||||
{
|
||||
while (*wildcard && *match)
|
||||
{
|
||||
if (*wildcard == '*')
|
||||
return 1;
|
||||
|
||||
if (*wildcard != *match)
|
||||
return 0;
|
||||
|
||||
++wildcard;
|
||||
++match;
|
||||
}
|
||||
|
||||
return *wildcard == *match;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user