Compare commits

...

125 Commits

Author SHA1 Message Date
Simon Kelley
39f6a04ca4 Better fix for interfaces without broadcast address on *BSD. 2013-01-09 19:57:47 +00:00
Simon Kelley
37c9ccebd1 DHCPv6 address allocation - same DUID, two IAIDs 2013-01-09 19:51:04 +00:00
Simon Kelley
71c73ac17c Fix crash on startup on Solaris 11 2013-01-08 21:22:24 +00:00
Simon Kelley
c6cb7407b3 Don't do AXFR unless auth-sec-servers is set. 2013-01-07 21:55:54 +00:00
Simon Kelley
333b2ceb97 Documentation updates for auth-DNS and constructed dhcp ranges. 2013-01-07 21:46:03 +00:00
Simon Kelley
b456b9fdfe Linked-list bug in new "use longest prefixes first" code. 2013-01-02 17:59:28 +00:00
Simon Kelley
34d0a36a1d Man page updates 2013-01-02 11:40:56 +00:00
Simon Kelley
355736f36f Fix auth-DNS filtering problems with contructed ranges. 2012-12-30 17:54:04 +00:00
Simon Kelley
771287be11 Wildcards in dhcp-range constructors 2012-12-30 17:38:09 +00:00
Simon Kelley
dc9476b670 Use RA_INTERVAL for lifetimes. 2012-12-29 22:08:26 +00:00
Simon Kelley
1e14cc0f48 Make it legal to have no subnet in --auth-zone, may be contructed instead. 2012-12-29 17:27:59 +00:00
Simon Kelley
55b548ae2b Add RA_INTERVAL parameter in config.h 2012-12-29 17:13:04 +00:00
Simon Kelley
3b43646a08 Use /proc/sys/net/ipv6/conf/<iface>/mtu for RA advertised MTU. 2012-12-28 11:55:45 +00:00
Simon Kelley
3bc0d932d0 More work on lease and router lifetime calculation. 2012-12-28 11:31:44 +00:00
Simon Kelley
60225f4e75 Allow constructed prefixes in auth zones. 2012-12-28 11:29:01 +00:00
Simon Kelley
1962446269 Join multicast groups only on IPv6 addresses! 2012-12-28 11:18:09 +00:00
Simon Kelley
be37986a0f Better error checking in DHCPv6 dhcp-range option parsing. 2012-12-23 12:01:39 +00:00
Simon Kelley
d7346a1e8c Tweak context-construct logic. 2012-12-22 22:45:54 +00:00
Simon Kelley
87d346f6a7 saner function name 2012-12-22 22:35:11 +00:00
Simon Kelley
f0dd7f807d Fix new-address logic and ordering for first address on new interface. 2012-12-22 22:31:58 +00:00
Simon Kelley
0c0502426f Check for new SLAAC addresses when we add new prefixes. 2012-12-22 22:13:19 +00:00
Simon Kelley
7f035f58c6 Don't cap prefx lifetimes below RA retransmit interval. 2012-12-22 21:27:08 +00:00
Simon Kelley
81e84f8dac preferred and valid times in bpf.c 2012-12-21 20:54:00 +00:00
Simon Kelley
55b42f6de3 Default to global, not link-local address in RA DNS field. 2012-12-21 16:53:15 +00:00
Simon Kelley
ed8b68ad06 Simplify and fix RA lifetime calculation. 2012-12-21 16:23:26 +00:00
Simon Kelley
bad7b875eb add general flag param to iface_enumerate IPv6 callback 2012-12-20 22:00:39 +00:00
Simon Kelley
5d162f20a9 Rationalise join_multicast() 2012-12-20 14:55:46 +00:00
Simon Kelley
9d29949440 typo 2012-12-18 21:48:15 +00:00
Simon Kelley
1b75c1e61f Per-context control over ra short period. 2012-12-18 19:55:25 +00:00
Simon Kelley
293fd0f700 Missed interface re-read path in netlink.c 2012-12-18 18:31:11 +00:00
Simon Kelley
c1be917782 DHCP context logging, more tweaks 2012-12-18 18:31:11 +00:00
Simon Kelley
bb86e858b6 Error dhcp constructors on platforms where no interface detection. 2012-12-18 18:31:11 +00:00
Simon Kelley
8445f5d2e2 Fix initialisation order. 2012-12-18 18:31:11 +00:00
Simon Kelley
72c9c3b11b complicated DHCP context logging. 2012-12-18 18:31:11 +00:00
Simon Kelley
6e3dba3fde Ignore template contexts where appropriate. 2012-12-18 18:31:11 +00:00
Simon Kelley
7558ecd9ac Fix periodic loop 2012-12-18 18:31:11 +00:00
Simon Kelley
1f776932a1 First checkin of interface-address constructor mode for DHCPv6 and RA. 2012-12-18 18:31:11 +00:00
Simon Kelley
4820dce97a Make authoritative stuff a compile-time option. 2012-12-18 18:30:30 +00:00
Simon Kelley
f8abe0c566 Fix crash in auth code for queries where class != C_IN 2012-12-15 11:59:25 +00:00
Simon Kelley
9def963c65 Bump debian version. 2012-12-14 11:58:56 +00:00
Simon Kelley
990123a937 Fix regexp foobar. 2012-12-14 11:56:15 +00:00
Simon Kelley
1d6c639310 Fix broken cache. 2012-12-14 11:19:36 +00:00
Simon Kelley
429798fd08 Allow addresses as well as interface names in --auth-server. 2012-12-10 20:45:53 +00:00
Simon Kelley
b5a8dd1dec Fix FTBFS with NO_DHCP. 2012-12-10 11:37:25 +00:00
Simon Kelley
95a0bd3701 Add .gitignore file. 2012-12-10 11:29:03 +00:00
Simon Kelley
8ff556739e SOA serial tweak. 2012-12-09 21:09:01 +00:00
Simon Kelley
496787677e Zone-transfer peer restriction option. 2012-12-09 18:31:10 +00:00
Simon Kelley
e1ff419cf9 Complete AXFR support 2012-12-09 17:08:47 +00:00
Simon Kelley
ee86ce68fc Fix TCP query forwarding to non-default servers. 2012-12-07 11:54:46 +00:00
Simon Kelley
b75e936372 First cut at zone transfer. 2012-12-07 11:50:41 +00:00
Simon Kelley
aa79235194 zero arcount. 2012-12-06 19:41:35 +00:00
Simon Kelley
7c305be1bd Bump Debian version. 2012-12-04 20:59:06 +00:00
Simon Kelley
f7fe362721 Tidy merge. 2012-12-04 20:55:54 +00:00
Simon Kelley
36bec089f7 Merge branch 'auth' 2012-12-04 20:50:38 +00:00
Simon Kelley
45dd1fece4 Correct NS and SOA records in auth mode for PTR queries. 2012-12-04 20:49:24 +00:00
Simon Kelley
29d28dda95 Don't send RAs to the wrong place when DAD in progress. 2012-12-03 14:05:59 +00:00
Simon Kelley
421594f83d Forgot --dhcp-except check in previous commit. 2012-12-02 12:17:35 +00:00
Simon Kelley
d89fb4ed4f Check interface for router advertisements. 2012-12-01 21:21:13 +00:00
Simon Kelley
295a54eed3 SetDomainServers Dbus method. 2012-12-01 21:02:15 +00:00
Simon Kelley
5c0bd5b112 CNAME auth support. 2012-12-01 16:42:47 +00:00
Simon Kelley
86e3b9a026 Post-test fixes. 2012-11-30 13:46:48 +00:00
Simon Kelley
2f38141f43 Don't elide code needed for --bind-dynamic if compiled without IPv6. 2012-11-29 21:16:44 +00:00
Simon Kelley
8273ea5a19 Add MX support. 2012-11-29 21:12:33 +00:00
Simon Kelley
4f7b304f53 Initial code to do authoritative DNS. 2012-11-28 21:27:02 +00:00
Simon Kelley
8e4b87918f Header-file dependency checking in Makefile. 2012-11-14 14:12:56 +00:00
Simon Kelley
83b2198e86 Add warning to man page, -d option 2012-11-12 21:07:44 +00:00
Simon Kelley
d1a5975f9b No lease-time in DHCPINFORM replies. 2012-11-05 16:50:30 +00:00
Simon Kelley
52002051ad Doc update for previous checkin. 2012-10-26 11:39:02 +01:00
Simon Kelley
b191a77901 trivial indent fix. 2012-10-24 14:16:00 +01:00
Simon Kelley
23780dd577 Set tag "dhcpv6" rather than "DHCPv6", hardwired tags in lower-case is consistent. 2012-10-23 17:04:37 +01:00
Simon Kelley
d1e9a582ad Use dhcp-range tags when replying to DHCPv6 information-request. 2012-10-23 17:00:57 +01:00
Simon Kelley
819ff4dd0f Wildcard IPv6 dhcp-range. 2012-10-21 18:25:12 +01:00
Simon Kelley
de604c18a0 Remove non-7-bit character from CHANGELOG 2012-10-19 09:50:01 +01:00
Simon Kelley
be6cfb42ab Fix DHCPv6 to do access control correctly when it's configured with --listen-address. 2012-10-16 20:38:31 +01:00
Simon Kelley
2022310f95 SO_REUSEADDR and SO_V6ONLY options on DHCPv6 socket. 2012-10-15 10:41:17 +01:00
Simon Kelley
657ed09693 Add contrib/dbus-test/dbus-test.py 2012-10-12 14:45:55 +01:00
Simon Kelley
c99df938d7 Fix compilation warnings. 2012-10-12 13:39:04 +01:00
Simon Kelley
cf568a3726 Fix typos in sample config file. 2012-10-09 20:51:31 +01:00
Simon Kelley
e4807d8bb2 Fix breakage of --host-record parsing. 2012-09-27 21:52:26 +01:00
Simon Kelley
35239a302a Tweak dhcp-config sanity checking. 2012-09-24 15:09:33 +01:00
Simon Kelley
db3946c358 Debian changelog update. 2012-09-21 17:21:05 +01:00
Simon Kelley
0d28af84d0 Set tag "DHCPv6" for v6 transactions. 2012-09-20 21:24:06 +01:00
Simon Kelley
42698cb7ab Log ignored DHCPv6 information-requests. 2012-09-20 21:19:35 +01:00
Simon Kelley
1d860415f2 Add --max-cache-ttl option. 2012-09-20 20:48:04 +01:00
Simon Kelley
289a253569 Fix build with later Lua libraries. 2012-09-20 15:29:35 +01:00
Simon Kelley
faafb3f7b7 Add SetServersEX method in DBus interface. 2012-09-20 14:17:39 +01:00
Simon Kelley
2b127a1eab Flag DHCP or DHCPv6 in starup logging. 2012-09-18 21:51:22 +01:00
Simon Kelley
dfb23b3f77 Don't report spurious netlink errors. 2012-09-18 21:44:47 +01:00
Simon Kelley
b269221c00 Address allocation tweaking - lease outside dhcp-range but in subnet. 2012-09-16 22:22:23 +01:00
Simon Kelley
8b46061e73 Fix DHCPv6 address allocation for some pathalogical cases. 2012-09-08 21:47:28 +01:00
Simon Kelley
4d0f5b4c44 Fix BOOTP option processing. 2012-09-05 23:29:30 +01:00
Simon Kelley
1dedeb87cc Fix Debian package adduser dependency. 2012-09-04 21:50:52 +01:00
Simon Kelley
79cfefd856 Make pid-file creation immune to symlink attack. 2012-09-02 13:29:51 +01:00
Simon Kelley
0c0d4793ac Tidy buffer use in DHCP startup logging. 2012-09-02 12:57:43 +01:00
Simon Kelley
12d71ed28c Finesse the check for /etc/hosts names which conflict with DHCP names. 2012-08-30 15:16:41 +01:00
Simon Kelley
9fed0f71c2 Further tweaks to DHCP FQDN option. 2012-08-30 11:43:35 +01:00
Simon Kelley
2e34ac1403 Handle DHCP FQDN option with all flags zero and --dhcp-client-update 2012-08-29 14:15:25 +01:00
Simon Kelley
bc54ae392b Debian packaging fixes. 2012-08-28 21:26:56 +01:00
Simon Kelley
00acd06340 Tweak get-version to do the right thing with multiple head tags. 2012-08-17 14:18:50 +01:00
Simon Kelley
476e4a03c1 Bump Debian version 2012-08-17 13:45:49 +01:00
Simon Kelley
5f11b3e5e0 Cope with --listen-address for not yet existent addr in bind-dynamic mode. 2012-08-16 14:04:05 +01:00
Simon Kelley
3169daad46 Fix TFTP access control, broken earlier in release. 2012-08-13 17:39:57 +01:00
Simon Kelley
fd05f12790 Set prefix on-link bit in RAs 2012-08-12 17:48:50 +01:00
Simon Kelley
ad094275b0 Alternate DBus service name via --enable-dbus 2012-08-10 17:10:54 +01:00
Simon Kelley
c740e4f342 Fix FTBFS when -DNO_DHCP - thanks Sung Pae. 2012-08-09 16:19:01 +01:00
Simon Kelley
132255b5da OpenBSD build fix. 2012-08-06 20:12:04 +01:00
Simon Kelley
c4c0488ac6 Update french translation. 2012-08-06 20:09:15 +01:00
Simon Kelley
a2ce6fcc91 Man page typos 2012-08-06 20:05:48 +01:00
Simon Kelley
12090548d2 Add debian/dnsmasq-base.conffiles 2012-08-06 20:00:58 +01:00
Simon Kelley
8223cb15e7 Update FAQ to cover --bind-dynamic. 2012-07-29 20:21:57 +01:00
Simon Kelley
4ba9b38cc5 Debian package: move /etc/dbus-1/system.d/dnsmasq.conf. 2012-07-29 17:07:48 +01:00
Simon Kelley
42243214b5 "w" multiplier in lease times. 2012-07-20 15:19:18 +01:00
Simon Kelley
23245c0cb2 RFC 4242 support. 2012-07-18 16:21:11 +01:00
Simon Kelley
b271446f82 Typo. 2012-07-17 12:09:26 +01:00
Simon Kelley
611ebc5f1e Fix broken caching of CNAME chains. 2012-07-16 16:23:46 +01:00
Simon Kelley
be0f45cdbc Typo fix. Thanks Wieland Hoffmann. 2012-07-16 13:35:25 +01:00
Simon Kelley
9b40cbf587 Fix FTBFS when TFTP disabled. 2012-07-13 19:58:26 +01:00
Simon Kelley
c4a7f90ebb Config parsing error-handling update. 2012-07-12 20:52:12 +01:00
Simon Kelley
9609baee41 Merge branch 'access_control' 2012-07-10 15:06:34 +01:00
Simon Kelley
395eb71931 Better log message when dhcp hosts|opts file cannot be read. 2012-07-06 22:07:05 +01:00
Simon Kelley
8bc4cecee6 Remove libvirt-inspired but never used access control features. 2012-07-03 21:04:11 +01:00
Simon Kelley
6b617c0d15 Logging library in Android build scaffold. 2012-06-29 22:11:26 +01:00
Simon Kelley
55d290a3bf Handle pid-file location in Android. 2012-06-29 20:58:32 +01:00
Simon Kelley
e17b4b3871 Fix build-failure with -DNO_DHCP6. 2012-06-28 21:44:30 +01:00
Simon Kelley
236e072cab Typo in BSD-only code. 2012-06-26 21:33:01 +01:00
44 changed files with 3880 additions and 1742 deletions

13
.gitignore vendored Normal file
View 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/

143
CHANGELOG
View File

@@ -1,3 +1,106 @@
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.
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.
@@ -10,6 +113,43 @@ version 2.63
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
@@ -36,7 +176,6 @@ version 2.62
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,
@@ -323,7 +462,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

105
FAQ
View File

@@ -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.

View File

@@ -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 slaac.o
dhcp-common.o outpacket.o radv.o slaac.o auth.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)

View File

@@ -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 slaac.c
radv.c slaac.c auth.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)

View File

@@ -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 | sed 's/^v//'
echo "${vers}" | sort | head -n 1 | sed 's/^v//'
else
cat $1/VERSION
fi

43
contrib/dbus-test/dbus-test.py Executable file
View 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")

View File

@@ -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
----------

41
debian/changelog vendored
View File

@@ -1,6 +1,47 @@
dnsmasq (2.66-1) unstable; urgency=low
* New upstream.
-- Simon Kelley <simon@thekelleys.org.uk> Fri, 14 Dec 2012 11:58:41 +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

1
debian/conffiles vendored
View File

@@ -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

7
debian/control vendored
View File

@@ -7,7 +7,7 @@ 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

1
debian/dnsmasq-base.conffiles vendored Normal file
View File

@@ -0,0 +1 @@
/etc/dbus-1/system.d/dnsmasq.conf

24
debian/dnsmasq-base.postinst vendored Normal file
View 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
View 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

27
debian/postinst vendored
View File

@@ -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
View File

@@ -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

7
debian/rules vendored
View File

@@ -85,7 +85,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 +95,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,6 +109,7 @@ 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 \
@@ -129,10 +128,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 \

View File

@@ -326,6 +326,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
@@ -477,7 +480,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.
@@ -522,7 +525,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
@@ -618,6 +621,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

View File

@@ -6,24 +6,25 @@ 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-advertisement 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 RA 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 +72,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 +87,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.
@@ -173,6 +182,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 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.
@@ -207,12 +226,12 @@ dnsmasq which provide DHCP service to run in the same machine.
.B --bind-dynamic
Enable a network mode which is a hybrid between
.B --bind-interfaces
and the default. Dnsmasq binds the address of indivdual 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 availble
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
@@ -271,11 +290,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
@@ -461,14 +482,16 @@ 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 for 01:23:45 or 01 23 45 or
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>
@@ -519,6 +542,29 @@ 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 contructed DHCP
range. The subnet(s) are also used to define in-addr.arpa and
ipv6.arpa domains which are served for reverse-DNS queries.
.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 and 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 wil 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
@@ -529,9 +575,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>][,<mode>][,<netmask>[,<broadcast>]][,<lease time>]
.B \-F, --dhcp-range=[tag:<tag>[,tag:<tag>],][set:<tag],]<start-addr>[,<end-addr>][,<mode>][,<netmask>[,<broadcast>]][,<lease time>]
.TP
.B \-F, --dhcp-range=[interface:<interface>,][tag:<tag>[,tag:<tag>],][set:<tag],]<start-IPv6addr>[,<end-IPv6addr>][,<mode>][,<prefix-len>][,<lease time>]
.B \-F, --dhcp-range=[tag:<tag>[,tag:<tag>],][set:<tag],]<start-IPv6addr>[,<end-IPv6addr>|constuctor:<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
@@ -563,6 +609,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,::4,constructor:eth0
will look for addreses 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
@@ -577,7 +631,11 @@ 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
For IPv4, the <mode> may be
.B proxy
@@ -624,8 +682,6 @@ can be combined with
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
@@ -1369,12 +1425,11 @@ 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
@@ -1638,51 +1693,152 @@ 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 accesible 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 192.0.43.10
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.
If the external address is dynamic,
then it must be done using something like
.nf
.B --interface-name=our.zone.com,eth0
.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

View File

@@ -232,6 +232,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
@@ -532,9 +544,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
@@ -601,9 +621,9 @@ 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.
.TP
.B \-F, --dhcp-range=[interface:<interface>,][tag:<label>[,tag:<label>],][set:<label>],]<adresse de début>[,<adresse de fin>][,<mode>][,<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>][,<mode>][,<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>][,<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
@@ -705,8 +725,6 @@ peut-être combiné avec
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
@@ -1517,12 +1535,11 @@ 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
@@ -1560,6 +1577,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é,
@@ -1823,51 +1847,6 @@ 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
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.
.SH CODES DE SORTIE
.PP
0 - Dnsmasq s'est correctement lancé en tâche de fond, ou alors s'est

756
src/auth.c Normal file
View File

@@ -0,0 +1,756 @@
/* dnsmasq is Copyright (c) 2000-2012 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)
{
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

View File

@@ -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,7 +119,10 @@ int iface_enumerate(int family, void *parm, int (*callback)())
struct in_addr addr, netmask, broadcast;
addr = ((struct sockaddr_in *) addrs->ifa_addr)->sin_addr;
netmask = ((struct sockaddr_in *) addrs->ifa_netmask)->sin_addr;
broadcast = ((struct sockaddr_in *) addrs->ifa_broadaddr)->sin_addr;
if (addrs->ifa_broadaddr)
broadcast = ((struct sockaddr_in *) addrs->ifa_broadaddr)->sin_addr;
else
broadcast.s_addr = 0;
if (!((*callback)(addr, iface_index, netmask, broadcast, parm)))
goto err;
}
@@ -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

View File

@@ -235,6 +235,29 @@ 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))
@@ -243,7 +266,7 @@ static int is_outdated_cname_pointer(struct crec *crecp)
/* 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)) &&
(crecp->addr.cname.cache->flags & (F_IPV4 | F_IPV6 | F_CNAME)) &&
crecp->addr.cname.uid == crecp->addr.cname.cache->uid)
return 0;
@@ -371,6 +394,9 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
int freed_all = flags & F_REVERSE;
int free_avail = 0;
if (daemon->max_cache_ttl != 0 && daemon->max_cache_ttl < ttl)
ttl = daemon->max_cache_ttl;
/* Don't log keys */
if (flags & (F_IPV4 | F_IPV6))
log_query(flags | F_UPSTREAM, name, addr, NULL);
@@ -645,13 +671,29 @@ struct crec *cache_find_by_addr(struct crec *crecp, struct all_addr *addr,
return NULL;
}
static void add_hosts_cname(struct crec *target)
{
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;
struct cname *a;
unsigned int j;
/* Remove duplicates in hosts files. */
@@ -702,16 +744,7 @@ static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrl
/* 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_get_name(cache), 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)
@@ -1003,13 +1036,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
@@ -1020,29 +1081,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))
{
@@ -1052,21 +1105,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));
@@ -1083,27 +1149,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
@@ -1225,14 +1271,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)
@@ -1293,6 +1339,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";

View File

@@ -33,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"
@@ -41,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
@@ -93,12 +97,18 @@ HAVE_CONNTRACK
a build-dependency on libnetfilter_conntrack, but the resulting binary will
still run happily on a kernel without conntrack support.
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
@@ -120,6 +130,7 @@ RESOLVFILE
#define HAVE_DHCP6
#define HAVE_TFTP
#define HAVE_SCRIPT
#define HAVE_AUTH
/* #define HAVE_LUASCRIPT */
/* #define HAVE_BROKEN_RTC */
/* #define HAVE_DBUS */
@@ -158,7 +169,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
@@ -302,6 +319,9 @@ HAVE_SOCKADDR_SA_LEN
#define HAVE_SCRIPT
#endif
#ifdef NO_AUTH
#undef HAVE_AUTH
#endif
/* Define a string indicating which options are in use.
DNSMASQP_COMPILE_OPTS is only defined in dnsmasq.c */
@@ -360,7 +380,11 @@ static char *compile_opts =
#ifndef HAVE_CONNTRACK
"no-"
#endif
"conntrack";
"conntrack "
#ifndef HAVE_AUTH
"no-"
#endif
"auth";
#endif

View File

@@ -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;
@@ -97,20 +105,118 @@ static void remove_watch(DBusWatch *watch, void *data)
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 +275,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 +286,183 @@ 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 = 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 = 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 +470,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 +498,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 +515,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 +545,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 +555,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 +660,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);

View File

@@ -246,40 +246,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.
@@ -289,7 +255,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;
@@ -331,7 +297,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;
@@ -339,7 +306,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;
@@ -365,93 +333,6 @@ 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)
{
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;
/* 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;
if ((fd = socket(PF_INET6, SOCK_DGRAM, 0)) == -1)
return 0;
if (!indextoname(fd, if_index, ifrn_name))
{
close(fd);
return 0;
}
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->ra_contexts ? daemon->ra_contexts : 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);
}
#endif
#ifdef HAVE_LINUX_NETWORK
void bindtodevice(int fd)
{
@@ -462,7 +343,7 @@ void bindtodevice(int fd)
SO_BINDTODEVICE is only available Linux. */
struct irec *iface, *found;
for (found = NULL, iface = daemon->interfaces; iface; iface = iface->next)
if (iface->dhcp_ok)
{
@@ -477,14 +358,14 @@ void bindtodevice(int fd)
}
if (found)
{
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);
}
{
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
@@ -531,15 +412,15 @@ static const struct opttab_t {
{ "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_DEC },
{ "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_DEC},
{ "T2", 59, OT_INTERNAL | OT_DEC},
{ "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 },
@@ -590,6 +471,7 @@ static const struct opttab_t opttab6[] = {
{ "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 },
@@ -754,14 +636,17 @@ char *option_string(int prot, unsigned int opt, unsigned char *val, int opt_len,
}
}
#endif
else if ((ot[o].size & OT_DEC) && opt_len != 0)
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];
sprintf(buf, "%u", dec);
if (ot[o].size & OT_TIME)
prettyprint_time(buf, dec);
else
sprintf(buf, "%u", dec);
}
else
nodecode = 1;
@@ -788,4 +673,91 @@ char *option_string(int prot, unsigned int opt, unsigned char *val, int opt_len,
}
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 *n = "", *m = "", *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 ((context->flags & CONTEXT_DHCP) ||
!(context->flags & (CONTEXT_CONSTRUCTED | CONTEXT_TEMPLATE)))
{
if (family != AF_INET && (context->flags & CONTEXT_DEPRECATE))
strcpy(daemon->namebuff, _(", prefix deprecated"));
else
{
p += sprintf(p, _(", lease time "));
m = p;
prettyprint_time(p, context->lease_time);
p += strlen(p);
}
}
#ifdef HAVE_DHCP6
if (context->flags & CONTEXT_CONSTRUCTED)
{
char ifrn_name[IFNAMSIZ];
if (indextoname(daemon->doing_dhcp6 ? daemon->dhcp6fd : daemon->icmp6fd, context->if_index, ifrn_name))
{
n = p;
p += sprintf(p, ", constructed for %s", ifrn_name);
}
}
if (context->flags & CONTEXT_TEMPLATE)
{
n = p;
p += sprintf(p, ", template for %s%s", context->template_interface,
(context->flags & CONTEXT_WILDCARD) ? "*" : "");
}
#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") :
(context->flags & CONTEXT_STATIC) ?
_("%s, static leases only on %.0s%s%s") :
(context->flags & CONTEXT_PROXY) ?
_("%s, proxy on subnet %.0s%s%.0s") :
_("%s, IP range %s -- %s%s"),
(family != AF_INET) ? "DHCPv6" : "DHCP",
daemon->dhcp_buff, daemon->dhcp_buff3, daemon->namebuff);
}
#ifdef HAVE_DHCP6
if (context->flags & CONTEXT_RA_NAME)
my_syslog(MS_DHCP | LOG_INFO, _("DHCPv4-derived IPv6 names on %s%s"),
daemon->addrbuff, n);
if (context->flags & CONTEXT_RA)
my_syslog(MS_DHCP | LOG_INFO, _("router advertisement on %s%s%s"),
daemon->addrbuff,
(context->flags & (CONTEXT_CONSTRUCTED | CONTEXT_TEMPLATE)) ? "" : ", prefix valid ",
(context->flags & (CONTEXT_CONSTRUCTED | CONTEXT_TEMPLATE)) ? n : m);
#endif
}
#endif

View File

@@ -116,9 +116,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)
@@ -257,14 +255,6 @@ void dhcp_packet(time_t now, int pxe_fd)
if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
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 +262,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;

View File

@@ -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);
@@ -36,15 +37,31 @@ void dhcp6_init(void)
#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(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 to make this posible, or REUSEPORT on *BSD. */
if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND))
{
#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));
#endif
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,7 +88,6 @@ 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;
@@ -98,41 +114,52 @@ 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 && (strcmp(tmp->name, ifr.ifr_name) == 0))
return;
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
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 && (strcmp(tmp->name, ifr.ifr_name) == 0))
break;
if (!tmp && !parm.addr_match)
return;
}
lease_prune(NULL, now); /* lose any expired leases */
port = dhcp6_reply(parm.current, if_index, ifr.ifr_name, &parm.fallback,
@@ -155,19 +182,27 @@ void dhcp6_packet(time_t now)
}
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
@@ -176,16 +211,39 @@ 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_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 = &param->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;
}
}
}
@@ -270,6 +328,7 @@ int address6_allocate(struct dhcp_context *context, unsigned char *clid, int cl
return 0;
}
/* can dynamically allocate addr */
struct dhcp_context *address6_available(struct dhcp_context *context,
struct in6_addr *taddr,
struct dhcp_netid *netids)
@@ -294,6 +353,22 @@ struct dhcp_context *address6_available(struct dhcp_context *context,
return NULL;
}
/* address OK if configured */
struct dhcp_context *address6_valid(struct dhcp_context *context,
struct in6_addr *taddr,
struct dhcp_netid *netids)
{
struct dhcp_context *tmp;
for (tmp = context; tmp; tmp = tmp->current)
if ((tmp->flags & CONTEXT_STATIC) &&
is_same_net6(&tmp->start6, taddr, tmp->prefix) &&
match_netid(tmp->filter, netids, 1))
return tmp;
return NULL;
}
struct dhcp_context *narrow_context6(struct dhcp_context *context,
struct in6_addr *taddr,
struct dhcp_netid *netids)
@@ -308,21 +383,12 @@ struct dhcp_context *narrow_context6(struct dhcp_context *context,
struct dhcp_context *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;
}
if (!(tmp = address6_available(context, taddr, netids)) &&
!(tmp = address6_valid(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))
break;
/* Only one context allowed now */
if (tmp)
@@ -331,14 +397,21 @@ struct dhcp_context *narrow_context6(struct dhcp_context *context,
return tmp;
}
static int is_addr_in_context6(struct dhcp_context *context, struct dhcp_config *config)
static int is_config_in_context6(struct dhcp_context *context, struct dhcp_config *config)
{
if (!context) /* called via find_config() from lease_update_from_configs() */
return 1;
if (!(config->flags & CONFIG_ADDR6))
if (!(config->flags & CONFIG_ADDR6) || is_addr_in_context6(context, &config->addr6))
return 1;
return 0;
}
int is_addr_in_context6(struct dhcp_context *context, struct in6_addr *addr)
{
for (; context; context = context->current)
if (is_same_net6(&config->addr6, &context->start6, context->prefix))
if (is_same_net6(addr, &context->start6, context->prefix))
return 1;
return 0;
@@ -358,7 +431,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;
}
@@ -366,7 +439,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;
@@ -426,6 +499,142 @@ 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) &&
strncmp(template->template_interface, ifrn_name, strlen(template->template_interface)) == 0 &&
(strlen(template->template_interface) == strlen(ifrn_name) || (template->flags & CONTEXT_WILDCARD)))
{
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, &param, construct_worker);
for (up = &daemon->dhcp6, context = daemon->dhcp6; context; context = tmp)
{
tmp = context->next;
if (context->flags & CONTEXT_GC)
{
*up = context->next;
free(context);
param.newone = 1; /* include deletion */
if (context->flags & CONTEXT_RA_NAME)
param.newname = 1;
}
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

View File

@@ -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

View File

@@ -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)
{
@@ -120,12 +122,12 @@ int main (int argc, char **argv)
{
bind_fallback = 1;
set_option_bool(OPT_NOWILD);
reset_option_bool(OPT_CLVERBIND);
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
@@ -147,69 +149,77 @@ 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 is not configured. */
if (daemon->authinterface && daemon->soa_sn == 0)
#ifdef HAVE_BROKEN_RTC
die(_("zone serial must be configured in --auth-soa"));
#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->dhcp6)
dhcp6_init();
if (daemon->doing_dhcp6)
dhcp6_init();
# endif
}
#endif
#ifdef HAVE_LINUX_NETWORK
/* After lease_init */
netlink_init();
if (option_bool(OPT_NOWILD) && option_bool(OPT_CLEVERBIND))
die(_("cannot set --bind-interfaces and --bind-dynamic"), NULL, EC_BADCONF);
#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);
#endif
if (!enumerate_interfaces())
die(_("failed to find list of interfaces: %s"), NULL, EC_MISC);
@@ -239,6 +249,12 @@ int main (int argc, char **argv)
}
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();
@@ -383,15 +399,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);
@@ -512,7 +561,7 @@ int main (int argc, char **argv)
#endif
#ifdef HAVE_TFTP
if (daemon->tftp_unlimited || daemon->tftp_interfaces)
if (option_bool(OPT_TFTP))
{
DIR *dir;
struct tftp_prefix *p;
@@ -581,94 +630,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 || daemon->ra_contexts)
{
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;
struct in6_addr subnet = dhcp_tmp->start6;
setaddr6part(&subnet, 0);
inet_ntop(AF_INET6, &subnet, daemon->addrbuff, 256);
}
#endif
if (family != AF_INET && (dhcp_tmp->flags & CONTEXT_DEPRECATE))
strcpy(daemon->namebuff, _("prefix deprecated"));
else
{
char *p = daemon->namebuff;
p += sprintf(p, _("lease time "));
prettyprint_time(p, dhcp_tmp->lease_time);
}
if (daemon->dhcp_buff)
inet_ntop(family, start, daemon->dhcp_buff, 256);
if (daemon->dhcp_buff3)
inet_ntop(family, end, daemon->dhcp_buff3, 256);
if ((dhcp_tmp->flags & CONTEXT_DHCP) || family == AF_INET)
my_syslog(MS_DHCP | LOG_INFO,
(dhcp_tmp->flags & CONTEXT_RA_STATELESS) ?
_("stateless DHCPv6 on %s%.0s%.0s") :
(dhcp_tmp->flags & CONTEXT_STATIC) ?
_("DHCP, static leases only on %.0s%s, %s") :
(dhcp_tmp->flags & CONTEXT_PROXY) ?
_("DHCP, proxy on subnet %.0s%s%.0s") :
_("DHCP, IP range %s -- %s, %s"),
daemon->dhcp_buff, daemon->dhcp_buff3, daemon->namebuff);
for (context = daemon->dhcp; context; context = context->next)
log_context(AF_INET, context);
if (dhcp_tmp->flags & CONTEXT_RA_NAME)
my_syslog(MS_DHCP | LOG_INFO, _("DHCPv4-derived IPv6 names on %s"),
daemon->addrbuff);
if (dhcp_tmp->flags & (CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS))
{
if (!(dhcp_tmp->flags & CONTEXT_DEPRECATE))
{
char *p = daemon->namebuff;
p += sprintf(p, _("prefix valid "));
prettyprint_time(p, dhcp_tmp->lease_time > 7200 ? dhcp_tmp->lease_time : 7200);
}
my_syslog(MS_DHCP | LOG_INFO, _("SLAAC on %s %s"),
daemon->addrbuff, daemon->namebuff);
}
}
#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
# ifdef HAVE_DHCP6
for (context = daemon->dhcp6; context; context = context->next)
log_context(AF_INET6, context);
}
#endif
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)
@@ -772,13 +757,13 @@ 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)
if (daemon->doing_ra)
{
FD_SET(daemon->icmp6fd, &rset);
bump_maxfd(daemon->icmp6fd, &maxfd);
@@ -842,7 +827,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. */
@@ -890,11 +875,11 @@ int main (int argc, char **argv)
}
#ifdef HAVE_DHCP6
if (daemon->dhcp6 && FD_ISSET(daemon->dhcp6fd, &rset))
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
@@ -1074,13 +1059,13 @@ 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)
else if (daemon->doing_ra)
/* Not doing DHCP, so no lease system, manage alarms for ra only */
send_alarm(periodic_ra(now), now);
#endif
@@ -1237,19 +1222,18 @@ 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(1);
}
#ifdef HAVE_DHCP6
else if (daemon->ra_contexts)
else if (daemon->doing_ra)
/* Not doing DHCP, so no lease system, manage
alarms for ra only */
send_alarm(periodic_ra(now), now);
@@ -1409,11 +1393,18 @@ static void check_dns_listeners(fd_set *set, time_t now)
struct server *s;
int flags;
struct in_addr netmask;
int auth_dns;
if (iface)
netmask = iface->netmask;
{
netmask = iface->netmask;
auth_dns = iface->dns_auth;
}
else
netmask.s_addr = 0;
{
netmask.s_addr = 0;
auth_dns = 0;
}
#ifndef NO_FORK
/* Arrange for SIGALARM after CHILD_LIFETIME seconds to
@@ -1432,7 +1423,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, &tcp_addr, netmask);
buff = tcp_request(confd, now, &tcp_addr, netmask, auth_dns);
shutdown(confd, SHUT_RDWR);
close(confd);
@@ -1545,7 +1536,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);
@@ -1564,8 +1555,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

View File

@@ -220,7 +220,8 @@ struct event_desc {
#define OPT_RA 37
#define OPT_TFTP_LC 38
#define OPT_CLEVERBIND 39
#define OPT_LAST 40
#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. */
@@ -277,6 +278,20 @@ 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;
@@ -356,6 +371,8 @@ struct crec {
#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 */
@@ -372,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 */
@@ -411,8 +433,8 @@ struct server {
struct irec {
union mysockaddr addr;
struct in_addr netmask; /* only valid for IPv4 */
int tftp_ok, dhcp_ok, mtu, done, dad;
char *name;
int tftp_ok, dhcp_ok, mtu, done, dad, dns_auth, index, multicast_done;
char *name;
struct irec *next;
};
@@ -474,7 +496,7 @@ struct frec {
#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
@@ -650,7 +672,7 @@ struct cond_domain {
#endif
int is6;
struct cond_domain *next;
};
};
struct dhcp_context {
unsigned int lease_time, addr_epoch;
@@ -661,10 +683,11 @@ struct dhcp_context {
struct in6_addr start6, end6; /* range of available addresses */
struct in6_addr local6;
int prefix, if_index;
time_t ra_time;
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;
};
@@ -679,6 +702,11 @@ struct dhcp_context {
#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_WILDCARD 16384
struct ping_result {
struct in_addr addr;
@@ -712,11 +740,6 @@ struct addr_list {
struct addr_list *next;
};
struct interface_list {
char *interface;
struct interface_list *next;
};
struct tftp_prefix {
char *interface;
char *prefix;
@@ -738,17 +761,21 @@ extern struct daemon {
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;
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;
int log_fac; /* log facility */
@@ -756,9 +783,9 @@ extern struct daemon {
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;
@@ -769,6 +796,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;
@@ -780,10 +808,10 @@ 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;
/* globally used stuff for DNS */
char *packet; /* packet buffer */
@@ -841,7 +869,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);
@@ -857,6 +885,7 @@ 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);
char *get_domain(struct in_addr addr);
#ifdef HAVE_IPV6
char *get_domain6(struct in6_addr *addr);
@@ -885,6 +914,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);
@@ -897,7 +938,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
@@ -934,12 +975,14 @@ 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);
int send_from(int fd, int nowild, char *packet, size_t len,
@@ -957,12 +1000,15 @@ 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 fix_fd(int fd);
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
@@ -998,9 +1044,11 @@ struct dhcp_lease *lease4_allocate(struct in_addr addr);
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_filter(int lease_type, int iaid, struct dhcp_context *context);
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, time_t now, int force);
@@ -1043,7 +1091,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 */
@@ -1097,9 +1145,13 @@ 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);
int is_addr_in_context6(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 *address6_valid(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);
@@ -1110,6 +1162,7 @@ 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 */
@@ -1130,7 +1183,6 @@ 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);
u16 lookup_dhcp_opt(int prot, char *name);
u16 lookup_dhcp_len(int prot, u16 val);
@@ -1141,8 +1193,8 @@ void bindtodevice(int fd);
#endif
# ifdef HAVE_DHCP6
void display_opts6(void);
void join_multicast(void);
# endif
void log_context(int family, struct dhcp_context *context);
#endif
/* outpacket.c */
@@ -1161,16 +1213,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 dhcp_context *context);
#endif
/* slaac.c */
#ifdef HAVE_DHCP6
void build_subnet_map(void);
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);
void schedule_subnet_map(void);
#endif

View File

@@ -95,26 +95,19 @@ int 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;
}
@@ -642,6 +635,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;
@@ -664,17 +658,20 @@ void receive_query(struct listener *listen, time_t now)
/* packet buffer overwritten */
daemon->srv_save = NULL;
if (listen->iface && listen->family == AF_INET && option_bool(OPT_NOWILD))
dst_addr_4.s_addr = 0;
netmask.s_addr = 0;
if (listen->iface && option_bool(OPT_NOWILD))
{
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;
@@ -767,7 +764,7 @@ 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))
!iface_check(listen->family, &dst_addr, ifr.ifr_name, &auth_dns))
return;
if (listen->family == AF_INET && option_bool(OPT_LOCALISE))
@@ -803,7 +800,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,
@@ -815,19 +812,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) || option_bool(OPT_CLEVERBIND),
(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,
@@ -835,13 +845,14 @@ 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);
@@ -877,7 +888,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,
@@ -894,142 +905,150 @@ 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
}
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;
}
#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, 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);
#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) ||
if (m == 0 ||
!read_write(confd, &c1, 1, 0) ||
!read_write(confd, &c2, 1, 0) ||
!read_write(confd, packet, m, 0))
return packet;

View File

@@ -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);

View File

@@ -308,7 +308,7 @@ void lease_update_file(time_t now)
#ifdef HAVE_DHCP6
/* do timed RAs and determine when the next is, also pings to potential SLAAC addresses */
if (daemon->ra_contexts)
if (daemon->doing_ra)
{
time_t event;
@@ -363,12 +363,15 @@ static int find_interface_v4(struct in_addr local, int if_index,
#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)dad;
(void)flags;
(void)preferred;
(void)valid;
for (lease = leases; lease; lease = lease->next)
if ((lease->flags & (LEASE_TA | LEASE_NA)))
@@ -386,6 +389,18 @@ void lease_ping_reply(struct in6_addr *sender, unsigned char *packet, char *inte
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
@@ -395,10 +410,6 @@ void lease_ping_reply(struct in6_addr *sender, unsigned char *packet, char *inte
start-time. */
void lease_find_interfaces(time_t now)
{
#ifdef HAVE_DHCP6
build_subnet_map();
#endif
iface_enumerate(AF_INET, &now, find_interface_v4);
#ifdef HAVE_DHCP6
iface_enumerate(AF_INET6, &now, find_interface_v6);
@@ -420,6 +431,11 @@ void lease_update_dns(int force)
if (daemon->port != 0 && (dns_dirty || force))
{
#ifndef HAVE_BROKEN_RTC
/* force transfer to authoritative secondaries */
daemon->soa_sn++;
#endif
cache_unhash_dhcp();
for (lease = leases; lease; lease = lease->next)
@@ -562,18 +578,31 @@ struct dhcp_lease *lease6_find(unsigned char *clid, int clid_len,
memcmp(clid, lease->clid, clid_len) != 0))
continue;
if (clid || addr)
{
lease->flags |= LEASE_USED;
return lease;
}
else
lease->flags &= ~LEASE_USED;
lease->flags |= LEASE_USED;
return lease;
}
return NULL;
}
void lease6_filter(int lease_type, int iaid, struct dhcp_context *context)
{
struct dhcp_lease *lease;
for (lease = leases; lease; lease = lease->next)
{
/* reset "USED flag */
lease->flags &= ~LEASE_USED;
if (!(lease->flags & lease_type) || lease->hwaddr_type != iaid)
continue;
/* leases on the wrong interface get filtered out here */
if (!is_addr_in_context6(context, (struct in6_addr *)&lease->hwaddr))
lease->flags |= LEASE_USED;
}
}
struct dhcp_lease *lease6_find_by_addr(struct in6_addr *net, int prefix, u64 addr)
{
struct dhcp_lease *lease;

View File

@@ -39,7 +39,7 @@ static struct iovec iov;
static u32 netlink_pid;
static int nl_async(struct nlmsghdr *h);
static void nl_routechange(struct nlmsghdr *h);
static void nl_newaddress(time_t now);
void netlink_init(void)
{
@@ -51,10 +51,14 @@ void netlink_init(void)
addr.nl_pid = 0; /* autobind */
addr.nl_groups = RTMGRP_IPV4_ROUTE;
if (option_bool(OPT_CLEVERBIND))
addr.nl_groups |= RTMGRP_IPV4_IFADDR;
addr.nl_groups |= RTMGRP_IPV4_IFADDR;
#ifdef HAVE_IPV6
addr.nl_groups |= RTMGRP_IPV6_ROUTE;
if (daemon->ra_contexts || option_bool(OPT_CLEVERBIND))
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
@@ -188,7 +192,7 @@ int iface_enumerate(int family, void *parm, int (*callback)())
if (h->nlmsg_seq != seq || h->nlmsg_pid != netlink_pid || h->nlmsg_type == NLMSG_ERROR)
{
/* May be multicast arriving async */
if (nl_async(h) && option_bool(OPT_CLEVERBIND))
if (nl_async(h))
newaddr = 1;
}
else if (h->nlmsg_type == NLMSG_DONE)
@@ -196,11 +200,8 @@ int iface_enumerate(int family, void *parm, int (*callback)())
/* handle async new interface address arrivals, these have to be done
after we complete as we're not re-entrant */
if (newaddr)
{
enumerate_interfaces();
create_bound_listeners(0);
}
nl_newaddress(dnsmasq_time());
return callback_ok;
}
else if (h->nlmsg_type == RTM_NEWADDR && family != AF_UNSPEC && family != AF_LOCAL)
@@ -237,17 +238,32 @@ int iface_enumerate(int family, void *parm, int (*callback)())
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
@@ -306,7 +322,7 @@ 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;
@@ -319,17 +335,14 @@ void netlink_multicast(void)
if ((len = netlink_recv()) != -1)
for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len))
if (nl_async(h) && option_bool(OPT_CLEVERBIND))
if (nl_async(h))
newaddr = 1;
/* restore non-blocking status */
fcntl(daemon->netlinkfd, F_SETFL, flags);
if (newaddr)
{
enumerate_interfaces();
create_bound_listeners(0);
}
nl_newaddress(now);
}
static int nl_async(struct nlmsghdr *h)
@@ -337,61 +350,65 @@ static int nl_async(struct nlmsghdr *h)
if (h->nlmsg_type == NLMSG_ERROR)
{
struct nlmsgerr *err = NLMSG_DATA(h);
my_syslog(LOG_ERR, _("netlink returns error: %s"), strerror(-(err->error)));
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)
{
nl_routechange(h);
/* 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)
{
#ifdef HAVE_DHCP6
/* force RAs to sync new network and pick up new interfaces. */
if (daemon->ra_contexts)
{
schedule_subnet_map();
ra_start_unsolicted(dnsmasq_time(), NULL);
/* 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(0, 0);
}
return 1; /* clever bind mode - rescan */
}
#endif
else if (h->nlmsg_type == RTM_NEWADDR || h->nlmsg_type == RTM_DELADDR)
return 1; /* clever bind mode - rescan */
return 0;
}
/* 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. */
static void nl_routechange(struct nlmsghdr *h)
static void nl_newaddress(time_t now)
{
struct rtmsg *rtm = NLMSG_DATA(h);
int fd;
if (option_bool(OPT_CLEVERBIND) || daemon->doing_dhcp6 || daemon->doing_ra)
enumerate_interfaces();
if (rtm->rtm_type != RTN_UNICAST || rtm->rtm_scope != RT_SCOPE_LINK)
return;
if (option_bool(OPT_CLEVERBIND))
create_bound_listeners(0);
/* Force re-reading resolv file right now, for luck. */
daemon->last_resolv = 0;
if (daemon->srv_save)
#ifdef HAVE_DHCP6
if (daemon->doing_dhcp6 || daemon->doing_ra)
{
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());
join_multicast(0);
dhcp_construct_contexts(now);
}
if (daemon->doing_dhcp6)
lease_find_interfaces(now);
#endif
}

View File

@@ -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,43 +115,59 @@ 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))
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))
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;
}
@@ -161,12 +177,12 @@ static int iface_allowed(struct irec **irecp, int if_index,
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. */
@@ -220,38 +236,32 @@ static int iface_allowed(struct irec **irecp, int if_index,
}
}
#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;
dhcp_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 (addr->sa.sa_family == AF_INET &&
!iface_check(AF_INET, (struct all_addr *)&addr->in.sin_addr, ifr.ifr_name, &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, ifr.ifr_name, &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 && (strcmp(tmp->name, ifr.ifr_name) == 0))
{
tftp_ok = 0;
dhcp_ok = 0;
}
#endif
/* add to list */
if ((iface = whine_malloc(sizeof(struct irec))))
{
@@ -259,9 +269,11 @@ static int iface_allowed(struct irec **irecp, int if_index,
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);
@@ -270,6 +282,7 @@ static int iface_allowed(struct irec **irecp, int if_index,
return 1;
}
free(iface);
}
errno = ENOMEM;
@@ -278,7 +291,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 */
@@ -286,6 +300,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
@@ -296,7 +312,7 @@ 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, &addr, netmask, !!(flags & IFACE_TENTATIVE));
}
#endif
@@ -361,13 +377,22 @@ static int make_sock(union mysockaddr *addr, int type, int dienow)
sprintf(daemon->addrbuff, "port %d", port);
s = _("failed to create listening socket for %s: %s");
if (dienow)
die(s, daemon->addrbuff, EC_BADNET);
if (fd != -1)
close (fd);
if (dienow)
{
/* 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));
my_syslog(LOG_ERR, s, daemon->addrbuff, strerror(errno));
return -1;
}
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 || !fix_fd(fd))
goto err;
@@ -485,8 +510,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
@@ -496,7 +520,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));
@@ -506,11 +530,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;
@@ -536,7 +561,8 @@ void create_bound_listeners(int dienow)
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().
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
@@ -544,7 +570,7 @@ void create_bound_listeners(int dienow)
for (if_tmp = daemon->if_addrs; if_tmp; if_tmp = if_tmp->next)
if (!if_tmp->used &&
(new = create_listeners(&if_tmp->addr, daemon->tftp_unlimited, dienow)))
(new = create_listeners(&if_tmp->addr, !!option_bool(OPT_TFTP), dienow)))
{
new->iface = NULL;
new->next = daemon->listeners;
@@ -563,6 +589,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)

File diff suppressed because it is too large Load Diff

View File

@@ -27,25 +27,28 @@
#include <netinet/icmp6.h>
struct ra_param {
time_t now;
int ind, managed, other, found_context, first;
char *if_name;
struct dhcp_netid *tags;
struct in6_addr link_local;
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)
{
@@ -62,7 +65,7 @@ void ra_init(time_t now)
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->ra_contexts; context; context = context->next)
for (context = daemon->dhcp6; context; context = context->next)
if ((context->flags & CONTEXT_RA_NAME))
break;
@@ -94,18 +97,20 @@ void ra_start_unsolicted(time_t now, struct dhcp_context *context)
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 */
if (context)
context->ra_time = now;
context->ra_short_period_start = context->ra_time = now;
else
for (context = daemon->ra_contexts; context; context = context->next)
context->ra_time = now + (rand16()/13000); /* range 0 - 5 */
/* re-do frequently for a minute or so, in case the first gets lost. */
ra_short_period_start = now;
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;
@@ -119,7 +124,6 @@ void icmp6_packet(void)
struct sockaddr_in6 from;
unsigned char *packet;
struct iname *tmp;
struct dhcp_context *context;
/* Note: use outpacket for input buffer */
msg.msg_control = control_u.control6;
@@ -150,22 +154,16 @@ 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))
return;
/* weird libvirt-inspired access control */
for (context = daemon->ra_contexts ? daemon->ra_contexts : daemon->dhcp6;
context; context = context->next)
if (!context->interface || strcmp(context->interface, interface) == 0)
break;
if (!context || packet[1] != 0)
if (packet[1] != 0)
return;
if (packet[0] == ICMP6_ECHO_REPLY)
lease_ping_reply(&from.sin6_addr, packet, interface);
else if (packet[0] == ND_ROUTER_SOLICIT)
@@ -181,11 +179,11 @@ void icmp6_packet(void)
my_syslog(MS_DHCP | LOG_INFO, "RTR-SOLICIT(%s) %s", interface, mac);
/* source address may not be valid in solicit request. */
send_ra(if_index, interface, !IN6_IS_ADDR_UNSPECIFIED(&from.sin6_addr) ? &from.sin6_addr : NULL);
send_ra(now, if_index, interface, !IN6_IS_ADDR_UNSPECIFIED(&from.sin6_addr) ? &from.sin6_addr : NULL);
}
}
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;
@@ -195,7 +193,10 @@ static void send_ra(int iface, char *iface_name, struct in6_addr *dest)
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));
@@ -203,7 +204,7 @@ static void send_ra(int iface, char *iface_name, struct in6_addr *dest)
ra->code = 0;
ra->hop_limit = hop_limit;
ra->flags = 0x00;
ra->lifetime = htons(1800); /* AdvDefaultLifetime*/
ra->lifetime = htons(RA_INTERVAL * 3); /* AdvDefaultLifetime * 3 */
ra->reachable_time = 0;
ra->retrans_time = 0;
@@ -213,13 +214,15 @@ static void send_ra(int iface, char *iface_name, struct in6_addr *dest)
parm.found_context = 0;
parm.if_name = iface_name;
parm.first = 1;
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->ra_contexts; context; context = context->next)
for (context = daemon->dhcp6; context; context = context->next)
{
context->flags &= ~CONTEXT_RA_DONE;
context->netid.next = &context->netid;
@@ -230,14 +233,23 @@ static void send_ra(int iface, char *iface_name, struct in6_addr *dest)
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);
@@ -263,11 +275,11 @@ static void send_ra(int iface, char *iface_name, struct in6_addr *dest)
put_opt6_char(ICMP6_OPT_RDNSS);
put_opt6_char((opt_cfg->len/8) + 1);
put_opt6_short(0);
put_opt6_long(1800); /* lifetime - twice RA retransmit */
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_local, IN6ADDRSZ);
put_opt6(&parm.link_global, IN6ADDRSZ);
else
put_opt6(a, IN6ADDRSZ);
}
@@ -294,8 +306,8 @@ static void send_ra(int iface, char *iface_name, struct in6_addr *dest)
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);
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 */
@@ -327,29 +339,30 @@ 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 ra_param *param = vparam;
(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))
{
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->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))
{
@@ -372,13 +385,21 @@ static int add_prefixes(struct in6_addr *local, int prefix,
param->managed = 1;
param->other = 1;
}
/* find floor time */
if (time > context->lease_time)
time = context->lease_time;
/* find floor time, don't reduce below RA interval. */
if (time > context->lease_time)
{
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)
@@ -402,6 +423,26 @@ static int add_prefixes(struct in6_addr *local, int prefix,
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)
{
@@ -412,17 +453,13 @@ static int add_prefixes(struct in6_addr *local, int prefix,
/* zero net part of address */
setaddr6part(local, addr6part(local) & ~((prefix == 64) ? (u64)-1LL : (1LLU << (128 - prefix)) - 1LLU));
/* 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 if we're not doing dhcp */
opt->flags = do_slaac ? 0x40 : 0x00;
opt->valid_lifetime = htonl(time);
opt->preferred_lifetime = htonl(deprecate ? 0 : time);
/* 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;
@@ -464,11 +501,12 @@ 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)
@@ -489,42 +527,64 @@ time_t periodic_ra(time_t now)
ever be able to send ra's and satistfy it. */
if (iface_enumerate(AF_INET6, &param, 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 && (strcmp(tmp->name, interface) == 0))
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 */
}

View File

@@ -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);
@@ -1189,8 +1185,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;
@@ -1201,8 +1197,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 */
@@ -1210,8 +1224,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)
{
@@ -1307,7 +1319,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;
@@ -1689,7 +1701,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))
@@ -1727,7 +1739,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",
@@ -1857,7 +1869,3 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
return ansp - (unsigned char *)header;
}

View File

@@ -493,8 +493,9 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
lease_set_interface(lease, int_index, now);
clear_packet(mess, end);
match_vendor_opts(NULL, daemon->dhcp_opts); /* clear flags */
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, 0, now);
}
}
@@ -515,11 +516,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)
@@ -1375,6 +1387,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
@@ -1385,16 +1398,6 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
if (lease)
{
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, now);
}
do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr),
netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now);
@@ -2261,7 +2264,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;

View File

@@ -180,7 +180,7 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
char *client_hostname= NULL, *hostname = NULL;
char *domain = NULL, *send_domain = NULL;
struct dhcp_config *config = NULL;
struct dhcp_netid known_id, iface_id;
struct dhcp_netid known_id, iface_id, v6_id;
int done_dns = 0, hostname_auth = 0, do_encap = 0;
unsigned char *outmsgtypep;
struct dhcp_opt *opt_cfg;
@@ -194,6 +194,11 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
iface_id.next = tags;
tags = &iface_id;
/* set tag "dhcpv6" */
v6_id.net = "dhcpv6";
v6_id.next = tags;
tags = &v6_id;
/* copy over transaction-id, and save pointer to message type */
outmsgtypep = put_opt6(inbuff, 4);
start_opts = save_counter(-1);
@@ -497,7 +502,7 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
ia_option = opt6_find(opt6_ptr(opt, ia_type == OPTION6_IA_NA ? 12 : 4), ia_end, OPTION6_IAADDR, 24);
/* reset "USED" flags on leases */
lease6_find(NULL, 0, ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, iaid, NULL);
lease6_filter(ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, iaid, context);
o = new_opt6(ia_type);
put_opt6_long(iaid);
@@ -520,32 +525,38 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
{
struct in6_addr *req_addr = opt6_ptr(ia_option, 0);
requested_time = opt6_uint(ia_option, 16, 4);
if (!address6_available(context, req_addr, tags) &&
(!have_config(config, CONFIG_ADDR6) || memcmp(&config->addr6, req_addr, IN6ADDRSZ) != 0))
struct dhcp_context *dynamic;
if ((dynamic = address6_available(context, req_addr, tags)) || address6_valid(context, req_addr, tags))
{
if (msg_type == DHCP6REQUEST)
if (!dynamic && !(have_config(config, CONFIG_ADDR6) && memcmp(&config->addr6, req_addr, IN6ADDRSZ) == 0))
{
/* host has a lease, but it's not on the correct link */
/* Static range, not configured. */
o1 = new_opt6(OPTION6_STATUS_CODE);
put_opt6_short(DHCP6NOTONLINK);
put_opt6_string("Not on link");
put_opt6_short(DHCP6UNSPEC);
put_opt6_string("Address unavailable");
end_opt6(o1);
}
else if (lease6_find_by_addr(req_addr, 128, 0) &&
!(lease = lease6_find(clid, clid_len, ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, iaid, req_addr)))
{
/* Address leased to another DUID/IAID */
o1 = new_opt6(OPTION6_STATUS_CODE);
put_opt6_short(DHCP6UNSPEC);
put_opt6_string("Address in use");
end_opt6(o1);
}
else
addrp = req_addr;
}
else if ((lease = lease6_find(NULL, 0, ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA,
iaid, req_addr)) &&
(clid_len != lease->clid_len ||
memcmp(clid, lease->clid, clid_len) != 0))
else if (msg_type == DHCP6REQUEST)
{
/* Address leased to another DUID */
/* requested address not on the correct link */
o1 = new_opt6(OPTION6_STATUS_CODE);
put_opt6_short(DHCP6UNSPEC);
put_opt6_string("Address in use");
put_opt6_short(DHCP6NOTONLINK);
put_opt6_string("Not on link");
end_opt6(o1);
}
else
addrp = req_addr;
}
}
else
{
@@ -570,14 +581,18 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it was previously declined"),
daemon->addrbuff);
else
addrp = &config->addr6;
{
addrp = &config->addr6;
/* may have existing lease for this address */
lease = lease6_find(clid, clid_len,
ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, iaid, addrp);
}
}
/* existing lease */
if (!addrp &&
(lease = lease6_find(clid, clid_len,
ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, iaid, NULL)) &&
address6_available(context, (struct in6_addr *)&lease->hwaddr, tags) &&
!config_find_by_address6(daemon->dhcp_conf, (struct in6_addr *)&lease->hwaddr, 128, 0))
addrp = (struct in6_addr *)&lease->hwaddr;
@@ -616,10 +631,16 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
hostname = NULL;
}
}
if (have_config(valid_config, CONFIG_TIME))
lease_time = valid_config->lease_time;
else
lease_time = this_context->lease_time;
lease_time = have_config(valid_config, CONFIG_TIME) ? valid_config->lease_time : this_context->lease_time;
if (this_context->valid < lease_time)
lease_time = this_context->valid;
if (ia_option)
if (ia_option)
{
if (requested_time < 120u )
requested_time = 120u; /* sanity */
@@ -731,7 +752,8 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
o1 = new_opt6(OPTION6_IAADDR);
put_opt6(addrp, sizeof(*addrp));
/* preferred lifetime */
put_opt6_long(this_context && (this_context->flags & CONTEXT_DEPRECATE) ? 0 : lease_time);
put_opt6_long(this_context && (this_context->preferred < lease_time) ?
this_context->preferred : lease_time);
put_opt6_long(lease_time); /* valid lifetime */
end_opt6(o1);
@@ -832,7 +854,7 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
iacntr = save_counter(-1);
/* reset "USED" flags on leases */
lease6_find(NULL, 0, ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, iaid, NULL);
lease6_filter(ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, iaid, context);
ia_option = opt6_ptr(opt, ia_type == OPTION6_IA_NA ? 12 : 4);
ia_end = opt6_ptr(opt, opt6_len(opt));
@@ -1001,10 +1023,17 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
case DHCP6IREQ:
{
/* We can't discriminate contexts based on address, as we don't know it.
If there is only one possible context, we can use its tags */
if (context && !context->current)
{
context->netid.next = NULL;
context_tags = &context->netid;
}
log6_packet("DHCPINFORMATION-REQUEST", clid, clid_len, NULL, xid, iface_name, ignore ? "ignored" : hostname);
if (ignore)
return 0;
*outmsgtypep = DHCP6REPLY;
log6_packet("DHCPINFORMATION-REQUEST", clid, clid_len, NULL, xid, iface_name, hostname);
break;
}
@@ -1036,7 +1065,7 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
ia_option = opt6_ptr(opt, ia_type == OPTION6_IA_NA ? 12 : 4);
/* reset "USED" flags on leases */
lease6_find(NULL, 0, ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, iaid, NULL);
lease6_filter(ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, iaid, context);
for (ia_option = opt6_find(ia_option, ia_end, OPTION6_IAADDR, 24);
ia_option;
@@ -1115,7 +1144,7 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
ia_option = opt6_ptr(opt, ia_type == OPTION6_IA_NA ? 12 : 4);
/* reset "USED" flags on leases */
lease6_find(NULL, 0, ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, iaid, NULL);
lease6_filter(ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, iaid, context);
for (ia_option = opt6_find(ia_option, ia_end, OPTION6_IAADDR, 24);
ia_option;

View File

@@ -20,7 +20,6 @@
#include <netinet/icmp6.h>
static int map_rebuild = 0;
static int ping_id = 0;
void slaac_add_addrs(struct dhcp_lease *lease, time_t now, int force)
@@ -38,7 +37,7 @@ void slaac_add_addrs(struct dhcp_lease *lease, time_t now, int force)
old = lease->slaac_address;
lease->slaac_address = NULL;
for (context = daemon->ra_contexts; context; context = context->next)
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;
@@ -123,7 +122,7 @@ time_t periodic_slaac(time_t now, struct dhcp_lease *leases)
struct slaac_address *slaac;
time_t next_event = 0;
for (context = daemon->ra_contexts; context; context = context->next)
for (context = daemon->dhcp6; context; context = context->next)
if ((context->flags & CONTEXT_RA_NAME))
break;
@@ -134,12 +133,6 @@ time_t periodic_slaac(time_t now, struct dhcp_lease *leases)
while (ping_id == 0)
ping_id = rand16();
if (map_rebuild)
{
map_rebuild = 0;
build_subnet_map();
}
for (lease = leases; lease; lease = lease->next)
for (slaac = lease->slaac_address; slaac; slaac = slaac->next)
{
@@ -211,51 +204,4 @@ void slaac_ping_reply(struct in6_addr *sender, unsigned char *packet, char *inte
lease_update_dns(gotone);
}
/* 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.
*/
static int add_subnet(struct in6_addr *local, int prefix,
int scope, int if_index, int dad, void *vparam)
{
struct dhcp_context *context;
(void)scope;
(void)dad;
(void)vparam;
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))
{
context->if_index = if_index;
context->local6 = *local;
}
return 1;
}
void build_subnet_map(void)
{
struct dhcp_context *context;
int ok = 0;
for (context = daemon->ra_contexts; context; context = context->next)
{
context->if_index = 0;
if ((context->flags & CONTEXT_RA_NAME))
ok = 1;
}
/* ra-names configured */
if (ok)
iface_enumerate(AF_INET6, NULL, add_subnet);
}
void schedule_subnet_map(void)
{
map_rebuild = 1;
}
#endif

View File

@@ -18,7 +18,7 @@
#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);
@@ -48,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
@@ -61,7 +61,6 @@ void tftp_request(struct listener *listen, time_t now)
char *name = NULL;
char *prefix = daemon->tftp_prefix;
struct tftp_prefix *pref;
struct interface_list *ir;
union {
struct cmsghdr align; /* this ensures alignment */
@@ -114,8 +113,6 @@ void tftp_request(struct listener *listen, time_t now)
else
{
struct cmsghdr *cmptr;
int check;
struct interface_list *ir;
if (msg.msg_controllen < sizeof(struct cmsghdr))
return;
@@ -195,29 +192,22 @@ void tftp_request(struct listener *listen, time_t now)
#ifdef HAVE_IPV6
if (listen->family == AF_INET6)
check = iface_check(AF_INET6, (struct all_addr *)&addr.in6.sin6_addr, name);
{
if (!iface_check(AF_INET6, (struct all_addr *)&addr.in6.sin6_addr, name, NULL))
return;
}
else
#endif
check = iface_check(AF_INET, (struct all_addr *)&addr.in.sin_addr, name);
if (!iface_check(AF_INET, (struct all_addr *)&addr.in.sin_addr, name, NULL))
return;
/* wierd TFTP service override */
for (ir = daemon->tftp_interfaces; ir; ir = ir->next)
if (strcmp(ir->interface, name) == 0)
break;
if (!ir)
{
if (!daemon->tftp_unlimited || !check)
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;
/* 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
}
strncpy(ifr.ifr_name, name, IF_NAMESIZE);
if (ioctl(listen->tftpfd, SIOCGIFMTU, &ifr) != -1)
mtu = ifr.ifr_mtu;
@@ -228,12 +218,7 @@ void tftp_request(struct listener *listen, time_t now)
/* 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;
prefix = pref->prefix;
}
if (listen->family == AF_INET)
@@ -325,8 +310,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)
@@ -363,7 +347,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;
@@ -390,7 +374,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);
@@ -411,7 +395,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;
@@ -448,7 +432,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

View File

@@ -280,7 +280,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;