Compare commits

...
113 Commits
Author SHA1 Message Date
Simon Kelley f6381cf482 Declare utime(). 2015-07-27 19:48:43 +01:00
Kevin Darbyshire-Bryant 34b5d19488 Update DNSSEC timestamp file on process TERM. 2015-07-27 19:34:23 +01:00
Conrad Kostecki d6cb7e4815 Update german translation 2015-07-27 19:22:49 +01:00
Simon Kelley d389e0191b DNSSEC fix: correct logic for signed records in unsigned DNS space. 2015-07-27 18:56:43 +01:00
Simon Kelley d3699bb6bc Small tweak to DNSSEC fix. 2015-07-16 22:37:37 +01:00
Simon Kelley 13480e8c2a DNSSEC fix, signed wildcard CNAME to unsigned domain. 2015-07-16 22:23:13 +01:00
Jan Psota 5b3b93f80a Update Polish translation. 2015-07-15 19:57:47 +01:00
Simon Kelley b69e845b1c Close Lauchpad bug in Debian changelog. 2015-07-15 19:54:50 +01:00
Simon Kelley 90c3822bfa Grow pollfds array exponentially. 2015-07-13 12:47:52 +01:00
Simon Kelley c895a0626d Merge messages for translations. 2015-07-12 21:27:40 +01:00
Simon Kelley b842bc97bb Use poll() instead of select() to remove limits on open file descriptors. 2015-07-12 21:09:11 +01:00
Simon Kelley 0f38fa05a6 Log message typo. 2015-07-08 22:42:14 +01:00
Simon Kelley 45c5cb1f8f Fix compilation warning. 2015-07-08 22:40:57 +01:00
Simon Kelley f6d6956261 Test for overflowing platform FD_SET size. 2015-07-08 22:38:13 +01:00
Simon Kelley 60176c7bf4 Bump version in Debian changelog. 2015-07-07 21:54:55 +01:00
Simon Kelley 362c9303da Fix inotify code to handle dangling symlinks better. 2015-07-06 21:48:49 +01:00
Simon Kelley 5e95a552ee Avoid hanngs in DHCP ping code when system time goes backwards. 2015-07-05 22:31:30 +01:00
Simon Kelley 90cb222551 --conf-file should read no file, not try and read the default file. 2015-07-05 21:59:10 +01:00
Ján Sáreník 850163288d Manpage typo fix. 2015-07-05 21:23:27 +01:00
Simon Kelley e3ec6f0bd7 Handle CNAMEs to DS records when confirming absence of DS for DNSSEC. 2015-06-12 21:39:11 +01:00
Simon Kelley f7bfbdc872 Merge messages and fix makefile process to do this. 2015-06-10 22:31:02 +01:00
Neil Jerram 4918bd5505 Documenation updates for --bridge-interface and "off-link". 2015-06-10 22:23:20 +01:00
Neil Jerram 9bad339af8 Apply --bridge-interfaces to unsolicited router advertisements. 2015-06-10 22:16:35 +01:00
Neil Jerram ba4fc0f996 Upply --bridge-interface aliasing to solicited router advertisements. 2015-06-10 22:14:49 +01:00
Neil Jerram 2fd5bc952d Allow router advertisements to have the "off-link" bit set. 2015-06-10 22:13:06 +01:00
Neil Jerram 0ddb8769bb Extend --bridge-interface aliasing to DHCPv6. 2015-06-10 22:11:06 +01:00
Neil Jerram 654f59e762 Fix logging of unknown interface in --bridge-interface, DHPCv4. 2015-06-10 22:06:33 +01:00
Simon Kelley d91b1fd402 Add a couple of missed logging strings to the catalogue. 2015-06-09 20:45:07 +01:00
Nicolas Cavallari c6d82c9ba6 Add Dbus methods to create and delete DHCP leases. 2015-06-09 20:42:20 +01:00
Simon Kelley 4d25cf89d5 Handle corner cases in NSEC coverage checks. 2015-06-06 23:13:57 +01:00
Simon Kelley 24e9207e13 More reproducibility fixes for Debian package. 2015-06-04 22:32:43 +01:00
Simon Kelley 89130d91d6 DHCPv6: DHCPCONFIRM should be OK for any address on link, not just dynamic addresses. 2015-06-03 22:34:14 +01:00
Simon Kelley d644b2a17d Close Debian bug for bug fixed upstream. 2015-06-01 21:00:16 +01:00
swigger bd7bfa21c4 Correctly sanitise DNS header bits in answer when recreating query for retry. 2015-06-01 20:54:59 +01:00
Simon Kelley 403de05925 Merge branch 'master' of ssh://thekelleys.org.uk/var/cache/git/dnsmasq 2015-05-26 22:12:01 +01:00
John Hanks 46c89f2bd0 Add infiniband to example config file. 2015-05-26 22:07:57 +01:00
Christian Demsar 23facf0d77 Man page typo. 2015-05-20 20:26:23 +01:00
Simon Kelley 549b1a478c Tweak immediately previous patch. 2015-05-20 20:20:24 +01:00
Simon Kelley 7f8565b94c Select correct DHCP context when in PXE bootserver mode. 2015-05-19 23:01:27 +01:00
Simon Kelley 06568c6636 Remove support for DNS Extended Label Types.
The support was only partial, and the whole concept is
now deprecated in the standards.
2015-05-15 20:43:48 +01:00
Simon Kelley 5d07d77e75 Fix buffer overflow introduced in 2.73rc6.
Fix off-by-one in code which checks for over-long domain names
in received DNS packets. This enables buffer overflow attacks
which can certainly crash dnsmasq and may allow for arbitrary
code execution. The problem was introduced in commit b8f16556d,
release 2.73rc6, so has not escaped into any stable release.
Note that the off-by-one was in the label length determination,
so the buffer can be overflowed by as many bytes as there are
labels in the name - ie, many.

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

Also, since we don't have the infrastructure to track that these
non-validated replies have RRSIGS, don't cache them, so we don't
provide answers with missing RRSIGS from the cache.
2015-04-03 21:25:05 +01:00
Stefan Tomanek 7aa970e2c7 Whitespace fixes. 2015-04-01 17:55:07 +01:00
Stefan Tomanek 30d0879ed5 add --tftp-no-fail to ignore missing tftp root 2015-03-31 22:32:11 +01:00
Simon Kelley fd6ad9e481 Merge message translations. 2015-03-30 07:52:21 +01:00
Simon Kelley 794fccca7f Fix crash in last commit. 2015-03-29 22:35:44 +01:00
Simon Kelley 394ff492da Allow control characters in names in the cache, handle when logging. 2015-03-29 22:17:14 +01:00
Simon Kelley 1e153945de DNSSEC fix for non-ascii characters in labels. 2015-03-28 21:34:07 +00:00
Simon Kelley 0b8a5a30a7 Protect against broken DNSSEC upstreams. 2015-03-27 11:44:55 +00:00
Simon Kelley 150162bc37 Return SERVFAIL when validation abandoned. 2015-03-27 09:58:26 +00:00
Simon Kelley 8805283088 Don't fail DNSSEC when a signed CNAME dangles into an unsigned zone. 2015-03-26 21:15:43 +00:00
Lung-Pin Chang 65c7212000 dhcp: set outbound interface via cmsg in unicast reply
If multiple routes to the same network exist, Linux blindly picks
  the first interface (route) based on destination address, which might not be
  the one we're actually offering leases. Rather than relying on this,
  always set the interface for outgoing unicast DHCP packets.
2015-03-19 23:22:21 +00:00
Simon Kelley 979fe86bc8 Make --address=/example.com/ equivalent to --server=/example.com/ 2015-03-19 22:50:22 +00:00
Simon Kelley ff841ebf5a Fix boilerplate code for re-running system calls on EINTR and EAGAIN etc.
The nasty code with static variable in retry_send() which
avoids looping forever needs to be called on success of the syscall,
to reset the static variable.
2015-03-11 21:36:30 +00:00
Simon Kelley 360f2513ab Tweak DNSSEC timestamp code to create file later, removing need to chown it. 2015-03-07 18:28:06 +00:00
Simon Kelley 4c960fa90a New version of contrib/reverse-dns 2015-03-04 20:32:26 +00:00
Simon Kelley 9003b50b13 Fix last commit to not crash if uid changing not configured. 2015-03-02 22:47:23 +00:00
Simon Kelley f6e62e2af9 Add --dnssec-timestamp option and facility. 2015-03-01 18:17:54 +00:00
Joachim Zobel 47b9ac59c7 Log parsing utils in contrib/reverse-dns 2015-02-23 21:38:11 +00:00
Tomas Hozza 0705a7e2d5 Fix uninitialized value used in get_client_mac() 2015-02-23 21:26:26 +00:00
Chen Wei 28b879ac47 Fix trivial memory leaks to quieten valgrind. 2015-02-17 22:07:35 +00:00
Simon Kelley caeea190f1 Make dynamic hosts files work when --no-hosts set. 2015-02-14 20:08:56 +00:00
Simon Kelley 8ff70de618 Typos. 2015-02-14 20:02:37 +00:00
Simon Kelley ee4d1cea92 Debian systemd fixes. 2015-02-12 18:30:32 +00:00
Shantanu Gadgil f4f400776b Fix get-version script which returned wrong tag in some situations. 2015-02-11 20:16:59 +00:00
Chris Lamb b467a454b4 Make Debian build reproducible. 2015-02-09 11:52:30 +00:00
Simon Kelley efb8b5566a man page typo. 2015-02-07 22:36:34 +00:00
Simon Kelley f9c863708c Extra logging for inotify code. 2015-02-03 21:52:48 +00:00
Simon Kelley 2941d3ac89 Fixup dhcp-configs after reading extra hostfiles with inotify. 2015-02-02 22:36:42 +00:00
Thiébaud Weksteen d36b732c4c Manpage typo fix. 2015-02-02 21:38:27 +00:00
Simon Kelley d2c5458e31 Debian changelog bugfix. 2015-02-02 21:27:39 +00:00
Simon Kelley 8d8a54ec79 Fix build failure on openBSD. 2015-02-01 21:48:46 +00:00
Simon Kelley 1062667618 BSD make support 2015-02-01 00:15:16 +00:00
Simon Kelley 6ef15b34ca Fix broken ECDSA DNSSEC signatures. 2015-01-31 22:44:26 +00:00
Simon Kelley 3d04f46334 inotify documentation updates. 2015-01-31 21:59:13 +00:00
Simon Kelley aff3396280 Update copyrights for dawn of 2015. 2015-01-31 20:13:40 +00:00
Simon Kelley 70d1873dd9 Expand inotify code to dhcp-hostsdir, dhcp-optsdir and hostsdir. 2015-01-31 19:59:29 +00:00
Simon Kelley 0491805d2f Allow inotify to be disabled at compile time on Linux. 2015-01-26 11:23:43 +00:00
Win King Wan 61b838dd57 Don't reply to DHCPv6 SOLICIT messages when not configured for statefull DHCPv6. 2015-01-21 20:41:48 +00:00
Conrad Kostecki fbf01f7046 Update German translation. 2015-01-20 21:07:56 +00:00
Simon Kelley 5f4dc5c6ca Add --dhcp-hostsdir config option. 2015-01-20 20:51:02 +00:00
Simon Kelley 2ae195f5a7 Don't treat SERVFAIL as a recoverable error..... 2015-01-18 22:20:48 +00:00
Simon Kelley 393415597c Cope with multiple interfaces with the same LL address. 2015-01-18 22:11:10 +00:00
Simon Kelley ae4624bf46 Logs for DS records consistent. 2015-01-12 23:22:08 +00:00
Simon Kelley 5e321739db Don't answer from cache RRsets from wildcards, as we don't have NSECs. 2015-01-12 23:16:56 +00:00
Simon Kelley 9f79ee4ae3 Log port of requestor when doing extra logging. 2015-01-12 20:18:18 +00:00
RinSatsuki 28de38768e Add --min-cache-ttl option. 2015-01-10 15:22:21 +00:00
Simon Kelley 25cf5e373e Add --log-queries=extra option for more complete logging. 2015-01-09 15:53:03 +00:00
68 changed files with 8050 additions and 5226 deletions

No files matched your search

+108 -1
View File
@@ -1,3 +1,15 @@
version 2.74
Fix reversion in 2.73 where --conf-file would attempt to
read the default file, rather than no file.
Fix inotify code to handle dangling symlinks better and
not SEGV in some circumstances.
DNSSEC fix. In the case of a signed CNAME generated by a
wildcard which pointed to an unsigned domain, the wrong
status would be logged, and some necessary checks omitted.
version 2.73 version 2.73
Fix crash at startup when an empty suffix is supplied to Fix crash at startup when an empty suffix is supplied to
--conf-dir, also trivial memory leak. Thanks to --conf-dir, also trivial memory leak. Thanks to
@@ -9,7 +21,9 @@ version 2.73
Use inotify for checking on updates to /etc/resolv.conf and Use inotify for checking on updates to /etc/resolv.conf and
friends under Linux. This fixes race conditions when the files are friends under Linux. This fixes race conditions when the files are
updated rapidly and saves CPU by noy polling. updated rapidly and saves CPU by noy polling. To build
a binary that runs on old Linux kernels without inotify,
use make COPTS=-DNO_INOTIFY
Fix breakage of --domain=<domain>,<subnet>,local - only reverse Fix breakage of --domain=<domain>,<subnet>,local - only reverse
queries were intercepted. THis appears to have been broken queries were intercepted. THis appears to have been broken
@@ -40,6 +54,99 @@ version 2.73
nameservers in the unsigned parts of the DNS tree nameservers in the unsigned parts of the DNS tree
which don't respond well to DNSSEC queries. which don't respond well to DNSSEC queries.
Add --log-queries=extra option, which makes logs easier
to search automatically.
Add --min-cache-ttl option. I've resisted this for a long
time, on the grounds that disbelieving TTLs is never a
good idea, but I've been persuaded that there are
sometimes reasons to do it. (Step forward, GFW).
To avoid misuse, there's a hard limit on the TTL
floor of one hour. Thansk to RinSatsuki for the patch.
Cope with multiple interfaces with the same link-local
address. (IPv6 addresses are scoped, so this is allowed.)
Thanks to Cory Benfield for help with this.
Add --dhcp-hostsdir. This allows addition of new host
configurations to a running dnsmasq instance much more
cheaply than having dnsmasq re-read all its existing
configuration each time.
Don't reply to DHCPv6 SOLICIT messages if we're not
configured to do stateful DHCPv6. Thanks to Win King Wan
for the patch.
Fix broken DNSSEC validation of ECDSA signatures.
Add --dnssec-timestamp option, which provides an automatic
way to detect when the system time becomes valid after
boot on systems without an RTC, whilst allowing DNS
queries before the clock is valid so that NTP can run.
Thanks to Kevin Darbyshire-Bryant for developing this idea.
Add --tftp-no-fail option. Thanks to Stefan Tomanek for
the patch.
Fix crash caused by looking up servers.bind, CHAOS text
record, when more than about five --servers= lines are
in the dnsmasq config. This causes memory corruption
which causes a crash later. Thanks to Matt Coddington for
sterling work chasing this down.
Fix crash on receipt of certain malformed DNS requests.
Thanks to Nick Sampanis for spotting the problem.
Note that this is could allow the dnsmasq process's
memory to be read by an attacker under certain
circumstances, so it has a CVE, CVE-2015-3294
Fix crash in authoritative DNS code, if a .arpa zone
is declared as authoritative, and then a PTR query which
is not to be treated as authoritative arrived. Normally,
directly declaring .arpa zone as authoritative is not
done, so this crash wouldn't be seen. Instead the
relevant .arpa zone should be specified as a subnet
in the auth-zone declaration. Thanks to Johnny S. Lee
for the bugreport and initial patch.
Fix authoritative DNS code to correctly reply to NS
and SOA queries for .arpa zones for which we are
declared authoritative by means of a subnet in auth-zone.
Previously we provided correct answers to PTR queries
in such zones (including NS and SOA) but not direct
NS and SOA queries. Thanks to Johnny S. Lee for
pointing out the problem.
Fix logging of DHCPREPLY which should be suppressed
by quiet-dhcp6. Thanks to J. Pablo Abonia for
spotting the problem.
Try and handle net connections with broken fragmentation
that lose large UDP packets. If a server times out,
reduce the maximum UDP packet size field in the EDNS0
header to 1280 bytes. If it then answers, make that
change permanent.
Check IPv4-mapped IPv6 addresses when --stop-rebind
is active. Thanks to Jordan Milne for spotting this.
Allow DHCPv4 options T1 and T2 to be set using --dhcp-option.
Thanks to Kevin Benton for patches and work on this.
Fix code for DHCPCONFIRM DHCPv6 messages to confirm addresses
in the correct subnet, even of not in dynamic address
allocation range. Thanks to Steve Hirsch for spotting
the problem.
Add AddDhcpLease and DeleteDhcpLease DBus methods. Thanks
to Nicolas Cavallari for the patch.
Allow configuration of router advertisements without the
"on-link" bit set. Thanks to Neil Jerram for the patch.
Extend --bridge-interface to DHCPv6 and router
advertisements. Thanks to Neil Jerram for the patch.
version 2.72 version 2.72
Add ra-advrouter mode, for RFC-3775 mobile IPv6 support. Add ra-advrouter mode, for RFC-3775 mobile IPv6 support.
+7 -5
View File
@@ -1,4 +1,4 @@
# dnsmasq is Copyright (c) 2000-2014 Simon Kelley # dnsmasq is Copyright (c) 2000-2015 Simon Kelley
# #
# This program is free software; you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@@ -64,14 +64,16 @@ nettle_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC $(PKG_CONFIG
gmp_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC NO_GMP --copy -lgmp` gmp_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC NO_GMP --copy -lgmp`
sunos_libs = `if uname | grep SunOS >/dev/null 2>&1; then echo -lsocket -lnsl -lposix4; fi` sunos_libs = `if uname | grep SunOS >/dev/null 2>&1; then echo -lsocket -lnsl -lposix4; fi`
version = -DVERSION='\"`$(top)/bld/get-version $(top)`\"' version = -DVERSION='\"`$(top)/bld/get-version $(top)`\"'
copts_conf = .copts_$(shell $(CC) -DDNSMASQ_COMPILE_OPTS $(COPTS) -E $(top)/$(SRC)/dnsmasq.h | \
( md5sum 2>/dev/null || md5 ) | cut -f 1 -d ' ') sum?=$(shell $(CC) -DDNSMASQ_COMPILE_OPTS $(COPTS) -E $(top)/$(SRC)/dnsmasq.h | ( md5sum 2>/dev/null || md5 ) | cut -f 1 -d ' ')
sum!=$(CC) -DDNSMASQ_COMPILE_OPTS $(COPTS) -E $(top)/$(SRC)/dnsmasq.h | ( md5sum 2>/dev/null || md5 ) | cut -f 1 -d ' '
copts_conf = .copts_$(sum)
objs = cache.o rfc1035.o util.o option.o forward.o network.o \ 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 \ 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 \ helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o \
dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o \ dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o \
domain.o dnssec.o blockdata.o tables.o loop.o inotify.o domain.o dnssec.o blockdata.o tables.o loop.o inotify.o poll.o
hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \ hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \
dns-protocol.h radv-protocol.h ip6addr.h dns-protocol.h radv-protocol.h ip6addr.h
@@ -115,7 +117,7 @@ install-i18n : all-i18n install-common
cd $(MAN); ../bld/install-man $(DESTDIR)$(MANDIR) $(INSTALL) cd $(MAN); ../bld/install-man $(DESTDIR)$(MANDIR) $(INSTALL)
merge : merge :
@cd $(BUILDDIR) && $(MAKE) -f $(top)/Makefile dnsmasq.pot @cd $(BUILDDIR) && $(MAKE) top="$(top)" -f $(top)/Makefile dnsmasq.pot
for f in `cd $(PO); echo *.po`; do \ for f in `cd $(PO); echo *.po`; do \
echo -n msgmerge $(PO)/$$f && $(MSGMERGE) --no-wrap -U $(PO)/$$f $(BUILDDIR)/dnsmasq.pot; \ echo -n msgmerge $(PO)/$$f && $(MSGMERGE) --no-wrap -U $(PO)/$$f $(BUILDDIR)/dnsmasq.pot; \
done done
+1 -1
View File
@@ -10,7 +10,7 @@ LOCAL_SRC_FILES := bpf.c cache.c dbus.c dhcp.c dnsmasq.c \
dhcp6.c rfc3315.c dhcp-common.c outpacket.c \ dhcp6.c rfc3315.c dhcp-common.c outpacket.c \
radv.c slaac.c auth.c ipset.c domain.c \ radv.c slaac.c auth.c ipset.c domain.c \
dnssec.c dnssec-openssl.c blockdata.c tables.c \ dnssec.c dnssec-openssl.c blockdata.c tables.c \
loop.c inotify.c loop.c inotify.c poll.c
LOCAL_MODULE := dnsmasq LOCAL_MODULE := dnsmasq
+4 -3
View File
@@ -11,8 +11,9 @@
# If there is more than one v[0-9].* tag, sort them and use the # If there is more than one v[0-9].* tag, sort them and use the
# first. This favours, eg v2.63 over 2.63rc6. # first. This favours, eg v2.63 over 2.63rc6.
if which git >/dev/null 2>&1 && [ -d $1/.git ]; then if which git >/dev/null 2>&1 && \
cd $1; git describe | sed 's/^v//' ([ -d $1/.git ] || grep '^gitdir:' $1/.git >/dev/null 2>&1); then
cd $1; git describe | sed 's/^v//'
elif grep '\$Format:%d\$' $1/VERSION >/dev/null 2>&1; then elif grep '\$Format:%d\$' $1/VERSION >/dev/null 2>&1; then
# unsubstituted VERSION, but no git available. # unsubstituted VERSION, but no git available.
echo UNKNOWN echo UNKNOWN
@@ -20,7 +21,7 @@ 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 if [ $? -eq 0 ]; then
echo "${vers}" | sort | head -n 1 | sed 's/^v//' echo "${vers}" | sort -r | head -n 1 | sed 's/^v//'
else else
cat $1/VERSION cat $1/VERSION
fi fi
+18
View File
@@ -0,0 +1,18 @@
The script reads stdin and replaces all IP addresses with names before
outputting it again. IPs from private networks are reverse looked up
via dns. Other IP adresses are searched for in the dnsmasq query log.
This gives names (CNAMEs if I understand DNS correctly) that are closer
to the name the client originally asked for then the names obtained by
reverse lookup. Just run
netstat -n -4 | ./reverse_replace.sh
to see what it does. It needs
log-queries
log-facility=/var/log/dnsmasq.log
in the dnsmasq configuration.
The script runs on debian (with ash installed) and on busybox.
+125
View File
@@ -0,0 +1,125 @@
#!/bin/ash
# $Id: reverse_replace.sh 18 2015-03-01 16:12:35Z jo $
#
# Usage e.g.: netstat -n -4 | reverse_replace.sh
# Parses stdin for IP4 addresses and replaces them
# with names retrieved by parsing the dnsmasq log.
# This currently only gives CNAMEs. But these
# usually tell ou more than the mones from reverse
# lookups.
#
# This has been tested on debian and asuswrt. Plese
# report successful tests on other platforms.
#
# Author: Joachim Zobel <jz-2014@heute-morgen.de>
# License: Consider this MIT style licensed. You can
# do as you ike, but you must not remove my name.
#
LOG=/var/log/dnsmasq.log
MAX_LINES=15000
# sed regex do match IPs
IP_regex='[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}'
# private IP ranges
IP_private='\(^127\.\)\|\(^192\.168\.\)\|\(^10\.\)\|\(^172\.1[6-9]\.\)\|\(^172\.2[0-9]\.\)\|\(^172\.3[0-1]\.\)'
#######################################################################
# Find Commands
HOST=nslookup
if type host > /dev/null 2>&1; then
# echo "No need for nslookup, host is there"
HOST=host
fi
#######################################################################
# Functions
# Use shell variables for an (IP) lookup table
create_lookup_table()
{
# Parse log into lookup table
local CMDS="$( tail -"$MAX_LINES" "$LOG" | \
grep " is $IP_regex" | \
sed "s#.* \([^ ]*\) is \($IP_regex\).*#set_val \2 \1;#" )"
local IFS='
'
for CMD in $CMDS
do
eval $CMD
done
}
set_val()
{
local _IP=$(echo $1 | tr . _)
local KEY="__IP__$_IP"
eval "$KEY"=$2
}
get_val()
{
local _IP=$(echo $1 | tr . _)
local KEY="__IP__$_IP"
eval echo -n '${'"$KEY"'}'
}
dns_lookup()
{
local IP=$1
local RTN="$($HOST $IP | \
sed 's#\s\+#\n#g' | \
grep -v '^$' | \
tail -1 | tr -d '\n' | \
sed 's#\.$##')"
if echo $RTN | grep -q NXDOMAIN; then
echo -n $IP
else
echo -n "$RTN"
fi
}
reverse_dns()
{
local IP=$1
# Skip if it is not an IP
if ! echo $IP | grep -q "^$IP_regex$"; then
echo -n $IP
return
fi
# Do a dns lookup, if it is a local IP
if echo $IP | grep -q $IP_private; then
dns_lookup $IP
return
fi
local NAME="$(get_val $IP)"
if [ -z "$NAME" ]; then
echo -n $IP
else
echo -n $NAME
fi
}
#######################################################################
# Main
create_lookup_table
while read LINE; do
for IP in $(echo "$LINE" | \
sed "s#\b\($IP_regex\)\b#\n\1\n#g" | \
grep $IP_regex)
do
NAME=`reverse_dns $IP `
# echo "$NAME $IP"
LINE=`echo "$LINE" | sed "s#$IP#$NAME#" `
done
echo $LINE
done
+5
View File
@@ -277,6 +277,11 @@ int main(int argc, char **argv)
exit(1); exit(1);
} }
if (inet_addr(argv[2]) == INADDR_NONE)
{
perror("invalid ip address");
exit(1);
}
lease.s_addr = inet_addr(argv[2]); lease.s_addr = inet_addr(argv[2]);
server = find_interface(lease, nl, if_nametoindex(argv[1])); server = find_interface(lease, nl, if_nametoindex(argv[1]));
+74
View File
@@ -169,6 +169,80 @@ Return an array of strings, each string is the IP address of an upstream
server which has been found to loop queries back to this dnsmasq instance, and server which has been found to loop queries back to this dnsmasq instance, and
it therefore not being used. it therefore not being used.
AddDhcpLease
------------
Returns nothing. Adds or updates a DHCP or DHCPv6 lease to the internal lease
database, as if a client requested and obtained a lease.
If a lease for the IPv4 or IPv6 address already exist, it is overwritten.
Note that this function will trigger the DhcpLeaseAdded or DhcpLeaseUpdated
D-Bus signal and will run the configured DHCP lease script accordingly.
This function takes many arguments which are the lease parameters:
- A string with the textual representation of the IPv4 or IPv6 address of the
client.
Examples:
"192.168.1.115"
"1003:1234:abcd::1%eth0"
"2001:db8:abcd::1"
- A string representing the hardware address of the client, using the same
format as the one used in the lease database.
Examples:
"00:23:45:67:89:ab"
"06-00:20:e0:3b:13:af" (token ring)
- The hostname of the client, as an array of bytes (so there is no problem
with non-ASCII character encoding). May be empty.
Example (for "hostname.or.fqdn"):
[104, 111, 115, 116, 110, 97, 109, 101, 46, 111, 114, 46, 102, 113, 100, 110]
- The client identifier (IPv4) or DUID (IPv6) as an array of bytes. May be
empty.
Examples:
DHCPv6 DUID:
[0, 3, 0, 1, 0, 35, 69, 103, 137, 171]
DHCPv4 client identifier:
[255, 12, 34, 56, 78, 0, 1, 0, 1, 29, 9, 99, 190, 35, 69, 103, 137, 171]
- The duration of the lease, in seconds. If the lease is updated, then
the duration replaces the previous duration.
Example:
7200
- The IAID (Identity association identifier) of the DHCPv6 lease, as a network
byte-order unsigned integer. For DHCPv4 leases, this must be set to 0.
Example (for IPv6):
203569230
- A boolean which, if true, indicates that the DHCPv6 lease is for a temporary
address (IA_TA). If false, the DHCPv6 lease is for a non-temporary address
(IA_NA). For DHCPv4 leases, this must be set to false.
RemoveDhcpLease
---------------
Returns nothing. Removes a DHCP or DHCPv6 lease to the internal lease
database, as if a client sent a release message to abandon a lease.
This function takes only one parameter: the text representation of the
IPv4 or IPv6 address of the lease to remove.
Note that this function will trigger the DhcpLeaseRemoved signal and the
configured DHCP lease script will be run with the "del" action.
2. SIGNALS 2. SIGNALS
+29 -2
View File
@@ -1,10 +1,37 @@
dnsmasq (2.74-1) unstable; urgency=low
* New upstream. (LP: #1468611)
-- Simon Kelley <simon@thekelleys.org.uk> Wed, 15 Jul 2015 21:54:11 +0000
dnsmasq (2.73-2) unstable; urgency=low
* Fix behaviour of empty --conf-file (closes: #790341)
-- Simon Kelley <simon@thekelleys.org.uk> Thu, 7 Jul 2015 21:46:42 +0000
dnsmasq (2.73-1) unstable; urgency=low dnsmasq (2.73-1) unstable; urgency=low
* New upstream. * New upstream. (closes: #786996)
* Tweak field width in cache dump to avoid truncating IPv6 * Tweak field width in cache dump to avoid truncating IPv6
addresses. (closes: #771557) addresses. (closes: #771557)
* Add newline at the end of example config file. (LP: #1416895)
* Make Debian package build reproducible. (closes: #777323)
* Add Requires=network.target to systemd unit.
-- Simon Kelley <simon@thekelleys.org.uk> Fri, 03 Oct 2014 08:49:42 +0000 -- Simon Kelley <simon@thekelleys.org.uk> Thu, 4 Jun 2015 22:31:42 +0000
dnsmasq (2.72-3) unstable; urgency=medium
* debian/systemd.service: switch from Type=dbus to Type=forking.
dnsmasq does not depend on dbus, but Type=dbus systemd services cannot
work without it. (Closes: #769486, #776530)
- debian/init: when called with systemd-exec argument, let dnsmasq
go into the background, so Type=forking can detect when it is ready
* Remove line containing only whitespace in debian/contol.
(closes: #777571)
-- Simon Kelley <simon@thekelleys.org.uk> Wed, 11 Feb 2015 21:56:12 +0000
dnsmasq (2.72-2) unstable; urgency=low dnsmasq (2.72-2) unstable; urgency=low
-2
View File
@@ -42,5 +42,3 @@ Description: Utilities for manipulating DHCP leases
Small utilities to query a DHCP server's lease database and Small utilities to query a DHCP server's lease database and
remove leases from it. These programs are distributed with dnsmasq remove leases from it. These programs are distributed with dnsmasq
and may not work correctly with other DHCP servers. and may not work correctly with other DHCP servers.
+1 -1
View File
@@ -1,4 +1,4 @@
dnsmasq is Copyright (c) 2000-2013 Simon Kelley dnsmasq is Copyright (c) 2000-2015 Simon Kelley
It was downloaded from: http://www.thekelleys.org.uk/dnsmasq/ It was downloaded from: http://www.thekelleys.org.uk/dnsmasq/
Vendored
+1 -3
View File
@@ -293,9 +293,7 @@ case "$1" in
mkdir /var/run/dnsmasq || return 2 mkdir /var/run/dnsmasq || return 2
chown dnsmasq:nogroup /var/run/dnsmasq || return 2 chown dnsmasq:nogroup /var/run/dnsmasq || return 2
fi fi
# Enable DBus by default because we use DBus activation with systemd. exec $DAEMON -x /var/run/dnsmasq/$NAME.pid \
exec $DAEMON --keep-in-foreground --enable-dbus \
-x /var/run/dnsmasq/$NAME.pid \
${MAILHOSTNAME:+ -m $MAILHOSTNAME} \ ${MAILHOSTNAME:+ -m $MAILHOSTNAME} \
${MAILTARGET:+ -t $MAILTARGET} \ ${MAILTARGET:+ -t $MAILTARGET} \
${DNSMASQ_USER:+ -u $DNSMASQ_USER} \ ${DNSMASQ_USER:+ -u $DNSMASQ_USER} \
+17 -13
View File
@@ -24,6 +24,7 @@ DEB_COPTS = $(COPTS)
TARGET = install-i18n TARGET = install-i18n
DEB_HOST_ARCH_OS := $(shell dpkg-architecture -qDEB_HOST_ARCH_OS) DEB_HOST_ARCH_OS := $(shell dpkg-architecture -qDEB_HOST_ARCH_OS)
BUILD_DATE := $(shell dpkg-parsechangelog --show-field Date)
# Force package version based on git tags. # Force package version based on git tags.
ifneq (,$(filter gitversion,$(DEB_BUILD_OPTIONS))) ifneq (,$(filter gitversion,$(DEB_BUILD_OPTIONS)))
@@ -118,8 +119,9 @@ binary-indep: checkroot
install -m 644 debian/systemd.service debian/daemon/lib/systemd/system/dnsmasq.service 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 install -m 644 debian/insserv debian/daemon/etc/insserv.conf.d/dnsmasq
ln -s $(package) debian/daemon/usr/share/doc/dnsmasq ln -s $(package) debian/daemon/usr/share/doc/dnsmasq
cd debian/daemon && find . -type f ! -regex '.*DEBIAN/.*' -printf '%P\0' | xargs -r0 md5sum > DEBIAN/md5sums cd debian/daemon && find . -type f ! -regex '.*DEBIAN/.*' -printf '%P\0' | LC_ALL=C sort -z | xargs -r0 md5sum > DEBIAN/md5sums
dpkg-gencontrol $(PACKAGE_VERSION) -T -pdnsmasq -Pdebian/daemon dpkg-gencontrol $(PACKAGE_VERSION) -T -pdnsmasq -Pdebian/daemon
find debian/daemon -depth -newermt '$(BUILD_DATE)' -print0 | xargs -0r touch --no-dereference --date='$(BUILD_DATE)'
chown -R root.root debian/daemon chown -R root.root debian/daemon
chmod -R g-ws debian/daemon chmod -R g-ws debian/daemon
dpkg --build debian/daemon .. dpkg --build debian/daemon ..
@@ -144,34 +146,35 @@ ifeq (,$(findstring nodocs,$(DEB_BUILD_OPTIONS)))
install -m 644 dnsmasq.conf.example debian/base/usr/share/doc/$(package)/examples/. install -m 644 dnsmasq.conf.example debian/base/usr/share/doc/$(package)/examples/.
install -m 644 trust-anchors.conf debian/base/usr/share/$(package)/. install -m 644 trust-anchors.conf debian/base/usr/share/$(package)/.
install -m 644 FAQ debian/base/usr/share/doc/$(package)/. install -m 644 FAQ debian/base/usr/share/doc/$(package)/.
gzip -9 debian/base/usr/share/doc/$(package)/FAQ gzip -9n debian/base/usr/share/doc/$(package)/FAQ
install -m 644 CHANGELOG debian/base/usr/share/doc/$(package)/changelog install -m 644 CHANGELOG debian/base/usr/share/doc/$(package)/changelog
gzip -9 debian/base/usr/share/doc/$(package)/changelog gzip -9n debian/base/usr/share/doc/$(package)/changelog
install -m 644 CHANGELOG.archive debian/base/usr/share/doc/$(package)/changelog.archive install -m 644 CHANGELOG.archive debian/base/usr/share/doc/$(package)/changelog.archive
gzip -9 debian/base/usr/share/doc/$(package)/changelog.archive gzip -9n debian/base/usr/share/doc/$(package)/changelog.archive
install -m 644 dbus/DBus-interface debian/base/usr/share/doc/$(package)/. install -m 644 dbus/DBus-interface debian/base/usr/share/doc/$(package)/.
gzip -9 debian/base/usr/share/doc/$(package)/DBus-interface gzip -9n debian/base/usr/share/doc/$(package)/DBus-interface
endif endif
install -m 644 debian/dnsmasq-base.conffiles debian/base/DEBIAN/conffiles 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.postinst debian/base/DEBIAN/postinst
install -m 755 debian/dnsmasq-base.postrm debian/base/DEBIAN/postrm install -m 755 debian/dnsmasq-base.postrm debian/base/DEBIAN/postrm
install -m 644 debian/changelog debian/base/usr/share/doc/$(package)/changelog.Debian install -m 644 debian/changelog debian/base/usr/share/doc/$(package)/changelog.Debian
gzip -9 debian/base/usr/share/doc/$(package)/changelog.Debian gzip -9n debian/base/usr/share/doc/$(package)/changelog.Debian
install -m 644 debian/readme debian/base/usr/share/doc/$(package)/README.Debian install -m 644 debian/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/copyright debian/base/usr/share/doc/$(package)/copyright
install -m 644 debian/dbus.conf debian/base/etc/dbus-1/system.d/dnsmasq.conf 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 gzip -9n debian/base/usr/share/man/man8/dnsmasq.8
for f in debian/base/usr/share/man/*; do \ for f in debian/base/usr/share/man/*; do \
if [ -f $$f/man8/dnsmasq.8 ]; then \ if [ -f $$f/man8/dnsmasq.8 ]; then \
gzip -9 $$f/man8/dnsmasq.8 ; \ gzip -9n $$f/man8/dnsmasq.8 ; \
fi \ fi \
done done
ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS))) ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
strip -R .note -R .comment debian/base/usr/sbin/dnsmasq strip -R .note -R .comment debian/base/usr/sbin/dnsmasq
endif endif
cd debian/base && find . -type f ! -regex '.*DEBIAN/.*' -printf '%P\0' | xargs -r0 md5sum > DEBIAN/md5sums cd debian/base && find . -type f ! -regex '.*DEBIAN/.*' -printf '%P\0' | LC_ALL=C sort -z | xargs -r0 md5sum > DEBIAN/md5sums
dpkg-shlibdeps --warnings=1 debian/base/usr/sbin/dnsmasq dpkg-shlibdeps --warnings=1 debian/base/usr/sbin/dnsmasq
dpkg-gencontrol $(PACKAGE_VERSION) -pdnsmasq-base -Pdebian/base dpkg-gencontrol $(PACKAGE_VERSION) -pdnsmasq-base -Pdebian/base
find debian/base -depth -newermt '$(BUILD_DATE)' -print0 | xargs -0r touch --no-dereference --date='$(BUILD_DATE)'
chown -R root.root debian/base chown -R root.root debian/base
chmod -R g-ws debian/base chmod -R g-ws debian/base
dpkg --build debian/base .. dpkg --build debian/base ..
@@ -185,20 +188,21 @@ ifeq ($(DEB_HOST_ARCH_OS),linux)
make -C contrib/wrt PREFIX=/usr DESTDIR=`pwd`/debian/utils CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" COPTS="$(DEB_COPTS)" CC=gcc make -C contrib/wrt PREFIX=/usr DESTDIR=`pwd`/debian/utils CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" COPTS="$(DEB_COPTS)" CC=gcc
install -m 755 contrib/wrt/dhcp_release debian/utils/usr/bin/dhcp_release install -m 755 contrib/wrt/dhcp_release debian/utils/usr/bin/dhcp_release
install -m 644 contrib/wrt/dhcp_release.1 debian/utils/usr/share/man/man1/dhcp_release.1 install -m 644 contrib/wrt/dhcp_release.1 debian/utils/usr/share/man/man1/dhcp_release.1
gzip -9 debian/utils/usr/share/man/man1/dhcp_release.1 gzip -9n debian/utils/usr/share/man/man1/dhcp_release.1
install -m 755 contrib/wrt/dhcp_lease_time debian/utils/usr/bin/dhcp_lease_time install -m 755 contrib/wrt/dhcp_lease_time debian/utils/usr/bin/dhcp_lease_time
install -m 644 contrib/wrt/dhcp_lease_time.1 debian/utils/usr/share/man/man1/dhcp_lease_time.1 install -m 644 contrib/wrt/dhcp_lease_time.1 debian/utils/usr/share/man/man1/dhcp_lease_time.1
install -m 644 debian/copyright debian/utils/usr/share/doc/dnsmasq-utils/copyright install -m 644 debian/copyright debian/utils/usr/share/doc/dnsmasq-utils/copyright
install -m 644 debian/changelog debian/utils/usr/share/doc/dnsmasq-utils/changelog.Debian install -m 644 debian/changelog debian/utils/usr/share/doc/dnsmasq-utils/changelog.Debian
gzip -9 debian/utils/usr/share/doc/dnsmasq-utils/changelog.Debian gzip -9n debian/utils/usr/share/doc/dnsmasq-utils/changelog.Debian
gzip -9 debian/utils/usr/share/man/man1/dhcp_lease_time.1 gzip -9n debian/utils/usr/share/man/man1/dhcp_lease_time.1
ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS))) ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
strip -R .note -R .comment debian/utils/usr/bin/dhcp_release strip -R .note -R .comment debian/utils/usr/bin/dhcp_release
strip -R .note -R .comment debian/utils/usr/bin/dhcp_lease_time strip -R .note -R .comment debian/utils/usr/bin/dhcp_lease_time
endif endif
cd debian/utils && find . -type f ! -regex '.*DEBIAN/.*' -printf '%P\0' | xargs -r0 md5sum > DEBIAN/md5sums cd debian/utils && find . -type f ! -regex '.*DEBIAN/.*' -printf '%P\0' | LC_ALL=C sort -z | xargs -r0 md5sum > DEBIAN/md5sums
dpkg-shlibdeps -Tdebian/utils-substvars debian/utils/usr/bin/dhcp_release debian/utils/usr/bin/dhcp_lease_time dpkg-shlibdeps -Tdebian/utils-substvars debian/utils/usr/bin/dhcp_release debian/utils/usr/bin/dhcp_lease_time
dpkg-gencontrol $(PACKAGE_VERSION) -Tdebian/utils-substvars -pdnsmasq-utils -Pdebian/utils dpkg-gencontrol $(PACKAGE_VERSION) -Tdebian/utils-substvars -pdnsmasq-utils -Pdebian/utils
find debian/utils -depth -newermt '$(BUILD_DATE)' -print0 | xargs -0r touch --no-dereference --date='$(BUILD_DATE)'
chown -R root.root debian/utils chown -R root.root debian/utils
chmod -R g-ws debian/utils chmod -R g-ws debian/utils
dpkg --build debian/utils .. dpkg --build debian/utils ..
+3 -7
View File
@@ -1,9 +1,10 @@
[Unit] [Unit]
Description=dnsmasq - A lightweight DHCP and caching DNS server Description=dnsmasq - A lightweight DHCP and caching DNS server
Requires=network.target
[Service] [Service]
Type=dbus Type=forking
BusName=uk.org.thekelleys.dnsmasq PIDFile=/var/run/dnsmasq/dnsmasq.pid
# Test the config file and refuse starting if it is not valid. # Test the config file and refuse starting if it is not valid.
ExecStartPre=/usr/sbin/dnsmasq --test ExecStartPre=/usr/sbin/dnsmasq --test
@@ -11,11 +12,6 @@ ExecStartPre=/usr/sbin/dnsmasq --test
# We run dnsmasq via the /etc/init.d/dnsmasq script which acts as a # We run dnsmasq via the /etc/init.d/dnsmasq script which acts as a
# wrapper picking up extra configuration files and then execs dnsmasq # wrapper picking up extra configuration files and then execs dnsmasq
# itself, when called with the "systemd-exec" function. # itself, when called with the "systemd-exec" function.
#
# It also adds the command-line flags
# --keep-in-foreground --enable-dbus
# to enable DBus by default because we use DBus activation.
#
ExecStart=/etc/init.d/dnsmasq systemd-exec ExecStart=/etc/init.d/dnsmasq systemd-exec
# The systemd-*-resolvconf functions configure (and deconfigure) # The systemd-*-resolvconf functions configure (and deconfigure)
+18
View File
@@ -251,6 +251,13 @@
# the IP address 192.168.0.60 # the IP address 192.168.0.60
#dhcp-host=id:01:02:02:04,192.168.0.60 #dhcp-host=id:01:02:02:04,192.168.0.60
# Always give the Infiniband interface with hardware address
# 80:00:00:48:fe:80:00:00:00:00:00:00:f4:52:14:03:00:28:05:81 the
# ip address 192.168.0.61. The client id is derived from the prefix
# ff:00:00:00:00:00:02:00:00:02:c9:00 and the last 8 pairs of
# hex digits of the hardware address.
#dhcp-host=id:ff:00:00:00:00:00:02:00:00:02:c9:00:f4:52:14:03:00:28:05:81,192.168.0.61
# Always give the host with client identifier "marjorie" # Always give the host with client identifier "marjorie"
# the IP address 192.168.0.60 # the IP address 192.168.0.60
#dhcp-host=id:marjorie,192.168.0.60 #dhcp-host=id:marjorie,192.168.0.60
@@ -345,6 +352,14 @@
# Ask client to poll for option changes every six hours. (RFC4242) # Ask client to poll for option changes every six hours. (RFC4242)
#dhcp-option=option6:information-refresh-time,6h #dhcp-option=option6:information-refresh-time,6h
# Set option 58 client renewal time (T1). Defaults to half of the
# lease time if not specified. (RFC2132)
#dhcp-option=option:T1:1m
# Set option 59 rebinding time (T2). Defaults to 7/8 of the
# lease time if not specified. (RFC2132)
#dhcp-option=option:T2:2m
# Set the NTP time server address to be the same machine as # Set the NTP time server address to be the same machine as
# is running dnsmasq # is running dnsmasq
#dhcp-option=42,0.0.0.0 #dhcp-option=42,0.0.0.0
@@ -486,6 +501,9 @@
# Set the root directory for files available via FTP. # Set the root directory for files available via FTP.
#tftp-root=/var/ftpd #tftp-root=/var/ftpd
# Do not abort if the tftp-root is unavailable
#tftp-no-fail
# Make the TFTP server more secure: with this set, only files owned by # Make the TFTP server more secure: with this set, only files owned by
# the user dnsmasq is running as will be send over the net. # the user dnsmasq is running as will be send over the net.
#tftp-secure #tftp-secure
+3 -1
View File
@@ -74,7 +74,9 @@ for details.
There is a dnsmasq mailing list at <A There is a dnsmasq mailing list at <A
HREF="http://lists.thekelleys.org.uk/mailman/listinfo/dnsmasq-discuss"> HREF="http://lists.thekelleys.org.uk/mailman/listinfo/dnsmasq-discuss">
http://lists.thekelleys.org.uk/mailman/listinfo/dnsmasq-discuss</A> which should be the http://lists.thekelleys.org.uk/mailman/listinfo/dnsmasq-discuss</A> which should be the
first location for queries, bugreports, suggestions etc. first location for queries, bugreports, suggestions etc. The list is mirrored, with a
search facility, at <A HREF="https://www.mail-archive.com/dnsmasq-discuss@lists.thekelleys.org.uk/">
https://www.mail-archive.com/dnsmasq-discuss@lists.thekelleys.org.uk/</A>.
You can contact me at <A You can contact me at <A
HREF="mailto:simon@thekelleys.org.uk">simon@thekelleys.org.uk</A>. HREF="mailto:simon@thekelleys.org.uk">simon@thekelleys.org.uk</A>.
+57 -13
View File
@@ -50,6 +50,10 @@ Additional hosts file. Read the specified file as well as /etc/hosts. If -h is g
only the specified file. This option may be repeated for more than one only the specified file. This option may be repeated for more than one
additional hosts file. If a directory is given, then read all the files contained in that directory. additional hosts file. If a directory is given, then read all the files contained in that directory.
.TP .TP
.B --hostsdir=<path>
Read all the hosts files contained in the directory. New or changed files
are read automatically. See --dhcp-hostsdir for details.
.TP
.B \-E, --expand-hosts .B \-E, --expand-hosts
Add the domain to simple names (without a period) in /etc/hosts Add the domain to simple names (without a period) in /etc/hosts
in the same way as for DHCP-derived names. Note that this does not in the same way as for DHCP-derived names. Note that this does not
@@ -81,6 +85,12 @@ the upstream DNS servers.
.B --max-cache-ttl=<time> .B --max-cache-ttl=<time>
Set a maximum TTL value for entries in the cache. Set a maximum TTL value for entries in the cache.
.TP .TP
.B --min-cache-ttl=<time>
Extend short TTL values to the time given when caching them. Note that
artificially extending TTL values is in general a bad idea, do not do it
unless you have a good reason, and understand what you are doing.
Dnsmasq limits the value of this option to one hour, unless recompiled.
.TP
.B --auth-ttl=<time> .B --auth-ttl=<time>
Set the TTL value returned in answers from the authoritative server. Set the TTL value returned in answers from the authoritative server.
.TP .TP
@@ -98,7 +108,10 @@ only, to stop dnsmasq daemonising in production, use
.B -k. .B -k.
.TP .TP
.B \-q, --log-queries .B \-q, --log-queries
Log the results of DNS queries handled by dnsmasq. Enable a full cache dump on receipt of SIGUSR1. Log the results of DNS queries handled by dnsmasq. Enable a full cache dump on receipt of SIGUSR1. If the argument "extra" is supplied, ie
.B --log-queries=extra
then the log has extra information at the start of each line.
This consists of a serial number which ties together the log lines associated with an individual query, and the IP address of the requestor.
.TP .TP
.B \-8, --log-facility=<facility> .B \-8, --log-facility=<facility>
Set the facility to which dnsmasq will send syslog entries, this Set the facility to which dnsmasq will send syslog entries, this
@@ -447,7 +460,7 @@ but provides some syntactic sugar to make specifying address-to-name queries eas
is exactly equivalent to is exactly equivalent to
.B --server=/3.2.1.in-addr.arpa/192.168.0.1 .B --server=/3.2.1.in-addr.arpa/192.168.0.1
.TP .TP
.B \-A, --address=/<domain>/[domain/]<ipaddr> .B \-A, --address=/<domain>/[domain/][<ipaddr>]
Specify an IP address to return for any host in the given domains. Specify an IP address to return for any host in the given domains.
Queries in the domains are never forwarded and always replied to Queries in the domains are never forwarded and always replied to
with the specified IP address which may be IPv4 or IPv6. To give with the specified IP address which may be IPv4 or IPv6. To give
@@ -459,7 +472,10 @@ domain specification works in the same was as for --server, with the
additional facility that /#/ matches any domain. Thus additional facility that /#/ matches any domain. Thus
--address=/#/1.2.3.4 will always return 1.2.3.4 for any query not --address=/#/1.2.3.4 will always return 1.2.3.4 for any query not
answered from /etc/hosts or DHCP and not sent to an upstream answered from /etc/hosts or DHCP and not sent to an upstream
nameserver by a more specific --server directive. nameserver by a more specific --server directive. As for --server,
one or more domains with no address returns a no-such-domain answer, so
--address=/example.com/ is equivalent to --server=/example.com/ and returns
NXDOMAIN for example.com and all its subdomains.
.TP .TP
.B --ipset=/<domain>/[domain/]<ipset>[,<ipset>] .B --ipset=/<domain>/[domain/]<ipset>[,<ipset>]
Places the resolved IP addresses of queries for the specified domains Places the resolved IP addresses of queries for the specified domains
@@ -503,7 +519,7 @@ zone files: the port, weight and priority numbers are in a different
order. More than one SRV record for a given service/domain is allowed, order. More than one SRV record for a given service/domain is allowed,
all that match are returned. all that match are returned.
.TP .TP
.B --host-record=<name>[,<name>....][<IPv4-address>],[<IPv6-address>] .B --host-record=<name>[,<name>....],[<IPv4-address>],[<IPv6-address>]
Add A, AAAA and PTR records to the DNS. This adds one or more names to Add A, AAAA and PTR records to the DNS. This adds one or more names to
the DNS with associated IPv4 (A) and IPv6 (AAAA) records. A name may the DNS with associated IPv4 (A) and IPv6 (AAAA) records. A name may
appear in more than one appear in more than one
@@ -638,7 +654,7 @@ Provide DS records to act a trust anchors for DNSSEC
validation. Typically these will be the DS record(s) for Zone Signing validation. Typically these will be the DS record(s) for Zone Signing
key(s) of the root zone, key(s) of the root zone,
but trust anchors for limited domains are also possible. The current but trust anchors for limited domains are also possible. The current
root-zone trust anchors may be donwloaded from https://data.iana.org/root-anchors/root-anchors.xml root-zone trust anchors may be downloaded from https://data.iana.org/root-anchors/root-anchors.xml
.TP .TP
.B --dnssec-check-unsigned .B --dnssec-check-unsigned
As a default, dnsmasq does not check that unsigned DNS replies are As a default, dnsmasq does not check that unsigned DNS replies are
@@ -661,6 +677,13 @@ that dnsmasq should be started with this flag when the platform determines that
reliable time is established, a SIGHUP should be sent to dnsmasq, which enables time checking, and purges the cache of DNS records reliable time is established, a SIGHUP should be sent to dnsmasq, which enables time checking, and purges the cache of DNS records
which have not been throughly checked. which have not been throughly checked.
.TP .TP
.B --dnssec-timestamp=<path>
Enables an alternative way of checking the validity of the system time for DNSSEC (see --dnssec-no-timecheck). In this case, the
system time is considered to be valid once it becomes later than the timestamp on the specified file. The file is created and
its timestamp set automatically by dnsmasq. The file must be stored on a persistent filesystem, so that it and its mtime are carried
over system restarts. The timestamp file is created after dnsmasq has dropped root, so it must be in a location writable by the
unprivileged user that dnsmasq runs as.
.TP
.B --proxy-dnssec .B --proxy-dnssec
Copy the DNSSEC Authenticated Data bit from upstream servers to downstream clients and cache it. This is an Copy the DNSSEC Authenticated Data bit from upstream servers to downstream clients and cache it. This is an
alternative to having dnsmasq validate DNSSEC, but it depends on the security of the network between alternative to having dnsmasq validate DNSSEC, but it depends on the security of the network between
@@ -810,7 +833,7 @@ and
for details.) for details.)
For IPv6, the mode may be some combination of For IPv6, the mode may be some combination of
.B ra-only, slaac, ra-names, ra-stateless, ra-advrouter. .B ra-only, slaac, ra-names, ra-stateless, ra-advrouter, off-link.
.B ra-only .B ra-only
tells dnsmasq to offer Router Advertisement only on this subnet, tells dnsmasq to offer Router Advertisement only on this subnet,
@@ -850,6 +873,9 @@ enables a mode where router address(es) rather than prefix(es) are included in t
This is described in RFC-3775 section 7.2 and is used in mobile IPv6. In this mode the interval option This is described in RFC-3775 section 7.2 and is used in mobile IPv6. In this mode the interval option
is also included, as described in RFC-3775 section 7.3. is also included, as described in RFC-3775 section 7.3.
.B off-link
tells dnsmasq to advertise the prefix without the on-link (aka L) bit set.
.TP .TP
.B \-G, --dhcp-host=[<hwaddr>][,id:<client_id>|*][,set:<tag>][,<ipaddr>][,<hostname>][,<lease_time>][,ignore] .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 Specify per host parameters for the DHCP server. This allows a machine
@@ -968,6 +994,18 @@ is given, then read all the files contained in that directory. The advantage of
using this option is the same as for --dhcp-hostsfile: the using this option is the same as for --dhcp-hostsfile: the
dhcp-optsfile will be re-read when dnsmasq receives SIGHUP. Note that dhcp-optsfile will be re-read when dnsmasq receives SIGHUP. Note that
it is possible to encode the information in a it is possible to encode the information in a
.TP
.B --dhcp-hostsdir=<path>
This is equivalent to dhcp-hostsfile, except for the following. The path MUST be a
directory, and not an individual file. Changed or new files within
the directory are read automatically, without the need to send SIGHUP.
If a file is deleted for changed after it has been read by dnsmasq, then the
host record it contained will remain until dnsmasq recieves a SIGHUP, or
is restarted; ie host records are only added dynamically.
.TP
.B --dhcp-optsdir=<path>
This is equivalent to dhcp-optsfile, with the differences noted for --dhcp-hostsdir.
.TP
.B --dhcp-boot .B --dhcp-boot
flag as DHCP options, using the options names bootfile-name, flag as DHCP options, using the options names bootfile-name,
server-ip-address and tftp-server. This allows these to be included server-ip-address and tftp-server. This allows these to be included
@@ -1353,7 +1391,7 @@ enables dynamic allocation. With tags, only when the tags are all
set. It may be repeated with different tag sets. set. It may be repeated with different tag sets.
.TP .TP
.B \-5, --no-ping .B \-5, --no-ping
(IPv4 only) By default, the DHCP server will attempt to ensure that an address in (IPv4 only) By default, the DHCP server will attempt to ensure that an address is
not in use before allocating it to a host. It does this by sending an not in use before allocating it to a host. It does this by sending an
ICMP echo request (aka "ping") to the address in question. If it gets ICMP echo request (aka "ping") to the address in question. If it gets
a reply, then the address must already be in use, and another is a reply, then the address must already be in use, and another is
@@ -1562,11 +1600,13 @@ option also forces the leasechange script to be called on changes
to the client-id and lease length and expiry time. to the client-id and lease length and expiry time.
.TP .TP
.B --bridge-interface=<interface>,<alias>[,<alias>] .B --bridge-interface=<interface>,<alias>[,<alias>]
Treat DHCP request packets arriving at any of the <alias> interfaces Treat DHCP (v4 and v6) request and IPv6 Router Solicit packets
as if they had arrived at <interface>. This option is necessary when arriving at any of the <alias> interfaces as if they had arrived at
using "old style" bridging on BSD platforms, since <interface>. This option allows dnsmasq to provide DHCP and RA
packets arrive at tap interfaces which don't have an IP address. service over unaddressed and unbridged Ethernet interfaces, e.g. on an
A trailing '*' wildcard can be used in each <alias>. OpenStack compute host where each such interface is a TAP interface to
a VM, or as in "old style bridging" on BSD platforms. A trailing '*'
wildcard can be used in each <alias>.
.TP .TP
.B \-s, --domain=<domain>[,<address range>[,local]] .B \-s, --domain=<domain>[,<address range>[,local]]
Specifies DNS domains for the DHCP server. Domains may be be given Specifies DNS domains for the DHCP server. Domains may be be given
@@ -1676,6 +1716,9 @@ Absolute paths (starting with /) are allowed, but they must be within
the tftp-root. If the optional interface argument is given, the the tftp-root. If the optional interface argument is given, the
directory is only used for TFTP requests via that interface. directory is only used for TFTP requests via that interface.
.TP .TP
.B --tftp-no-fail
Do not abort startup if specified tftp root directories are inaccessible.
.TP
.B --tftp-unique-root .B --tftp-unique-root
Add the IP address of the TFTP client as a path component on the end Add the IP address of the TFTP client as a path component on the end
of the TFTP-root (in standard dotted-quad format). Only valid if a of the TFTP-root (in standard dotted-quad format). Only valid if a
@@ -1773,7 +1816,8 @@ clears its cache and then re-loads
.I /etc/hosts .I /etc/hosts
and and
.I /etc/ethers .I /etc/ethers
and any file given by --dhcp-hostsfile, --dhcp-optsfile or --addn-hosts. and any file given by --dhcp-hostsfile, --dhcp-hostsdir, --dhcp-optsfile,
--dhcp-optsdir, --addn-hosts or --hostsdir.
The dhcp lease change script is called for all The dhcp lease change script is called for all
existing DHCP leases. If existing DHCP leases. If
.B .B
+8 -4
View File
@@ -1062,10 +1062,14 @@ esta opci
cuando hay cambios hechos a el client-id y tiempos de arriendo y vencimiento. cuando hay cambios hechos a el client-id y tiempos de arriendo y vencimiento.
.TP .TP
.B --bridge-interface=<nombre de interface>,<alias>[,<alias>] .B --bridge-interface=<nombre de interface>,<alias>[,<alias>]
Tratar paquetes de pedidos DHCP que llegan a cualquiera de las interfaces <alias> Tratar paquetes de pedidos DHCP (v4 y v6) y de IPv6 Router Solicit que
como si hubieran llegado a la interface <nombre de interface>. Esta opción llegan a cualquiera de las interfaces <alias> como si hubieran llegado
es necesaria al usar bridging estilo viejo en plataformas BSD, dado a que a la interface <nombre de interface>. Esta opción permite que dnsmasq
los paquetes llegan a interfaces tap que no tienen una dirección IP. puede proporcionar los servicios DHCP y RA a través de interfaces
ethernet sin dirección y sin puente; por ejemplo en un nodo de cálculo
de OpenStack, donde cada una de esas interfaces es una interfaz TAP
para una máquina virtual, o al usar bridging estilo viejo en
plataformas BSD.
.TP .TP
.B \-s, --domain=<dominio>[,<rango de IPs>] .B \-s, --domain=<dominio>[,<rango de IPs>]
Especifica los dominios DNS para el servidor DHCP. Dominios pueden ser Especifica los dominios DNS para el servidor DHCP. Dominios pueden ser
+12 -6
View File
@@ -852,7 +852,7 @@ et
pour plus de détails). pour plus de détails).
Pour IPv6, le mode peut-être une combinaison des valeurs Pour IPv6, le mode peut-être une combinaison des valeurs
.B ra-only, slaac, ra-names, ra-stateless. .B ra-only, slaac, ra-names, ra-stateless, off-link.
.B ra-only .B ra-only
indique à dnsmasq de n'effectuer que des annonces de routeur (Router indique à dnsmasq de n'effectuer que des annonces de routeur (Router
@@ -888,6 +888,9 @@ peut-être combiné avec
et et
.B slaac. .B slaac.
.B off-link
indique à dnsmasq d'annoncer le préfixe sans le bit L (sur lien).
.TP .TP
.B \-G, --dhcp-host=[<adresse matérielle>][,id:<identifiant client>|*][,set:<label>][,<adresse IP>][,<nom d'hôte>][,<durée de bail>][,ignore] .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 Spécifie les paramètres DHCP relatifs à un hôte. Cela permet à une machine
@@ -1655,11 +1658,14 @@ changement d'état de bail à chaque changement de l'identifiant de client, de
longueur de bail ou de date d'expiration. longueur de bail ou de date d'expiration.
.TP .TP
.B --bridge-interface=<interface>,<alias>[,<alias>] .B --bridge-interface=<interface>,<alias>[,<alias>]
Traiter les requêtes DHCP arrivant sur n'importe laquelle des interfaces <alias> Traiter les requêtes DHCP (v4 et v6) et IPv6 Router Solicit arrivant
comme si elles arrivaient de l'interface <interface>. Cette option est sur n'importe laquelle des interfaces <alias> comme si elles
nécessaire lors de l'utilisation de pont ethernet "ancien mode" sur plate-forme arrivaient de l'interface <interface>. Cette option permet à dnsmasq
BSD, puisque dans ce cas les paquets arrivent sur des interfaces "tap" n'ont de fournir les service DHCP et RA sur les interfaces ethernet non
pas d'adresse IP. Chaque <alias> peut finir avec un simple '*' joker. adressés et non pontés; par exemple sur un hôte de calcul d'OpenStack
où chaque telle interface est une interface TAP à une machine
virtuelle, ou lors de l'utilisation de pont ethernet "ancien mode" sur
plate-forme BSD. Chaque <alias> peut finir avec un simple '*' joker.
.TP .TP
.B \-s, --domain=<domaine>[,<gamme d'adresses>[,local]] .B \-s, --domain=<domaine>[,<gamme d'adresses>[,local]]
Spécifie le domaine du serveur DHCP. Le domaine peut être donné de manière Spécifie le domaine du serveur DHCP. Le domaine peut être donné de manière
+563 -470
View File
File diff suppressed because it is too large. Load diff
+529 -414
View File
File diff suppressed because it is too large. Load diff
+627 -522
View File
File diff suppressed because it is too large. Load diff
+531 -413
View File
File diff suppressed because it is too large. Load diff
+542 -416
View File
File diff suppressed because it is too large. Load diff
+627 -522
View File
File diff suppressed because it is too large. Load diff
+528 -414
View File
File diff suppressed because it is too large. Load diff
+531 -422
View File
File diff suppressed because it is too large. Load diff
+627 -522
View File
File diff suppressed because it is too large. Load diff
+528 -414
View File
File diff suppressed because it is too large. Load diff
+41 -26
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley /* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -131,24 +131,27 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
continue; continue;
} }
if (qtype == T_PTR) if ((qtype == T_PTR || qtype == T_SOA || qtype == T_NS) &&
(flag = in_arpa_name_2_addr(name, &addr)) &&
!local_query)
{ {
if (!(flag = in_arpa_name_2_addr(name, &addr))) for (zone = daemon->auth_zones; zone; zone = zone->next)
continue; if ((subnet = find_subnet(zone, flag, &addr)))
break;
if (!local_query) if (!zone)
{ {
for (zone = daemon->auth_zones; zone; zone = zone->next) auth = 0;
if ((subnet = find_subnet(zone, flag, &addr))) continue;
break;
if (!zone)
{
auth = 0;
continue;
}
} }
else if (qtype == T_SOA)
soa = 1, found = 1;
else if (qtype == T_NS)
ns = 1, found = 1;
}
if (qtype == T_PTR && flag)
{
intr = NULL; intr = NULL;
if (flag == F_IPV4) if (flag == F_IPV4)
@@ -186,7 +189,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
if (intr) if (intr)
{ {
if (in_zone(zone, intr->name, NULL)) if (local_query || in_zone(zone, intr->name, NULL))
{ {
found = 1; found = 1;
log_query(flag | F_REVERSE | F_CONFIG, intr->name, &addr, NULL); log_query(flag | F_REVERSE | F_CONFIG, intr->name, &addr, NULL);
@@ -208,8 +211,11 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
*p = 0; /* must be bare name */ *p = 0; /* must be bare name */
/* add external domain */ /* add external domain */
strcat(name, "."); if (zone)
strcat(name, zone->domain); {
strcat(name, ".");
strcat(name, zone->domain);
}
log_query(flag | F_DHCP | F_REVERSE, name, &addr, record_source(crecp->uid)); log_query(flag | F_DHCP | F_REVERSE, name, &addr, record_source(crecp->uid));
found = 1; found = 1;
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
@@ -217,7 +223,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
T_PTR, C_IN, "d", name)) T_PTR, C_IN, "d", name))
anscount++; anscount++;
} }
else if (crecp->flags & (F_DHCP | F_HOSTS) && in_zone(zone, name, NULL)) else if (crecp->flags & (F_DHCP | F_HOSTS) && (local_query || in_zone(zone, name, NULL)))
{ {
log_query(crecp->flags & ~F_FORWARD, name, &addr, record_source(crecp->uid)); log_query(crecp->flags & ~F_FORWARD, name, &addr, record_source(crecp->uid));
found = 1; found = 1;
@@ -240,14 +246,20 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
} }
cname_restart: cname_restart:
for (zone = daemon->auth_zones; zone; zone = zone->next) if (found)
if (in_zone(zone, name, &cut)) /* NS and SOA .arpa requests have set found above. */
break; cut = NULL;
else
if (!zone)
{ {
auth = 0; for (zone = daemon->auth_zones; zone; zone = zone->next)
continue; if (in_zone(zone, name, &cut))
break;
if (!zone)
{
auth = 0;
continue;
}
} }
for (rec = daemon->mxnames; rec; rec = rec->next) for (rec = daemon->mxnames; rec; rec = rec->next)
@@ -413,7 +425,10 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
peer_addr->in.sin_port = 0; peer_addr->in.sin_port = 0;
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
else else
peer_addr->in6.sin6_port = 0; {
peer_addr->in6.sin6_port = 0;
peer_addr->in6.sin6_scope_id = 0;
}
#endif #endif
for (peers = daemon->auth_peers; peers; peers = peers->next) for (peers = daemon->auth_peers; peers; peers = peers->next)
+1 -1
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley /* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
+2 -2
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley /* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -359,7 +359,7 @@ void send_via_bpf(struct dhcp_packet *mess, size_t len,
iov[3].iov_base = mess; iov[3].iov_base = mess;
iov[3].iov_len = len; iov[3].iov_len = len;
while (writev(daemon->dhcp_raw_fd, iov, 4) == -1 && retry_send()); while (retry_send(writev(daemon->dhcp_raw_fd, iov, 4)));
} }
#endif /* defined(HAVE_BSD_NETWORK) && defined(HAVE_DHCP) */ #endif /* defined(HAVE_BSD_NETWORK) && defined(HAVE_DHCP) */
+89 -33
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley /* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -461,9 +461,11 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
if (flags & (F_IPV4 | F_IPV6 | F_CNAME)) if (flags & (F_IPV4 | F_IPV6 | F_CNAME))
{ {
log_query(flags | F_UPSTREAM, name, addr, NULL); log_query(flags | F_UPSTREAM, name, addr, NULL);
/* Don;t mess with TTL for DNSSEC records. */ /* Don't mess with TTL for DNSSEC records. */
if (daemon->max_cache_ttl != 0 && daemon->max_cache_ttl < ttl) if (daemon->max_cache_ttl != 0 && daemon->max_cache_ttl < ttl)
ttl = daemon->max_cache_ttl; ttl = daemon->max_cache_ttl;
if (daemon->min_cache_ttl != 0 && daemon->min_cache_ttl > ttl)
ttl = daemon->min_cache_ttl;
} }
/* if previous insertion failed give up now. */ /* if previous insertion failed give up now. */
@@ -833,25 +835,40 @@ static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrl
Only insert each unique address once into this hashing structure. Only insert each unique address once into this hashing structure.
This complexity avoids O(n^2) divergent CPU use whilst reading This complexity avoids O(n^2) divergent CPU use whilst reading
large (10000 entry) hosts files. */ large (10000 entry) hosts files.
/* hash address */ Note that we only do this process when bulk-reading hosts files,
for (j = 0, i = 0; i < addrlen; i++) for incremental reads, rhash is NULL, and we use cache lookups
j = (j*2 +((unsigned char *)addr)[i]) % hashsz; instead.
*/
for (lookup = rhash[j]; lookup; lookup = lookup->next) if (rhash)
if ((lookup->flags & cache->flags & (F_IPV4 | F_IPV6)) &&
memcmp(&lookup->addr.addr, addr, addrlen) == 0)
{
cache->flags &= ~F_REVERSE;
break;
}
/* maintain address hash chain, insert new unique address */
if (!lookup)
{ {
cache->next = rhash[j]; /* hash address */
rhash[j] = cache; for (j = 0, i = 0; i < addrlen; i++)
j = (j*2 +((unsigned char *)addr)[i]) % hashsz;
for (lookup = rhash[j]; lookup; lookup = lookup->next)
if ((lookup->flags & cache->flags & (F_IPV4 | F_IPV6)) &&
memcmp(&lookup->addr.addr, addr, addrlen) == 0)
{
cache->flags &= ~F_REVERSE;
break;
}
/* maintain address hash chain, insert new unique address */
if (!lookup)
{
cache->next = rhash[j];
rhash[j] = cache;
}
}
else
{
/* incremental read, lookup in cache */
lookup = cache_find_by_addr(NULL, addr, 0, cache->flags & (F_IPV4 | F_IPV6));
if (lookup && lookup->flags & F_HOSTS)
cache->flags &= ~F_REVERSE;
} }
cache->uid = index; cache->uid = index;
@@ -910,7 +927,7 @@ static int gettok(FILE *f, char *token)
} }
} }
static int read_hostsfile(char *filename, unsigned int index, int cache_size, struct crec **rhash, int hashsz) int read_hostsfile(char *filename, unsigned int index, int cache_size, struct crec **rhash, int hashsz)
{ {
FILE *f = fopen(filename, "r"); FILE *f = fopen(filename, "r");
char *token = daemon->namebuff, *domain_suffix = NULL; char *token = daemon->namebuff, *domain_suffix = NULL;
@@ -956,7 +973,7 @@ static int read_hostsfile(char *filename, unsigned int index, int cache_size, st
addr_count++; addr_count++;
/* rehash every 1000 names. */ /* rehash every 1000 names. */
if ((name_count - cache_size) > 1000) if (rhash && ((name_count - cache_size) > 1000))
{ {
rehash(name_count); rehash(name_count);
cache_size = name_count; cache_size = name_count;
@@ -1003,7 +1020,9 @@ static int read_hostsfile(char *filename, unsigned int index, int cache_size, st
} }
fclose(f); fclose(f);
rehash(name_count);
if (rhash)
rehash(name_count);
my_syslog(LOG_INFO, _("read %s - %d addresses"), filename, addr_count); my_syslog(LOG_INFO, _("read %s - %d addresses"), filename, addr_count);
@@ -1114,16 +1133,22 @@ void cache_reload(void)
{ {
if (daemon->cachesize > 0) if (daemon->cachesize > 0)
my_syslog(LOG_INFO, _("cleared cache")); my_syslog(LOG_INFO, _("cleared cache"));
return; }
else
{
if (!option_bool(OPT_NO_HOSTS))
total_size = read_hostsfile(HOSTSFILE, SRC_HOSTS, total_size, (struct crec **)daemon->packet, revhashsz);
daemon->addn_hosts = expand_filelist(daemon->addn_hosts);
for (ah = daemon->addn_hosts; ah; ah = ah->next)
if (!(ah->flags & AH_INACTIVE))
total_size = read_hostsfile(ah->fname, ah->index, total_size, (struct crec **)daemon->packet, revhashsz);
} }
if (!option_bool(OPT_NO_HOSTS)) #ifdef HAVE_INOTIFY
total_size = read_hostsfile(HOSTSFILE, SRC_HOSTS, total_size, (struct crec **)daemon->packet, revhashsz); set_dynamic_inotify(AH_HOSTS, total_size, (struct crec **)daemon->packet, revhashsz);
#endif
daemon->addn_hosts = expand_filelist(daemon->addn_hosts);
for (ah = daemon->addn_hosts; ah; ah = ah->next)
if (!(ah->flags & AH_INACTIVE))
total_size = read_hostsfile(ah->fname, ah->index, total_size, (struct crec **)daemon->packet, revhashsz);
} }
#ifdef HAVE_DHCP #ifdef HAVE_DHCP
@@ -1342,7 +1367,7 @@ int cache_make_stat(struct txt_record *t)
} }
port = prettyprint_addr(&serv->addr, daemon->addrbuff); port = prettyprint_addr(&serv->addr, daemon->addrbuff);
lenp = p++; /* length */ lenp = p++; /* length */
bytes_avail = (p - buff) + bufflen; bytes_avail = bufflen - (p - buff );
bytes_needed = snprintf(p, bytes_avail, "%s#%d %u %u", daemon->addrbuff, port, queries, failed_queries); bytes_needed = snprintf(p, bytes_avail, "%s#%d %u %u", daemon->addrbuff, port, queries, failed_queries);
if (bytes_needed >= bytes_avail) if (bytes_needed >= bytes_avail)
{ {
@@ -1356,7 +1381,7 @@ int cache_make_stat(struct txt_record *t)
lenp = p - 1; lenp = p - 1;
buff = new; buff = new;
bufflen = newlen; bufflen = newlen;
bytes_avail = (p - buff) + bufflen; bytes_avail = bufflen - (p - buff );
bytes_needed = snprintf(p, bytes_avail, "%s#%d %u %u", daemon->addrbuff, port, queries, failed_queries); bytes_needed = snprintf(p, bytes_avail, "%s#%d %u %u", daemon->addrbuff, port, queries, failed_queries);
} }
*lenp = bytes_needed; *lenp = bytes_needed;
@@ -1374,6 +1399,20 @@ int cache_make_stat(struct txt_record *t)
return 1; return 1;
} }
/* There can be names in the cache containing control chars, don't
mess up logging or open security holes. */
static char *sanitise(char *name)
{
unsigned char *r;
if (name)
for (r = (unsigned char *)name; *r; r++)
if (!isprint((int)*r))
return "<name unprintable>";
return name;
}
void dump_cache(time_t now) void dump_cache(time_t now)
{ {
struct server *serv, *serv1; struct server *serv, *serv1;
@@ -1427,9 +1466,9 @@ void dump_cache(time_t now)
*a = 0; *a = 0;
if (strlen(n) == 0 && !(cache->flags & F_REVERSE)) if (strlen(n) == 0 && !(cache->flags & F_REVERSE))
n = "<Root>"; n = "<Root>";
p += sprintf(p, "%-30.30s ", n); p += sprintf(p, "%-30.30s ", sanitise(n));
if ((cache->flags & F_CNAME) && !is_outdated_cname_pointer(cache)) if ((cache->flags & F_CNAME) && !is_outdated_cname_pointer(cache))
a = cache_get_cname_target(cache); a = sanitise(cache_get_cname_target(cache));
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
else if (cache->flags & F_DS) else if (cache->flags & F_DS)
{ {
@@ -1504,6 +1543,12 @@ char *record_source(unsigned int index)
if (ah->index == index) if (ah->index == index)
return ah->fname; return ah->fname;
#ifdef HAVE_INOTIFY
for (ah = daemon->dynamic_dirs; ah; ah = ah->next)
if (ah->index == index)
return ah->fname;
#endif
return "<unknown>"; return "<unknown>";
} }
@@ -1556,6 +1601,8 @@ void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
if (!option_bool(OPT_LOG)) if (!option_bool(OPT_LOG))
return; return;
name = sanitise(name);
if (addr) if (addr)
{ {
if (flags & F_KEYTAG) if (flags & F_KEYTAG)
@@ -1638,7 +1685,16 @@ void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
if (strlen(name) == 0) if (strlen(name) == 0)
name = "."; name = ".";
my_syslog(LOG_INFO, "%s %s %s %s", source, name, verb, dest); if (option_bool(OPT_EXTRALOG))
{
int port = prettyprint_addr(daemon->log_source_addr, daemon->addrbuff2);
if (flags & F_NOEXTRA)
my_syslog(LOG_INFO, "* %s/%u %s %s %s %s", daemon->addrbuff2, port, source, name, verb, dest);
else
my_syslog(LOG_INFO, "%u %s/%u %s %s %s %s", daemon->log_display_id, daemon->addrbuff2, port, source, name, verb, dest);
}
else
my_syslog(LOG_INFO, "%s %s %s %s", source, name, verb, dest);
} }
+16 -2
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley /* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -17,7 +17,9 @@
#define FTABSIZ 150 /* max number of outstanding requests (default) */ #define FTABSIZ 150 /* max number of outstanding requests (default) */
#define MAX_PROCS 20 /* max no children for TCP requests */ #define MAX_PROCS 20 /* max no children for TCP requests */
#define CHILD_LIFETIME 150 /* secs 'till terminated (RFC1035 suggests > 120s) */ #define CHILD_LIFETIME 150 /* secs 'till terminated (RFC1035 suggests > 120s) */
#define TCP_MAX_QUERIES 100 /* Maximum number of queries per incoming TCP connection */
#define EDNS_PKTSZ 4096 /* default max EDNS.0 UDP packet from RFC5625 */ #define EDNS_PKTSZ 4096 /* default max EDNS.0 UDP packet from RFC5625 */
#define SAFE_PKTSZ 1280 /* "go anywhere" UDP packet size */
#define KEYBLOCK_LEN 40 /* choose to mininise fragmentation when storing DNSSEC keys */ #define KEYBLOCK_LEN 40 /* choose to mininise fragmentation when storing DNSSEC keys */
#define DNSSEC_WORK 50 /* Max number of queries to validate one question */ #define DNSSEC_WORK 50 /* Max number of queries to validate one question */
#define TIMEOUT 10 /* drop UDP queries after TIMEOUT seconds */ #define TIMEOUT 10 /* drop UDP queries after TIMEOUT seconds */
@@ -26,6 +28,7 @@
#define RANDOM_SOCKS 64 /* max simultaneous random ports */ #define RANDOM_SOCKS 64 /* max simultaneous random ports */
#define LEASE_RETRY 60 /* on error, retry writing leasefile after LEASE_RETRY seconds */ #define LEASE_RETRY 60 /* on error, retry writing leasefile after LEASE_RETRY seconds */
#define CACHESIZ 150 /* default cache size */ #define CACHESIZ 150 /* default cache size */
#define TTL_FLOOR_LIMIT 3600 /* don't allow --min-cache-ttl to raise TTL above this under any circumstances */
#define MAXLEASES 1000 /* maximum number of DHCP leases */ #define MAXLEASES 1000 /* maximum number of DHCP leases */
#define PING_WAIT 3 /* wait for ping address-in-use test */ #define PING_WAIT 3 /* wait for ping address-in-use test */
#define PING_CACHE_TIME 30 /* Ping test assumed to be valid this long. */ #define PING_CACHE_TIME 30 /* Ping test assumed to be valid this long. */
@@ -113,6 +116,8 @@ HAVE_DNSSEC
HAVE_LOOP HAVE_LOOP
include functionality to probe for and remove DNS forwarding loops. include functionality to probe for and remove DNS forwarding loops.
HAVE_INOTIFY
use the Linux inotify facility to efficiently re-read configuration files.
NO_IPV6 NO_IPV6
NO_TFTP NO_TFTP
@@ -121,6 +126,7 @@ NO_DHCP6
NO_SCRIPT NO_SCRIPT
NO_LARGEFILE NO_LARGEFILE
NO_AUTH NO_AUTH
NO_INOTIFY
these are avilable to explictly disable compile time options which would these are avilable to explictly disable compile time options which would
otherwise be enabled automatically (HAVE_IPV6, >2Gb file sizes) or otherwise be enabled automatically (HAVE_IPV6, >2Gb file sizes) or
which are enabled by default in the distributed source tree. Building dnsmasq which are enabled by default in the distributed source tree. Building dnsmasq
@@ -353,6 +359,10 @@ HAVE_SOCKADDR_SA_LEN
#undef HAVE_LOOP #undef HAVE_LOOP
#endif #endif
#if defined (HAVE_LINUX_NETWORK) && !defined(NO_INOTIFY)
#define HAVE_INOTIFY
#endif
/* Define a string indicating which options are in use. /* Define a string indicating which options are in use.
DNSMASQP_COMPILE_OPTS is only defined in dnsmasq.c */ DNSMASQP_COMPILE_OPTS is only defined in dnsmasq.c */
@@ -426,7 +436,11 @@ static char *compile_opts =
#ifndef HAVE_LOOP #ifndef HAVE_LOOP
"no-" "no-"
#endif #endif
"loop-detect"; "loop-detect "
#ifndef HAVE_INOTIFY
"no-"
#endif
"inotify";
#endif #endif
+1 -1
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley /* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
+202 -14
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley /* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -70,6 +70,21 @@ const char* introspection_xml_template =
" <arg name=\"hwaddr\" type=\"s\"/>\n" " <arg name=\"hwaddr\" type=\"s\"/>\n"
" <arg name=\"hostname\" type=\"s\"/>\n" " <arg name=\"hostname\" type=\"s\"/>\n"
" </signal>\n" " </signal>\n"
#ifdef HAVE_DHCP
" <method name=\"AddDhcpLease\">\n"
" <arg name=\"ipaddr\" type=\"s\"/>\n"
" <arg name=\"hwaddr\" type=\"s\"/>\n"
" <arg name=\"hostname\" type=\"ay\"/>\n"
" <arg name=\"clid\" type=\"ay\"/>\n"
" <arg name=\"lease_duration\" type=\"u\"/>\n"
" <arg name=\"ia_id\" type=\"u\"/>\n"
" <arg name=\"is_temporary\" type=\"b\"/>\n"
" </method>\n"
" <method name=\"DeleteDhcpLease\">\n"
" <arg name=\"ipaddr\" type=\"s\"/>\n"
" <arg name=\"success\" type=\"b\" direction=\"out\"/>\n"
" </method>\n"
#endif
" </interface>\n" " </interface>\n"
"</node>\n"; "</node>\n";
@@ -421,18 +436,184 @@ static DBusMessage *dbus_set_bool(DBusMessage *message, int flag, char *name)
if (enabled) if (enabled)
{ {
my_syslog(LOG_INFO, "Enabling --%s option from D-Bus", name); my_syslog(LOG_INFO, _("Enabling --%s option from D-Bus"), name);
set_option_bool(flag); set_option_bool(flag);
} }
else else
{ {
my_syslog(LOG_INFO, "Disabling --%s option from D-Bus", name); my_syslog(LOG_INFO, _("Disabling --%s option from D-Bus"), name);
reset_option_bool(flag); reset_option_bool(flag);
} }
return NULL; return NULL;
} }
#ifdef HAVE_DHCP
static DBusMessage *dbus_add_lease(DBusMessage* message)
{
struct dhcp_lease *lease;
const char *ipaddr, *hwaddr, *hostname, *tmp;
const unsigned char* clid;
int clid_len, hostname_len, hw_len, hw_type;
dbus_uint32_t expires, ia_id;
dbus_bool_t is_temporary;
struct all_addr addr;
time_t now = dnsmasq_time();
unsigned char dhcp_chaddr[DHCP_CHADDR_MAX];
DBusMessageIter iter, array_iter;
if (!dbus_message_iter_init(message, &iter))
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"Failed to initialize dbus message iter");
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"Expected string as first argument");
dbus_message_iter_get_basic(&iter, &ipaddr);
dbus_message_iter_next(&iter);
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"Expected string as second argument");
dbus_message_iter_get_basic(&iter, &hwaddr);
dbus_message_iter_next(&iter);
if ((dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) ||
(dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_BYTE))
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"Expected byte array as third argument");
dbus_message_iter_recurse(&iter, &array_iter);
dbus_message_iter_get_fixed_array(&array_iter, &hostname, &hostname_len);
tmp = memchr(hostname, '\0', hostname_len);
if (tmp)
{
if (tmp == &hostname[hostname_len - 1])
hostname_len--;
else
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"Hostname contains an embedded NUL character");
}
dbus_message_iter_next(&iter);
if ((dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) ||
(dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_BYTE))
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"Expected byte array as fourth argument");
dbus_message_iter_recurse(&iter, &array_iter);
dbus_message_iter_get_fixed_array(&array_iter, &clid, &clid_len);
dbus_message_iter_next(&iter);
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"Expected uint32 as fifth argument");
dbus_message_iter_get_basic(&iter, &expires);
dbus_message_iter_next(&iter);
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"Expected uint32 as sixth argument");
dbus_message_iter_get_basic(&iter, &ia_id);
dbus_message_iter_next(&iter);
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN)
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"Expected uint32 as sixth argument");
dbus_message_iter_get_basic(&iter, &is_temporary);
if (inet_pton(AF_INET, ipaddr, &addr.addr.addr4))
{
if (ia_id != 0 || is_temporary)
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"ia_id and is_temporary must be zero for IPv4 lease");
if (!(lease = lease_find_by_addr(addr.addr.addr4)))
lease = lease4_allocate(addr.addr.addr4);
}
#ifdef HAVE_DHCP6
else if (inet_pton(AF_INET6, ipaddr, &addr.addr.addr6))
{
if (!(lease = lease6_find_by_addr(&addr.addr.addr6, 128, 0)))
lease = lease6_allocate(&addr.addr.addr6,
is_temporary ? LEASE_TA : LEASE_NA);
lease_set_iaid(lease, ia_id);
}
#endif
else
return dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
"Invalid IP address '%s'", ipaddr);
hw_len = parse_hex((char*)hwaddr, dhcp_chaddr, DHCP_CHADDR_MAX, NULL,
&hw_type);
if (hw_type == 0 && hw_len != 0)
hw_type = ARPHRD_ETHER;
lease_set_hwaddr(lease, dhcp_chaddr, clid, hw_len, hw_type,
clid_len, now, 0);
lease_set_expires(lease, expires, now);
if (hostname_len != 0)
lease_set_hostname(lease, hostname, 0, get_domain(lease->addr), NULL);
lease_update_file(now);
lease_update_dns(0);
return NULL;
}
static DBusMessage *dbus_del_lease(DBusMessage* message)
{
struct dhcp_lease *lease;
DBusMessageIter iter;
const char *ipaddr;
DBusMessage *reply;
struct all_addr addr;
dbus_bool_t ret = 1;
time_t now = dnsmasq_time();
if (!dbus_message_iter_init(message, &iter))
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"Failed to initialize dbus message iter");
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"Expected string as first argument");
dbus_message_iter_get_basic(&iter, &ipaddr);
if (inet_pton(AF_INET, ipaddr, &addr.addr.addr4))
lease = lease_find_by_addr(addr.addr.addr4);
#ifdef HAVE_DHCP6
else if (inet_pton(AF_INET6, ipaddr, &addr.addr.addr6))
lease = lease6_find_by_addr(&addr.addr.addr6, 128, 0);
#endif
else
return dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
"Invalid IP address '%s'", ipaddr);
if (lease)
{
lease_prune(lease, now);
lease_update_file(now);
lease_update_dns(0);
}
else
ret = 0;
if ((reply = dbus_message_new_method_return(message)))
dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &ret,
DBUS_TYPE_INVALID);
return reply;
}
#endif
DBusHandlerResult message_handler(DBusConnection *connection, DBusHandlerResult message_handler(DBusConnection *connection,
DBusMessage *message, DBusMessage *message,
void *user_data) void *user_data)
@@ -490,6 +671,16 @@ DBusHandlerResult message_handler(DBusConnection *connection,
{ {
reply = dbus_set_bool(message, OPT_BOGUSPRIV, "bogus-priv"); reply = dbus_set_bool(message, OPT_BOGUSPRIV, "bogus-priv");
} }
#ifdef HAVE_DHCP
else if (strcmp(method, "AddDhcpLease") == 0)
{
reply = dbus_add_lease(message);
}
else if (strcmp(method, "DeleteDhcpLease") == 0)
{
reply = dbus_del_lease(message);
}
#endif
else if (strcmp(method, "ClearCache") == 0) else if (strcmp(method, "ClearCache") == 0)
clear_cache = 1; clear_cache = 1;
else else
@@ -558,8 +749,7 @@ char *dbus_init(void)
} }
void set_dbus_listeners(int *maxfdp, void set_dbus_listeners(void)
fd_set *rset, fd_set *wset, fd_set *eset)
{ {
struct watch *w; struct watch *w;
@@ -569,19 +759,17 @@ void set_dbus_listeners(int *maxfdp,
unsigned int flags = dbus_watch_get_flags(w->watch); unsigned int flags = dbus_watch_get_flags(w->watch);
int fd = dbus_watch_get_unix_fd(w->watch); int fd = dbus_watch_get_unix_fd(w->watch);
bump_maxfd(fd, maxfdp);
if (flags & DBUS_WATCH_READABLE) if (flags & DBUS_WATCH_READABLE)
FD_SET(fd, rset); poll_listen(fd, POLLIN);
if (flags & DBUS_WATCH_WRITABLE) if (flags & DBUS_WATCH_WRITABLE)
FD_SET(fd, wset); poll_listen(fd, POLLOUT);
FD_SET(fd, eset); poll_listen(fd, POLLERR);
} }
} }
void check_dbus_listeners(fd_set *rset, fd_set *wset, fd_set *eset) void check_dbus_listeners()
{ {
DBusConnection *connection = (DBusConnection *)daemon->dbus; DBusConnection *connection = (DBusConnection *)daemon->dbus;
struct watch *w; struct watch *w;
@@ -592,13 +780,13 @@ void check_dbus_listeners(fd_set *rset, fd_set *wset, fd_set *eset)
unsigned int flags = 0; unsigned int flags = 0;
int fd = dbus_watch_get_unix_fd(w->watch); int fd = dbus_watch_get_unix_fd(w->watch);
if (FD_ISSET(fd, rset)) if (poll_check(fd, POLLIN))
flags |= DBUS_WATCH_READABLE; flags |= DBUS_WATCH_READABLE;
if (FD_ISSET(fd, wset)) if (poll_check(fd, POLLOUT))
flags |= DBUS_WATCH_WRITABLE; flags |= DBUS_WATCH_WRITABLE;
if (FD_ISSET(fd, eset)) if (poll_check(fd, POLLERR))
flags |= DBUS_WATCH_ERROR; flags |= DBUS_WATCH_ERROR;
if (flags != 0) if (flags != 0)
+3 -3
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley /* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -545,8 +545,8 @@ static const struct opttab_t {
{ "parameter-request", 55, OT_INTERNAL }, { "parameter-request", 55, OT_INTERNAL },
{ "message", 56, OT_INTERNAL }, { "message", 56, OT_INTERNAL },
{ "max-message-size", 57, OT_INTERNAL }, { "max-message-size", 57, OT_INTERNAL },
{ "T1", 58, OT_INTERNAL | OT_TIME}, { "T1", 58, OT_TIME},
{ "T2", 59, OT_INTERNAL | OT_TIME}, { "T2", 59, OT_TIME},
{ "vendor-class", 60, 0 }, { "vendor-class", 60, 0 },
{ "client-id", 61, OT_INTERNAL }, { "client-id", 61, OT_INTERNAL },
{ "nis+-domain", 64, OT_NAME }, { "nis+-domain", 64, OT_NAME },
+1 -1
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley /* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
+34 -26
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley /* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -225,10 +225,11 @@ void dhcp_packet(time_t now, int pxe_fd)
strncpy(arp_req.arp_dev, ifr.ifr_name, 16); strncpy(arp_req.arp_dev, ifr.ifr_name, 16);
#endif #endif
/* One form of bridging on BSD has the property that packets /* If the interface on which the DHCP request was received is an
can be recieved on bridge interfaces which do not have an IP address. alias of some other interface (as specified by the
We allow these to be treated as aliases of another interface which does have --bridge-interface option), change ifr.ifr_name so that we look
an IP address with --dhcp-bridge=interface,alias,alias */ for DHCP contexts associated with the aliased interface instead
of with the aliasing one. */
for (bridge = daemon->bridges; bridge; bridge = bridge->next) for (bridge = daemon->bridges; bridge; bridge = bridge->next)
{ {
for (alias = bridge->alias; alias; alias = alias->next) for (alias = bridge->alias; alias; alias = alias->next)
@@ -236,7 +237,9 @@ void dhcp_packet(time_t now, int pxe_fd)
{ {
if (!(iface_index = if_nametoindex(bridge->iface))) if (!(iface_index = if_nametoindex(bridge->iface)))
{ {
my_syslog(LOG_WARNING, _("unknown interface %s in bridge-interface"), ifr.ifr_name); my_syslog(MS_DHCP | LOG_WARNING,
_("unknown interface %s in bridge-interface"),
bridge->iface);
return; return;
} }
else else
@@ -376,10 +379,9 @@ void dhcp_packet(time_t now, int pxe_fd)
} }
} }
#if defined(HAVE_LINUX_NETWORK) #if defined(HAVE_LINUX_NETWORK)
else if ((ntohs(mess->flags) & 0x8000) || mess->hlen == 0 || else
mess->hlen > sizeof(ifr.ifr_addr.sa_data) || mess->htype == 0)
{ {
/* broadcast to 255.255.255.255 (or mac address invalid) */ /* fill cmsg for outbound interface (both broadcast & unicast) */
struct in_pktinfo *pkt; struct in_pktinfo *pkt;
msg.msg_control = control_u.control; msg.msg_control = control_u.control;
msg.msg_controllen = sizeof(control_u); msg.msg_controllen = sizeof(control_u);
@@ -390,22 +392,28 @@ void dhcp_packet(time_t now, int pxe_fd)
msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
cmptr->cmsg_level = IPPROTO_IP; cmptr->cmsg_level = IPPROTO_IP;
cmptr->cmsg_type = IP_PKTINFO; cmptr->cmsg_type = IP_PKTINFO;
dest.sin_addr.s_addr = INADDR_BROADCAST;
dest.sin_port = htons(daemon->dhcp_client_port); if ((ntohs(mess->flags) & 0x8000) || mess->hlen == 0 ||
} mess->hlen > sizeof(ifr.ifr_addr.sa_data) || mess->htype == 0)
else {
{ /* broadcast to 255.255.255.255 (or mac address invalid) */
/* unicast to unconfigured client. Inject mac address direct into ARP cache. dest.sin_addr.s_addr = INADDR_BROADCAST;
struct sockaddr limits size to 14 bytes. */ dest.sin_port = htons(daemon->dhcp_client_port);
dest.sin_addr = mess->yiaddr; }
dest.sin_port = htons(daemon->dhcp_client_port); else
memcpy(&arp_req.arp_pa, &dest, sizeof(struct sockaddr_in)); {
arp_req.arp_ha.sa_family = mess->htype; /* unicast to unconfigured client. Inject mac address direct into ARP cache.
memcpy(arp_req.arp_ha.sa_data, mess->chaddr, mess->hlen); struct sockaddr limits size to 14 bytes. */
/* interface name already copied in */ dest.sin_addr = mess->yiaddr;
arp_req.arp_flags = ATF_COM; dest.sin_port = htons(daemon->dhcp_client_port);
if (ioctl(daemon->dhcpfd, SIOCSARP, &arp_req) == -1) memcpy(&arp_req.arp_pa, &dest, sizeof(struct sockaddr_in));
my_syslog(MS_DHCP | LOG_ERR, _("ARP-cache injection failed: %s"), strerror(errno)); arp_req.arp_ha.sa_family = mess->htype;
memcpy(arp_req.arp_ha.sa_data, mess->chaddr, mess->hlen);
/* interface name already copied in */
arp_req.arp_flags = ATF_COM;
if (ioctl(daemon->dhcpfd, SIOCSARP, &arp_req) == -1)
my_syslog(MS_DHCP | LOG_ERR, _("ARP-cache injection failed: %s"), strerror(errno));
}
} }
#elif defined(HAVE_SOLARIS_NETWORK) #elif defined(HAVE_SOLARIS_NETWORK)
else if ((ntohs(mess->flags) & 0x8000) || mess->hlen != ETHER_ADDR_LEN || mess->htype != ARPHRD_ETHER) else if ((ntohs(mess->flags) & 0x8000) || mess->hlen != ETHER_ADDR_LEN || mess->htype != ARPHRD_ETHER)
@@ -443,7 +451,7 @@ void dhcp_packet(time_t now, int pxe_fd)
setsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &iface_index, sizeof(iface_index)); setsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &iface_index, sizeof(iface_index));
#endif #endif
while(sendmsg(fd, &msg, 0) == -1 && retry_send()); while(retry_send(sendmsg(fd, &msg, 0)));
} }
/* check against secondary interface addresses */ /* check against secondary interface addresses */
+1 -1
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley /* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
+32 -4
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley /* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -144,6 +144,8 @@ void dhcp6_packet(time_t now)
if ((port = relay_reply6(&from, sz, ifr.ifr_name)) == 0) if ((port = relay_reply6(&from, sz, ifr.ifr_name)) == 0)
{ {
struct dhcp_bridge *bridge, *alias;
for (tmp = daemon->if_except; tmp; tmp = tmp->next) for (tmp = daemon->if_except; tmp; tmp = tmp->next)
if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name)) if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
return; return;
@@ -161,6 +163,30 @@ void dhcp6_packet(time_t now)
memset(&parm.ll_addr, 0, IN6ADDRSZ); memset(&parm.ll_addr, 0, IN6ADDRSZ);
memset(&parm.ula_addr, 0, IN6ADDRSZ); memset(&parm.ula_addr, 0, IN6ADDRSZ);
/* If the interface on which the DHCPv6 request was received is
an alias of some other interface (as specified by the
--bridge-interface option), change parm.ind so that we look
for DHCPv6 contexts associated with the aliased interface
instead of with the aliasing one. */
for (bridge = daemon->bridges; bridge; bridge = bridge->next)
{
for (alias = bridge->alias; alias; alias = alias->next)
if (wildcard_matchn(alias->iface, ifr.ifr_name, IF_NAMESIZE))
{
parm.ind = if_nametoindex(bridge->iface);
if (!parm.ind)
{
my_syslog(MS_DHCP | LOG_WARNING,
_("unknown interface %s in bridge-interface"),
bridge->iface);
return;
}
break;
}
if (alias)
break;
}
for (context = daemon->dhcp6; context; context = context->next) for (context = daemon->dhcp6; context; context = context->next)
if (IN6_IS_ADDR_UNSPECIFIED(&context->start6) && context->prefix == 0) if (IN6_IS_ADDR_UNSPECIFIED(&context->start6) && context->prefix == 0)
{ {
@@ -225,9 +251,9 @@ void dhcp6_packet(time_t now)
if (port != 0) if (port != 0)
{ {
from.sin6_port = htons(port); from.sin6_port = htons(port);
while (sendto(daemon->dhcp6fd, daemon->outpacket.iov_base, save_counter(0), while (retry_send(sendto(daemon->dhcp6fd, daemon->outpacket.iov_base,
0, (struct sockaddr *)&from, sizeof(from)) == -1 && save_counter(0), 0, (struct sockaddr *)&from,
retry_send()); sizeof(from))));
} }
} }
@@ -246,6 +272,8 @@ void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsi
neigh.code = 0; neigh.code = 0;
neigh.reserved = 0; neigh.reserved = 0;
neigh.target = *client; neigh.target = *client;
/* RFC4443 section-2.3: checksum has to be zero to be calculated */
neigh.checksum = 0;
memset(&addr, 0, sizeof(addr)); memset(&addr, 0, sizeof(addr));
#ifdef HAVE_SOCKADDR_SA_LEN #ifdef HAVE_SOCKADDR_SA_LEN
+16 -8
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley /* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -84,15 +84,15 @@ struct dns_header {
u16 qdcount,ancount,nscount,arcount; u16 qdcount,ancount,nscount,arcount;
}; };
#define HB3_QR 0x80 #define HB3_QR 0x80 /* Query */
#define HB3_OPCODE 0x78 #define HB3_OPCODE 0x78
#define HB3_AA 0x04 #define HB3_AA 0x04 /* Authoritative Answer */
#define HB3_TC 0x02 #define HB3_TC 0x02 /* TrunCated */
#define HB3_RD 0x01 #define HB3_RD 0x01 /* Recursion Desired */
#define HB4_RA 0x80 #define HB4_RA 0x80 /* Recursion Available */
#define HB4_AD 0x20 #define HB4_AD 0x20 /* Authenticated Data */
#define HB4_CD 0x10 #define HB4_CD 0x10 /* Checking Disabled */
#define HB4_RCODE 0x0f #define HB4_RCODE 0x0f
#define OPCODE(x) (((x)->hb3 & HB3_OPCODE) >> 3) #define OPCODE(x) (((x)->hb3 & HB3_OPCODE) >> 3)
@@ -142,3 +142,11 @@ struct dns_header {
#define ADD_RDLEN(header, pp, plen, len) \ #define ADD_RDLEN(header, pp, plen, len) \
(!CHECK_LEN(header, pp, plen, len) ? 0 : (((pp) += (len)), 1)) (!CHECK_LEN(header, pp, plen, len) ? 0 : (((pp) += (len)), 1))
/* Escape character in our presentation format for names.
Cannot be '.' or /000 and must be !isprint().
Note that escaped chars are stored as
<NAME_ESCAPE> <orig-char+1>
to ensure that the escaped form of /000 doesn't include /000
*/
#define NAME_ESCAPE 1
+204 -175
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley /* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -24,8 +24,8 @@ struct daemon *daemon;
static volatile pid_t pid = 0; static volatile pid_t pid = 0;
static volatile int pipewrite; static volatile int pipewrite;
static int set_dns_listeners(time_t now, fd_set *set, int *maxfdp); static int set_dns_listeners(time_t now);
static void check_dns_listeners(fd_set *set, time_t now); static void check_dns_listeners(time_t now);
static void sig_handler(int sig); static void sig_handler(int sig);
static void async_event(int pipe, time_t now); static void async_event(int pipe, time_t now);
static void fatal_event(struct event_desc *ev, char *msg); static void fatal_event(struct event_desc *ev, char *msg);
@@ -58,6 +58,9 @@ int main (int argc, char **argv)
struct dhcp_context *context; struct dhcp_context *context;
struct dhcp_relay *relay; struct dhcp_relay *relay;
#endif #endif
#ifdef HAVE_TFTP
int tftp_prefix_missing = 0;
#endif
#ifdef LOCALEDIR #ifdef LOCALEDIR
setlocale(LC_ALL, ""); setlocale(LC_ALL, "");
@@ -93,12 +96,25 @@ int main (int argc, char **argv)
daemon->packet = safe_malloc(daemon->packet_buff_sz); daemon->packet = safe_malloc(daemon->packet_buff_sz);
daemon->addrbuff = safe_malloc(ADDRSTRLEN); daemon->addrbuff = safe_malloc(ADDRSTRLEN);
if (option_bool(OPT_EXTRALOG))
daemon->addrbuff2 = safe_malloc(ADDRSTRLEN);
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID)) if (option_bool(OPT_DNSSEC_VALID))
{ {
daemon->keyname = safe_malloc(MAXDNAME); /* Note that both /000 and '.' are allowed within labels. These get
daemon->workspacename = safe_malloc(MAXDNAME); represented in presentation format using NAME_ESCAPE as an escape
character when in DNSSEC mode.
In theory, if all the characters in a name were /000 or
'.' or NAME_ESCAPE then all would have to be escaped, so the
presentation format would be twice as long as the spec.
daemon->namebuff was previously allocated by the option-reading
code before we knew if we're in DNSSEC mode, so reallocate here. */
free(daemon->namebuff);
daemon->namebuff = safe_malloc(MAXDNAME * 2);
daemon->keyname = safe_malloc(MAXDNAME * 2);
daemon->workspacename = safe_malloc(MAXDNAME * 2);
} }
#endif #endif
@@ -142,14 +158,19 @@ int main (int argc, char **argv)
} }
#endif #endif
#ifndef HAVE_INOTIFY
if (daemon->dynamic_dirs)
die(_("dhcp-hostsdir, dhcp-optsdir and hostsdir are not supported on this platform"), NULL, EC_BADCONF);
#endif
if (option_bool(OPT_DNSSEC_VALID)) if (option_bool(OPT_DNSSEC_VALID))
{ {
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
if (!daemon->ds) if (!daemon->ds)
die(_("No trust anchors provided for DNSSEC"), NULL, EC_BADCONF); die(_("no trust anchors provided for DNSSEC"), NULL, EC_BADCONF);
if (daemon->cachesize < CACHESIZ) if (daemon->cachesize < CACHESIZ)
die(_("Cannot reduce cache size from default when DNSSEC enabled"), NULL, EC_BADCONF); die(_("cannot reduce cache size from default when DNSSEC enabled"), NULL, EC_BADCONF);
#else #else
die(_("DNSSEC not available: set HAVE_DNSSEC in src/config.h"), NULL, EC_BADCONF); die(_("DNSSEC not available: set HAVE_DNSSEC in src/config.h"), NULL, EC_BADCONF);
#endif #endif
@@ -162,10 +183,10 @@ int main (int argc, char **argv)
#ifdef HAVE_CONNTRACK #ifdef HAVE_CONNTRACK
if (option_bool(OPT_CONNTRACK) && (daemon->query_port != 0 || daemon->osport)) if (option_bool(OPT_CONNTRACK) && (daemon->query_port != 0 || daemon->osport))
die (_("Cannot use --conntrack AND --query-port"), NULL, EC_BADCONF); die (_("cannot use --conntrack AND --query-port"), NULL, EC_BADCONF);
#else #else
if (option_bool(OPT_CONNTRACK)) if (option_bool(OPT_CONNTRACK))
die(_("Conntrack support not available: set HAVE_CONNTRACK in src/config.h"), NULL, EC_BADCONF); die(_("conntrack support not available: set HAVE_CONNTRACK in src/config.h"), NULL, EC_BADCONF);
#endif #endif
#ifdef HAVE_SOLARIS_NETWORK #ifdef HAVE_SOLARIS_NETWORK
@@ -185,7 +206,7 @@ int main (int argc, char **argv)
#ifndef HAVE_LOOP #ifndef HAVE_LOOP
if (option_bool(OPT_LOOP_DETECT)) if (option_bool(OPT_LOOP_DETECT))
die(_("Loop detection not available: set HAVE_LOOP in src/config.h"), NULL, EC_BADCONF); die(_("loop detection not available: set HAVE_LOOP in src/config.h"), NULL, EC_BADCONF);
#endif #endif
now = dnsmasq_time(); now = dnsmasq_time();
@@ -314,13 +335,15 @@ int main (int argc, char **argv)
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
blockdata_init(); blockdata_init();
#endif #endif
#ifdef HAVE_LINUX_NETWORK
if (!option_bool(OPT_NO_POLL))
inotify_dnsmasq_init();
#endif
} }
#ifdef HAVE_INOTIFY
if (daemon->port != 0 || daemon->dhcp || daemon->doing_dhcp6)
inotify_dnsmasq_init();
else
daemon->inotifyfd = -1;
#endif
if (option_bool(OPT_DBUS)) if (option_bool(OPT_DBUS))
#ifdef HAVE_DBUS #ifdef HAVE_DBUS
{ {
@@ -435,7 +458,7 @@ int main (int argc, char **argv)
char *msg; char *msg;
/* close our copy of write-end */ /* close our copy of write-end */
close(err_pipe[1]); while (retry_send(close(err_pipe[1])));
/* check for errors after the fork */ /* check for errors after the fork */
if (read_event(err_pipe[0], &ev, &msg)) if (read_event(err_pipe[0], &ev, &msg))
@@ -444,7 +467,7 @@ int main (int argc, char **argv)
_exit(EC_GOOD); _exit(EC_GOOD);
} }
close(err_pipe[0]); while (retry_send(close(err_pipe[0])));
/* NO calls to die() from here on. */ /* NO calls to die() from here on. */
@@ -496,10 +519,12 @@ int main (int argc, char **argv)
{ {
if (!read_write(fd, (unsigned char *)daemon->namebuff, strlen(daemon->namebuff), 0)) if (!read_write(fd, (unsigned char *)daemon->namebuff, strlen(daemon->namebuff), 0))
err = 1; err = 1;
else
while (!err && close(fd) == -1) {
if (!retry_send()) while (retry_send(close(fd)));
err = 1; if (errno != 0)
err = 1;
}
} }
if (err) if (err)
@@ -618,12 +643,14 @@ int main (int argc, char **argv)
} }
#ifdef HAVE_LINUX_NETWORK #ifdef HAVE_LINUX_NETWORK
free(hdr);
free(data);
if (option_bool(OPT_DEBUG)) if (option_bool(OPT_DEBUG))
prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
#endif #endif
#ifdef HAVE_TFTP #ifdef HAVE_TFTP
if (option_bool(OPT_TFTP)) if (option_bool(OPT_TFTP))
{ {
DIR *dir; DIR *dir;
struct tftp_prefix *p; struct tftp_prefix *p;
@@ -632,20 +659,31 @@ int main (int argc, char **argv)
{ {
if (!((dir = opendir(daemon->tftp_prefix)))) if (!((dir = opendir(daemon->tftp_prefix))))
{ {
send_event(err_pipe[1], EVENT_TFTP_ERR, errno, daemon->tftp_prefix); tftp_prefix_missing = 1;
_exit(0); if (!option_bool(OPT_TFTP_NO_FAIL))
{
send_event(err_pipe[1], EVENT_TFTP_ERR, errno, daemon->tftp_prefix);
_exit(0);
}
} }
closedir(dir); else
closedir(dir);
} }
for (p = daemon->if_prefix; p; p = p->next) for (p = daemon->if_prefix; p; p = p->next)
{ {
p->missing = 0;
if (!((dir = opendir(p->prefix)))) if (!((dir = opendir(p->prefix))))
{ {
send_event(err_pipe[1], EVENT_TFTP_ERR, errno, p->prefix); p->missing = 1;
_exit(0); if (!option_bool(OPT_TFTP_NO_FAIL))
} {
closedir(dir); send_event(err_pipe[1], EVENT_TFTP_ERR, errno, p->prefix);
_exit(0);
}
}
else
closedir(dir);
} }
} }
#endif #endif
@@ -675,9 +713,24 @@ int main (int argc, char **argv)
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID)) if (option_bool(OPT_DNSSEC_VALID))
{ {
int rc;
/* Delay creating the timestamp file until here, after we've changed user, so that
it has the correct owner to allow updating the mtime later.
This means we have to report fatal errors via the pipe. */
if ((rc = setup_timestamp()) == -1)
{
send_event(err_pipe[1], EVENT_TIME_ERR, errno, daemon->timestamp_file);
_exit(0);
}
my_syslog(LOG_INFO, _("DNSSEC validation enabled")); my_syslog(LOG_INFO, _("DNSSEC validation enabled"));
if (option_bool(OPT_DNSSEC_TIME)) if (option_bool(OPT_DNSSEC_TIME))
my_syslog(LOG_INFO, _("DNSSEC signature timestamps not checked until first cache reload")); my_syslog(LOG_INFO, _("DNSSEC signature timestamps not checked until first cache reload"));
if (rc == 1)
my_syslog(LOG_INFO, _("DNSSEC signature timestamps not checked until system time valid"));
} }
#endif #endif
@@ -743,18 +796,22 @@ int main (int argc, char **argv)
#endif #endif
#ifdef HAVE_TFTP #ifdef HAVE_TFTP
if (option_bool(OPT_TFTP)) if (option_bool(OPT_TFTP))
{ {
#ifdef FD_SETSIZE struct tftp_prefix *p;
if (FD_SETSIZE < (unsigned)max_fd)
max_fd = FD_SETSIZE;
#endif
my_syslog(MS_TFTP | LOG_INFO, "TFTP %s%s %s", my_syslog(MS_TFTP | LOG_INFO, "TFTP %s%s %s",
daemon->tftp_prefix ? _("root is ") : _("enabled"), daemon->tftp_prefix ? _("root is ") : _("enabled"),
daemon->tftp_prefix ? daemon->tftp_prefix: "", daemon->tftp_prefix ? daemon->tftp_prefix: "",
option_bool(OPT_TFTP_SECURE) ? _("secure mode") : ""); option_bool(OPT_TFTP_SECURE) ? _("secure mode") : "");
if (tftp_prefix_missing)
my_syslog(MS_TFTP | LOG_WARNING, _("warning: %s inaccessible"), daemon->tftp_prefix);
for (p = daemon->if_prefix; p; p = p->next)
if (p->missing)
my_syslog(MS_TFTP | LOG_WARNING, _("warning: TFTP directory %s inaccessible"), p->prefix);
/* This is a guess, it assumes that for small limits, /* This is a guess, it assumes that for small limits,
disjoint files might be served, but for large limits, disjoint files might be served, but for large limits,
a single file will be sent to may clients (the file only needs a single file will be sent to may clients (the file only needs
@@ -787,99 +844,72 @@ int main (int argc, char **argv)
/* finished start-up - release original process */ /* finished start-up - release original process */
if (err_pipe[1] != -1) if (err_pipe[1] != -1)
close(err_pipe[1]); while (retry_send(close(err_pipe[1])));
if (daemon->port != 0) if (daemon->port != 0)
check_servers(); check_servers();
pid = getpid(); pid = getpid();
#ifdef HAVE_LINUX_NETWORK #ifdef HAVE_INOTIFY
/* Using inotify, have to select a resolv file at startup */ /* Using inotify, have to select a resolv file at startup */
poll_resolv(1, 0, now); poll_resolv(1, 0, now);
#endif #endif
while (1) while (1)
{ {
int maxfd = -1; int t, timeout = -1;
struct timeval t, *tp = NULL;
fd_set rset, wset, eset;
FD_ZERO(&rset); poll_reset();
FD_ZERO(&wset);
FD_ZERO(&eset);
/* if we are out of resources, find how long we have to wait /* if we are out of resources, find how long we have to wait
for some to come free, we'll loop around then and restart for some to come free, we'll loop around then and restart
listening for queries */ listening for queries */
if ((t.tv_sec = set_dns_listeners(now, &rset, &maxfd)) != 0) if ((t = set_dns_listeners(now)) != 0)
{ timeout = t * 1000;
t.tv_usec = 0;
tp = &t;
}
/* Whilst polling for the dbus, or doing a tftp transfer, wake every quarter second */ /* Whilst polling for the dbus, or doing a tftp transfer, wake every quarter second */
if (daemon->tftp_trans || if (daemon->tftp_trans ||
(option_bool(OPT_DBUS) && !daemon->dbus)) (option_bool(OPT_DBUS) && !daemon->dbus))
{ timeout = 250;
t.tv_sec = 0;
t.tv_usec = 250000;
tp = &t;
}
/* Wake every second whilst waiting for DAD to complete */ /* Wake every second whilst waiting for DAD to complete */
else if (is_dad_listeners()) else if (is_dad_listeners())
{ timeout = 1000;
t.tv_sec = 1;
t.tv_usec = 0;
tp = &t;
}
#ifdef HAVE_DBUS #ifdef HAVE_DBUS
set_dbus_listeners(&maxfd, &rset, &wset, &eset); set_dbus_listeners();
#endif #endif
#ifdef HAVE_DHCP #ifdef HAVE_DHCP
if (daemon->dhcp || daemon->relay4) if (daemon->dhcp || daemon->relay4)
{ {
FD_SET(daemon->dhcpfd, &rset); poll_listen(daemon->dhcpfd, POLLIN);
bump_maxfd(daemon->dhcpfd, &maxfd);
if (daemon->pxefd != -1) if (daemon->pxefd != -1)
{ poll_listen(daemon->pxefd, POLLIN);
FD_SET(daemon->pxefd, &rset);
bump_maxfd(daemon->pxefd, &maxfd);
}
} }
#endif #endif
#ifdef HAVE_DHCP6 #ifdef HAVE_DHCP6
if (daemon->doing_dhcp6 || daemon->relay6) if (daemon->doing_dhcp6 || daemon->relay6)
{ poll_listen(daemon->dhcp6fd, POLLIN);
FD_SET(daemon->dhcp6fd, &rset);
bump_maxfd(daemon->dhcp6fd, &maxfd);
}
if (daemon->doing_ra) if (daemon->doing_ra)
{ poll_listen(daemon->icmp6fd, POLLIN);
FD_SET(daemon->icmp6fd, &rset); #endif
bump_maxfd(daemon->icmp6fd, &maxfd);
} #ifdef HAVE_INOTIFY
if (daemon->inotifyfd != -1)
poll_listen(daemon->inotifyfd, POLLIN);
#endif #endif
#if defined(HAVE_LINUX_NETWORK) #if defined(HAVE_LINUX_NETWORK)
FD_SET(daemon->netlinkfd, &rset); poll_listen(daemon->netlinkfd, POLLIN);
bump_maxfd(daemon->netlinkfd, &maxfd);
if (daemon->port != 0 && !option_bool(OPT_NO_POLL))
{
FD_SET(daemon->inotifyfd, &rset);
bump_maxfd(daemon->inotifyfd, &maxfd);
}
#elif defined(HAVE_BSD_NETWORK) #elif defined(HAVE_BSD_NETWORK)
FD_SET(daemon->routefd, &rset); poll_listen(daemon->routefd, POLLIN);
bump_maxfd(daemon->routefd, &maxfd);
#endif #endif
FD_SET(piperead, &rset); poll_listen(piperead, POLLIN);
bump_maxfd(piperead, &maxfd);
#ifdef HAVE_DHCP #ifdef HAVE_DHCP
# ifdef HAVE_SCRIPT # ifdef HAVE_SCRIPT
@@ -890,10 +920,7 @@ int main (int argc, char **argv)
# endif # endif
if (!helper_buf_empty()) if (!helper_buf_empty())
{ poll_listen(daemon->helperfd, POLLOUT);
FD_SET(daemon->helperfd, &wset);
bump_maxfd(daemon->helperfd, &maxfd);
}
# else # else
/* need this for other side-effects */ /* need this for other side-effects */
while (do_script_run(now)); while (do_script_run(now));
@@ -907,17 +934,14 @@ int main (int argc, char **argv)
/* must do this just before select(), when we know no /* must do this just before select(), when we know no
more calls to my_syslog() can occur */ more calls to my_syslog() can occur */
set_log_writer(&wset, &maxfd); set_log_writer();
if (select(maxfd+1, &rset, &wset, &eset, tp) < 0) if (do_poll(timeout) < 0)
{ continue;
/* otherwise undefined after error */
FD_ZERO(&rset); FD_ZERO(&wset); FD_ZERO(&eset);
}
now = dnsmasq_time(); now = dnsmasq_time();
check_log_writer(&wset); check_log_writer(0);
/* prime. */ /* prime. */
enumerate_interfaces(1); enumerate_interfaces(1);
@@ -933,16 +957,19 @@ int main (int argc, char **argv)
} }
#if defined(HAVE_LINUX_NETWORK) #if defined(HAVE_LINUX_NETWORK)
if (FD_ISSET(daemon->netlinkfd, &rset)) if (poll_check(daemon->netlinkfd, POLLIN))
netlink_multicast(); netlink_multicast();
#elif defined(HAVE_BSD_NETWORK) #elif defined(HAVE_BSD_NETWORK)
if (FD_ISSET(daemon->routefd, &rset)) if (poll_check(daemon->routefd, POLLIN))
route_sock(); route_sock();
#endif #endif
#ifdef HAVE_LINUX_NETWORK #ifdef HAVE_INOTIFY
if (daemon->port != 0 && !option_bool(OPT_NO_POLL) && FD_ISSET(daemon->inotifyfd, &rset) && inotify_check()) if (daemon->inotifyfd != -1 && poll_check(daemon->inotifyfd, POLLIN) && inotify_check(now))
poll_resolv(1, 1, now); {
if (daemon->port != 0 && !option_bool(OPT_NO_POLL))
poll_resolv(1, 1, now);
}
#else #else
/* Check for changes to resolv files once per second max. */ /* Check for changes to resolv files once per second max. */
/* Don't go silent for long periods if the clock goes backwards. */ /* Don't go silent for long periods if the clock goes backwards. */
@@ -958,7 +985,7 @@ int main (int argc, char **argv)
} }
#endif #endif
if (FD_ISSET(piperead, &rset)) if (poll_check(piperead, POLLIN))
async_event(piperead, now); async_event(piperead, now);
#ifdef HAVE_DBUS #ifdef HAVE_DBUS
@@ -971,34 +998,34 @@ int main (int argc, char **argv)
if (daemon->dbus) if (daemon->dbus)
my_syslog(LOG_INFO, _("connected to system DBus")); my_syslog(LOG_INFO, _("connected to system DBus"));
} }
check_dbus_listeners(&rset, &wset, &eset); check_dbus_listeners();
#endif #endif
check_dns_listeners(&rset, now); check_dns_listeners(now);
#ifdef HAVE_TFTP #ifdef HAVE_TFTP
check_tftp_listeners(&rset, now); check_tftp_listeners(now);
#endif #endif
#ifdef HAVE_DHCP #ifdef HAVE_DHCP
if (daemon->dhcp || daemon->relay4) if (daemon->dhcp || daemon->relay4)
{ {
if (FD_ISSET(daemon->dhcpfd, &rset)) if (poll_check(daemon->dhcpfd, POLLIN))
dhcp_packet(now, 0); dhcp_packet(now, 0);
if (daemon->pxefd != -1 && FD_ISSET(daemon->pxefd, &rset)) if (daemon->pxefd != -1 && poll_check(daemon->pxefd, POLLIN))
dhcp_packet(now, 1); dhcp_packet(now, 1);
} }
#ifdef HAVE_DHCP6 #ifdef HAVE_DHCP6
if ((daemon->doing_dhcp6 || daemon->relay6) && FD_ISSET(daemon->dhcp6fd, &rset)) if ((daemon->doing_dhcp6 || daemon->relay6) && poll_check(daemon->dhcp6fd, POLLIN))
dhcp6_packet(now); dhcp6_packet(now);
if (daemon->doing_ra && FD_ISSET(daemon->icmp6fd, &rset)) if (daemon->doing_ra && poll_check(daemon->icmp6fd, POLLIN))
icmp6_packet(now); icmp6_packet(now);
#endif #endif
# ifdef HAVE_SCRIPT # ifdef HAVE_SCRIPT
if (daemon->helperfd != -1 && FD_ISSET(daemon->helperfd, &wset)) if (daemon->helperfd != -1 && poll_check(daemon->helperfd, POLLIN))
helper_write(); helper_write();
# endif # endif
#endif #endif
@@ -1144,6 +1171,9 @@ static void fatal_event(struct event_desc *ev, char *msg)
case EVENT_TFTP_ERR: case EVENT_TFTP_ERR:
die(_("TFTP directory %s inaccessible: %s"), msg, EC_FILE); die(_("TFTP directory %s inaccessible: %s"), msg, EC_FILE);
case EVENT_TIME_ERR:
die(_("cannot create timestamp file %s: %s" ), msg, EC_BADCONF);
} }
} }
@@ -1284,13 +1314,22 @@ static void async_event(int pipe, time_t now)
do { do {
helper_write(); helper_write();
} while (!helper_buf_empty() || do_script_run(now)); } while (!helper_buf_empty() || do_script_run(now));
close(daemon->helperfd); while (retry_send(close(daemon->helperfd)));
} }
#endif #endif
if (daemon->lease_stream) if (daemon->lease_stream)
fclose(daemon->lease_stream); fclose(daemon->lease_stream);
#ifdef HAVE_DNSSEC
/* update timestamp file on TERM if time is considered valid */
if (daemon->back_to_the_future)
{
if (utime(daemon->timestamp_file, NULL) == -1)
my_syslog(LOG_ERR, _("failed to update mtime on %s: %s"), daemon->timestamp_file, strerror(errno));
}
#endif
if (daemon->runfile) if (daemon->runfile)
unlink(daemon->runfile); unlink(daemon->runfile);
@@ -1383,6 +1422,9 @@ void clear_cache_and_reload(time_t now)
if (option_bool(OPT_ETHERS)) if (option_bool(OPT_ETHERS))
dhcp_read_ethers(); dhcp_read_ethers();
reread_dhcp(); reread_dhcp();
#ifdef HAVE_INOTIFY
set_dynamic_inotify(AH_DHCP_HST | AH_DHCP_OPT, 0, NULL, 0);
#endif
dhcp_update_configs(daemon->dhcp_conf); dhcp_update_configs(daemon->dhcp_conf);
lease_update_from_configs(); lease_update_from_configs();
lease_update_file(now); lease_update_file(now);
@@ -1397,7 +1439,7 @@ void clear_cache_and_reload(time_t now)
#endif #endif
} }
static int set_dns_listeners(time_t now, fd_set *set, int *maxfdp) static int set_dns_listeners(time_t now)
{ {
struct serverfd *serverfdp; struct serverfd *serverfdp;
struct listener *listener; struct listener *listener;
@@ -1409,8 +1451,7 @@ static int set_dns_listeners(time_t now, fd_set *set, int *maxfdp)
for (transfer = daemon->tftp_trans; transfer; transfer = transfer->next) for (transfer = daemon->tftp_trans; transfer; transfer = transfer->next)
{ {
tftp++; tftp++;
FD_SET(transfer->sockfd, set); poll_listen(transfer->sockfd, POLLIN);
bump_maxfd(transfer->sockfd, maxfdp);
} }
#endif #endif
@@ -1419,27 +1460,18 @@ static int set_dns_listeners(time_t now, fd_set *set, int *maxfdp)
get_new_frec(now, &wait, 0); get_new_frec(now, &wait, 0);
for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next) for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
{ poll_listen(serverfdp->fd, POLLIN);
FD_SET(serverfdp->fd, set);
bump_maxfd(serverfdp->fd, maxfdp);
}
if (daemon->port != 0 && !daemon->osport) if (daemon->port != 0 && !daemon->osport)
for (i = 0; i < RANDOM_SOCKS; i++) for (i = 0; i < RANDOM_SOCKS; i++)
if (daemon->randomsocks[i].refcount != 0) if (daemon->randomsocks[i].refcount != 0)
{ poll_listen(daemon->randomsocks[i].fd, POLLIN);
FD_SET(daemon->randomsocks[i].fd, set);
bump_maxfd(daemon->randomsocks[i].fd, maxfdp);
}
for (listener = daemon->listeners; listener; listener = listener->next) for (listener = daemon->listeners; listener; listener = listener->next)
{ {
/* only listen for queries if we have resources */ /* only listen for queries if we have resources */
if (listener->fd != -1 && wait == 0) if (listener->fd != -1 && wait == 0)
{ poll_listen(listener->fd, POLLIN);
FD_SET(listener->fd, set);
bump_maxfd(listener->fd, maxfdp);
}
/* death of a child goes through the select loop, so /* death of a child goes through the select loop, so
we don't need to explicitly arrange to wake up here */ we don't need to explicitly arrange to wake up here */
@@ -1447,17 +1479,13 @@ static int set_dns_listeners(time_t now, fd_set *set, int *maxfdp)
for (i = 0; i < MAX_PROCS; i++) for (i = 0; i < MAX_PROCS; i++)
if (daemon->tcp_pids[i] == 0) if (daemon->tcp_pids[i] == 0)
{ {
FD_SET(listener->tcpfd, set); poll_listen(listener->tcpfd, POLLIN);
bump_maxfd(listener->tcpfd, maxfdp);
break; break;
} }
#ifdef HAVE_TFTP #ifdef HAVE_TFTP
if (tftp <= daemon->tftp_max && listener->tftpfd != -1) if (tftp <= daemon->tftp_max && listener->tftpfd != -1)
{ poll_listen(listener->tftpfd, POLLIN);
FD_SET(listener->tftpfd, set);
bump_maxfd(listener->tftpfd, maxfdp);
}
#endif #endif
} }
@@ -1465,33 +1493,33 @@ static int set_dns_listeners(time_t now, fd_set *set, int *maxfdp)
return wait; return wait;
} }
static void check_dns_listeners(fd_set *set, time_t now) static void check_dns_listeners(time_t now)
{ {
struct serverfd *serverfdp; struct serverfd *serverfdp;
struct listener *listener; struct listener *listener;
int i; int i;
for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next) for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
if (FD_ISSET(serverfdp->fd, set)) if (poll_check(serverfdp->fd, POLLIN))
reply_query(serverfdp->fd, serverfdp->source_addr.sa.sa_family, now); reply_query(serverfdp->fd, serverfdp->source_addr.sa.sa_family, now);
if (daemon->port != 0 && !daemon->osport) if (daemon->port != 0 && !daemon->osport)
for (i = 0; i < RANDOM_SOCKS; i++) for (i = 0; i < RANDOM_SOCKS; i++)
if (daemon->randomsocks[i].refcount != 0 && if (daemon->randomsocks[i].refcount != 0 &&
FD_ISSET(daemon->randomsocks[i].fd, set)) poll_check(daemon->randomsocks[i].fd, POLLIN))
reply_query(daemon->randomsocks[i].fd, daemon->randomsocks[i].family, now); reply_query(daemon->randomsocks[i].fd, daemon->randomsocks[i].family, now);
for (listener = daemon->listeners; listener; listener = listener->next) for (listener = daemon->listeners; listener; listener = listener->next)
{ {
if (listener->fd != -1 && FD_ISSET(listener->fd, set)) if (listener->fd != -1 && poll_check(listener->fd, POLLIN))
receive_query(listener, now); receive_query(listener, now);
#ifdef HAVE_TFTP #ifdef HAVE_TFTP
if (listener->tftpfd != -1 && FD_ISSET(listener->tftpfd, set)) if (listener->tftpfd != -1 && poll_check(listener->tftpfd, POLLIN))
tftp_request(listener, now); tftp_request(listener, now);
#endif #endif
if (listener->tcpfd != -1 && FD_ISSET(listener->tcpfd, set)) if (listener->tcpfd != -1 && poll_check(listener->tcpfd, POLLIN))
{ {
int confd, client_ok = 1; int confd, client_ok = 1;
struct irec *iface = NULL; struct irec *iface = NULL;
@@ -1506,7 +1534,7 @@ static void check_dns_listeners(fd_set *set, time_t now)
if (getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) == -1) if (getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) == -1)
{ {
close(confd); while (retry_send(close(confd)));
continue; continue;
} }
@@ -1571,7 +1599,7 @@ static void check_dns_listeners(fd_set *set, time_t now)
if (!client_ok) if (!client_ok)
{ {
shutdown(confd, SHUT_RDWR); shutdown(confd, SHUT_RDWR);
close(confd); while (retry_send(close(confd)));
} }
#ifndef NO_FORK #ifndef NO_FORK
else if (!option_bool(OPT_DEBUG) && (p = fork()) != 0) else if (!option_bool(OPT_DEBUG) && (p = fork()) != 0)
@@ -1586,7 +1614,10 @@ static void check_dns_listeners(fd_set *set, time_t now)
break; break;
} }
} }
close(confd); while (retry_send(close(confd)));
/* The child can use up to TCP_MAX_QUERIES ids, so skip that many. */
daemon->log_id += TCP_MAX_QUERIES;
} }
#endif #endif
else else
@@ -1628,7 +1659,7 @@ static void check_dns_listeners(fd_set *set, time_t now)
buff = tcp_request(confd, now, &tcp_addr, netmask, auth_dns); buff = tcp_request(confd, now, &tcp_addr, netmask, auth_dns);
shutdown(confd, SHUT_RDWR); shutdown(confd, SHUT_RDWR);
close(confd); while (retry_send(close(confd)));
if (buff) if (buff)
free(buff); free(buff);
@@ -1637,7 +1668,7 @@ static void check_dns_listeners(fd_set *set, time_t now)
if (s->tcpfd != -1) if (s->tcpfd != -1)
{ {
shutdown(s->tcpfd, SHUT_RDWR); shutdown(s->tcpfd, SHUT_RDWR);
close(s->tcpfd); while (retry_send(close(s->tcpfd)));
} }
#ifndef NO_FORK #ifndef NO_FORK
if (!option_bool(OPT_DEBUG)) if (!option_bool(OPT_DEBUG))
@@ -1679,14 +1710,22 @@ int icmp_ping(struct in_addr addr)
better not use any resources our caller has in use...) better not use any resources our caller has in use...)
but we remain deaf to signals or further DHCP packets. */ but we remain deaf to signals or further DHCP packets. */
int fd; /* There can be a problem using dnsmasq_time() to end the loop, since
it's not monotonic, and can go backwards if the system clock is
tweaked, leading to the code getting stuck in this loop and
ignoring DHCP requests. To fix this, we check to see if select returned
as a result of a timeout rather than a socket becoming available. We
only allow this to happen as many times as it takes to get to the wait time
in quarter-second chunks. This provides a fallback way to end loop. */
int fd, rc;
struct sockaddr_in saddr; struct sockaddr_in saddr;
struct { struct {
struct ip ip; struct ip ip;
struct icmp icmp; struct icmp icmp;
} packet; } packet;
unsigned short id = rand16(); unsigned short id = rand16();
unsigned int i, j; unsigned int i, j, timeout_count;
int gotreply = 0; int gotreply = 0;
time_t start, now; time_t start, now;
@@ -1715,57 +1754,47 @@ int icmp_ping(struct in_addr addr)
j = (j & 0xffff) + (j >> 16); j = (j & 0xffff) + (j >> 16);
packet.icmp.icmp_cksum = (j == 0xffff) ? j : ~j; packet.icmp.icmp_cksum = (j == 0xffff) ? j : ~j;
while (sendto(fd, (char *)&packet.icmp, sizeof(struct icmp), 0, while (retry_send(sendto(fd, (char *)&packet.icmp, sizeof(struct icmp), 0,
(struct sockaddr *)&saddr, sizeof(saddr)) == -1 && (struct sockaddr *)&saddr, sizeof(saddr))));
retry_send());
for (now = start = dnsmasq_time(); for (now = start = dnsmasq_time(), timeout_count = 0;
difftime(now, start) < (float)PING_WAIT;) (difftime(now, start) < (float)PING_WAIT) && (timeout_count < PING_WAIT * 4);)
{ {
struct timeval tv;
fd_set rset, wset;
struct sockaddr_in faddr; struct sockaddr_in faddr;
int maxfd = fd;
socklen_t len = sizeof(faddr); socklen_t len = sizeof(faddr);
tv.tv_usec = 250000; poll_reset();
tv.tv_sec = 0; poll_listen(fd, POLLIN);
set_dns_listeners(now);
FD_ZERO(&rset); set_log_writer();
FD_ZERO(&wset);
FD_SET(fd, &rset);
set_dns_listeners(now, &rset, &maxfd);
set_log_writer(&wset, &maxfd);
#ifdef HAVE_DHCP6 #ifdef HAVE_DHCP6
if (daemon->doing_ra) if (daemon->doing_ra)
{ poll_listen(daemon->icmp6fd, POLLIN);
FD_SET(daemon->icmp6fd, &rset);
bump_maxfd(daemon->icmp6fd, &maxfd);
}
#endif #endif
if (select(maxfd+1, &rset, &wset, NULL, &tv) < 0) rc = do_poll(250);
{
FD_ZERO(&rset); if (rc < 0)
FD_ZERO(&wset); continue;
} else if (rc == 0)
timeout_count++;
now = dnsmasq_time(); now = dnsmasq_time();
check_log_writer(&wset); check_log_writer(0);
check_dns_listeners(&rset, now); check_dns_listeners(now);
#ifdef HAVE_DHCP6 #ifdef HAVE_DHCP6
if (daemon->doing_ra && FD_ISSET(daemon->icmp6fd, &rset)) if (daemon->doing_ra && poll_check(daemon->icmp6fd, POLLIN))
icmp6_packet(now); icmp6_packet(now);
#endif #endif
#ifdef HAVE_TFTP #ifdef HAVE_TFTP
check_tftp_listeners(&rset, now); check_tftp_listeners(now);
#endif #endif
if (FD_ISSET(fd, &rset) && if (poll_check(fd, POLLIN) &&
recvfrom(fd, &packet, sizeof(packet), 0, recvfrom(fd, &packet, sizeof(packet), 0,
(struct sockaddr *)&faddr, &len) == sizeof(packet) && (struct sockaddr *)&faddr, &len) == sizeof(packet) &&
saddr.sin_addr.s_addr == faddr.sin_addr.s_addr && saddr.sin_addr.s_addr == faddr.sin_addr.s_addr &&
@@ -1779,7 +1808,7 @@ int icmp_ping(struct in_addr addr)
} }
#if defined(HAVE_LINUX_NETWORK) || defined(HAVE_SOLARIS_NETWORK) #if defined(HAVE_LINUX_NETWORK) || defined(HAVE_SOLARIS_NETWORK)
close(fd); while (retry_send(close(fd)));
#else #else
opt = 1; opt = 1;
setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)); setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt));
+60 -24
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley /* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -14,7 +14,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#define COPYRIGHT "Copyright (c) 2000-2014 Simon Kelley" #define COPYRIGHT "Copyright (c) 2000-2015 Simon Kelley"
#ifndef NO_LARGEFILE #ifndef NO_LARGEFILE
/* Ensure we can use files >2GB (log files may grow this big) */ /* Ensure we can use files >2GB (log files may grow this big) */
@@ -82,7 +82,7 @@ typedef unsigned long long u64;
#if defined(HAVE_SOLARIS_NETWORK) #if defined(HAVE_SOLARIS_NETWORK)
# include <sys/sockio.h> # include <sys/sockio.h>
#endif #endif
#include <sys/select.h> #include <sys/poll.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <sys/time.h> #include <sys/time.h>
#include <sys/un.h> #include <sys/un.h>
@@ -117,6 +117,7 @@ typedef unsigned long long u64;
#include <sys/uio.h> #include <sys/uio.h>
#include <syslog.h> #include <syslog.h>
#include <dirent.h> #include <dirent.h>
#include <utime.h>
#ifndef HAVE_LINUX_NETWORK #ifndef HAVE_LINUX_NETWORK
# include <net/if_dl.h> # include <net/if_dl.h>
#endif #endif
@@ -167,6 +168,7 @@ struct event_desc {
#define EVENT_INIT 21 #define EVENT_INIT 21
#define EVENT_NEWADDR 22 #define EVENT_NEWADDR 22
#define EVENT_NEWROUTE 23 #define EVENT_NEWROUTE 23
#define EVENT_TIME_ERR 24
/* Exit codes. */ /* Exit codes. */
#define EC_GOOD 0 #define EC_GOOD 0
@@ -238,7 +240,9 @@ struct event_desc {
#define OPT_DNSSEC_NO_SIGN 48 #define OPT_DNSSEC_NO_SIGN 48
#define OPT_LOCAL_SERVICE 49 #define OPT_LOCAL_SERVICE 49
#define OPT_LOOP_DETECT 50 #define OPT_LOOP_DETECT 50
#define OPT_LAST 51 #define OPT_EXTRALOG 51
#define OPT_TFTP_NO_FAIL 52
#define OPT_LAST 53
/* extra flags for my_syslog, we use a couple of facilities since they are known /* 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. */ not to occupy the same bits as priorities, no matter how syslog.h is set up. */
@@ -442,6 +446,7 @@ struct crec {
#define F_NO_RR (1u<<25) #define F_NO_RR (1u<<25)
#define F_IPSET (1u<<26) #define F_IPSET (1u<<26)
#define F_NSIGMATCH (1u<<27) #define F_NSIGMATCH (1u<<27)
#define F_NOEXTRA (1u<<28)
/* Values of uid in crecs with F_CONFIG bit set. */ /* Values of uid in crecs with F_CONFIG bit set. */
#define SRC_INTERFACE 0 #define SRC_INTERFACE 0
@@ -500,7 +505,7 @@ struct server {
char interface[IF_NAMESIZE+1]; char interface[IF_NAMESIZE+1];
struct serverfd *sfd; struct serverfd *sfd;
char *domain; /* set if this server only handles a domain. */ char *domain; /* set if this server only handles a domain. */
int flags, tcpfd; int flags, tcpfd, edns_pktsz;
unsigned int queries, failed_queries; unsigned int queries, failed_queries;
#ifdef HAVE_LOOP #ifdef HAVE_LOOP
u32 uid; u32 uid;
@@ -542,19 +547,26 @@ struct resolvc {
int is_default, logged; int is_default, logged;
time_t mtime; time_t mtime;
char *name; char *name;
#ifdef HAVE_LINUX_NETWORK #ifdef HAVE_INOTIFY
int wd; /* inotify watch descriptor */ int wd; /* inotify watch descriptor */
char *file; /* pointer to file part if path */ char *file; /* pointer to file part if path */
#endif #endif
}; };
/* adn-hosts parms from command-line (also dhcp-hostsfile and dhcp-optsfile */ /* adn-hosts parms from command-line (also dhcp-hostsfile and dhcp-optsfile and dhcp-hostsdir*/
#define AH_DIR 1 #define AH_DIR 1
#define AH_INACTIVE 2 #define AH_INACTIVE 2
#define AH_WD_DONE 4
#define AH_HOSTS 8
#define AH_DHCP_HST 16
#define AH_DHCP_OPT 32
struct hostsfile { struct hostsfile {
struct hostsfile *next; struct hostsfile *next;
int flags; int flags;
char *fname; char *fname;
#ifdef HAVE_INOTIFY
int wd; /* inotify watch descriptor */
#endif
unsigned int index; /* matches to cache entries for logging */ unsigned int index; /* matches to cache entries for logging */
}; };
@@ -572,6 +584,7 @@ struct hostsfile {
#define STAT_NO_NS 10 #define STAT_NO_NS 10
#define STAT_NEED_DS_NEG 11 #define STAT_NEED_DS_NEG 11
#define STAT_CHASE_CNAME 12 #define STAT_CHASE_CNAME 12
#define STAT_INSECURE_DS 13
#define FREC_NOREBIND 1 #define FREC_NOREBIND 1
#define FREC_CHECKING_DISABLED 2 #define FREC_CHECKING_DISABLED 2
@@ -582,6 +595,7 @@ struct hostsfile {
#define FREC_DO_QUESTION 64 #define FREC_DO_QUESTION 64
#define FREC_ADDED_PHEADER 128 #define FREC_ADDED_PHEADER 128
#define FREC_CHECK_NOSIGN 256 #define FREC_CHECK_NOSIGN 256
#define FREC_TEST_PKTSZ 512
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
#define HASH_SIZE 20 /* SHA-1 digest size */ #define HASH_SIZE 20 /* SHA-1 digest size */
@@ -599,7 +613,7 @@ struct frec {
#endif #endif
unsigned int iface; unsigned int iface;
unsigned short orig_id, new_id; unsigned short orig_id, new_id;
int fd, forwardall, flags; int log_id, fd, forwardall, flags;
time_t time; time_t time;
unsigned char *hash[HASH_SIZE]; unsigned char *hash[HASH_SIZE];
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
@@ -855,6 +869,7 @@ struct dhcp_context {
#define CONTEXT_USED (1u<<15) #define CONTEXT_USED (1u<<15)
#define CONTEXT_OLD (1u<<16) #define CONTEXT_OLD (1u<<16)
#define CONTEXT_V6 (1u<<17) #define CONTEXT_V6 (1u<<17)
#define CONTEXT_RA_OFF_LINK (1u<<18)
struct ping_result { struct ping_result {
struct in_addr addr; struct in_addr addr;
@@ -891,6 +906,7 @@ struct addr_list {
struct tftp_prefix { struct tftp_prefix {
char *interface; char *interface;
char *prefix; char *prefix;
int missing;
struct tftp_prefix *next; struct tftp_prefix *next;
}; };
@@ -941,7 +957,7 @@ extern struct daemon {
int max_logs; /* queue limit */ int max_logs; /* queue limit */
int cachesize, ftabsize; int cachesize, ftabsize;
int port, query_port, min_port; int port, query_port, min_port;
unsigned long local_ttl, neg_ttl, max_ttl, max_cache_ttl, auth_ttl; unsigned long local_ttl, neg_ttl, max_ttl, min_cache_ttl, max_cache_ttl, auth_ttl;
struct hostsfile *addn_hosts; struct hostsfile *addn_hosts;
struct dhcp_context *dhcp, *dhcp6; struct dhcp_context *dhcp, *dhcp6;
struct ra_interface *ra_interfaces; struct ra_interface *ra_interfaces;
@@ -959,7 +975,7 @@ extern struct daemon {
int doing_ra, doing_dhcp6; int doing_ra, doing_dhcp6;
struct dhcp_netid_list *dhcp_ignore, *dhcp_ignore_names, *dhcp_gen_names; struct dhcp_netid_list *dhcp_ignore, *dhcp_ignore_names, *dhcp_gen_names;
struct dhcp_netid_list *force_broadcast, *bootp_dynamic; struct dhcp_netid_list *force_broadcast, *bootp_dynamic;
struct hostsfile *dhcp_hosts_file, *dhcp_opts_file; struct hostsfile *dhcp_hosts_file, *dhcp_opts_file, *dynamic_dirs;
int dhcp_max, tftp_max; int dhcp_max, tftp_max;
int dhcp_server_port, dhcp_client_port; int dhcp_server_port, dhcp_client_port;
int start_tftp_port, end_tftp_port; int start_tftp_port, end_tftp_port;
@@ -977,6 +993,8 @@ extern struct daemon {
#endif #endif
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
struct ds_config *ds; struct ds_config *ds;
int back_to_the_future;
char *timestamp_file;
#endif #endif
/* globally used stuff for DNS */ /* globally used stuff for DNS */
@@ -1002,11 +1020,16 @@ extern struct daemon {
struct randfd randomsocks[RANDOM_SOCKS]; struct randfd randomsocks[RANDOM_SOCKS];
int v6pktinfo; int v6pktinfo;
struct addrlist *interface_addrs; /* list of all addresses/prefix lengths associated with all local interfaces */ struct addrlist *interface_addrs; /* list of all addresses/prefix lengths associated with all local interfaces */
int log_id, log_display_id; /* ids of transactions for logging */
union mysockaddr *log_source_addr;
/* DHCP state */ /* DHCP state */
int dhcpfd, helperfd, pxefd; int dhcpfd, helperfd, pxefd;
#ifdef HAVE_INOTIFY
int inotifyfd;
#endif
#if defined(HAVE_LINUX_NETWORK) #if defined(HAVE_LINUX_NETWORK)
int netlinkfd, inotifyfd; int netlinkfd;
#elif defined(HAVE_BSD_NETWORK) #elif defined(HAVE_BSD_NETWORK)
int dhcp_raw_fd, dhcp_icmp_fd, routefd; int dhcp_raw_fd, dhcp_icmp_fd, routefd;
#endif #endif
@@ -1033,6 +1056,7 @@ extern struct daemon {
/* utility string buffer, hold max sized IP address as string */ /* utility string buffer, hold max sized IP address as string */
char *addrbuff; char *addrbuff;
char *addrbuff2; /* only allocated when OPT_EXTRALOG */
} *daemon; } *daemon;
@@ -1059,6 +1083,8 @@ int cache_make_stat(struct txt_record *t);
char *cache_get_name(struct crec *crecp); char *cache_get_name(struct crec *crecp);
char *cache_get_cname_target(struct crec *crecp); char *cache_get_cname_target(struct crec *crecp);
struct crec *cache_enumerate(int init); struct crec *cache_enumerate(int init);
int read_hostsfile(char *filename, unsigned int index, int cache_size,
struct crec **rhash, int hashsz);
/* blockdata.c */ /* blockdata.c */
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
@@ -1126,7 +1152,7 @@ int in_zone(struct auth_zone *zone, char *name, char **cut);
#endif #endif
/* dnssec.c */ /* dnssec.c */
size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, int class, int type, union mysockaddr *addr); size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, int class, int type, union mysockaddr *addr, int edns_pktsz);
int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t n, char *name, char *keyname, int class); int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t n, char *name, char *keyname, int class);
int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class); int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class);
int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class, int *neganswer, int *nons); int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class, int *neganswer, int *nons);
@@ -1134,6 +1160,7 @@ int dnssec_chase_cname(time_t now, struct dns_header *header, size_t plen, char
int dnskey_keytag(int alg, int flags, unsigned char *rdata, int rdlen); int dnskey_keytag(int alg, int flags, unsigned char *rdata, int rdlen);
size_t filter_rrsigs(struct dns_header *header, size_t plen); size_t filter_rrsigs(struct dns_header *header, size_t plen);
unsigned char* hash_questions(struct dns_header *header, size_t plen, char *name); unsigned char* hash_questions(struct dns_header *header, size_t plen, char *name);
int setup_timestamp(void);
/* util.c */ /* util.c */
void rand_init(void); void rand_init(void);
@@ -1157,7 +1184,7 @@ int is_same_net6(struct in6_addr *a, struct in6_addr *b, int prefixlen);
u64 addr6part(struct in6_addr *addr); u64 addr6part(struct in6_addr *addr);
void setaddr6part(struct in6_addr *addr, u64 host); void setaddr6part(struct in6_addr *addr, u64 host);
#endif #endif
int retry_send(void); int retry_send(ssize_t rc);
void prettyprint_time(char *buf, unsigned int t); void prettyprint_time(char *buf, unsigned int t);
int prettyprint_addr(union mysockaddr *addr, char *buf); int prettyprint_addr(union mysockaddr *addr, char *buf);
int parse_hex(char *in, unsigned char *out, int maxlen, int parse_hex(char *in, unsigned char *out, int maxlen,
@@ -1166,7 +1193,6 @@ int memcmp_masked(unsigned char *a, unsigned char *b, int len,
unsigned int mask); unsigned int mask);
int expand_buf(struct iovec *iov, size_t size); int expand_buf(struct iovec *iov, size_t size);
char *print_mac(char *buff, unsigned char *mac, int len); char *print_mac(char *buff, unsigned char *mac, int len);
void bump_maxfd(int fd, int *max);
int read_write(int fd, unsigned char *packet, int size, int rw); int read_write(int fd, unsigned char *packet, int size, int rw);
int wildcard_match(const char* wildcard, const char* match); int wildcard_match(const char* wildcard, const char* match);
@@ -1177,8 +1203,8 @@ void die(char *message, char *arg1, int exit_code);
int log_start(struct passwd *ent_pw, int errfd); int log_start(struct passwd *ent_pw, int errfd);
int log_reopen(char *log_file); int log_reopen(char *log_file);
void my_syslog(int priority, const char *format, ...); void my_syslog(int priority, const char *format, ...);
void set_log_writer(fd_set *set, int *maxfdp); void set_log_writer(void);
void check_log_writer(fd_set *set); void check_log_writer(int force);
void flush_log(void); void flush_log(void);
/* option.c */ /* option.c */
@@ -1192,6 +1218,7 @@ void reset_option_bool(unsigned int opt);
struct hostsfile *expand_filelist(struct hostsfile *list); struct hostsfile *expand_filelist(struct hostsfile *list);
char *parse_server(char *arg, union mysockaddr *addr, char *parse_server(char *arg, union mysockaddr *addr,
union mysockaddr *source_addr, char *interface, int *flags); union mysockaddr *source_addr, char *interface, int *flags);
int option_read_dynfile(char *file, int flags);
/* forward.c */ /* forward.c */
void reply_query(int fd, int family, time_t now); void reply_query(int fd, int family, time_t now);
@@ -1280,9 +1307,10 @@ void lease_update_slaac(time_t now);
void lease_set_iaid(struct dhcp_lease *lease, int iaid); void lease_set_iaid(struct dhcp_lease *lease, int iaid);
void lease_make_duid(time_t now); void lease_make_duid(time_t now);
#endif #endif
void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr, void lease_set_hwaddr(struct dhcp_lease *lease, const unsigned char *hwaddr,
unsigned char *clid, int hw_len, int hw_type, int clid_len, time_t now, int force); const unsigned char *clid, int hw_len, int hw_type,
void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth, char *domain, char *config_domain); int clid_len, time_t now, int force);
void lease_set_hostname(struct dhcp_lease *lease, const char *name, int auth, char *domain, char *config_domain);
void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now); void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now);
void lease_set_interface(struct dhcp_lease *lease, int interface, time_t now); void lease_set_interface(struct dhcp_lease *lease, int interface, time_t now);
struct dhcp_lease *lease_find_by_client(unsigned char *hwaddr, int hw_len, int hw_type, struct dhcp_lease *lease_find_by_client(unsigned char *hwaddr, int hw_len, int hw_type,
@@ -1339,8 +1367,8 @@ int iface_enumerate(int family, void *parm, int (callback)());
/* dbus.c */ /* dbus.c */
#ifdef HAVE_DBUS #ifdef HAVE_DBUS
char *dbus_init(void); char *dbus_init(void);
void check_dbus_listeners(fd_set *rset, fd_set *wset, fd_set *eset); void check_dbus_listeners(void);
void set_dbus_listeners(int *maxfdp, fd_set *rset, fd_set *wset, fd_set *eset); void set_dbus_listeners(void);
# ifdef HAVE_DHCP # ifdef HAVE_DHCP
void emit_dbus_signal(int action, struct dhcp_lease *lease, char *hostname); void emit_dbus_signal(int action, struct dhcp_lease *lease, char *hostname);
# endif # endif
@@ -1367,7 +1395,7 @@ int helper_buf_empty(void);
/* tftp.c */ /* tftp.c */
#ifdef HAVE_TFTP #ifdef HAVE_TFTP
void tftp_request(struct listener *listen, time_t now); void tftp_request(struct listener *listen, time_t now);
void check_tftp_listeners(fd_set *rset, time_t now); void check_tftp_listeners(time_t now);
int do_tftp_script_run(void); int do_tftp_script_run(void);
#endif #endif
@@ -1479,7 +1507,15 @@ int detect_loop(char *query, int type);
#endif #endif
/* inotify.c */ /* inotify.c */
#ifdef HAVE_LINUX_NETWORK #ifdef HAVE_INOTIFY
void inotify_dnsmasq_init(); void inotify_dnsmasq_init();
int inotify_check(void); int inotify_check(time_t now);
void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revhashsz);
#endif #endif
/* poll.c */
void poll_reset(void);
int poll_check(int fd, short event);
void poll_listen(int fd, short event);
int do_poll(int timeout);
+227 -37
View File
@@ -1,5 +1,5 @@
/* dnssec.c is Copyright (c) 2012 Giovanni Bajo <rasky@develer.com> /* dnssec.c is Copyright (c) 2012 Giovanni Bajo <rasky@develer.com>
and Copyright (c) 2012-2014 Simon Kelley and Copyright (c) 2012-2015 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -34,7 +34,6 @@
#include <nettle/dsa-compat.h> #include <nettle/dsa-compat.h>
#endif #endif
#define SERIAL_UNDEF -100 #define SERIAL_UNDEF -100
#define SERIAL_EQ 0 #define SERIAL_EQ 0
#define SERIAL_LT -1 #define SERIAL_LT -1
@@ -275,7 +274,7 @@ static int dnsmasq_ecdsa_verify(struct blockdata *key_data, unsigned int key_len
} }
if (sig_len != 2*t || key_len != 2*t || if (sig_len != 2*t || key_len != 2*t ||
(p = blockdata_retrieve(key_data, key_len, NULL))) !(p = blockdata_retrieve(key_data, key_len, NULL)))
return 0; return 0;
mpz_import(x, t , 1, 1, 0, 0, p); mpz_import(x, t , 1, 1, 0, 0, p);
@@ -320,10 +319,18 @@ static int verify(struct blockdata *key_data, unsigned int key_len, unsigned cha
thus generating names in canonical form. thus generating names in canonical form.
Calling to_wire followed by from_wire is almost an identity, Calling to_wire followed by from_wire is almost an identity,
except that the UC remains mapped to LC. except that the UC remains mapped to LC.
Note that both /000 and '.' are allowed within labels. These get
represented in presentation format using NAME_ESCAPE as an escape
character. In theory, if all the characters in a name were /000 or
'.' or NAME_ESCAPE then all would have to be escaped, so the
presentation format would be twice as long as the spec (1024).
The buffers are all delcared as 2049 (allowing for the trailing zero)
for this reason.
*/ */
static int to_wire(char *name) static int to_wire(char *name)
{ {
unsigned char *l, *p, term; unsigned char *l, *p, *q, term;
int len; int len;
for (l = (unsigned char*)name; *l != 0; l = p) for (l = (unsigned char*)name; *l != 0; l = p)
@@ -331,7 +338,12 @@ static int to_wire(char *name)
for (p = l; *p != '.' && *p != 0; p++) for (p = l; *p != '.' && *p != 0; p++)
if (*p >= 'A' && *p <= 'Z') if (*p >= 'A' && *p <= 'Z')
*p = *p - 'A' + 'a'; *p = *p - 'A' + 'a';
else if (*p == NAME_ESCAPE)
{
for (q = p; *q; q++)
*q = *(q+1);
(*p)--;
}
term = *p; term = *p;
if ((len = p - l) != 0) if ((len = p - l) != 0)
@@ -350,13 +362,24 @@ static int to_wire(char *name)
/* Note: no compression allowed in input. */ /* Note: no compression allowed in input. */
static void from_wire(char *name) static void from_wire(char *name)
{ {
unsigned char *l; unsigned char *l, *p, *last;
int len; int len;
for (last = (unsigned char *)name; *last != 0; last += *last+1);
for (l = (unsigned char *)name; *l != 0; l += len+1) for (l = (unsigned char *)name; *l != 0; l += len+1)
{ {
len = *l; len = *l;
memmove(l, l+1, len); memmove(l, l+1, len);
for (p = l; p < l + len; p++)
if (*p == '.' || *p == 0 || *p == NAME_ESCAPE)
{
memmove(p+1, p, 1 + last - p);
len++;
*p++ = NAME_ESCAPE;
(*p)++;
}
l[len] = '.'; l[len] = '.';
} }
@@ -394,16 +417,88 @@ static int serial_compare_32(unsigned long s1, unsigned long s2)
return SERIAL_UNDEF; return SERIAL_UNDEF;
} }
/* Called at startup. If the timestamp file is configured and exists, put its mtime on
timestamp_time. If it doesn't exist, create it, and set the mtime to 1-1-2015.
return -1 -> Cannot create file.
0 -> not using timestamp, or timestamp exists and is in past.
1 -> timestamp exists and is in future.
*/
static time_t timestamp_time;
int setup_timestamp(void)
{
struct stat statbuf;
daemon->back_to_the_future = 0;
if (!daemon->timestamp_file)
return 0;
if (stat(daemon->timestamp_file, &statbuf) != -1)
{
timestamp_time = statbuf.st_mtime;
check_and_exit:
if (difftime(timestamp_time, time(0)) <= 0)
{
/* time already OK, update timestamp, and do key checking from the start. */
if (utime(daemon->timestamp_file, NULL) == -1)
my_syslog(LOG_ERR, _("failed to update mtime on %s: %s"), daemon->timestamp_file, strerror(errno));
daemon->back_to_the_future = 1;
return 0;
}
return 1;
}
if (errno == ENOENT)
{
/* NB. for explanation of O_EXCL flag, see comment on pidfile in dnsmasq.c */
int fd = open(daemon->timestamp_file, O_WRONLY | O_CREAT | O_NONBLOCK | O_EXCL, 0666);
if (fd != -1)
{
struct utimbuf timbuf;
close(fd);
timestamp_time = timbuf.actime = timbuf.modtime = 1420070400; /* 1-1-2015 */
if (utime(daemon->timestamp_file, &timbuf) == 0)
goto check_and_exit;
}
}
return -1;
}
/* Check whether today/now is between date_start and date_end */ /* Check whether today/now is between date_start and date_end */
static int check_date_range(unsigned long date_start, unsigned long date_end) static int check_date_range(unsigned long date_start, unsigned long date_end)
{ {
unsigned long curtime; unsigned long curtime = time(0);
/* Checking timestamps may be temporarily disabled */ /* Checking timestamps may be temporarily disabled */
if (option_bool(OPT_DNSSEC_TIME))
return 1;
curtime = time(0); /* If the current time if _before_ the timestamp
on our persistent timestamp file, then assume the
time if not yet correct, and don't check the
key timestamps. As soon as the current time is
later then the timestamp, update the timestamp
and start checking keys */
if (daemon->timestamp_file)
{
if (daemon->back_to_the_future == 0 && difftime(timestamp_time, curtime) <= 0)
{
if (utime(daemon->timestamp_file, NULL) != 0)
my_syslog(LOG_ERR, _("failed to update mtime on %s: %s"), daemon->timestamp_file, strerror(errno));
daemon->back_to_the_future = 1;
set_option_bool(OPT_DNSSEC_TIME);
queue_event(EVENT_RELOAD); /* purge cache */
}
if (daemon->back_to_the_future == 0)
return 1;
}
else if (option_bool(OPT_DNSSEC_TIME))
return 1;
/* We must explicitly check against wanted values, because of SERIAL_UNDEF */ /* We must explicitly check against wanted values, because of SERIAL_UNDEF */
return serial_compare_32(curtime, date_start) == SERIAL_GT return serial_compare_32(curtime, date_start) == SERIAL_GT
@@ -571,7 +666,7 @@ static void sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int
if (left1 != 0) if (left1 != 0)
memmove(buff1, buff1 + len1 - left1, left1); memmove(buff1, buff1 + len1 - left1, left1);
if ((len1 = get_rdata(header, plen, end1, buff1 + left1, MAXDNAME - left1, &p1, &dp1)) == 0) if ((len1 = get_rdata(header, plen, end1, buff1 + left1, (MAXDNAME * 2) - left1, &p1, &dp1)) == 0)
{ {
quit = 1; quit = 1;
len1 = end1 - p1; len1 = end1 - p1;
@@ -582,7 +677,7 @@ static void sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int
if (left2 != 0) if (left2 != 0)
memmove(buff2, buff2 + len2 - left2, left2); memmove(buff2, buff2 + len2 - left2, left2);
if ((len2 = get_rdata(header, plen, end2, buff2 + left2, MAXDNAME - left2, &p2, &dp2)) == 0) if ((len2 = get_rdata(header, plen, end2, buff2 + left2, (MAXDNAME *2) - left2, &p2, &dp2)) == 0)
{ {
quit = 1; quit = 1;
len2 = end2 - p2; len2 = end2 - p2;
@@ -828,10 +923,11 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
end = p + rdlen; end = p + rdlen;
/* canonicalise rdata and calculate length of same, use name buffer as workspace */ /* canonicalise rdata and calculate length of same, use name buffer as workspace.
Note that name buffer is twice MAXDNAME long in DNSSEC mode. */
cp = p; cp = p;
dp = rr_desc; dp = rr_desc;
for (len = 0; (seg = get_rdata(header, plen, end, name, MAXDNAME, &cp, &dp)) != 0; len += seg); for (len = 0; (seg = get_rdata(header, plen, end, name, MAXDNAME * 2, &cp, &dp)) != 0; len += seg);
len += end - cp; len += end - cp;
len = htons(len); len = htons(len);
hash->update(ctx, 2, (unsigned char *)&len); hash->update(ctx, 2, (unsigned char *)&len);
@@ -839,7 +935,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
/* Now canonicalise again and digest. */ /* Now canonicalise again and digest. */
cp = p; cp = p;
dp = rr_desc; dp = rr_desc;
while ((seg = get_rdata(header, plen, end, name, MAXDNAME, &cp, &dp))) while ((seg = get_rdata(header, plen, end, name, MAXDNAME * 2, &cp, &dp)))
hash->update(ctx, seg, (unsigned char *)name); hash->update(ctx, seg, (unsigned char *)name);
if (cp != end) if (cp != end)
hash->update(ctx, end - cp, cp); hash->update(ctx, end - cp, cp);
@@ -907,7 +1003,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
/* If we've cached that DS provably doesn't exist, result must be INSECURE */ /* If we've cached that DS provably doesn't exist, result must be INSECURE */
if (crecp->flags & F_NEG) if (crecp->flags & F_NEG)
return STAT_INSECURE; return STAT_INSECURE_DS;
/* NOTE, we need to find ONE DNSKEY which matches the DS */ /* NOTE, we need to find ONE DNSKEY which matches the DS */
for (valid = 0, j = ntohs(header->ancount); j != 0 && !valid; j--) for (valid = 0, j = ntohs(header->ancount); j != 0 && !valid; j--)
@@ -1038,7 +1134,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
else else
{ {
a.addr.keytag = keytag; a.addr.keytag = keytag;
log_query(F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %u"); log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %u");
recp1->addr.key.keylen = rdlen - 4; recp1->addr.key.keylen = rdlen - 4;
recp1->addr.key.keydata = key; recp1->addr.key.keydata = key;
@@ -1092,7 +1188,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
return STAT_SECURE; return STAT_SECURE;
} }
log_query(F_UPSTREAM, name, NULL, "BOGUS DNSKEY"); log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DNSKEY");
return STAT_BOGUS; return STAT_BOGUS;
} }
@@ -1103,7 +1199,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
STAT_NO_DS It's proved there's no DS here. STAT_NO_DS It's proved there's no DS here.
STAT_NO_NS It's proved there's no DS _or_ NS here. STAT_NO_NS It's proved there's no DS _or_ NS here.
STAT_BOGUS no DS in reply or not signed, fails validation, bad packet. STAT_BOGUS no DS in reply or not signed, fails validation, bad packet.
STAT_NEED_DNSKEY DNSKEY records to validate a DS not found, name in keyname STAT_NEED_KEY DNSKEY records to validate a DS not found, name in keyname
*/ */
int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class) int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class)
@@ -1124,7 +1220,7 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
val = dnssec_validate_reply(now, header, plen, name, keyname, NULL, &neganswer, &nons); val = dnssec_validate_reply(now, header, plen, name, keyname, NULL, &neganswer, &nons);
/* Note dnssec_validate_reply() will have cached positive answers */ /* Note dnssec_validate_reply() will have cached positive answers */
if (val == STAT_NO_SIG || val == STAT_INSECURE) if (val == STAT_INSECURE)
val = STAT_BOGUS; val = STAT_BOGUS;
p = (unsigned char *)(header+1); p = (unsigned char *)(header+1);
@@ -1134,9 +1230,19 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
if (!(p = skip_section(p, ntohs(header->ancount), header, plen))) if (!(p = skip_section(p, ntohs(header->ancount), header, plen)))
val = STAT_BOGUS; val = STAT_BOGUS;
if (val == STAT_BOGUS) /* If we return STAT_NO_SIG, name contains the name of the DS query */
if (val == STAT_NO_SIG)
{ {
log_query(F_UPSTREAM, name, NULL, "BOGUS DS"); *keyname = 0;
return val;
}
/* If the key needed to validate the DS is on the same domain as the DS, we'll
loop getting nowhere. Stop that now. This can happen of the DS answer comes
from the DS's zone, and not the parent zone. */
if (val == STAT_BOGUS || (val == STAT_NEED_KEY && hostname_isequal(name, keyname)))
{
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS");
return STAT_BOGUS; return STAT_BOGUS;
} }
@@ -1201,7 +1307,7 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
cache_end_insert(); cache_end_insert();
log_query(F_UPSTREAM, name, NULL, nons ? "no delegation" : "no DS"); log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, nons ? "no delegation" : "no DS");
} }
return nons ? STAT_NO_NS : STAT_NO_DS; return nons ? STAT_NO_NS : STAT_NO_DS;
@@ -1391,13 +1497,13 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
{ {
/* Normal case, name falls between NSEC name and next domain name, /* Normal case, name falls between NSEC name and next domain name,
wrap around case, name falls between NSEC name (rc == -1) and end */ wrap around case, name falls between NSEC name (rc == -1) and end */
if (hostname_cmp(workspace2, name) == 1 || hostname_cmp(workspace1, workspace2) == 1) if (hostname_cmp(workspace2, name) >= 0 || hostname_cmp(workspace1, workspace2) >= 0)
return STAT_SECURE; return STAT_SECURE;
} }
else else
{ {
/* wrap around case, name falls between start and next domain name */ /* wrap around case, name falls between start and next domain name */
if (hostname_cmp(workspace1, workspace2) == 1 && hostname_cmp(workspace2, name) == 1) if (hostname_cmp(workspace1, workspace2) >= 0 && hostname_cmp(workspace2, name) >=0 )
return STAT_SECURE; return STAT_SECURE;
} }
} }
@@ -1530,17 +1636,17 @@ static int check_nsec3_coverage(struct dns_header *header, size_t plen, int dige
return 1; return 1;
} }
else if (rc <= 0) else if (rc < 0)
{ {
/* Normal case, hash falls between NSEC3 name-hash and next domain name-hash, /* Normal case, hash falls between NSEC3 name-hash and next domain name-hash,
wrap around case, name-hash falls between NSEC3 name-hash and end */ wrap around case, name-hash falls between NSEC3 name-hash and end */
if (memcmp(p, digest, digest_len) > 0 || memcmp(workspace2, p, digest_len) > 0) if (memcmp(p, digest, digest_len) >= 0 || memcmp(workspace2, p, digest_len) >= 0)
return 1; return 1;
} }
else else
{ {
/* wrap around case, name falls between start and next domain name */ /* wrap around case, name falls between start and next domain name */
if (memcmp(workspace2, p, digest_len) > 0 && memcmp(p, digest, digest_len) > 0) if (memcmp(workspace2, p, digest_len) >= 0 && memcmp(p, digest, digest_len) >= 0)
return 1; return 1;
} }
} }
@@ -1776,7 +1882,10 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
/* No data, therefore no sigs */ /* No data, therefore no sigs */
if (ntohs(header->ancount) + ntohs(header->nscount) == 0) if (ntohs(header->ancount) + ntohs(header->nscount) == 0)
return STAT_NO_SIG; {
*keyname = 0;
return STAT_NO_SIG;
}
for (p1 = ans_start, i = 0; i < ntohs(header->ancount) + ntohs(header->nscount); i++) for (p1 = ans_start, i = 0; i < ntohs(header->ancount) + ntohs(header->nscount); i++)
{ {
@@ -1818,11 +1927,14 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
struct blockdata *key; struct blockdata *key;
struct crec *crecp; struct crec *crecp;
char *wildname; char *wildname;
int have_wildcard = 0;
rc = validate_rrset(now, header, plen, class1, type1, name, keyname, &wildname, NULL, 0, 0, 0); rc = validate_rrset(now, header, plen, class1, type1, name, keyname, &wildname, NULL, 0, 0, 0);
if (rc == STAT_SECURE_WILDCARD) if (rc == STAT_SECURE_WILDCARD)
{ {
have_wildcard = 1;
/* An attacker replay a wildcard answer with a different /* An attacker replay a wildcard answer with a different
answer and overlay a genuine RR. To prove this answer and overlay a genuine RR. To prove this
hasn't happened, the answer must prove that hasn't happened, the answer must prove that
@@ -1843,6 +1955,19 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
{ {
if (class) if (class)
*class = class1; /* Class for DS or DNSKEY */ *class = class1; /* Class for DS or DNSKEY */
if (rc == STAT_NO_SIG)
{
/* If we dropped off the end of a CNAME chain, return
STAT_NO_SIG and the last name is keyname. This is used for proving non-existence
if DS records in CNAME chains. */
if (cname_count == CNAME_CHAIN || i < ntohs(header->ancount))
/* No CNAME chain, or no sig in answer section, return empty name. */
*keyname = 0;
else if (!extract_name(header, plen, &qname, keyname, 1, 0))
return STAT_BOGUS;
}
return rc; return rc;
} }
@@ -1885,7 +2010,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
else else
{ {
a.addr.keytag = keytag; a.addr.keytag = keytag;
log_query(F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %u"); log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %u");
crecp->addr.ds.digest = digest; crecp->addr.ds.digest = digest;
crecp->addr.ds.keydata = key; crecp->addr.ds.keydata = key;
crecp->addr.ds.algo = algo; crecp->addr.ds.algo = algo;
@@ -1913,7 +2038,11 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
p2 += 13; /* labels, orig_ttl, expiration, inception */ p2 += 13; /* labels, orig_ttl, expiration, inception */
GETSHORT(keytag, p2); GETSHORT(keytag, p2);
if ((key = blockdata_alloc((char*)psave, rdlen2))) /* We don't cache sigs for wildcard answers, because to reproduce the
answer from the cache will require one or more NSEC/NSEC3 records
which we don't cache. The lack of the RRSIG ensures that a query for
this RRset asking for a secure answer will always be forwarded. */
if (!have_wildcard && (key = blockdata_alloc((char*)psave, rdlen2)))
{ {
if (!(crecp = cache_insert(name, &a, now, ttl, F_FORWARD | F_DNSKEY | F_DS))) if (!(crecp = cache_insert(name, &a, now, ttl, F_FORWARD | F_DNSKEY | F_DS)))
blockdata_free(key); blockdata_free(key);
@@ -1951,7 +2080,17 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
/* NXDOMAIN or NODATA reply, prove that (name, class1, type1) can't exist */ /* NXDOMAIN or NODATA reply, prove that (name, class1, type1) can't exist */
/* First marshall the NSEC records, if we've not done it previously */ /* First marshall the NSEC records, if we've not done it previously */
if (!nsec_type && !(nsec_type = find_nsec_records(header, plen, &nsecs, &nsec_count, qclass))) if (!nsec_type && !(nsec_type = find_nsec_records(header, plen, &nsecs, &nsec_count, qclass)))
return STAT_BOGUS; /* No NSECs */ {
/* No NSEC records. If we dropped off the end of a CNAME chain, return
STAT_NO_SIG and the last name is keyname. This is used for proving non-existence
if DS records in CNAME chains. */
if (cname_count == CNAME_CHAIN) /* No CNAME chain, return empty name. */
*keyname = 0;
else if (!extract_name(header, plen, &qname, keyname, 1, 0))
return STAT_BOGUS;
return STAT_NO_SIG; /* No NSECs, this is probably a dangling CNAME pointing into
an unsigned zone. Return STAT_NO_SIG to cause this to be proved. */
}
/* Get name of missing answer */ /* Get name of missing answer */
if (!extract_name(header, plen, &qname, name, 1, 0)) if (!extract_name(header, plen, &qname, name, 1, 0))
@@ -1974,6 +2113,7 @@ int dnssec_chase_cname(time_t now, struct dns_header *header, size_t plen, char
unsigned char *p = (unsigned char *)(header+1); unsigned char *p = (unsigned char *)(header+1);
int type, class, qclass, rdlen, j, rc; int type, class, qclass, rdlen, j, rc;
int cname_count = CNAME_CHAIN; int cname_count = CNAME_CHAIN;
char *wildname;
/* Get question */ /* Get question */
if (!extract_name(header, plen, &p, name, 1, 4)) if (!extract_name(header, plen, &p, name, 1, 4))
@@ -2007,7 +2147,50 @@ int dnssec_chase_cname(time_t now, struct dns_header *header, size_t plen, char
return STAT_INSECURE; return STAT_INSECURE;
/* validate CNAME chain, return if insecure or need more data */ /* validate CNAME chain, return if insecure or need more data */
rc = validate_rrset(now, header, plen, class, type, name, keyname, NULL, NULL, 0, 0, 0); rc = validate_rrset(now, header, plen, class, type, name, keyname, &wildname, NULL, 0, 0, 0);
if (rc == STAT_SECURE_WILDCARD)
{
int nsec_type, nsec_count, i;
unsigned char **nsecs;
/* An attacker can replay a wildcard answer with a different
answer and overlay a genuine RR. To prove this
hasn't happened, the answer must prove that
the genuine record doesn't exist. Check that here. */
if (!(nsec_type = find_nsec_records(header, plen, &nsecs, &nsec_count, class)))
return STAT_BOGUS; /* No NSECs or bad packet */
/* Note that we're called here because something didn't validate in validate_reply,
so we can't assume that any NSEC records have been validated. We do them by steam here */
for (i = 0; i < nsec_count; i++)
{
unsigned char *p1 = nsecs[i];
if (!extract_name(header, plen, &p1, daemon->workspacename, 1, 0))
return STAT_BOGUS;
rc = validate_rrset(now, header, plen, class, nsec_type, daemon->workspacename, keyname, NULL, NULL, 0, 0, 0);
/* NSECs can't be wildcards. */
if (rc == STAT_SECURE_WILDCARD)
rc = STAT_BOGUS;
if (rc != STAT_SECURE)
return rc;
}
if (nsec_type == T_NSEC)
rc = prove_non_existence_nsec(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, type, NULL);
else
rc = prove_non_existence_nsec3(header, plen, nsecs, nsec_count, daemon->workspacename,
keyname, name, type, wildname, NULL);
if (rc != STAT_SECURE)
return rc;
}
if (rc != STAT_SECURE) if (rc != STAT_SECURE)
{ {
if (rc == STAT_NO_SIG) if (rc == STAT_NO_SIG)
@@ -2052,16 +2235,18 @@ int dnskey_keytag(int alg, int flags, unsigned char *key, int keylen)
} }
} }
size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, int class, int type, union mysockaddr *addr) size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, int class,
int type, union mysockaddr *addr, int edns_pktsz)
{ {
unsigned char *p; unsigned char *p;
char *types = querystr("dnssec-query", type); char *types = querystr("dnssec-query", type);
size_t ret;
if (addr->sa.sa_family == AF_INET) if (addr->sa.sa_family == AF_INET)
log_query(F_DNSSEC | F_IPV4, name, (struct all_addr *)&addr->in.sin_addr, types); log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, name, (struct all_addr *)&addr->in.sin_addr, types);
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
else else
log_query(F_DNSSEC | F_IPV6, name, (struct all_addr *)&addr->in6.sin6_addr, types); log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, name, (struct all_addr *)&addr->in6.sin6_addr, types);
#endif #endif
header->qdcount = htons(1); header->qdcount = htons(1);
@@ -2084,7 +2269,12 @@ size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, i
PUTSHORT(type, p); PUTSHORT(type, p);
PUTSHORT(class, p); PUTSHORT(class, p);
return add_do_bit(header, p - (unsigned char *)header, end); ret = add_do_bit(header, p - (unsigned char *)header, end);
if (find_pseudoheader(header, ret, NULL, &p, NULL))
PUTSHORT(edns_pktsz, p);
return ret;
} }
/* Go through a domain name, find "pointers" and fix them up based on how many bytes /* Go through a domain name, find "pointers" and fix them up based on how many bytes
+1 -1
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley /* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
+241 -63
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley /* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -103,15 +103,11 @@ int send_from(int fd, int nowild, char *packet, size_t len,
#endif #endif
} }
while (sendmsg(fd, &msg, 0) == -1) while (retry_send(sendmsg(fd, &msg, 0)));
/* If interface is still in DAD, EINVAL results - ignore that. */
if (errno != 0 && errno != EINVAL)
{ {
if (retry_send())
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)); my_syslog(LOG_ERR, _("failed to send packet: %s"), strerror(errno));
return 0; return 0;
} }
@@ -257,6 +253,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
void *hash = &crc; void *hash = &crc;
#endif #endif
unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL); unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL);
unsigned char *pheader;
(void)do_bit; (void)do_bit;
@@ -265,6 +262,12 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
forward = NULL; forward = NULL;
else if (forward || (hash && (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, hash)))) else if (forward || (hash && (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, hash))))
{ {
/* If we didn't get an answer advertising a maximal packet in EDNS,
fall back to 1280, which should work everywhere on IPv6.
If that generates an answer, it will become the new default
for this server */
forward->flags |= FREC_TEST_PKTSZ;
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
/* If we've already got an answer to this query, but we're awaiting keys for validation, /* If we've already got an answer to this query, but we're awaiting keys for validation,
there's no point retrying the query, retry the key query instead...... */ there's no point retrying the query, retry the key query instead...... */
@@ -272,17 +275,24 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
{ {
int fd; int fd;
forward->flags &= ~FREC_TEST_PKTSZ;
while (forward->blocking_query) while (forward->blocking_query)
forward = forward->blocking_query; forward = forward->blocking_query;
forward->flags |= FREC_TEST_PKTSZ;
blockdata_retrieve(forward->stash, forward->stash_len, (void *)header); blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
plen = forward->stash_len; plen = forward->stash_len;
if (find_pseudoheader(header, plen, NULL, &pheader, NULL))
PUTSHORT((forward->flags & FREC_TEST_PKTSZ) ? SAFE_PKTSZ : forward->sentto->edns_pktsz, pheader);
if (forward->sentto->addr.sa.sa_family == AF_INET) if (forward->sentto->addr.sa.sa_family == AF_INET)
log_query(F_DNSSEC | F_IPV4, "retry", (struct all_addr *)&forward->sentto->addr.in.sin_addr, "dnssec"); log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, "retry", (struct all_addr *)&forward->sentto->addr.in.sin_addr, "dnssec");
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
else else
log_query(F_DNSSEC | F_IPV6, "retry", (struct all_addr *)&forward->sentto->addr.in6.sin6_addr, "dnssec"); log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, "retry", (struct all_addr *)&forward->sentto->addr.in6.sin6_addr, "dnssec");
#endif #endif
if (forward->sentto->sfd) if (forward->sentto->sfd)
@@ -297,9 +307,9 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
fd = forward->rfd4->fd; fd = forward->rfd4->fd;
} }
while (sendto(fd, (char *)header, plen, 0, while (retry_send( sendto(fd, (char *)header, plen, 0,
&forward->sentto->addr.sa, &forward->sentto->addr.sa,
sa_len(&forward->sentto->addr)) == -1 && retry_send()); sa_len(&forward->sentto->addr))));
return 1; return 1;
} }
@@ -389,6 +399,9 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
struct server *firstsentto = start; struct server *firstsentto = start;
int forwarded = 0; int forwarded = 0;
/* If a query is retried, use the log_id for the retry when logging the answer. */
forward->log_id = daemon->log_id;
if (option_bool(OPT_ADD_MAC)) if (option_bool(OPT_ADD_MAC))
plen = add_mac(header, plen, ((char *) header) + daemon->packet_buff_sz, &forward->source); plen = add_mac(header, plen, ((char *) header) + daemon->packet_buff_sz, &forward->source);
@@ -466,14 +479,15 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
#endif #endif
} }
if (sendto(fd, (char *)header, plen, 0, if (find_pseudoheader(header, plen, NULL, &pheader, NULL))
&start->addr.sa, PUTSHORT((forward->flags & FREC_TEST_PKTSZ) ? SAFE_PKTSZ : start->edns_pktsz, pheader);
sa_len(&start->addr)) == -1)
{ if (retry_send(sendto(fd, (char *)header, plen, 0,
if (retry_send()) &start->addr.sa,
continue; sa_len(&start->addr))))
} continue;
else
if (errno == 0)
{ {
/* Keep info in case we want to re-send this packet */ /* Keep info in case we want to re-send this packet */
daemon->srv_save = start; daemon->srv_save = start;
@@ -524,7 +538,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
} }
static size_t process_reply(struct dns_header *header, time_t now, struct server *server, size_t n, int check_rebind, static size_t process_reply(struct dns_header *header, time_t now, struct server *server, size_t n, int check_rebind,
int no_cache, int cache_secure, int ad_reqd, int do_bit, int added_pheader, int check_subnet, union mysockaddr *query_source) int no_cache, int cache_secure, int bogusanswer, int ad_reqd, int do_bit, int added_pheader,
int check_subnet, union mysockaddr *query_source)
{ {
unsigned char *pheader, *sizep; unsigned char *pheader, *sizep;
char **sets = 0; char **sets = 0;
@@ -532,7 +547,8 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
size_t plen; size_t plen;
(void)ad_reqd; (void)ad_reqd;
(void) do_bit; (void)do_bit;
(void)bogusanswer;
#ifdef HAVE_IPSET #ifdef HAVE_IPSET
if (daemon->ipsets && extract_request(header, n, daemon->namebuff, NULL)) if (daemon->ipsets && extract_request(header, n, daemon->namebuff, NULL))
@@ -637,7 +653,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
} }
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
if (no_cache && !(header->hb4 & HB4_CD)) if (bogusanswer && !(header->hb4 & HB4_CD))
{ {
if (!option_bool(OPT_DNSSEC_DEBUG)) if (!option_bool(OPT_DNSSEC_DEBUG))
{ {
@@ -666,6 +682,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
header->ancount = htons(0); header->ancount = htons(0);
header->nscount = htons(0); header->nscount = htons(0);
header->arcount = htons(0); header->arcount = htons(0);
header->hb3 &= ~HB3_TC;
} }
/* the bogus-nxdomain stuff, doctor and NXDOMAIN->NODATA munging can all elide /* the bogus-nxdomain stuff, doctor and NXDOMAIN->NODATA munging can all elide
@@ -725,11 +742,16 @@ void reply_query(int fd, int family, time_t now)
if (!(forward = lookup_frec(ntohs(header->id), hash))) if (!(forward = lookup_frec(ntohs(header->id), hash)))
return; return;
/* log_query gets called indirectly all over the place, so
pass these in global variables - sorry. */
daemon->log_display_id = forward->log_id;
daemon->log_source_addr = &forward->source;
if (daemon->ignore_addr && RCODE(header) == NOERROR && if (daemon->ignore_addr && RCODE(header) == NOERROR &&
check_for_ignored_address(header, n, daemon->ignore_addr)) check_for_ignored_address(header, n, daemon->ignore_addr))
return; return;
if ((RCODE(header) == SERVFAIL || RCODE(header) == REFUSED) && if (RCODE(header) == REFUSED &&
!option_bool(OPT_ORDER) && !option_bool(OPT_ORDER) &&
forward->forwardall == 0) forward->forwardall == 0)
/* for broken servers, attempt to send to another one. */ /* for broken servers, attempt to send to another one. */
@@ -747,7 +769,8 @@ void reply_query(int fd, int family, time_t now)
header->arcount = htons(0); header->arcount = htons(0);
if ((nn = resize_packet(header, (size_t)n, pheader, plen))) if ((nn = resize_packet(header, (size_t)n, pheader, plen)))
{ {
header->hb3 &= ~(HB3_QR | HB3_TC); header->hb3 &= ~(HB3_QR | HB3_AA | HB3_TC);
header->hb4 &= ~(HB4_RA | HB4_RCODE);
forward_query(-1, NULL, NULL, 0, header, nn, now, forward, 0, 0); forward_query(-1, NULL, NULL, 0, header, nn, now, forward, 0, 0);
return; return;
} }
@@ -755,7 +778,6 @@ void reply_query(int fd, int family, time_t now)
} }
server = forward->sentto; server = forward->sentto;
if ((forward->sentto->flags & SERV_TYPE) == 0) if ((forward->sentto->flags & SERV_TYPE) == 0)
{ {
if (RCODE(header) == REFUSED) if (RCODE(header) == REFUSED)
@@ -777,13 +799,20 @@ void reply_query(int fd, int family, time_t now)
daemon->last_server = server; daemon->last_server = server;
} }
/* We tried resending to this server with a smaller maximum size and got an answer.
Make that permanent. To avoid reduxing the packet size for an single dropped packet,
only do this when we get a truncated answer, or one larger than the safe size. */
if (server && (forward->flags & FREC_TEST_PKTSZ) &&
((header->hb3 & HB3_TC) || n >= SAFE_PKTSZ))
server->edns_pktsz = SAFE_PKTSZ;
/* If the answer is an error, keep the forward record in place in case /* If the answer is an error, keep the forward record in place in case
we get a good reply from another server. Kill it when we've we get a good reply from another server. Kill it when we've
had replies from all to avoid filling the forwarding table when had replies from all to avoid filling the forwarding table when
everything is broken */ everything is broken */
if (forward->forwardall == 0 || --forward->forwardall == 1 || RCODE(header) != SERVFAIL) if (forward->forwardall == 0 || --forward->forwardall == 1 || RCODE(header) != SERVFAIL)
{ {
int check_rebind = 0, no_cache_dnssec = 0, cache_secure = 0; int check_rebind = 0, no_cache_dnssec = 0, cache_secure = 0, bogusanswer = 0;
if (option_bool(OPT_NO_REBIND)) if (option_bool(OPT_NO_REBIND))
check_rebind = !(forward->flags & FREC_NOREBIND); check_rebind = !(forward->flags & FREC_NOREBIND);
@@ -816,7 +845,24 @@ void reply_query(int fd, int family, time_t now)
else if (forward->flags & FREC_DS_QUERY) else if (forward->flags & FREC_DS_QUERY)
{ {
status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class); status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
if (status == STAT_NO_DS || status == STAT_NO_NS) /* Provably no DS, everything below is insecure, even if signatures are offered */
if (status == STAT_NO_DS)
/* We only cache sigs when we've validated a reply.
Avoid caching a reply with sigs if there's a vaildated break in the
DS chain, so we don't return replies from cache missing sigs. */
status = STAT_INSECURE_DS;
else if (status == STAT_NO_SIG)
{
if (option_bool(OPT_DNSSEC_NO_SIGN))
{
status = send_check_sign(forward, now, header, n, daemon->namebuff, daemon->keyname);
if (status == STAT_INSECURE)
status = STAT_INSECURE_DS;
}
else
status = STAT_INSECURE_DS;
}
else if (status == STAT_NO_NS)
status = STAT_BOGUS; status = STAT_BOGUS;
} }
else if (forward->flags & FREC_CHECK_NOSIGN) else if (forward->flags & FREC_CHECK_NOSIGN)
@@ -879,7 +925,7 @@ void reply_query(int fd, int family, time_t now)
{ {
new->flags |= FREC_DNSKEY_QUERY; new->flags |= FREC_DNSKEY_QUERY;
nn = dnssec_generate_query(header, ((char *) header) + daemon->packet_buff_sz, nn = dnssec_generate_query(header, ((char *) header) + daemon->packet_buff_sz,
daemon->keyname, forward->class, T_DNSKEY, &server->addr); daemon->keyname, forward->class, T_DNSKEY, &server->addr, server->edns_pktsz);
} }
else else
{ {
@@ -888,7 +934,7 @@ void reply_query(int fd, int family, time_t now)
else else
new->flags |= FREC_DS_QUERY; new->flags |= FREC_DS_QUERY;
nn = dnssec_generate_query(header,((char *) header) + daemon->packet_buff_sz, nn = dnssec_generate_query(header,((char *) header) + daemon->packet_buff_sz,
daemon->keyname, forward->class, T_DS, &server->addr); daemon->keyname, forward->class, T_DS, &server->addr, server->edns_pktsz);
} }
if ((hash = hash_questions(header, nn, daemon->namebuff))) if ((hash = hash_questions(header, nn, daemon->namebuff)))
memcpy(new->hash, hash, HASH_SIZE); memcpy(new->hash, hash, HASH_SIZE);
@@ -924,7 +970,9 @@ void reply_query(int fd, int family, time_t now)
if (fd != -1) if (fd != -1)
{ {
while (sendto(fd, (char *)header, nn, 0, &server->addr.sa, sa_len(&server->addr)) == -1 && retry_send()); while (retry_send(sendto(fd, (char *)header, nn, 0,
&server->addr.sa,
sa_len(&server->addr))));
server->queries++; server->queries++;
} }
@@ -954,8 +1002,25 @@ void reply_query(int fd, int family, time_t now)
else if (forward->flags & FREC_DS_QUERY) else if (forward->flags & FREC_DS_QUERY)
{ {
status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class); status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
if (status == STAT_NO_DS || status == STAT_NO_NS) /* Provably no DS, everything below is insecure, even if signatures are offered */
status = STAT_BOGUS; if (status == STAT_NO_DS)
/* We only cache sigs when we've validated a reply.
Avoid caching a reply with sigs if there's a vaildated break in the
DS chain, so we don't return replies from cache missing sigs. */
status = STAT_INSECURE_DS;
else if (status == STAT_NO_SIG)
{
if (option_bool(OPT_DNSSEC_NO_SIGN))
{
status = send_check_sign(forward, now, header, n, daemon->namebuff, daemon->keyname);
if (status == STAT_INSECURE)
status = STAT_INSECURE_DS;
}
else
status = STAT_INSECURE_DS;
}
else if (status == STAT_NO_NS)
status = STAT_BOGUS;
} }
else if (forward->flags & FREC_CHECK_NOSIGN) else if (forward->flags & FREC_CHECK_NOSIGN)
{ {
@@ -980,26 +1045,44 @@ void reply_query(int fd, int family, time_t now)
} }
} }
no_cache_dnssec = 0;
if (status == STAT_INSECURE_DS)
{
/* We only cache sigs when we've validated a reply.
Avoid caching a reply with sigs if there's a vaildated break in the
DS chain, so we don't return replies from cache missing sigs. */
status = STAT_INSECURE;
no_cache_dnssec = 1;
}
if (status == STAT_TRUNCATED) if (status == STAT_TRUNCATED)
header->hb3 |= HB3_TC; header->hb3 |= HB3_TC;
else else
{ {
char *result; char *result, *domain = "result";
if (forward->work_counter == 0) if (forward->work_counter == 0)
result = "ABANDONED"; {
result = "ABANDONED";
status = STAT_BOGUS;
}
else else
result = (status == STAT_SECURE ? "SECURE" : (status == STAT_INSECURE ? "INSECURE" : "BOGUS")); result = (status == STAT_SECURE ? "SECURE" : (status == STAT_INSECURE ? "INSECURE" : "BOGUS"));
log_query(F_KEYTAG | F_SECSTAT, "result", NULL, result); if (status == STAT_BOGUS && extract_request(header, n, daemon->namebuff, NULL))
} domain = daemon->namebuff;
no_cache_dnssec = 0; log_query(F_KEYTAG | F_SECSTAT, domain, NULL, result);
}
if (status == STAT_SECURE) if (status == STAT_SECURE)
cache_secure = 1; cache_secure = 1;
else if (status == STAT_BOGUS) else if (status == STAT_BOGUS)
no_cache_dnssec = 1; {
no_cache_dnssec = 1;
bogusanswer = 1;
}
} }
#endif #endif
@@ -1009,7 +1092,7 @@ void reply_query(int fd, int family, time_t now)
else else
header->hb4 &= ~HB4_CD; header->hb4 &= ~HB4_CD;
if ((nn = process_reply(header, now, server, (size_t)n, check_rebind, no_cache_dnssec, cache_secure, if ((nn = process_reply(header, now, server, (size_t)n, check_rebind, no_cache_dnssec, cache_secure, bogusanswer,
forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION, forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION,
forward->flags & FREC_ADDED_PHEADER, forward->flags & FREC_HAS_SUBNET, &forward->source))) forward->flags & FREC_ADDED_PHEADER, forward->flags & FREC_HAS_SUBNET, &forward->source)))
{ {
@@ -1259,6 +1342,11 @@ void receive_query(struct listener *listen, time_t now)
} }
} }
/* log_query gets called indirectly all over the place, so
pass these in global variables - sorry. */
daemon->log_display_id = ++daemon->log_id;
daemon->log_source_addr = &source_addr;
if (extract_request(header, (size_t)n, daemon->namebuff, &type)) if (extract_request(header, (size_t)n, daemon->namebuff, &type))
{ {
#ifdef HAVE_AUTH #ifdef HAVE_AUTH
@@ -1352,7 +1440,7 @@ static int send_check_sign(struct frec *forward, time_t now, struct dns_header *
static int do_check_sign(struct frec *forward, int status, time_t now, char *name, char *keyname) static int do_check_sign(struct frec *forward, int status, time_t now, char *name, char *keyname)
{ {
/* get domain we're checking back from blockdata store, it's stored on the original query. */ /* get domain we're checking back from blockdata store, it's stored on the original query. */
while (forward->dependent) while (forward->dependent && !forward->orig_domain)
forward = forward->dependent; forward = forward->dependent;
blockdata_retrieve(forward->orig_domain, forward->name_len, name); blockdata_retrieve(forward->orig_domain, forward->name_len, name);
@@ -1385,11 +1473,26 @@ static int do_check_sign(struct frec *forward, int status, time_t now, char *nam
/* Have entered non-signed part of DNS tree. */ /* Have entered non-signed part of DNS tree. */
if (status == STAT_NO_DS) if (status == STAT_NO_DS)
return STAT_INSECURE; return forward->dependent ? STAT_INSECURE_DS : STAT_INSECURE;
if (status == STAT_BOGUS) if (status == STAT_BOGUS)
return STAT_BOGUS; return STAT_BOGUS;
if (status == STAT_NO_SIG && *keyname != 0)
{
/* There is a validated CNAME chain that doesn't end in a DS record. Start
the search again in that domain. */
blockdata_free(forward->orig_domain);
forward->name_start = strlen(keyname);
forward->name_len = forward->name_start + 1;
if (!(forward->orig_domain = blockdata_alloc(keyname, forward->name_len)))
return STAT_BOGUS;
strcpy(name, keyname);
status = 0; /* force to cache when we iterate. */
continue;
}
/* There's a proven DS record, or we're within a zone, where there doesn't need /* There's a proven DS record, or we're within a zone, where there doesn't need
to be a DS record. Add a name and try again. to be a DS record. Add a name and try again.
If we've already tried the whole name, then fail */ If we've already tried the whole name, then fail */
@@ -1407,7 +1510,7 @@ static int do_check_sign(struct frec *forward, int status, time_t now, char *nam
} }
} }
/* Move toward the root, until we find a signed non-existance of a DS, in which case /* Move down from the root, until we find a signed non-existance of a DS, in which case
an unsigned answer is OK, or we find a signed DS, in which case there should be an unsigned answer is OK, or we find a signed DS, in which case there should be
a signature, and the answer is BOGUS */ a signature, and the answer is BOGUS */
static int tcp_check_for_unsigned_zone(time_t now, struct dns_header *header, size_t plen, int class, char *name, static int tcp_check_for_unsigned_zone(time_t now, struct dns_header *header, size_t plen, int class, char *name,
@@ -1484,7 +1587,7 @@ static int tcp_check_for_unsigned_zone(time_t now, struct dns_header *header, s
/* Can't find it in the cache, have to send a query */ /* Can't find it in the cache, have to send a query */
m = dnssec_generate_query(header, ((char *) header) + 65536, name_start, class, T_DS, &server->addr); m = dnssec_generate_query(header, ((char *) header) + 65536, name_start, class, T_DS, &server->addr, server->edns_pktsz);
*length = htons(m); *length = htons(m);
@@ -1506,6 +1609,21 @@ static int tcp_check_for_unsigned_zone(time_t now, struct dns_header *header, s
return STAT_INSECURE; return STAT_INSECURE;
} }
if (status == STAT_NO_SIG && *keyname != 0)
{
/* There is a validated CNAME chain that doesn't end in a DS record. Start
the search again in that domain. */
blockdata_free(block);
name_len = strlen(keyname) + 1;
name_start = name + name_len - 1;
if (!(block = blockdata_alloc(keyname, name_len)))
return STAT_BOGUS;
strcpy(name, keyname);
continue;
}
if (status == STAT_BOGUS) if (status == STAT_BOGUS)
{ {
free(packet); free(packet);
@@ -1557,8 +1675,24 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
else if (status == STAT_NEED_DS || status == STAT_NEED_DS_NEG) else if (status == STAT_NEED_DS || status == STAT_NEED_DS_NEG)
{ {
new_status = dnssec_validate_ds(now, header, n, name, keyname, class); new_status = dnssec_validate_ds(now, header, n, name, keyname, class);
if (status == STAT_NEED_DS && (new_status == STAT_NO_DS || new_status == STAT_NO_NS)) if (status == STAT_NEED_DS)
new_status = STAT_BOGUS; {
if (new_status == STAT_NO_DS)
new_status = STAT_INSECURE_DS;
if (new_status == STAT_NO_SIG)
{
if (option_bool(OPT_DNSSEC_NO_SIGN))
{
new_status = tcp_check_for_unsigned_zone(now, header, n, class, name, keyname, server, keycount);
if (new_status == STAT_INSECURE)
new_status = STAT_INSECURE_DS;
}
else
new_status = STAT_INSECURE_DS;
}
else if (new_status == STAT_NO_NS)
new_status = STAT_BOGUS;
}
} }
else if (status == STAT_CHASE_CNAME) else if (status == STAT_CHASE_CNAME)
new_status = dnssec_chase_cname(now, header, n, name, keyname); new_status = dnssec_chase_cname(now, header, n, name, keyname);
@@ -1591,7 +1725,7 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
another_tcp_key: another_tcp_key:
m = dnssec_generate_query(new_header, ((char *) new_header) + 65536, keyname, class, m = dnssec_generate_query(new_header, ((char *) new_header) + 65536, keyname, class,
new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS, &server->addr); new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS, &server->addr, server->edns_pktsz);
*length = htons(m); *length = htons(m);
@@ -1617,8 +1751,24 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
else if (status == STAT_NEED_DS || status == STAT_NEED_DS_NEG) else if (status == STAT_NEED_DS || status == STAT_NEED_DS_NEG)
{ {
new_status = dnssec_validate_ds(now, header, n, name, keyname, class); new_status = dnssec_validate_ds(now, header, n, name, keyname, class);
if (status == STAT_NEED_DS && (new_status == STAT_NO_DS || new_status == STAT_NO_NS)) if (status == STAT_NEED_DS)
new_status = STAT_BOGUS; /* Validated no DS */ {
if (new_status == STAT_NO_DS)
new_status = STAT_INSECURE_DS;
else if (new_status == STAT_NO_SIG)
{
if (option_bool(OPT_DNSSEC_NO_SIGN))
{
new_status = tcp_check_for_unsigned_zone(now, header, n, class, name, keyname, server, keycount);
if (new_status == STAT_INSECURE)
new_status = STAT_INSECURE_DS;
}
else
new_status = STAT_INSECURE_DS;
}
else if (new_status == STAT_NO_NS)
new_status = STAT_BOGUS;
}
} }
else if (status == STAT_CHASE_CNAME) else if (status == STAT_CHASE_CNAME)
new_status = dnssec_chase_cname(now, header, n, name, keyname); new_status = dnssec_chase_cname(now, header, n, name, keyname);
@@ -1660,7 +1810,7 @@ unsigned char *tcp_request(int confd, time_t now,
int local_auth = 0; int local_auth = 0;
#endif #endif
int checking_disabled, ad_question, do_bit, added_pheader = 0; int checking_disabled, ad_question, do_bit, added_pheader = 0;
int check_subnet, no_cache_dnssec = 0, cache_secure = 0; int check_subnet, no_cache_dnssec = 0, cache_secure = 0, bogusanswer = 0;
size_t m; size_t m;
unsigned short qtype; unsigned short qtype;
unsigned int gotname; unsigned int gotname;
@@ -1675,6 +1825,7 @@ unsigned char *tcp_request(int confd, time_t now,
struct in_addr dst_addr_4; struct in_addr dst_addr_4;
union mysockaddr peer_addr; union mysockaddr peer_addr;
socklen_t peer_len = sizeof(union mysockaddr); socklen_t peer_len = sizeof(union mysockaddr);
int query_count = 0;
if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) == -1) if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) == -1)
return packet; return packet;
@@ -1712,7 +1863,8 @@ unsigned char *tcp_request(int confd, time_t now,
while (1) while (1)
{ {
if (!packet || if (query_count == TCP_MAX_QUERIES ||
!packet ||
!read_write(confd, &c1, 1, 1) || !read_write(confd, &c2, 1, 1) || !read_write(confd, &c1, 1, 1) || !read_write(confd, &c2, 1, 1) ||
!(size = c1 << 8 | c2) || !(size = c1 << 8 | c2) ||
!read_write(confd, payload, size, 1)) !read_write(confd, payload, size, 1))
@@ -1721,6 +1873,13 @@ unsigned char *tcp_request(int confd, time_t now,
if (size < (int)sizeof(struct dns_header)) if (size < (int)sizeof(struct dns_header))
continue; continue;
query_count++;
/* log_query gets called indirectly all over the place, so
pass these in global variables - sorry. */
daemon->log_display_id = ++daemon->log_id;
daemon->log_source_addr = &peer_addr;
check_subnet = 0; check_subnet = 0;
/* save state of "cd" flag in query */ /* save state of "cd" flag in query */
@@ -1772,7 +1931,7 @@ unsigned char *tcp_request(int confd, time_t now,
dst_addr_4, netmask, now, &ad_question, &do_bit); dst_addr_4, netmask, now, &ad_question, &do_bit);
/* Do this by steam now we're not in the select() loop */ /* Do this by steam now we're not in the select() loop */
check_log_writer(NULL); check_log_writer(1);
if (m == 0) if (m == 0)
{ {
@@ -1917,17 +2076,35 @@ unsigned char *tcp_request(int confd, time_t now,
{ {
int keycount = DNSSEC_WORK; /* Limit to number of DNSSEC questions, to catch loops and avoid filling cache. */ int keycount = DNSSEC_WORK; /* Limit to number of DNSSEC questions, to catch loops and avoid filling cache. */
int status = tcp_key_recurse(now, STAT_TRUNCATED, header, m, 0, daemon->namebuff, daemon->keyname, last_server, &keycount); int status = tcp_key_recurse(now, STAT_TRUNCATED, header, m, 0, daemon->namebuff, daemon->keyname, last_server, &keycount);
char *result; char *result, *domain = "result";
if (status == STAT_INSECURE_DS)
{
/* We only cache sigs when we've validated a reply.
Avoid caching a reply with sigs if there's a vaildated break in the
DS chain, so we don't return replies from cache missing sigs. */
status = STAT_INSECURE;
no_cache_dnssec = 1;
}
if (keycount == 0) if (keycount == 0)
result = "ABANDONED"; {
result = "ABANDONED";
status = STAT_BOGUS;
}
else else
result = (status == STAT_SECURE ? "SECURE" : (status == STAT_INSECURE ? "INSECURE" : "BOGUS")); result = (status == STAT_SECURE ? "SECURE" : (status == STAT_INSECURE ? "INSECURE" : "BOGUS"));
log_query(F_KEYTAG | F_SECSTAT, "result", NULL, result); if (status == STAT_BOGUS && extract_request(header, m, daemon->namebuff, NULL))
domain = daemon->namebuff;
log_query(F_KEYTAG | F_SECSTAT, domain, NULL, result);
if (status == STAT_BOGUS) if (status == STAT_BOGUS)
no_cache_dnssec = 1; {
no_cache_dnssec = 1;
bogusanswer = 1;
}
if (status == STAT_SECURE) if (status == STAT_SECURE)
cache_secure = 1; cache_secure = 1;
@@ -1962,8 +2139,8 @@ unsigned char *tcp_request(int confd, time_t now,
#endif #endif
m = process_reply(header, now, last_server, (unsigned int)m, m = process_reply(header, now, last_server, (unsigned int)m,
option_bool(OPT_NO_REBIND) && !norebind, no_cache_dnssec, option_bool(OPT_NO_REBIND) && !norebind, no_cache_dnssec, cache_secure, bogusanswer,
cache_secure, ad_question, do_bit, added_pheader, check_subnet, &peer_addr); ad_question, do_bit, added_pheader, check_subnet, &peer_addr);
break; break;
} }
@@ -1975,7 +2152,7 @@ unsigned char *tcp_request(int confd, time_t now,
} }
} }
check_log_writer(NULL); check_log_writer(1);
*length = htons(m); *length = htons(m);
@@ -2206,8 +2383,9 @@ void resend_query()
else else
return; return;
while(sendto(fd, daemon->packet, daemon->packet_len, 0, while(retry_send(sendto(fd, daemon->packet, daemon->packet_len, 0,
&daemon->srv_save->addr.sa, sa_len(&daemon->srv_save->addr)) == -1 && retry_send()); &daemon->srv_save->addr.sa,
sa_len(&daemon->srv_save->addr))));
} }
} }
+1 -1
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley /* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
+197 -22
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley /* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -15,9 +15,10 @@
*/ */
#include "dnsmasq.h" #include "dnsmasq.h"
#ifdef HAVE_LINUX_NETWORK #ifdef HAVE_INOTIFY
#include <sys/inotify.h> #include <sys/inotify.h>
#include <sys/param.h> /* For MAXSYMLINKS */
/* the strategy is to set a inotify on the directories containing /* the strategy is to set a inotify on the directories containing
resolv files, for any files in the directory which are close-write resolv files, for any files in the directory which are close-write
@@ -35,13 +36,56 @@
static char *inotify_buffer; static char *inotify_buffer;
#define INOTIFY_SZ (sizeof(struct inotify_event) + NAME_MAX + 1) #define INOTIFY_SZ (sizeof(struct inotify_event) + NAME_MAX + 1)
/* If path is a symbolic link, return the path it
points to, made absolute if relative.
If path doesn't exist or is not a symlink, return NULL.
Return value is malloc'ed */
static char *my_readlink(char *path)
{
ssize_t rc, size = 64;
char *buf;
while (1)
{
buf = safe_malloc(size);
rc = readlink(path, buf, (size_t)size);
if (rc == -1)
{
/* Not link or doesn't exist. */
if (errno == EINVAL || errno == ENOENT)
return NULL;
else
die(_("cannot access path %s: %s"), path, EC_MISC);
}
else if (rc < size-1)
{
char *d;
buf[rc] = 0;
if (buf[0] != '/' && ((d = strrchr(path, '/'))))
{
/* Add path to relative link */
char *new_buf = safe_malloc((d - path) + strlen(buf) + 2);
*(d+1) = 0;
strcpy(new_buf, path);
strcat(new_buf, buf);
free(buf);
buf = new_buf;
}
return buf;
}
/* Buffer too small, increase and retry */
size += 64;
free(buf);
}
}
void inotify_dnsmasq_init() void inotify_dnsmasq_init()
{ {
struct resolvc *res; struct resolvc *res;
inotify_buffer = safe_malloc(INOTIFY_SZ); inotify_buffer = safe_malloc(INOTIFY_SZ);
daemon->inotifyfd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); daemon->inotifyfd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
if (daemon->inotifyfd == -1) if (daemon->inotifyfd == -1)
@@ -49,38 +93,117 @@ void inotify_dnsmasq_init()
for (res = daemon->resolv_files; res; res = res->next) for (res = daemon->resolv_files; res; res = res->next)
{ {
char *d = NULL, *path; char *d, *new_path, *path = safe_malloc(strlen(res->name) + 1);
int links = MAXSYMLINKS;
if (!(path = realpath(res->name, NULL))) strcpy(path, res->name);
/* Follow symlinks until we reach a non-symlink, or a non-existant file. */
while ((new_path = my_readlink(path)))
{ {
/* realpath will fail if the file doesn't exist, but if (links-- == 0)
dnsmasq copes with missing files, so fall back die(_("too many symlinks following %s"), res->name, EC_MISC);
and assume that symlinks are not in use in that case. */ free(path);
if (errno == ENOENT) path = new_path;
path = res->name;
else
die(_("cannot cannonicalise resolv-file %s: %s"), res->name, EC_MISC);
} }
res->wd = -1;
if ((d = strrchr(path, '/'))) if ((d = strrchr(path, '/')))
{ {
*d = 0; /* make path just directory */ *d = 0; /* make path just directory */
res->wd = inotify_add_watch(daemon->inotifyfd, path, IN_CLOSE_WRITE | IN_MOVED_TO); res->wd = inotify_add_watch(daemon->inotifyfd, path, IN_CLOSE_WRITE | IN_MOVED_TO);
res->file = d+1; /* pointer to filename */ res->file = d+1; /* pointer to filename */
*d = '/'; *d = '/';
if (res->wd == -1 && errno == ENOENT) if (res->wd == -1 && errno == ENOENT)
die(_("directory %s for resolv-file is missing, cannot poll"), res->name, EC_MISC); die(_("directory %s for resolv-file is missing, cannot poll"), res->name, EC_MISC);
if (res->wd == -1)
die(_("failed to create inotify for %s: %s"), res->name, EC_MISC);
} }
if (res->wd == -1)
die(_("failed to create inotify for %s: %s"), res->name, EC_MISC);
} }
} }
int inotify_check(void)
/* initialisation for dynamic-dir. Set inotify watch for each directory, and read pre-existing files */
void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revhashsz)
{
struct hostsfile *ah;
for (ah = daemon->dynamic_dirs; ah; ah = ah->next)
{
DIR *dir_stream = NULL;
struct dirent *ent;
struct stat buf;
if (!(ah->flags & flag))
continue;
if (stat(ah->fname, &buf) == -1 || !(S_ISDIR(buf.st_mode)))
{
my_syslog(LOG_ERR, _("bad dynamic directory %s: %s"),
ah->fname, strerror(errno));
continue;
}
if (!(ah->flags & AH_WD_DONE))
{
ah->wd = inotify_add_watch(daemon->inotifyfd, ah->fname, IN_CLOSE_WRITE | IN_MOVED_TO);
ah->flags |= AH_WD_DONE;
}
/* Read contents of dir _after_ calling add_watch, in the hope of avoiding
a race which misses files being added as we start */
if (ah->wd == -1 || !(dir_stream = opendir(ah->fname)))
{
my_syslog(LOG_ERR, _("failed to create inotify for %s: %s"),
ah->fname, strerror(errno));
continue;
}
while ((ent = readdir(dir_stream)))
{
size_t lendir = strlen(ah->fname);
size_t lenfile = strlen(ent->d_name);
char *path;
/* ignore emacs backups and dotfiles */
if (lenfile == 0 ||
ent->d_name[lenfile - 1] == '~' ||
(ent->d_name[0] == '#' && ent->d_name[lenfile - 1] == '#') ||
ent->d_name[0] == '.')
continue;
if ((path = whine_malloc(lendir + lenfile + 2)))
{
strcpy(path, ah->fname);
strcat(path, "/");
strcat(path, ent->d_name);
/* ignore non-regular files */
if (stat(path, &buf) != -1 && S_ISREG(buf.st_mode))
{
if (ah->flags & AH_HOSTS)
total_size = read_hostsfile(path, ah->index, total_size, rhash, revhashsz);
#ifdef HAVE_DHCP
else if (ah->flags & (AH_DHCP_HST | AH_DHCP_OPT))
option_read_dynfile(path, ah->flags);
#endif
}
free(path);
}
}
}
}
int inotify_check(time_t now)
{ {
int hit = 0; int hit = 0;
struct hostsfile *ah;
while (1) while (1)
{ {
@@ -101,13 +224,65 @@ int inotify_check(void)
for (res = daemon->resolv_files; res; res = res->next) for (res = daemon->resolv_files; res; res = res->next)
if (res->wd == in->wd && in->len != 0 && strcmp(res->file, in->name) == 0) if (res->wd == in->wd && in->len != 0 && strcmp(res->file, in->name) == 0)
hit = 1; hit = 1;
/* ignore emacs backups and dotfiles */
if (in->len == 0 ||
in->name[in->len - 1] == '~' ||
(in->name[0] == '#' && in->name[in->len - 1] == '#') ||
in->name[0] == '.')
continue;
for (ah = daemon->dynamic_dirs; ah; ah = ah->next)
if (ah->wd == in->wd)
{
size_t lendir = strlen(ah->fname);
char *path;
if ((path = whine_malloc(lendir + in->len + 2)))
{
strcpy(path, ah->fname);
strcat(path, "/");
strcat(path, in->name);
my_syslog(LOG_INFO, _("inotify, new or changed file %s"), path);
if (ah->flags & AH_HOSTS)
{
read_hostsfile(path, ah->index, 0, NULL, 0);
#ifdef HAVE_DHCP
if (daemon->dhcp || daemon->doing_dhcp6)
{
/* Propogate the consequences of loading a new dhcp-host */
dhcp_update_configs(daemon->dhcp_conf);
lease_update_from_configs();
lease_update_file(now);
lease_update_dns(1);
}
#endif
}
#ifdef HAVE_DHCP
else if (ah->flags & AH_DHCP_HST)
{
if (option_read_dynfile(path, AH_DHCP_HST))
{
/* Propogate the consequences of loading a new dhcp-host */
dhcp_update_configs(daemon->dhcp_conf);
lease_update_from_configs();
lease_update_file(now);
lease_update_dns(1);
}
}
else if (ah->flags & AH_DHCP_OPT)
option_read_dynfile(path, AH_DHCP_OPT);
#endif
free(path);
}
}
} }
} }
return hit; return hit;
} }
#endif #endif /* INOTIFY */
+1 -1
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley /* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
+4 -4
View File
@@ -121,7 +121,6 @@ static int new_add_to_ipset(const char *setname, const struct all_addr *ipaddr,
struct my_nlattr *nested[2]; struct my_nlattr *nested[2];
uint8_t proto; uint8_t proto;
int addrsz = INADDRSZ; int addrsz = INADDRSZ;
ssize_t rc;
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
if (af == AF_INET6) if (af == AF_INET6)
@@ -162,9 +161,10 @@ static int new_add_to_ipset(const char *setname, const struct all_addr *ipaddr,
nested[1]->nla_len = (void *)buffer + NL_ALIGN(nlh->nlmsg_len) - (void *)nested[1]; nested[1]->nla_len = (void *)buffer + NL_ALIGN(nlh->nlmsg_len) - (void *)nested[1];
nested[0]->nla_len = (void *)buffer + NL_ALIGN(nlh->nlmsg_len) - (void *)nested[0]; nested[0]->nla_len = (void *)buffer + NL_ALIGN(nlh->nlmsg_len) - (void *)nested[0];
while ((rc = sendto(ipset_sock, buffer, nlh->nlmsg_len, 0, while (retry_send(sendto(ipset_sock, buffer, nlh->nlmsg_len, 0,
(struct sockaddr *)&snl, sizeof(snl))) == -1 && retry_send()); (struct sockaddr *)&snl, sizeof(snl))));
return rc;
return errno == 0 ? 0 : -1;
} }
+5 -5
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley /* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -813,9 +813,9 @@ void lease_set_iaid(struct dhcp_lease *lease, int iaid)
} }
#endif #endif
void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr, void lease_set_hwaddr(struct dhcp_lease *lease, const unsigned char *hwaddr,
unsigned char *clid, int hw_len, int hw_type, int clid_len, const unsigned char *clid, int hw_len, int hw_type,
time_t now, int force) int clid_len, time_t now, int force)
{ {
#ifdef HAVE_DHCP6 #ifdef HAVE_DHCP6
int change = force; int change = force;
@@ -897,7 +897,7 @@ static void kill_name(struct dhcp_lease *lease)
lease->hostname = lease->fqdn = NULL; lease->hostname = lease->fqdn = NULL;
} }
void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth, char *domain, char *config_domain) void lease_set_hostname(struct dhcp_lease *lease, const char *name, int auth, char *domain, char *config_domain)
{ {
struct dhcp_lease *lease_tmp; struct dhcp_lease *lease_tmp;
char *new_name = NULL, *new_fqdn = NULL; char *new_name = NULL, *new_fqdn = NULL;
+5 -8
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley /* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -421,18 +421,15 @@ void my_syslog(int priority, const char *format, ...)
} }
} }
void set_log_writer(fd_set *set, int *maxfdp) void set_log_writer(void)
{ {
if (entries && log_fd != -1 && connection_good) if (entries && log_fd != -1 && connection_good)
{ poll_listen(log_fd, POLLOUT);
FD_SET(log_fd, set);
bump_maxfd(log_fd, maxfdp);
}
} }
void check_log_writer(fd_set *set) void check_log_writer(int force)
{ {
if (log_fd != -1 && (!set || FD_ISSET(log_fd, set))) if (log_fd != -1 && (force || poll_check(log_fd, POLLOUT)))
log_write(); log_write();
} }
+3 -2
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley /* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -45,7 +45,8 @@ void loop_send_probes()
fd = rfd->fd; fd = rfd->fd;
} }
while (sendto(fd, daemon->packet, len, 0, &serv->addr.sa, sa_len(&serv->addr)) == -1 && retry_send()); while (retry_send(sendto(fd, daemon->packet, len, 0,
&serv->addr.sa, sa_len(&serv->addr))));
free_rfd(rfd); free_rfd(rfd);
} }
+4 -4
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley /* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -169,10 +169,10 @@ int iface_enumerate(int family, void *parm, int (*callback)())
req.g.rtgen_family = family; req.g.rtgen_family = family;
/* Don't block in recvfrom if send fails */ /* Don't block in recvfrom if send fails */
while((len = sendto(daemon->netlinkfd, (void *)&req, sizeof(req), 0, while(retry_send(sendto(daemon->netlinkfd, (void *)&req, sizeof(req), 0,
(struct sockaddr *)&addr, sizeof(addr))) == -1 && retry_send()); (struct sockaddr *)&addr, sizeof(addr))));
if (len == -1) if (errno != 0)
return 0; return 0;
while (1) while (1)
+4 -3
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley /* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -1396,6 +1396,7 @@ void add_update_server(int flags,
serv->domain = domain_str; serv->domain = domain_str;
serv->next = next; serv->next = next;
serv->queries = serv->failed_queries = 0; serv->queries = serv->failed_queries = 0;
serv->edns_pktsz = daemon->edns_pktsz;
#ifdef HAVE_LOOP #ifdef HAVE_LOOP
serv->uid = rand32(); serv->uid = rand32();
#endif #endif
@@ -1459,7 +1460,7 @@ void check_servers(void)
} }
} }
if (!(serv->flags & SERV_NO_REBIND)) if (!(serv->flags & SERV_NO_REBIND) && !(serv->flags & SERV_LITERAL_ADDRESS))
{ {
if (serv->flags & (SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_USE_RESOLV)) if (serv->flags & (SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_USE_RESOLV))
{ {
@@ -1475,7 +1476,7 @@ void check_servers(void)
my_syslog(LOG_INFO, _("using local addresses only for %s %s"), s1, s2); my_syslog(LOG_INFO, _("using local addresses only for %s %s"), s1, s2);
else if (serv->flags & SERV_USE_RESOLV) else if (serv->flags & SERV_USE_RESOLV)
my_syslog(LOG_INFO, _("using standard nameservers for %s %s"), s1, s2); my_syslog(LOG_INFO, _("using standard nameservers for %s %s"), s1, s2);
else if (!(serv->flags & SERV_LITERAL_ADDRESS)) else
my_syslog(LOG_INFO, _("using nameserver %s#%d for %s %s"), daemon->namebuff, port, s1, s2); my_syslog(LOG_INFO, _("using nameserver %s#%d for %s %s"), daemon->namebuff, port, s1, s2);
} }
#ifdef HAVE_LOOP #ifdef HAVE_LOOP
+89 -16
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley /* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -148,6 +148,12 @@ struct myoption {
#define LOPT_DNSSEC_TIME 336 #define LOPT_DNSSEC_TIME 336
#define LOPT_LOOP_DETECT 337 #define LOPT_LOOP_DETECT 337
#define LOPT_IGNORE_ADDR 338 #define LOPT_IGNORE_ADDR 338
#define LOPT_MINCTTL 339
#define LOPT_DHCP_INOTIFY 340
#define LOPT_DHOPT_INOTIFY 341
#define LOPT_HOST_INOTIFY 342
#define LOPT_DNSSEC_STAMP 343
#define LOPT_TFTP_NO_FAIL 344
#ifdef HAVE_GETOPT_LONG #ifdef HAVE_GETOPT_LONG
static const struct option opts[] = static const struct option opts[] =
@@ -160,7 +166,7 @@ static const struct myoption opts[] =
{ "no-poll", 0, 0, 'n' }, { "no-poll", 0, 0, 'n' },
{ "help", 0, 0, 'w' }, { "help", 0, 0, 'w' },
{ "no-daemon", 0, 0, 'd' }, { "no-daemon", 0, 0, 'd' },
{ "log-queries", 0, 0, 'q' }, { "log-queries", 2, 0, 'q' },
{ "user", 2, 0, 'u' }, { "user", 2, 0, 'u' },
{ "group", 2, 0, 'g' }, { "group", 2, 0, 'g' },
{ "resolv-file", 2, 0, 'r' }, { "resolv-file", 2, 0, 'r' },
@@ -198,6 +204,7 @@ static const struct myoption opts[] =
{ "local-ttl", 1, 0, 'T' }, { "local-ttl", 1, 0, 'T' },
{ "no-negcache", 0, 0, 'N' }, { "no-negcache", 0, 0, 'N' },
{ "addn-hosts", 1, 0, 'H' }, { "addn-hosts", 1, 0, 'H' },
{ "hostsdir", 1, 0, LOPT_HOST_INOTIFY },
{ "query-port", 1, 0, 'Q' }, { "query-port", 1, 0, 'Q' },
{ "except-interface", 1, 0, 'I' }, { "except-interface", 1, 0, 'I' },
{ "no-dhcp-interface", 1, 0, '2' }, { "no-dhcp-interface", 1, 0, '2' },
@@ -229,6 +236,7 @@ static const struct myoption opts[] =
{ "dhcp-ignore-names", 2, 0, LOPT_NO_NAMES }, { "dhcp-ignore-names", 2, 0, LOPT_NO_NAMES },
{ "enable-tftp", 2, 0, LOPT_TFTP }, { "enable-tftp", 2, 0, LOPT_TFTP },
{ "tftp-secure", 0, 0, LOPT_SECURE }, { "tftp-secure", 0, 0, LOPT_SECURE },
{ "tftp-no-fail", 0, 0, LOPT_TFTP_NO_FAIL },
{ "tftp-unique-root", 0, 0, LOPT_APREF }, { "tftp-unique-root", 0, 0, LOPT_APREF },
{ "tftp-root", 1, 0, LOPT_PREFIX }, { "tftp-root", 1, 0, LOPT_PREFIX },
{ "tftp-max", 1, 0, LOPT_TFTP_MAX }, { "tftp-max", 1, 0, LOPT_TFTP_MAX },
@@ -246,6 +254,8 @@ static const struct myoption opts[] =
{ "interface-name", 1, 0, LOPT_INTNAME }, { "interface-name", 1, 0, LOPT_INTNAME },
{ "dhcp-hostsfile", 1, 0, LOPT_DHCP_HOST }, { "dhcp-hostsfile", 1, 0, LOPT_DHCP_HOST },
{ "dhcp-optsfile", 1, 0, LOPT_DHCP_OPTS }, { "dhcp-optsfile", 1, 0, LOPT_DHCP_OPTS },
{ "dhcp-hostsdir", 1, 0, LOPT_DHCP_INOTIFY },
{ "dhcp-optsdir", 1, 0, LOPT_DHOPT_INOTIFY },
{ "dhcp-no-override", 0, 0, LOPT_OVERRIDE }, { "dhcp-no-override", 0, 0, LOPT_OVERRIDE },
{ "tftp-port-range", 1, 0, LOPT_TFTPPORTS }, { "tftp-port-range", 1, 0, LOPT_TFTPPORTS },
{ "stop-dns-rebind", 0, 0, LOPT_REBIND }, { "stop-dns-rebind", 0, 0, LOPT_REBIND },
@@ -255,6 +265,7 @@ static const struct myoption opts[] =
{ "dhcp-broadcast", 2, 0, LOPT_BROADCAST }, { "dhcp-broadcast", 2, 0, LOPT_BROADCAST },
{ "neg-ttl", 1, 0, LOPT_NEGTTL }, { "neg-ttl", 1, 0, LOPT_NEGTTL },
{ "max-ttl", 1, 0, LOPT_MAXTTL }, { "max-ttl", 1, 0, LOPT_MAXTTL },
{ "min-cache-ttl", 1, 0, LOPT_MINCTTL },
{ "max-cache-ttl", 1, 0, LOPT_MAXCTTL }, { "max-cache-ttl", 1, 0, LOPT_MAXCTTL },
{ "dhcp-alternate-port", 2, 0, LOPT_ALTPORT }, { "dhcp-alternate-port", 2, 0, LOPT_ALTPORT },
{ "dhcp-scriptuser", 1, 0, LOPT_SCRIPTUSR }, { "dhcp-scriptuser", 1, 0, LOPT_SCRIPTUSR },
@@ -292,6 +303,7 @@ static const struct myoption opts[] =
{ "dnssec-debug", 0, 0, LOPT_DNSSEC_DEBUG }, { "dnssec-debug", 0, 0, LOPT_DNSSEC_DEBUG },
{ "dnssec-check-unsigned", 0, 0, LOPT_DNSSEC_CHECK }, { "dnssec-check-unsigned", 0, 0, LOPT_DNSSEC_CHECK },
{ "dnssec-no-timecheck", 0, 0, LOPT_DNSSEC_TIME }, { "dnssec-no-timecheck", 0, 0, LOPT_DNSSEC_TIME },
{ "dnssec-timestamp", 1, 0, LOPT_DNSSEC_STAMP },
#ifdef OPTION6_PREFIX_CLASS #ifdef OPTION6_PREFIX_CLASS
{ "dhcp-prefix-class", 1, 0, LOPT_PREF_CLSS }, { "dhcp-prefix-class", 1, 0, LOPT_PREF_CLSS },
#endif #endif
@@ -333,9 +345,12 @@ static struct {
{ 'G', ARG_DUP, "<hostspec>", gettext_noop("Set address or hostname for a specified machine."), NULL }, { 'G', ARG_DUP, "<hostspec>", gettext_noop("Set address or hostname for a specified machine."), NULL },
{ LOPT_DHCP_HOST, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from file."), NULL }, { LOPT_DHCP_HOST, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from file."), NULL },
{ LOPT_DHCP_OPTS, ARG_DUP, "<path>", gettext_noop("Read DHCP option specs from file."), NULL }, { LOPT_DHCP_OPTS, ARG_DUP, "<path>", gettext_noop("Read DHCP option specs from file."), NULL },
{ LOPT_DHCP_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from a directory."), NULL },
{ LOPT_DHOPT_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read DHCP options from a directory."), NULL },
{ LOPT_TAG_IF, ARG_DUP, "tag-expression", gettext_noop("Evaluate conditional tag expression."), NULL }, { LOPT_TAG_IF, ARG_DUP, "tag-expression", gettext_noop("Evaluate conditional tag expression."), NULL },
{ 'h', OPT_NO_HOSTS, NULL, gettext_noop("Do NOT load %s file."), HOSTSFILE }, { 'h', OPT_NO_HOSTS, NULL, gettext_noop("Do NOT load %s file."), HOSTSFILE },
{ 'H', ARG_DUP, "<path>", gettext_noop("Specify a hosts file to be read in addition to %s."), HOSTSFILE }, { 'H', ARG_DUP, "<path>", gettext_noop("Specify a hosts file to be read in addition to %s."), HOSTSFILE },
{ LOPT_HOST_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read hosts files from a directory."), NULL },
{ 'i', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) to listen on."), NULL }, { 'i', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) to listen on."), NULL },
{ 'I', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) NOT to listen on.") , NULL }, { 'I', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) NOT to listen on.") , NULL },
{ 'j', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP user class to tag."), NULL }, { 'j', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP user class to tag."), NULL },
@@ -357,7 +372,7 @@ static struct {
{ LOPT_FORCE, ARG_DUP, "<optspec>", gettext_noop("DHCP option sent even if the client does not request it."), NULL}, { LOPT_FORCE, ARG_DUP, "<optspec>", gettext_noop("DHCP option sent even if the client does not request it."), NULL},
{ 'p', ARG_ONE, "<integer>", gettext_noop("Specify port to listen for DNS requests on (defaults to 53)."), NULL }, { 'p', ARG_ONE, "<integer>", gettext_noop("Specify port to listen for DNS requests on (defaults to 53)."), NULL },
{ 'P', ARG_ONE, "<integer>", gettext_noop("Maximum supported UDP packet size for EDNS.0 (defaults to %s)."), "*" }, { 'P', ARG_ONE, "<integer>", gettext_noop("Maximum supported UDP packet size for EDNS.0 (defaults to %s)."), "*" },
{ 'q', OPT_LOG, NULL, gettext_noop("Log DNS queries."), NULL }, { 'q', ARG_DUP, NULL, gettext_noop("Log DNS queries."), NULL },
{ 'Q', ARG_ONE, "<integer>", gettext_noop("Force the originating port for upstream DNS queries."), NULL }, { 'Q', ARG_ONE, "<integer>", gettext_noop("Force the originating port for upstream DNS queries."), NULL },
{ 'R', OPT_NO_RESOLV, NULL, gettext_noop("Do NOT read resolv.conf."), NULL }, { 'R', OPT_NO_RESOLV, NULL, gettext_noop("Do NOT read resolv.conf."), NULL },
{ 'r', ARG_DUP, "<path>", gettext_noop("Specify path to resolv.conf (defaults to %s)."), RESOLVFILE }, { 'r', ARG_DUP, "<path>", gettext_noop("Specify path to resolv.conf (defaults to %s)."), RESOLVFILE },
@@ -370,6 +385,8 @@ static struct {
{ 'T', ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for replies from /etc/hosts."), NULL }, { 'T', ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for replies from /etc/hosts."), NULL },
{ LOPT_NEGTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for negative caching."), NULL }, { LOPT_NEGTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for negative caching."), NULL },
{ LOPT_MAXTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for maximum TTL to send to clients."), NULL }, { LOPT_MAXTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for maximum TTL to send to clients."), NULL },
{ LOPT_MAXCTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live ceiling for cache."), NULL },
{ LOPT_MINCTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live floor for cache."), NULL },
{ 'u', ARG_ONE, "<username>", gettext_noop("Change to this user after startup. (defaults to %s)."), CHUSER }, { 'u', ARG_ONE, "<username>", gettext_noop("Change to this user after startup. (defaults to %s)."), CHUSER },
{ 'U', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP vendor class to tag."), NULL }, { 'U', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP vendor class to tag."), NULL },
{ 'v', 0, NULL, gettext_noop("Display dnsmasq version and copyright information."), NULL }, { 'v', 0, NULL, gettext_noop("Display dnsmasq version and copyright information."), NULL },
@@ -404,6 +421,7 @@ static struct {
{ LOPT_PREFIX, ARG_DUP, "<dir>[,<iface>]", gettext_noop("Export files by TFTP only from the specified subtree."), NULL }, { LOPT_PREFIX, ARG_DUP, "<dir>[,<iface>]", gettext_noop("Export files by TFTP only from the specified subtree."), NULL },
{ LOPT_APREF, OPT_TFTP_APREF, NULL, gettext_noop("Add client IP address to tftp-root."), NULL }, { LOPT_APREF, OPT_TFTP_APREF, NULL, gettext_noop("Add client IP address to tftp-root."), NULL },
{ LOPT_SECURE, OPT_TFTP_SECURE, NULL, gettext_noop("Allow access only to files owned by the user running dnsmasq."), NULL }, { LOPT_SECURE, OPT_TFTP_SECURE, NULL, gettext_noop("Allow access only to files owned by the user running dnsmasq."), NULL },
{ LOPT_TFTP_NO_FAIL, OPT_TFTP_NO_FAIL, NULL, gettext_noop("Do not terminate the service if TFTP directories are inaccessible."), NULL },
{ LOPT_TFTP_MAX, ARG_ONE, "<integer>", gettext_noop("Maximum number of conncurrent TFTP transfers (defaults to %s)."), "#" }, { LOPT_TFTP_MAX, ARG_ONE, "<integer>", gettext_noop("Maximum number of conncurrent TFTP transfers (defaults to %s)."), "#" },
{ LOPT_NOBLOCK, OPT_TFTP_NOBLOCK, NULL, gettext_noop("Disable the TFTP blocksize extension."), NULL }, { LOPT_NOBLOCK, OPT_TFTP_NOBLOCK, NULL, gettext_noop("Disable the TFTP blocksize extension."), NULL },
{ LOPT_TFTP_LC, OPT_TFTP_LC, NULL, gettext_noop("Convert TFTP filenames to lowercase"), NULL }, { LOPT_TFTP_LC, OPT_TFTP_LC, NULL, gettext_noop("Convert TFTP filenames to lowercase"), NULL },
@@ -450,6 +468,7 @@ static struct {
{ LOPT_DNSSEC_DEBUG, OPT_DNSSEC_DEBUG, NULL, gettext_noop("Disable upstream checking for DNSSEC debugging."), NULL }, { LOPT_DNSSEC_DEBUG, OPT_DNSSEC_DEBUG, NULL, gettext_noop("Disable upstream checking for DNSSEC debugging."), NULL },
{ LOPT_DNSSEC_CHECK, OPT_DNSSEC_NO_SIGN, NULL, gettext_noop("Ensure answers without DNSSEC are in unsigned zones."), NULL }, { LOPT_DNSSEC_CHECK, OPT_DNSSEC_NO_SIGN, NULL, gettext_noop("Ensure answers without DNSSEC are in unsigned zones."), NULL },
{ LOPT_DNSSEC_TIME, OPT_DNSSEC_TIME, NULL, gettext_noop("Don't check DNSSEC signature timestamps until first cache-reload"), NULL }, { LOPT_DNSSEC_TIME, OPT_DNSSEC_TIME, NULL, gettext_noop("Don't check DNSSEC signature timestamps until first cache-reload"), NULL },
{ LOPT_DNSSEC_STAMP, ARG_ONE, "<path>", gettext_noop("Timestamp file to verify system clock for DNSSEC"), NULL },
#ifdef OPTION6_PREFIX_CLASS #ifdef OPTION6_PREFIX_CLASS
{ LOPT_PREF_CLSS, ARG_DUP, "set:tag,<class>", gettext_noop("Specify DHCPv6 prefix class"), NULL }, { LOPT_PREF_CLSS, ARG_DUP, "set:tag,<class>", gettext_noop("Specify DHCPv6 prefix class"), NULL },
#endif #endif
@@ -1705,9 +1724,12 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
break; break;
#endif /* HAVE_DHCP */ #endif /* HAVE_DHCP */
case LOPT_DHCP_HOST: /* --dhcp-hostfile */ case LOPT_DHCP_HOST: /* --dhcp-hostsfile */
case LOPT_DHCP_OPTS: /* --dhcp-optsfile */ case LOPT_DHCP_OPTS: /* --dhcp-optsfile */
case 'H': /* --addn-hosts */ case LOPT_DHCP_INOTIFY: /* --dhcp-hostsdir */
case LOPT_DHOPT_INOTIFY: /* --dhcp-optsdir */
case LOPT_HOST_INOTIFY: /* --hostsdir */
case 'H': /* --addn-hosts */
{ {
struct hostsfile *new = opt_malloc(sizeof(struct hostsfile)); struct hostsfile *new = opt_malloc(sizeof(struct hostsfile));
static unsigned int hosts_index = SRC_AH; static unsigned int hosts_index = SRC_AH;
@@ -1729,6 +1751,18 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
new->next = daemon->dhcp_opts_file; new->next = daemon->dhcp_opts_file;
daemon->dhcp_opts_file = new; daemon->dhcp_opts_file = new;
} }
else
{
new->next = daemon->dynamic_dirs;
daemon->dynamic_dirs = new;
if (option == LOPT_DHCP_INOTIFY)
new->flags |= AH_DHCP_HST;
else if (option == LOPT_DHOPT_INOTIFY)
new->flags |= AH_DHCP_OPT;
else if (option == LOPT_HOST_INOTIFY)
new->flags |= AH_HOSTS;
}
break; break;
} }
@@ -2253,8 +2287,6 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
{ {
if (!(newlist->flags & SERV_NO_REBIND)) if (!(newlist->flags & SERV_NO_REBIND))
newlist->flags |= SERV_NO_ADDR; /* no server */ newlist->flags |= SERV_NO_ADDR; /* no server */
if (newlist->flags & SERV_LITERAL_ADDRESS)
ret_err(gen_err);
} }
else if (strcmp(arg, "#") == 0) else if (strcmp(arg, "#") == 0)
@@ -2421,6 +2453,12 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
ret_err(gen_err); ret_err(gen_err);
break; break;
case 'q': /* --log-queries */
set_option_bool(OPT_LOG);
if (arg && strcmp(arg, "extra") == 0)
set_option_bool(OPT_EXTRALOG);
break;
case LOPT_MAX_LOGS: /* --log-async */ case LOPT_MAX_LOGS: /* --log-async */
daemon->max_logs = LOG_MAX; /* default */ daemon->max_logs = LOG_MAX; /* default */
if (arg && !atoi_check(arg, &daemon->max_logs)) if (arg && !atoi_check(arg, &daemon->max_logs))
@@ -2450,6 +2488,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
case 'T': /* --local-ttl */ case 'T': /* --local-ttl */
case LOPT_NEGTTL: /* --neg-ttl */ case LOPT_NEGTTL: /* --neg-ttl */
case LOPT_MAXTTL: /* --max-ttl */ case LOPT_MAXTTL: /* --max-ttl */
case LOPT_MINCTTL: /* --min-cache-ttl */
case LOPT_MAXCTTL: /* --max-cache-ttl */ case LOPT_MAXCTTL: /* --max-cache-ttl */
case LOPT_AUTHTTL: /* --auth-ttl */ case LOPT_AUTHTTL: /* --auth-ttl */
{ {
@@ -2460,6 +2499,12 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
daemon->neg_ttl = (unsigned long)ttl; daemon->neg_ttl = (unsigned long)ttl;
else if (option == LOPT_MAXTTL) else if (option == LOPT_MAXTTL)
daemon->max_ttl = (unsigned long)ttl; daemon->max_ttl = (unsigned long)ttl;
else if (option == LOPT_MINCTTL)
{
if (ttl > TTL_FLOOR_LIMIT)
ttl = TTL_FLOOR_LIMIT;
daemon->min_cache_ttl = (unsigned long)ttl;
}
else if (option == LOPT_MAXCTTL) else if (option == LOPT_MAXCTTL)
daemon->max_cache_ttl = (unsigned long)ttl; daemon->max_cache_ttl = (unsigned long)ttl;
else if (option == LOPT_AUTHTTL) else if (option == LOPT_AUTHTTL)
@@ -2654,6 +2699,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
new->flags |= CONTEXT_RA_ROUTER | CONTEXT_RA; new->flags |= CONTEXT_RA_ROUTER | CONTEXT_RA;
else if (strcmp(a[leasepos], "ra-stateless") == 0) else if (strcmp(a[leasepos], "ra-stateless") == 0)
new->flags |= CONTEXT_RA_STATELESS | CONTEXT_DHCP | CONTEXT_RA; new->flags |= CONTEXT_RA_STATELESS | CONTEXT_DHCP | CONTEXT_RA;
else if (strcmp(a[leasepos], "off-link") == 0)
new->flags |= CONTEXT_RA_OFF_LINK;
else if (leasepos == 1 && inet_pton(AF_INET6, a[leasepos], &new->end6)) else if (leasepos == 1 && inet_pton(AF_INET6, a[leasepos], &new->end6))
new->flags |= CONTEXT_DHCP; new->flags |= CONTEXT_DHCP;
else if (strstr(a[leasepos], "constructor:") == a[leasepos]) else if (strstr(a[leasepos], "constructor:") == a[leasepos])
@@ -3826,6 +3873,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
} }
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
case LOPT_DNSSEC_STAMP:
daemon->timestamp_file = opt_string_alloc(arg);
break;
case LOPT_TRUST_ANCHOR: case LOPT_TRUST_ANCHOR:
{ {
struct ds_config *new = opt_malloc(sizeof(struct ds_config)); struct ds_config *new = opt_malloc(sizeof(struct ds_config));
@@ -4024,6 +4075,20 @@ static void read_file(char *file, FILE *f, int hard_opt)
fclose(f); fclose(f);
} }
#ifdef HAVE_DHCP
int option_read_dynfile(char *file, int flags)
{
my_syslog(MS_DHCP | LOG_INFO, _("read %s"), file);
if (flags & AH_DHCP_HST)
return one_file(file, LOPT_BANK);
else if (flags & AH_DHCP_OPT)
return one_file(file, LOPT_OPTS);
return 0;
}
#endif
static int one_file(char *file, int hard_opt) static int one_file(char *file, int hard_opt)
{ {
FILE *f; FILE *f;
@@ -4421,22 +4486,30 @@ void read_opts(int argc, char **argv, char *compile_opts)
} }
if (conffile) if (conffile)
one_file(conffile, conffile_opt); {
one_file(conffile, conffile_opt);
if (conffile_opt == 0)
free(conffile);
}
/* port might not be known when the address is parsed - fill in here */ /* port might not be known when the address is parsed - fill in here */
if (daemon->servers) if (daemon->servers)
{ {
struct server *tmp; struct server *tmp;
for (tmp = daemon->servers; tmp; tmp = tmp->next) for (tmp = daemon->servers; tmp; tmp = tmp->next)
if (!(tmp->flags & SERV_HAS_SOURCE)) {
{ tmp->edns_pktsz = daemon->edns_pktsz;
if (tmp->source_addr.sa.sa_family == AF_INET)
tmp->source_addr.in.sin_port = htons(daemon->query_port); if (!(tmp->flags & SERV_HAS_SOURCE))
{
if (tmp->source_addr.sa.sa_family == AF_INET)
tmp->source_addr.in.sin_port = htons(daemon->query_port);
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
else if (tmp->source_addr.sa.sa_family == AF_INET6) else if (tmp->source_addr.sa.sa_family == AF_INET6)
tmp->source_addr.in6.sin6_port = htons(daemon->query_port); tmp->source_addr.in6.sin6_port = htons(daemon->query_port);
#endif #endif
} }
}
} }
if (daemon->if_addrs) if (daemon->if_addrs)
+1 -1
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley /* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
+125
View File
@@ -0,0 +1,125 @@
/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 dated June, 1991, or
(at your option) version 3 dated 29 June, 2007.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dnsmasq.h"
/* Wrapper for poll(). Allocates and extends array of struct pollfds,
keeps them in fd order so that we can set and test conditions on
fd using a simple but efficient binary chop. */
/* poll_reset()
poll_listen(fd, event)
.
.
poll_listen(fd, event);
hits = do_poll(timeout);
if (poll_check(fd, event)
.
.
if (poll_check(fd, event)
.
.
event is OR of POLLIN, POLLOUT, POLLERR, etc
*/
static struct pollfd *pollfds = NULL;
static nfds_t nfds, arrsize = 0;
/* Binary search. Returns either the pollfd with fd, or
if the fd doesn't match, or return equals nfds, the entry
to the left of which a new record should be inserted. */
static nfds_t fd_search(int fd)
{
nfds_t left, right, mid;
if ((right = nfds) == 0)
return 0;
left = 0;
while (1)
{
if (right == left + 1)
return (pollfds[left].fd >= fd) ? left : right;
mid = (left + right)/2;
if (pollfds[mid].fd > fd)
right = mid;
else
left = mid;
}
}
void poll_reset(void)
{
nfds = 0;
}
int do_poll(int timeout)
{
return poll(pollfds, nfds, timeout);
}
int poll_check(int fd, short event)
{
nfds_t i = fd_search(fd);
if (i < nfds && pollfds[i].fd == fd)
return pollfds[i].revents & event;
return 0;
}
void poll_listen(int fd, short event)
{
nfds_t i = fd_search(fd);
if (i < nfds && pollfds[i].fd == fd)
pollfds[i].events |= event;
else
{
if (arrsize != nfds)
memmove(&pollfds[i+1], &pollfds[i], (nfds - i) * sizeof(struct pollfd));
else
{
/* Array too small, extend. */
struct pollfd *new;
arrsize = (arrsize == 0) ? 64 : arrsize * 2;
if (!(new = whine_malloc(arrsize * sizeof(struct pollfd))))
return;
if (pollfds)
{
memcpy(new, pollfds, i * sizeof(struct pollfd));
memcpy(&new[i+1], &pollfds[i], (nfds - i) * sizeof(struct pollfd));
free(pollfds);
}
pollfds = new;
}
pollfds[i].fd = fd;
pollfds[i].events = event;
nfds++;
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley /* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
+136 -13
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley /* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -40,7 +40,18 @@ struct search_param {
char name[IF_NAMESIZE+1]; char name[IF_NAMESIZE+1];
}; };
struct alias_param {
int iface;
struct dhcp_bridge *bridge;
int num_alias_ifs;
int max_alias_ifs;
int *alias_ifs;
};
static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *dest); static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *dest);
static void send_ra_alias(time_t now, int iface, char *iface_name, struct in6_addr *dest,
int send_iface);
static int send_ra_to_aliases(int index, unsigned int type, char *mac, size_t maclen, void *parm);
static int add_prefixes(struct in6_addr *local, int prefix, static int add_prefixes(struct in6_addr *local, int prefix,
int scope, int if_index, int flags, int scope, int if_index, int flags,
unsigned int preferred, unsigned int valid, void *vparam); unsigned int preferred, unsigned int valid, void *vparam);
@@ -181,6 +192,7 @@ void icmp6_packet(time_t now)
else if (packet[0] == ND_ROUTER_SOLICIT) else if (packet[0] == ND_ROUTER_SOLICIT)
{ {
char *mac = ""; char *mac = "";
struct dhcp_bridge *bridge, *alias;
/* look for link-layer address option for logging */ /* look for link-layer address option for logging */
if (sz >= 16 && packet[8] == ICMP6_OPT_SOURCE_MAC && (packet[9] * 8) + 8 <= sz) if (sz >= 16 && packet[8] == ICMP6_OPT_SOURCE_MAC && (packet[9] * 8) + 8 <= sz)
@@ -191,12 +203,37 @@ void icmp6_packet(time_t now)
if (!option_bool(OPT_QUIET_RA)) if (!option_bool(OPT_QUIET_RA))
my_syslog(MS_DHCP | LOG_INFO, "RTR-SOLICIT(%s) %s", interface, mac); my_syslog(MS_DHCP | LOG_INFO, "RTR-SOLICIT(%s) %s", interface, mac);
/* source address may not be valid in solicit request. */
send_ra(now, if_index, interface, !IN6_IS_ADDR_UNSPECIFIED(&from.sin6_addr) ? &from.sin6_addr : NULL); /* If the incoming interface is an alias of some other one (as
specified by the --bridge-interface option), send an RA using
the context of the aliased interface. */
for (bridge = daemon->bridges; bridge; bridge = bridge->next)
{
int bridge_index = if_nametoindex(bridge->iface);
if (bridge_index)
{
for (alias = bridge->alias; alias; alias = alias->next)
if (wildcard_matchn(alias->iface, interface, IF_NAMESIZE))
{
/* Send an RA on if_index with information from
bridge_index. */
send_ra_alias(now, bridge_index, bridge->iface, NULL, if_index);
break;
}
if (alias)
break;
}
}
/* If the incoming interface wasn't an alias, send an RA using
the context of the incoming interface. */
if (!bridge)
/* source address may not be valid in solicit request. */
send_ra(now, if_index, interface, !IN6_IS_ADDR_UNSPECIFIED(&from.sin6_addr) ? &from.sin6_addr : NULL);
} }
} }
static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *dest) static void send_ra_alias(time_t now, int iface, char *iface_name, struct in6_addr *dest, int send_iface)
{ {
struct ra_packet *ra; struct ra_packet *ra;
struct ra_param parm; struct ra_param parm;
@@ -313,8 +350,10 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
opt->type = ICMP6_OPT_PREFIX; opt->type = ICMP6_OPT_PREFIX;
opt->len = 4; opt->len = 4;
opt->prefix_len = context->prefix; opt->prefix_len = context->prefix;
/* autonomous only if we're not doing dhcp, always set "on-link" */ /* autonomous only if we're not doing dhcp, set
opt->flags = do_slaac ? 0xC0 : 0x80; "on-link" unless "off-link" was specified */
opt->flags = (do_slaac ? 0x40 : 0) |
((context->flags & CONTEXT_RA_OFF_LINK) ? 0 : 0x80);
opt->valid_lifetime = htonl(context->saved_valid - old); opt->valid_lifetime = htonl(context->saved_valid - old);
opt->preferred_lifetime = htonl(0); opt->preferred_lifetime = htonl(0);
opt->reserved = 0; opt->reserved = 0;
@@ -368,7 +407,7 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
} }
#endif #endif
iface_enumerate(AF_LOCAL, &iface, add_lla); iface_enumerate(AF_LOCAL, &send_iface, add_lla);
/* RDNSS, RFC 6106, use relevant DHCP6 options */ /* RDNSS, RFC 6106, use relevant DHCP6 options */
(void)option_filter(parm.tags, NULL, daemon->dhcp_opts6); (void)option_filter(parm.tags, NULL, daemon->dhcp_opts6);
@@ -476,14 +515,22 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
else else
{ {
inet_pton(AF_INET6, ALL_NODES, &addr.sin6_addr); inet_pton(AF_INET6, ALL_NODES, &addr.sin6_addr);
setsockopt(daemon->icmp6fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &iface, sizeof(iface)); setsockopt(daemon->icmp6fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &send_iface, sizeof(send_iface));
} }
while (sendto(daemon->icmp6fd, daemon->outpacket.iov_base, save_counter(0), 0, while (retry_send(sendto(daemon->icmp6fd, daemon->outpacket.iov_base,
(struct sockaddr *)&addr, sizeof(addr)) == -1 && retry_send()); save_counter(0), 0, (struct sockaddr *)&addr,
sizeof(addr))));
} }
static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *dest)
{
/* Send an RA on the same interface that the RA content is based
on. */
send_ra_alias(now, iface, iface_name, dest, iface);
}
static int add_prefixes(struct in6_addr *local, int prefix, static int add_prefixes(struct in6_addr *local, int prefix,
int scope, int if_index, int flags, int scope, int if_index, int flags,
unsigned int preferred, unsigned int valid, void *vparam) unsigned int preferred, unsigned int valid, void *vparam)
@@ -513,6 +560,7 @@ static int add_prefixes(struct in6_addr *local, int prefix,
int deprecate = 0; int deprecate = 0;
int constructed = 0; int constructed = 0;
int adv_router = 0; int adv_router = 0;
int off_link = 0;
unsigned int time = 0xffffffff; unsigned int time = 0xffffffff;
struct dhcp_context *context; struct dhcp_context *context;
@@ -585,6 +633,7 @@ static int add_prefixes(struct in6_addr *local, int prefix,
context->ra_time = 0; context->ra_time = 0;
context->flags |= CONTEXT_RA_DONE; context->flags |= CONTEXT_RA_DONE;
real_prefix = context->prefix; real_prefix = context->prefix;
off_link = (context->flags & CONTEXT_RA_OFF_LINK);
} }
param->first = 0; param->first = 0;
@@ -635,8 +684,9 @@ static int add_prefixes(struct in6_addr *local, int prefix,
opt->type = ICMP6_OPT_PREFIX; opt->type = ICMP6_OPT_PREFIX;
opt->len = 4; opt->len = 4;
opt->prefix_len = real_prefix; opt->prefix_len = real_prefix;
/* autonomous only if we're not doing dhcp, always set "on-link" */ /* autonomous only if we're not doing dhcp, set
opt->flags = 0x80; "on-link" unless "off-link" was specified */
opt->flags = (off_link ? 0 : 0x80);
if (do_slaac) if (do_slaac)
opt->flags |= 0x40; opt->flags |= 0x40;
if (adv_router) if (adv_router)
@@ -682,6 +732,7 @@ time_t periodic_ra(time_t now)
struct search_param param; struct search_param param;
struct dhcp_context *context; struct dhcp_context *context;
time_t next_event; time_t next_event;
struct alias_param aparam;
param.now = now; param.now = now;
param.iface = 0; param.iface = 0;
@@ -729,12 +780,84 @@ time_t periodic_ra(time_t now)
if (tmp->name && wildcard_match(tmp->name, param.name)) if (tmp->name && wildcard_match(tmp->name, param.name))
break; break;
if (!tmp) if (!tmp)
send_ra(now, param.iface, param.name, NULL); {
send_ra(now, param.iface, param.name, NULL);
/* Also send on all interfaces that are aliases of this
one. */
for (aparam.bridge = daemon->bridges;
aparam.bridge;
aparam.bridge = aparam.bridge->next)
if ((int)if_nametoindex(aparam.bridge->iface) == param.iface)
{
/* Count the number of alias interfaces for this
'bridge', by calling iface_enumerate with
send_ra_to_aliases and NULL alias_ifs. */
aparam.iface = param.iface;
aparam.alias_ifs = NULL;
aparam.num_alias_ifs = 0;
iface_enumerate(AF_LOCAL, &aparam, send_ra_to_aliases);
my_syslog(MS_DHCP | LOG_INFO, "RTR-ADVERT(%s) %s => %d alias(es)",
param.name, daemon->addrbuff, aparam.num_alias_ifs);
/* Allocate memory to store the alias interface
indices. */
aparam.alias_ifs = (int *)whine_malloc(aparam.num_alias_ifs *
sizeof(int));
if (aparam.alias_ifs)
{
/* Use iface_enumerate again to get the alias
interface indices, then send on each of
those. */
aparam.max_alias_ifs = aparam.num_alias_ifs;
aparam.num_alias_ifs = 0;
iface_enumerate(AF_LOCAL, &aparam, send_ra_to_aliases);
for (; aparam.num_alias_ifs; aparam.num_alias_ifs--)
{
my_syslog(MS_DHCP | LOG_INFO, "RTR-ADVERT(%s) %s => i/f %d",
param.name, daemon->addrbuff,
aparam.alias_ifs[aparam.num_alias_ifs - 1]);
send_ra_alias(now,
param.iface,
param.name,
NULL,
aparam.alias_ifs[aparam.num_alias_ifs - 1]);
}
free(aparam.alias_ifs);
}
/* The source interface can only appear in at most
one --bridge-interface. */
break;
}
}
} }
} }
return next_event; return next_event;
} }
static int send_ra_to_aliases(int index, unsigned int type, char *mac, size_t maclen, void *parm)
{
struct alias_param *aparam = (struct alias_param *)parm;
char ifrn_name[IFNAMSIZ];
struct dhcp_bridge *alias;
(void)type;
(void)mac;
(void)maclen;
if (if_indextoname(index, ifrn_name))
for (alias = aparam->bridge->alias; alias; alias = alias->next)
if (wildcard_matchn(alias->iface, ifrn_name, IFNAMSIZ))
{
if (aparam->alias_ifs && (aparam->num_alias_ifs < aparam->max_alias_ifs))
aparam->alias_ifs[aparam->num_alias_ifs] = index;
aparam->num_alias_ifs++;
}
return 1;
}
static int iface_search(struct in6_addr *local, int prefix, static int iface_search(struct in6_addr *local, int prefix,
int scope, int if_index, int flags, int scope, int if_index, int flags,
int preferred, int valid, void *vparam) int preferred, int valid, void *vparam)
+61 -69
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley /* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -20,7 +20,7 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
char *name, int isExtract, int extrabytes) char *name, int isExtract, int extrabytes)
{ {
unsigned char *cp = (unsigned char *)name, *p = *pp, *p1 = NULL; unsigned char *cp = (unsigned char *)name, *p = *pp, *p1 = NULL;
unsigned int j, l, hops = 0; unsigned int j, l, namelen = 0, hops = 0;
int retvalue = 1; int retvalue = 1;
if (isExtract) if (isExtract)
@@ -77,49 +77,10 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
p = l + (unsigned char *)header; p = l + (unsigned char *)header;
} }
else if (label_type == 0x80) else if (label_type == 0x00)
return 0; /* reserved */
else if (label_type == 0x40)
{ /* ELT */
unsigned int count, digs;
if ((l & 0x3f) != 1)
return 0; /* we only understand bitstrings */
if (!isExtract)
return 0; /* Cannot compare bitsrings */
count = *p++;
if (count == 0)
count = 256;
digs = ((count-1)>>2)+1;
/* output is \[x<hex>/siz]. which is digs+9 chars */
if (cp - (unsigned char *)name + digs + 9 >= MAXDNAME)
return 0;
if (!CHECK_LEN(header, p, plen, (count-1)>>3))
return 0;
*cp++ = '\\';
*cp++ = '[';
*cp++ = 'x';
for (j=0; j<digs; j++)
{
unsigned int dig;
if (j%2 == 0)
dig = *p >> 4;
else
dig = *p++ & 0x0f;
*cp++ = dig < 10 ? dig + '0' : dig + 'A' - 10;
}
cp += sprintf((char *)cp, "/%d]", count);
/* do this here to overwrite the zero char from sprintf */
*cp++ = '.';
}
else
{ /* label_type = 0 -> label. */ { /* label_type = 0 -> label. */
if (cp - (unsigned char *)name + l + 1 >= MAXDNAME) namelen += l + 1; /* include period */
if (namelen >= MAXDNAME)
return 0; return 0;
if (!CHECK_LEN(header, p, plen, l)) if (!CHECK_LEN(header, p, plen, l))
return 0; return 0;
@@ -128,8 +89,21 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
if (isExtract) if (isExtract)
{ {
unsigned char c = *p; unsigned char c = *p;
if (isascii(c) && !iscntrl(c) && c != '.') #ifdef HAVE_DNSSEC
*cp++ = *p; if (option_bool(OPT_DNSSEC_VALID))
{
if (c == 0 || c == '.' || c == NAME_ESCAPE)
{
*cp++ = NAME_ESCAPE;
*cp++ = c+1;
}
else
*cp++ = c;
}
else
#endif
if (c != 0 && c != '.')
*cp++ = c;
else else
return 0; return 0;
} }
@@ -144,6 +118,11 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
cp++; cp++;
if (c1 >= 'A' && c1 <= 'Z') if (c1 >= 'A' && c1 <= 'Z')
c1 += 'a' - 'A'; c1 += 'a' - 'A';
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && c1 == NAME_ESCAPE)
c1 = (*cp++)-1;
#endif
if (c2 >= 'A' && c2 <= 'Z') if (c2 >= 'A' && c2 <= 'Z')
c2 += 'a' - 'A'; c2 += 'a' - 'A';
@@ -157,6 +136,8 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
else if (*cp != 0 && *cp++ != '.') else if (*cp != 0 && *cp++ != '.')
retvalue = 2; retvalue = 2;
} }
else
return 0; /* label types 0x40 and 0x80 not supported */
} }
} }
@@ -527,7 +508,7 @@ static size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned
return plen; return plen;
*p++ = 0; /* empty name */ *p++ = 0; /* empty name */
PUTSHORT(T_OPT, p); PUTSHORT(T_OPT, p);
PUTSHORT(daemon->edns_pktsz, p); /* max packet length */ PUTSHORT(SAFE_PKTSZ, p); /* max packet length, this will be overwritten */
PUTSHORT(0, p); /* extended RCODE and version */ PUTSHORT(0, p); /* extended RCODE and version */
PUTSHORT(set_do ? 0x8000 : 0, p); /* DO flag */ PUTSHORT(set_do ? 0x8000 : 0, p); /* DO flag */
lenp = p; lenp = p;
@@ -1092,10 +1073,23 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
memcpy(&addr, p1, addrlen); memcpy(&addr, p1, addrlen);
/* check for returned address in private space */ /* check for returned address in private space */
if (check_rebind && if (check_rebind)
(flags & F_IPV4) && {
private_net(addr.addr.addr4, !option_bool(OPT_LOCAL_REBIND))) if ((flags & F_IPV4) &&
return 1; private_net(addr.addr.addr4, !option_bool(OPT_LOCAL_REBIND)))
return 1;
#ifdef HAVE_IPV6
if ((flags & F_IPV6) &&
IN6_IS_ADDR_V4MAPPED(&addr.addr.addr6))
{
struct in_addr v4;
v4.s_addr = ((const uint32_t *) (&addr.addr.addr6))[3];
if (private_net(v4, !option_bool(OPT_LOCAL_REBIND)))
return 1;
}
#endif
}
#ifdef HAVE_IPSET #ifdef HAVE_IPSET
if (ipsets && (flags & (F_IPV4 | F_IPV6))) if (ipsets && (flags & (F_IPV4 | F_IPV6)))
@@ -1198,7 +1192,10 @@ unsigned int extract_request(struct dns_header *header, size_t qlen, char *name,
size_t setup_reply(struct dns_header *header, size_t qlen, size_t setup_reply(struct dns_header *header, size_t qlen,
struct all_addr *addrp, unsigned int flags, unsigned long ttl) struct all_addr *addrp, unsigned int flags, unsigned long ttl)
{ {
unsigned char *p = skip_questions(header, qlen); unsigned char *p;
if (!(p = skip_questions(header, qlen)))
return 0;
/* clear authoritative and truncated flags, set QR flag */ /* clear authoritative and truncated flags, set QR flag */
header->hb3 = (header->hb3 & ~(HB3_AA | HB3_TC)) | HB3_QR; header->hb3 = (header->hb3 & ~(HB3_AA | HB3_TC)) | HB3_QR;
@@ -1214,7 +1211,7 @@ size_t setup_reply(struct dns_header *header, size_t qlen,
SET_RCODE(header, NOERROR); /* empty domain */ SET_RCODE(header, NOERROR); /* empty domain */
else if (flags == F_NXDOMAIN) else if (flags == F_NXDOMAIN)
SET_RCODE(header, NXDOMAIN); SET_RCODE(header, NXDOMAIN);
else if (p && flags == F_IPV4) else if (flags == F_IPV4)
{ /* we know the address */ { /* we know the address */
SET_RCODE(header, NOERROR); SET_RCODE(header, NOERROR);
header->ancount = htons(1); header->ancount = htons(1);
@@ -1222,7 +1219,7 @@ size_t setup_reply(struct dns_header *header, size_t qlen,
add_resource_record(header, NULL, NULL, sizeof(struct dns_header), &p, ttl, NULL, T_A, C_IN, "4", addrp); add_resource_record(header, NULL, NULL, sizeof(struct dns_header), &p, ttl, NULL, T_A, C_IN, "4", addrp);
} }
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
else if (p && flags == F_IPV6) else if (flags == F_IPV6)
{ {
SET_RCODE(header, NOERROR); SET_RCODE(header, NOERROR);
header->ancount = htons(1); header->ancount = htons(1);
@@ -1509,7 +1506,6 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
unsigned short flag; unsigned short flag;
int q, ans, anscount = 0, addncount = 0; int q, ans, anscount = 0, addncount = 0;
int dryrun = 0, sec_reqd = 0, have_pseudoheader = 0; int dryrun = 0, sec_reqd = 0, have_pseudoheader = 0;
int is_sign;
struct crec *crecp; struct crec *crecp;
int nxdomain = 0, auth = 1, trunc = 0, sec_data = 1; int nxdomain = 0, auth = 1, trunc = 0, sec_data = 1;
struct mx_srv_record *rec; struct mx_srv_record *rec;
@@ -1529,28 +1525,19 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
forward rather than answering from the cache, which doesn't include forward rather than answering from the cache, which doesn't include
security information, unless we're in DNSSEC validation mode. */ security information, unless we're in DNSSEC validation mode. */
if (find_pseudoheader(header, qlen, NULL, &pheader, &is_sign)) if (find_pseudoheader(header, qlen, NULL, &pheader, NULL))
{ {
unsigned short udpsz, flags; unsigned short flags;
unsigned char *psave = pheader;
have_pseudoheader = 1; have_pseudoheader = 1;
GETSHORT(udpsz, pheader); pheader += 4; /* udp size, ext_rcode */
pheader += 2; /* ext_rcode */
GETSHORT(flags, pheader); GETSHORT(flags, pheader);
if ((sec_reqd = flags & 0x8000)) if ((sec_reqd = flags & 0x8000))
*do_bit = 1;/* do bit */ *do_bit = 1;/* do bit */
*ad_reqd = 1; *ad_reqd = 1;
/* If our client is advertising a larger UDP packet size
than we allow, trim it so that we don't get an overlarge
response from upstream */
if (!is_sign && (udpsz > daemon->edns_pktsz))
PUTSHORT(daemon->edns_pktsz, psave);
dryrun = 1; dryrun = 1;
} }
@@ -1580,6 +1567,11 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
GETSHORT(qtype, p); GETSHORT(qtype, p);
GETSHORT(qclass, p); GETSHORT(qclass, p);
/* Don't filter RRSIGS from answers to ANY queries, even if do-bit
not set. */
if (qtype == T_ANY)
*do_bit = 1;
ans = 0; /* have we answered this question */ ans = 0; /* have we answered this question */
if (qtype == T_TXT || qtype == T_ANY) if (qtype == T_TXT || qtype == T_ANY)
@@ -1643,7 +1635,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
{ {
if (crecp->flags & F_NXDOMAIN) if (crecp->flags & F_NXDOMAIN)
nxdomain = 1; nxdomain = 1;
log_query(F_UPSTREAM, name, NULL, "secure no DS"); log_query(F_UPSTREAM, name, NULL, "no DS");
} }
else if ((keydata = blockdata_retrieve(crecp->addr.ds.keydata, crecp->addr.ds.keylen, NULL))) else if ((keydata = blockdata_retrieve(crecp->addr.ds.keydata, crecp->addr.ds.keylen, NULL)))
{ {
+58 -24
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley /* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -52,7 +52,9 @@ static void do_options(struct dhcp_context *context,
int null_term, int pxearch, int null_term, int pxearch,
unsigned char *uuid, unsigned char *uuid,
int vendor_class_len, int vendor_class_len,
time_t now); time_t now,
unsigned int lease_time,
unsigned short fuzz);
static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt); static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt);
@@ -610,7 +612,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
clear_packet(mess, end); clear_packet(mess, end);
do_options(context, mess, end, NULL, hostname, get_domain(mess->yiaddr), do_options(context, mess, end, NULL, hostname, get_domain(mess->yiaddr),
netid, subnet_addr, 0, 0, -1, NULL, vendor_class_len, now); netid, subnet_addr, 0, 0, -1, NULL, vendor_class_len, now, 0xffffffff, 0);
} }
} }
@@ -803,7 +805,12 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
if (service->type == type) if (service->type == type)
break; break;
if (!service || !service->basename) for (; context; context = context->current)
if (match_netid(context->filter, tagif_netid, 1) &&
is_same_net(mess->ciaddr, context->start, context->netmask))
break;
if (!service || !service->basename || !context)
return 0; return 0;
clear_packet(mess, end); clear_packet(mess, end);
@@ -886,10 +893,10 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
option_put(mess, end, OPTION_MESSAGE_TYPE, 1, option_put(mess, end, OPTION_MESSAGE_TYPE, 1,
mess_type == DHCPDISCOVER ? DHCPOFFER : DHCPACK); mess_type == DHCPDISCOVER ? DHCPOFFER : DHCPACK);
option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr)); option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(tmp->local.s_addr));
pxe_misc(mess, end, uuid); pxe_misc(mess, end, uuid);
prune_vendor_opts(tagif_netid); prune_vendor_opts(tagif_netid);
do_encap_opts(pxe_opts(pxearch, tagif_netid, context->local, now), OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0); do_encap_opts(pxe_opts(pxearch, tagif_netid, tmp->local, now), OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
log_packet("PXE", NULL, emac, emac_len, iface_name, ignore ? "proxy-ignored" : "proxy", NULL, mess->xid); log_packet("PXE", NULL, emac, emac_len, iface_name, ignore ? "proxy-ignored" : "proxy", NULL, mess->xid);
log_tags(tagif_netid, ntohl(mess->xid)); log_tags(tagif_netid, ntohl(mess->xid));
@@ -1042,13 +1049,8 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr)); option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
option_put(mess, end, OPTION_LEASE_TIME, 4, time); option_put(mess, end, OPTION_LEASE_TIME, 4, time);
/* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */ /* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */
if (time != 0xffffffff)
{
option_put(mess, end, OPTION_T1, 4, (time/2));
option_put(mess, end, OPTION_T2, 4, (time*7)/8);
}
do_options(context, mess, end, req_options, offer_hostname, get_domain(mess->yiaddr), do_options(context, mess, end, req_options, offer_hostname, get_domain(mess->yiaddr),
netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now); netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz);
return dhcp_packet_size(mess, agent_id, real_end); return dhcp_packet_size(mess, agent_id, real_end);
@@ -1367,15 +1369,8 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK); option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr)); option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
option_put(mess, end, OPTION_LEASE_TIME, 4, time); option_put(mess, end, OPTION_LEASE_TIME, 4, time);
if (time != 0xffffffff)
{
while (fuzz > (time/16))
fuzz = fuzz/2;
option_put(mess, end, OPTION_T1, 4, (time/2) - fuzz);
option_put(mess, end, OPTION_T2, 4, ((time/8)*7) - fuzz);
}
do_options(context, mess, end, req_options, hostname, get_domain(mess->yiaddr), do_options(context, mess, end, req_options, hostname, get_domain(mess->yiaddr),
netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now); netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz);
} }
return dhcp_packet_size(mess, agent_id, real_end); return dhcp_packet_size(mess, agent_id, real_end);
@@ -1440,7 +1435,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
} }
do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr), do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr),
netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now); netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, 0xffffffff, 0);
*is_inform = 1; /* handle reply differently */ *is_inform = 1; /* handle reply differently */
return dhcp_packet_size(mess, agent_id, real_end); return dhcp_packet_size(mess, agent_id, real_end);
@@ -2137,7 +2132,9 @@ static void do_options(struct dhcp_context *context,
int null_term, int pxe_arch, int null_term, int pxe_arch,
unsigned char *uuid, unsigned char *uuid,
int vendor_class_len, int vendor_class_len,
time_t now) time_t now,
unsigned int lease_time,
unsigned short fuzz)
{ {
struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts; struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
struct dhcp_boot *boot; struct dhcp_boot *boot;
@@ -2262,6 +2259,41 @@ static void do_options(struct dhcp_context *context,
if (subnet_addr.s_addr) if (subnet_addr.s_addr)
option_put(mess, end, OPTION_SUBNET_SELECT, INADDRSZ, ntohl(subnet_addr.s_addr)); option_put(mess, end, OPTION_SUBNET_SELECT, INADDRSZ, ntohl(subnet_addr.s_addr));
if (lease_time != 0xffffffff)
{
unsigned int t1val = lease_time/2;
unsigned int t2val = (lease_time*7)/8;
unsigned int hval;
/* If set by user, sanity check, so not longer than lease. */
if ((opt = option_find2(OPTION_T1)))
{
hval = ntohl(*((unsigned int *)opt->val));
if (hval < lease_time && hval > 2)
t1val = hval;
}
if ((opt = option_find2(OPTION_T2)))
{
hval = ntohl(*((unsigned int *)opt->val));
if (hval < lease_time && hval > 2)
t2val = hval;
}
/* ensure T1 is still < T2 */
if (t2val <= t1val)
t1val = t2val - 1;
while (fuzz > (t1val/8))
fuzz = fuzz/2;
t1val -= fuzz;
t2val -= fuzz;
option_put(mess, end, OPTION_T1, 4, t1val);
option_put(mess, end, OPTION_T2, 4, t2val);
}
/* replies to DHCPINFORM may not have a valid context */ /* replies to DHCPINFORM may not have a valid context */
if (context) if (context)
{ {
@@ -2356,12 +2388,14 @@ static void do_options(struct dhcp_context *context,
if (!(opt->flags & DHOPT_FORCE) && !in_list(req_options, optno)) if (!(opt->flags & DHOPT_FORCE) && !in_list(req_options, optno))
continue; continue;
/* prohibit some used-internally options */ /* prohibit some used-internally options. T1 and T2 already handled. */
if (optno == OPTION_CLIENT_FQDN || if (optno == OPTION_CLIENT_FQDN ||
optno == OPTION_MAXMESSAGE || optno == OPTION_MAXMESSAGE ||
optno == OPTION_OVERLOAD || optno == OPTION_OVERLOAD ||
optno == OPTION_PAD || optno == OPTION_PAD ||
optno == OPTION_END) optno == OPTION_END ||
optno == OPTION_T1 ||
optno == OPTION_T2)
continue; continue;
if (optno == OPTION_SNAME && done_server) if (optno == OPTION_SNAME && done_server)
+13 -4
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley /* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -829,7 +829,16 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
put_opt6_short(DHCP6NOADDRS); put_opt6_short(DHCP6NOADDRS);
put_opt6_string(_("no addresses available")); put_opt6_string(_("no addresses available"));
end_opt6(o1); end_opt6(o1);
log6_packet(state, state->lease_allocate ? "DHCPREPLY" : "DHCPADVERTISE", NULL, _("no addresses available"));
/* Some clients will ask repeatedly when we're not giving
out addresses because we're in stateless mode. Avoid spamming
the log in that case. */
for (c = state->context; c; c = c->current)
if (!(c->flags & CONTEXT_RA_STATELESS))
{
log6_packet(state, state->lease_allocate ? "DHCPREPLY" : "DHCPADVERTISE", NULL, _("no addresses available"));
break;
}
} }
break; break;
@@ -1040,7 +1049,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
message = _("address invalid"); message = _("address invalid");
} }
if (message) if (message && (message != state->hostname))
log6_packet(state, "DHCPREPLY", req_addr, message); log6_packet(state, "DHCPREPLY", req_addr, message);
else else
log6_quiet(state, "DHCPREPLY", req_addr, message); log6_quiet(state, "DHCPREPLY", req_addr, message);
@@ -1080,7 +1089,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
{ {
struct in6_addr *req_addr = opt6_ptr(ia_option, 0); struct in6_addr *req_addr = opt6_ptr(ia_option, 0);
if (!address6_available(state->context, req_addr, tagif, 1)) if (!address6_valid(state->context, req_addr, tagif, 1))
{ {
o1 = new_opt6(OPTION6_STATUS_CODE); o1 = new_opt6(OPTION6_STATUS_CODE);
put_opt6_short(DHCP6NOTONLINK); put_opt6_short(DHCP6NOTONLINK);
+1 -1
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley /* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -21,7 +21,7 @@
#if defined(HAVE_IPSET) && defined(HAVE_BSD_NETWORK) #if defined(HAVE_IPSET) && defined(HAVE_BSD_NETWORK)
#ifndef __FreeBSD__ #ifndef __FreeBSD__
#include <bsd/string.h> #include <string.h>
#endif #endif
#include <sys/types.h> #include <sys/types.h>
+3 -3
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley /* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -502,7 +502,7 @@ static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix)
return NULL; return NULL;
} }
void check_tftp_listeners(fd_set *rset, time_t now) void check_tftp_listeners(time_t now)
{ {
struct tftp_transfer *transfer, *tmp, **up; struct tftp_transfer *transfer, *tmp, **up;
ssize_t len; ssize_t len;
@@ -518,7 +518,7 @@ void check_tftp_listeners(fd_set *rset, time_t now)
prettyprint_addr(&transfer->peer, daemon->addrbuff); prettyprint_addr(&transfer->peer, daemon->addrbuff);
if (FD_ISSET(transfer->sockfd, rset)) if (poll_check(transfer->sockfd, POLLIN))
{ {
/* we overwrote the buffer... */ /* we overwrote the buffer... */
daemon->srv_save = NULL; daemon->srv_save = NULL;
+41 -30
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley /* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -226,7 +226,14 @@ unsigned char *do_rfc1035_name(unsigned char *p, char *sval)
{ {
unsigned char *cp = p++; unsigned char *cp = p++;
for (j = 0; *sval && (*sval != '.'); sval++, j++) for (j = 0; *sval && (*sval != '.'); sval++, j++)
*p++ = *sval; {
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && *sval == NAME_ESCAPE)
*p++ = (*(++sval))-1;
else
#endif
*p++ = *sval;
}
*cp = j; *cp = j;
if (*sval) if (*sval)
sval++; sval++;
@@ -274,6 +281,7 @@ int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2)
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
if (s1->sa.sa_family == AF_INET6 && if (s1->sa.sa_family == AF_INET6 &&
s1->in6.sin6_port == s2->in6.sin6_port && s1->in6.sin6_port == s2->in6.sin6_port &&
s1->in6.sin6_scope_id == s2->in6.sin6_scope_id &&
IN6_ARE_ADDR_EQUAL(&s1->in6.sin6_addr, &s2->in6.sin6_addr)) IN6_ARE_ADDR_EQUAL(&s1->in6.sin6_addr, &s2->in6.sin6_addr))
return 1; return 1;
#endif #endif
@@ -562,23 +570,27 @@ char *print_mac(char *buff, unsigned char *mac, int len)
return buff; return buff;
} }
void bump_maxfd(int fd, int *max) /* rc is return from sendto and friends.
Return 1 if we should retry.
Set errno to zero if we succeeded. */
int retry_send(ssize_t rc)
{ {
if (fd > *max) static int retries = 0;
*max = fd; struct timespec waiter;
}
if (rc != -1)
{
retries = 0;
errno = 0;
return 0;
}
int retry_send(void)
{
/* Linux kernels can return EAGAIN in perpetuity when calling /* Linux kernels can return EAGAIN in perpetuity when calling
sendmsg() and the relevant interface has gone. Here we loop sendmsg() and the relevant interface has gone. Here we loop
retrying in EAGAIN for 1 second max, to avoid this hanging retrying in EAGAIN for 1 second max, to avoid this hanging
dnsmasq. */ dnsmasq. */
static int retries = 0; if (errno == EAGAIN || errno == EWOULDBLOCK)
struct timespec waiter;
if (errno == EAGAIN || errno == EWOULDBLOCK)
{ {
waiter.tv_sec = 0; waiter.tv_sec = 0;
waiter.tv_nsec = 10000; waiter.tv_nsec = 10000;
@@ -587,12 +599,12 @@ int retry_send(void)
return 1; return 1;
} }
retries = 0; retries = 0;
if (errno == EINTR) if (errno == EINTR)
return 1; return 1;
return 0; return 0;
} }
int read_write(int fd, unsigned char *packet, int size, int rw) int read_write(int fd, unsigned char *packet, int size, int rw)
@@ -601,22 +613,21 @@ int read_write(int fd, unsigned char *packet, int size, int rw)
for (done = 0; done < size; done += n) for (done = 0; done < size; done += n)
{ {
retry: do {
if (rw) if (rw)
n = read(fd, &packet[done], (size_t)(size - done)); n = read(fd, &packet[done], (size_t)(size - done));
else else
n = write(fd, &packet[done], (size_t)(size - done)); n = write(fd, &packet[done], (size_t)(size - done));
if (n == 0) if (n == 0)
return 0; return 0;
else if (n == -1)
{ } while (retry_send(n) || errno == ENOMEM || errno == ENOBUFS);
if (retry_send() || errno == ENOMEM || errno == ENOBUFS)
goto retry; if (errno != 0)
else return 0;
return 0;
}
} }
return 1; return 1;
} }