Compare commits

..
88 Commits
Author SHA1 Message Date
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
Simon Kelley 424c4a8a53 Merge branch 'unsigned' 2015-01-07 22:01:14 +00:00
Simon Kelley 97e618a0e3 DNSSEC: do top-down search for limit of secure delegation. 2015-01-07 21:55:43 +00:00
Yousong Zhou d8dbd903d0 Fix race condition issue in makefile. 2015-01-05 17:03:35 +00:00
Yousong Zhou 81c538efce Implement makefile dependencies on COPTS variable. 2015-01-03 16:36:14 +00:00
Matthias Andree d310ab7ecb Fix build failure in new inotify code on BSD. 2014-12-27 15:36:38 +00:00
Simon Kelley 0b1008d367 Bad packet protection. 2014-12-27 15:33:32 +00:00
Glen Huang 32fc6dbe03 Add --ignore-address option. 2014-12-27 15:28:12 +00:00
Simon Kelley 83d2ed09fc Initialise return value. 2014-12-23 18:42:38 +00:00
Simon Kelley fbc5205702 Fix problems validating NSEC3 and wildcards. 2014-12-23 15:46:08 +00:00
Simon Kelley cbc6524234 Make caching work for CNAMEs pointing to A/AAAA records shadowed in /etc/hosts
If the answer to an upstream query is a CNAME which points to an
A/AAAA record which also exists in /etc/hosts and friends, then
caching is suppressed, to avoid inconsistent answers. This is
now modified to allow caching when the upstream and local A/AAAA
records have the same value.
2014-12-21 21:21:53 +00:00
Simon Kelley 094b5c3d90 Fix crash in DNSSEC code when attempting to verify large RRs. 2014-12-21 16:11:52 +00:00
Simon Kelley 3267804598 Tweak field width in cache dump to avoid truncating IPv6 addresses. 2014-12-17 20:38:20 +00:00
Simon Kelley 476693678e Eliminate IPv6 privacy addresses from --interface-name answers. 2014-12-17 12:41:56 +00:00
Simon Kelley bd9520b7ad Remove redundant IN6_IS_ADDR_ULA(a) macro defn. 2014-12-16 20:41:29 +00:00
Simon Kelley 3ad3f3bbd4 Fix breakage of --domain=<domain>,<subnet>,local 2014-12-16 18:25:17 +00:00
Simon Kelley ad946d555d CHANGELOG re. inotify. 2014-12-15 17:52:22 +00:00
Simon Kelley 800c5cc1e7 Remove floor on EDNS0 packet size with DNSSEC. 2014-12-15 17:50:15 +00:00
Simon Kelley 857973e6f7 Teach the new inotify code about symlinks. 2014-12-15 15:58:13 +00:00
Simon Kelley 9c448c8018 Merge branch 'inotify' 2014-12-10 17:40:03 +00:00
Simon Kelley 193de4abf5 Use inotify instead of polling on Linux.
This should solve problems people are seeing when a file changes
twice within a second and thus is missed for polling.
2014-12-10 17:32:16 +00:00
Hans Dedecker 98906275a0 Fix conntrack with --bind-interfaces
Make sure dst_addr is assigned the correct address in receive_query when OPTNOWILD is
enabled so the assigned mark can be correctly retrieved and set in forward_query when
conntrack is enabled.

Signed-off-by: Hans Dedecker <dedeckeh@gmail.com>
2014-12-09 22:22:53 +00:00
Vladislav Grishenko b9ff5c8f43 Improve RFC-compliance when unable to supply addresses in DHCPv6
While testing https://github.com/sbyx/odhcp6c client I have noticed it
permanently crashes after startup.

The reason was it (odhcp6c) doesn't expect empty IA options in ADVERTISE
message without any suboptions.

Despite this validation bug of odhcp6c, dnsmasq should not generate
ADVERTISE messages with IA if there's nothing to advert per  RFC 3315
17.2.2:

   If the server will not assign any addresses to any IAs in a

   subsequent Request from the client, the server MUST send an Advertise

   message to the client that includes only a Status Code option with

   code NoAddrsAvail and a status message for the user, a Server

   Identifier option with the server's DUID, and a Client Identifier

   option with the client's DUID.

Meanwhile it's need to add status code for every IA in REPLY message per
RFC3315 18.2.1:

   If the server cannot assign any addresses to an IA in the message
   from the client, the server MUST include the IA in the Reply message
   with no addresses in the IA and a Status Code option in the IA
   containing status code NoAddrsAvail.

So, I've changed the logic to skip IA completely from ADVERTISE messages and
to add NoAddrsAvail subcode into IA of REPLY messages.

As for overhead, yes, I believe it's ok to return NoAddrsAvail twice in IA
and in global section for compatibility with all old and new clients.
2014-10-06 14:34:24 +01:00
Tomas Hozza 3d9d2dd001 Fit example conf file typo. 2014-10-06 10:46:48 +01:00
Daniel Collins 17b475912f Fix typo in new Dbus code.
Simon's fault.
2014-10-03 21:58:43 +01:00
Karl Vogel e9828b6f66 Set conntrack mark before connect() call.
SO_MARK has to be done before issuing the connect() call on the
TCP socket.
2014-10-03 21:45:15 +01:00
Simon Kelley 72eba2bffc Bump Debian version. 2014-10-03 08:50:37 +01:00
Simon Kelley 6ac3bc0452 Debian build fixes for kFreeBSD 2014-10-03 08:48:11 +01:00
Simon Kelley 00cd9d5519 crash at startup when an empty suffix is supplied to --conf-dir 2014-10-02 21:44:21 +01:00
Simon Kelley f2658275b2 Add newline at the end of example config file. 2014-09-25 21:51:25 +01:00
64 changed files with 6542 additions and 4399 deletions

No files matched your search

+1 -1
View File
@@ -3,7 +3,7 @@ src/*.mo
src/dnsmasq.pot
src/dnsmasq
src/dnsmasq_baseline
src/.configured
src/.copts_*
contrib/wrt/dhcp_lease_time
contrib/wrt/dhcp_release
debian/base/
+109 -1
View File
@@ -1,3 +1,111 @@
version 2.73
Fix crash at startup when an empty suffix is supplied to
--conf-dir, also trivial memory leak. Thanks to
Tomas Hozza for spotting this.
Remove floor of 4096 on advertised EDNS0 packet size when
DNSSEC in use, the original rationale for this has long gone.
Thanks to Anders Kaseorg for spotting this.
Use inotify for checking on updates to /etc/resolv.conf and
friends under Linux. This fixes race conditions when the files are
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
queries were intercepted. THis appears to have been broken
since 2.69. Thanks to Josh Stone for finding the bug.
Eliminate IPv6 privacy addresses and deprecated addresses from
the answers given by --interface-name. Note that reverse queries
(ie looking for names, given addresses) are not affected.
Thanks to Michael Gorbach for the suggestion.
Fix crash in DNSSEC code with long RRs. Thanks to Marco Davids
for the bug report.
Add --ignore-address option. Ignore replies to A-record
queries which include the specified address. No error is
generated, dnsmasq simply continues to listen for another
reply. This is useful to defeat blocking strategies which
rely on quickly supplying a forged answer to a DNS
request for certain domains, before the correct answer can
arrive. Thanks to Glen Huang for the patch.
Revisit the part of DNSSEC validation which determines if an
unsigned answer is legit, or is in some part of the DNS
tree which should be signed. Dnsmasq now works from the
DNS root downward looking for the limit of signed
delegations, rather than working bottom up. This is
both more correct, and less likely to trip over broken
nameservers in the unsigned parts of the DNS tree
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.
version 2.72
Add ra-advrouter mode, for RFC-3775 mobile IPv6 support.
@@ -41,7 +149,7 @@ version 2.72
Fix problem with --local-service option on big-endian platforms
Thanks to Richard Genoud for the patch.
version 2.71
Subtle change to error handling to help DNSSEC validation
when servers fail to provide NODATA answers for
+12 -6
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
# it under the terms of the GNU General Public License as published by
@@ -65,11 +65,15 @@ gmp_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC NO_GMP --cop
sunos_libs = `if uname | grep SunOS >/dev/null 2>&1; then echo -lsocket -lnsl -lposix4; fi`
version = -DVERSION='\"`$(top)/bld/get-version $(top)`\"'
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 \
dnsmasq.o dhcp.o lease.o rfc2131.o netlink.o dbus.o bpf.o \
helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o \
dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o \
domain.o dnssec.o blockdata.o tables.o loop.o
domain.o dnssec.o blockdata.o tables.o loop.o inotify.o
hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \
dns-protocol.h radv-protocol.h ip6addr.h
@@ -83,7 +87,7 @@ all : $(BUILDDIR)
mostly_clean :
rm -f $(BUILDDIR)/*.mo $(BUILDDIR)/*.pot
rm -f $(BUILDDIR)/.configured $(BUILDDIR)/*.o $(BUILDDIR)/dnsmasq.a $(BUILDDIR)/dnsmasq
rm -f $(BUILDDIR)/.copts_* $(BUILDDIR)/*.o $(BUILDDIR)/dnsmasq.a $(BUILDDIR)/dnsmasq
clean : mostly_clean
rm -f $(BUILDDIR)/dnsmasq_baseline
@@ -139,17 +143,19 @@ bloatcheck : $(BUILDDIR)/dnsmasq_baseline mostly_clean all
# rules below are targets in recusive makes with cwd=$(BUILDDIR)
.configured: $(hdrs)
@rm -f *.o
$(copts_conf): $(hdrs)
@rm -f *.o .copts_*
@touch $@
$(objs:.o=.c) $(hdrs):
ln -s $(top)/$(SRC)/$@ .
$(objs): $(copts_conf) $(hdrs)
.c.o:
$(CC) $(CFLAGS) $(COPTS) $(i18n) $(build_cflags) $(RPM_OPT_FLAGS) -c $<
dnsmasq : .configured $(hdrs) $(objs)
dnsmasq : $(objs)
$(CC) $(LDFLAGS) -o $@ $(objs) $(build_libs) $(LIBS)
dnsmasq.pot : $(objs:.o=.c) $(hdrs)
+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 \
radv.c slaac.c auth.c ipset.c domain.c \
dnssec.c dnssec-openssl.c blockdata.c tables.c \
loop.c
loop.c inotify.c
LOCAL_MODULE := dnsmasq
+1 -1
View File
@@ -20,7 +20,7 @@ else
vers=`cat $1/VERSION | sed 's/[(), ]/,/ g' | tr ',' '\n' | grep ^v[0-9]`
if [ $? -eq 0 ]; then
echo "${vers}" | sort | head -n 1 | sed 's/^v//'
echo "${vers}" | sort -r | head -n 1 | sed 's/^v//'
else
cat $1/VERSION
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);
}
if (inet_addr(argv[2]) == INADDR_NONE)
{
perror("invalid ip address");
exit(1);
}
lease.s_addr = inet_addr(argv[2]);
server = find_interface(lease, nl, if_nametoindex(argv[1]));
+28
View File
@@ -1,3 +1,31 @@
dnsmasq (2.73-1) unstable; urgency=low
* New upstream.
* Tweak field width in cache dump to avoid truncating IPv6
addresses. (closes: #771557)
* Add newline at the end of example config file. (LP: #1416895)
* Make Debian package build reproducible. (closes: #777323)
-- Simon Kelley <simon@thekelleys.org.uk> Wed, 11 Feb 2015 21:59: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
* Fix build in Debian-kFreeBSD. (closes: #763693)
-- Simon Kelley <simon@thekelleys.org.uk> Thu, 02 Oct 2014 22:34:12 +0000
dnsmasq (2.72-1) unstable; urgency=low
* New upstream.
+1 -3
View File
@@ -3,7 +3,7 @@ Section: net
Priority: optional
Build-depends: gettext, libnetfilter-conntrack-dev [linux-any],
libidn11-dev, libdbus-1-dev (>=0.61), libgmp-dev,
nettle-dev (>=2.4-3)
nettle-dev (>=2.4-3), libbsd-dev [!linux-any]
Maintainer: Simon Kelley <simon@thekelleys.org.uk>
Standards-Version: 3.9.5
@@ -42,5 +42,3 @@ Description: Utilities for manipulating DHCP leases
Small utilities to query a DHCP server's lease database and
remove leases from it. These programs are distributed with dnsmasq
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/
Vendored
+1 -3
View File
@@ -293,9 +293,7 @@ case "$1" in
mkdir /var/run/dnsmasq || return 2
chown dnsmasq:nogroup /var/run/dnsmasq || return 2
fi
# Enable DBus by default because we use DBus activation with systemd.
exec $DAEMON --keep-in-foreground --enable-dbus \
-x /var/run/dnsmasq/$NAME.pid \
exec $DAEMON -x /var/run/dnsmasq/$NAME.pid \
${MAILHOSTNAME:+ -m $MAILHOSTNAME} \
${MAILTARGET:+ -t $MAILTARGET} \
${DNSMASQ_USER:+ -u $DNSMASQ_USER} \
+18 -13
View File
@@ -23,7 +23,7 @@ DEB_COPTS = $(COPTS)
TARGET = install-i18n
DEB_BUILD_ARCH_OS := $(shell dpkg-architecture -qDEB_BUILD_ARCH_OS)
DEB_HOST_ARCH_OS := $(shell dpkg-architecture -qDEB_HOST_ARCH_OS)
# Force package version based on git tags.
ifneq (,$(filter gitversion,$(DEB_BUILD_OPTIONS)))
@@ -35,7 +35,7 @@ ifeq (,$(filter nodbus,$(DEB_BUILD_OPTIONS)))
endif
ifeq (,$(filter noconntrack,$(DEB_BUILD_OPTIONS)))
ifeq ($(DEB_BUILD_ARCH_OS),linux)
ifeq ($(DEB_HOST_ARCH_OS),linux)
DEB_COPTS += -DHAVE_CONNTRACK
endif
endif
@@ -83,6 +83,11 @@ ifeq (,$(filter nodnssec,$(DEB_BUILD_OPTIONS)))
DEB_COPTS += -DHAVE_DNSSEC
endif
ifneq ($(DEB_HOST_ARCH_OS),linux)
# For strlcpy in FreeBSD
LDFLAGS += -lbsd
endif
clean:
$(checkdir)
rm -rf debian/daemon debian/base debian/utils debian/*~ debian/files debian/substvars debian/utils-substvars
@@ -139,26 +144,26 @@ ifeq (,$(findstring nodocs,$(DEB_BUILD_OPTIONS)))
install -m 644 dnsmasq.conf.example debian/base/usr/share/doc/$(package)/examples/.
install -m 644 trust-anchors.conf debian/base/usr/share/$(package)/.
install -m 644 FAQ debian/base/usr/share/doc/$(package)/.
gzip -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
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
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)/.
gzip -9 debian/base/usr/share/doc/$(package)/DBus-interface
gzip -9n debian/base/usr/share/doc/$(package)/DBus-interface
endif
install -m 644 debian/dnsmasq-base.conffiles debian/base/DEBIAN/conffiles
install -m 755 debian/dnsmasq-base.postinst debian/base/DEBIAN/postinst
install -m 755 debian/dnsmasq-base.postrm debian/base/DEBIAN/postrm
install -m 644 debian/changelog debian/base/usr/share/doc/$(package)/changelog.Debian
gzip -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/copyright debian/base/usr/share/doc/$(package)/copyright
install -m 644 debian/dbus.conf debian/base/etc/dbus-1/system.d/dnsmasq.conf
gzip -9 debian/base/usr/share/man/man8/dnsmasq.8
gzip -9n debian/base/usr/share/man/man8/dnsmasq.8
for f in debian/base/usr/share/man/*; do \
if [ -f $$f/man8/dnsmasq.8 ]; then \
gzip -9 $$f/man8/dnsmasq.8 ; \
gzip -9n $$f/man8/dnsmasq.8 ; \
fi \
done
ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
@@ -171,7 +176,7 @@ endif
chmod -R g-ws debian/base
dpkg --build debian/base ..
ifeq ($(DEB_BUILD_ARCH_OS),linux)
ifeq ($(DEB_HOST_ARCH_OS),linux)
rm -rf debian/utils
install -m 755 -d debian/utils/DEBIAN \
-d debian/utils/usr/share/man/man1 \
@@ -180,13 +185,13 @@ ifeq ($(DEB_BUILD_ARCH_OS),linux)
make -C contrib/wrt PREFIX=/usr DESTDIR=`pwd`/debian/utils CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" COPTS="$(DEB_COPTS)" CC=gcc
install -m 755 contrib/wrt/dhcp_release debian/utils/usr/bin/dhcp_release
install -m 644 contrib/wrt/dhcp_release.1 debian/utils/usr/share/man/man1/dhcp_release.1
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 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/changelog debian/utils/usr/share/doc/dnsmasq-utils/changelog.Debian
gzip -9 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/doc/dnsmasq-utils/changelog.Debian
gzip -9n debian/utils/usr/share/man/man1/dhcp_lease_time.1
ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
strip -R .note -R .comment debian/utils/usr/bin/dhcp_release
strip -R .note -R .comment debian/utils/usr/bin/dhcp_lease_time
+2 -7
View File
@@ -2,8 +2,8 @@
Description=dnsmasq - A lightweight DHCP and caching DNS server
[Service]
Type=dbus
BusName=uk.org.thekelleys.dnsmasq
Type=forking
PIDFile=/var/run/dnsmasq/dnsmasq.pid
# Test the config file and refuse starting if it is not valid.
ExecStartPre=/usr/sbin/dnsmasq --test
@@ -11,11 +11,6 @@ ExecStartPre=/usr/sbin/dnsmasq --test
# We run dnsmasq via the /etc/init.d/dnsmasq script which acts as a
# wrapper picking up extra configuration files and then execs dnsmasq
# itself, when called with the "systemd-exec" function.
#
# 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
# The systemd-*-resolvconf functions configure (and deconfigure)
+4 -1
View File
@@ -486,6 +486,9 @@
# Set the root directory for files available via FTP.
#tftp-root=/var/ftpd
# Do not abort if the tftp-root is unavailable
#tftp-no-fail
# Make the TFTP server more secure: with this set, only files owned by
# the user dnsmasq is running as will be send over the net.
#tftp-secure
@@ -645,4 +648,4 @@
#conf-dir=/etc/dnsmasq.d,.bak
# Include all files in a directory which end in .conf
#conf-dir=/etc/dnsmasq.d/*.conf
#conf-dir=/etc/dnsmasq.d/,*.conf
+50 -5
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
additional hosts file. If a directory is given, then read all the files contained in that directory.
.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
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
@@ -81,6 +85,12 @@ the upstream DNS servers.
.B --max-cache-ttl=<time>
Set a maximum TTL value for entries in the cache.
.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>
Set the TTL value returned in answers from the authoritative server.
.TP
@@ -98,7 +108,10 @@ only, to stop dnsmasq daemonising in production, use
.B -k.
.TP
.B \-q, --log-queries
Log the results of DNS queries handled by dnsmasq. Enable a full cache dump on receipt of SIGUSR1.
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
.B \-8, --log-facility=<facility>
Set the facility to which dnsmasq will send syslog entries, this
@@ -293,6 +306,12 @@ an advertising web page in response to queries for unregistered names,
instead of the correct NXDOMAIN response. This option tells dnsmasq to
fake the correct response when it sees this behaviour. As at Sept 2003
the IP address being returned by Verisign is 64.94.110.11
.TP
.B \-B, --ignore-address=<ipaddr>
Ignore replies to A-record queries which include the specified address.
No error is generated, dnsmasq simply continues to listen for another reply.
This is useful to defeat blocking strategies which rely on quickly supplying a
forged answer to a DNS request for certain domain, before the correct answer can arrive.
.TP
.B \-f, --filterwin2k
Later versions of windows make periodic DNS requests which don't get sensible answers from
@@ -441,7 +460,7 @@ but provides some syntactic sugar to make specifying address-to-name queries eas
is exactly equivalent to
.B --server=/3.2.1.in-addr.arpa/192.168.0.1
.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.
Queries in the domains are never forwarded and always replied to
with the specified IP address which may be IPv4 or IPv6. To give
@@ -453,7 +472,10 @@ domain specification works in the same was as for --server, with the
additional facility that /#/ matches any domain. Thus
--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
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
.B --ipset=/<domain>/[domain/]<ipset>[,<ipset>]
Places the resolved IP addresses of queries for the specified domains
@@ -497,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,
all that match are returned.
.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
the DNS with associated IPv4 (A) and IPv6 (AAAA) records. A name may
appear in more than one
@@ -655,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
which have not been throughly checked.
.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
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
@@ -962,6 +991,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
dhcp-optsfile will be re-read when dnsmasq receives SIGHUP. Note that
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
flag as DHCP options, using the options names bootfile-name,
server-ip-address and tftp-server. This allows these to be included
@@ -1670,6 +1711,9 @@ Absolute paths (starting with /) are allowed, but they must be within
the tftp-root. If the optional interface argument is given, the
directory is only used for TFTP requests via that interface.
.TP
.B --tftp-no-fail
Do not abort startup if specified tftp root directories are inaccessible.
.TP
.B --tftp-unique-root
Add the IP address of the TFTP client as a path component on the end
of the TFTP-root (in standard dotted-quad format). Only valid if a
@@ -1767,7 +1811,8 @@ clears its cache and then re-loads
.I /etc/hosts
and
.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
existing DHCP leases. If
.B
+493 -413
View File
File diff suppressed because it is too large. Load diff
+443 -361
View File
File diff suppressed because it is too large. Load diff
+439 -363
View File
File diff suppressed because it is too large. Load diff
+445 -360
View File
File diff suppressed because it is too large. Load diff
+456 -364
View File
File diff suppressed because it is too large. Load diff
+439 -363
View File
File diff suppressed because it is too large. Load diff
+442 -361
View File
File diff suppressed because it is too large. Load diff
+448 -361
View File
File diff suppressed because it is too large. Load diff
+439 -363
View File
File diff suppressed because it is too large. Load diff
+442 -361
View File
File diff suppressed because it is too large. Load diff
+46 -27
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
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;
}
if (qtype == T_PTR)
if ((qtype == T_PTR || qtype == T_SOA || qtype == T_NS) &&
(flag = in_arpa_name_2_addr(name, &addr)) &&
!local_query)
{
if (!(flag = in_arpa_name_2_addr(name, &addr)))
continue;
if (!local_query)
for (zone = daemon->auth_zones; zone; zone = zone->next)
if ((subnet = find_subnet(zone, flag, &addr)))
break;
if (!zone)
{
for (zone = daemon->auth_zones; zone; zone = zone->next)
if ((subnet = find_subnet(zone, flag, &addr)))
break;
if (!zone)
{
auth = 0;
continue;
}
auth = 0;
continue;
}
else if (qtype == T_SOA)
soa = 1, found = 1;
else if (qtype == T_NS)
ns = 1, found = 1;
}
if (qtype == T_PTR && flag)
{
intr = NULL;
if (flag == F_IPV4)
@@ -186,7 +189,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
if (intr)
{
if (in_zone(zone, intr->name, NULL))
if (local_query || in_zone(zone, intr->name, NULL))
{
found = 1;
log_query(flag | F_REVERSE | F_CONFIG, intr->name, &addr, NULL);
@@ -208,8 +211,11 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
*p = 0; /* must be bare name */
/* add external domain */
strcat(name, ".");
strcat(name, zone->domain);
if (zone)
{
strcat(name, ".");
strcat(name, zone->domain);
}
log_query(flag | F_DHCP | F_REVERSE, name, &addr, record_source(crecp->uid));
found = 1;
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
@@ -217,7 +223,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
T_PTR, C_IN, "d", name))
anscount++;
}
else if (crecp->flags & (F_DHCP | F_HOSTS) && in_zone(zone, name, NULL))
else if (crecp->flags & (F_DHCP | F_HOSTS) && (local_query || in_zone(zone, name, NULL)))
{
log_query(crecp->flags & ~F_FORWARD, name, &addr, record_source(crecp->uid));
found = 1;
@@ -240,14 +246,20 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
}
cname_restart:
for (zone = daemon->auth_zones; zone; zone = zone->next)
if (in_zone(zone, name, &cut))
break;
if (!zone)
if (found)
/* NS and SOA .arpa requests have set found above. */
cut = NULL;
else
{
auth = 0;
continue;
for (zone = daemon->auth_zones; zone; zone = zone->next)
if (in_zone(zone, name, &cut))
break;
if (!zone)
{
auth = 0;
continue;
}
}
for (rec = daemon->mxnames; rec; rec = rec->next)
@@ -363,6 +375,10 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
if (((addrlist->flags & ADDRLIST_IPV6) ? T_AAAA : T_A) == qtype &&
(local_query || filter_zone(zone, flag, &addrlist->addr)))
{
#ifdef HAVE_IPV6
if (addrlist->flags & ADDRLIST_REVONLY)
continue;
#endif
found = 1;
log_query(F_FORWARD | F_CONFIG | flag, name, &addrlist->addr, NULL);
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
@@ -409,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;
#ifdef HAVE_IPV6
else
peer_addr->in6.sin6_port = 0;
{
peer_addr->in6.sin6_port = 0;
peer_addr->in6.sin6_scope_id = 0;
}
#endif
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
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
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_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) */
+120 -48
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
it under the terms of the GNU General Public License as published by
@@ -322,7 +322,7 @@ static int is_expired(time_t now, struct crec *crecp)
return 1;
}
static int cache_scan_free(char *name, struct all_addr *addr, time_t now, unsigned short flags)
static struct crec *cache_scan_free(char *name, struct all_addr *addr, time_t now, unsigned short flags)
{
/* Scan and remove old entries.
If (flags & F_FORWARD) then remove any forward entries for name and any expired
@@ -331,8 +331,8 @@ static int cache_scan_free(char *name, struct all_addr *addr, time_t now, unsign
entries in the whole cache.
If (flags == 0) remove any expired entries in the whole cache.
In the flags & F_FORWARD case, the return code is valid, and returns zero if the
name exists in the cache as a HOSTS or DHCP entry (these are never deleted)
In the flags & F_FORWARD case, the return code is valid, and returns a non-NULL pointer
to a cache entry if the name exists in the cache as a HOSTS or DHCP entry (these are never deleted)
We take advantage of the fact that hash chains have stuff in the order <reverse>,<other>,<immortal>
so that when we hit an entry which isn't reverse and is immortal, we're done. */
@@ -361,7 +361,7 @@ static int cache_scan_free(char *name, struct all_addr *addr, time_t now, unsign
(((crecp->flags | flags) & F_CNAME) && !(crecp->flags & (F_DNSKEY | F_DS))))
{
if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))
return 0;
return crecp;
*up = crecp->hash_next;
cache_unlink(crecp);
cache_free(crecp);
@@ -378,7 +378,7 @@ static int cache_scan_free(char *name, struct all_addr *addr, time_t now, unsign
crecp->addr.sig.type_covered == addr->addr.dnssec.type))
{
if (crecp->flags & F_CONFIG)
return 0;
return crecp;
*up = crecp->hash_next;
cache_unlink(crecp);
cache_free(crecp);
@@ -423,7 +423,7 @@ static int cache_scan_free(char *name, struct all_addr *addr, time_t now, unsign
up = &crecp->hash_next;
}
return 1;
return NULL;
}
/* Note: The normal calling sequence is
@@ -461,9 +461,11 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
if (flags & (F_IPV4 | F_IPV6 | F_CNAME))
{
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)
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. */
@@ -471,10 +473,26 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
return NULL;
/* First remove any expired entries and entries for the name/address we
are currently inserting. Fail if we attempt to delete a name from
/etc/hosts or DHCP. */
if (!cache_scan_free(name, addr, now, flags))
are currently inserting. */
if ((new = cache_scan_free(name, addr, now, flags)))
{
/* We're trying to insert a record over one from
/etc/hosts or DHCP, or other config. If the
existing record is for an A or AAAA and
the record we're trying to insert is the same,
just drop the insert, but don't error the whole process. */
if ((flags & (F_IPV4 | F_IPV6)) && (flags & F_FORWARD))
{
if ((flags & F_IPV4) && (new->flags & F_IPV4) &&
new->addr.addr.addr.addr4.s_addr == addr->addr.addr4.s_addr)
return new;
#ifdef HAVE_IPV6
else if ((flags & F_IPV6) && (new->flags & F_IPV6) &&
IN6_ARE_ADDR_EQUAL(&new->addr.addr.addr.addr6, &addr->addr.addr6))
return new;
#endif
}
insert_error = 1;
return NULL;
}
@@ -817,27 +835,42 @@ static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrl
Only insert each unique address once into this hashing structure.
This complexity avoids O(n^2) divergent CPU use whilst reading
large (10000 entry) hosts files. */
large (10000 entry) hosts files.
Note that we only do this process when bulk-reading hosts files,
for incremental reads, rhash is NULL, and we use cache lookups
instead.
*/
/* hash address */
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)
if (rhash)
{
cache->next = rhash[j];
rhash[j] = cache;
/* hash address */
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;
memcpy(&cache->addr.addr, addr, addrlen);
cache_hash(cache);
@@ -894,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");
char *token = daemon->namebuff, *domain_suffix = NULL;
@@ -940,7 +973,7 @@ static int read_hostsfile(char *filename, unsigned int index, int cache_size, st
addr_count++;
/* rehash every 1000 names. */
if ((name_count - cache_size) > 1000)
if (rhash && ((name_count - cache_size) > 1000))
{
rehash(name_count);
cache_size = name_count;
@@ -987,7 +1020,9 @@ static int read_hostsfile(char *filename, unsigned int index, int cache_size, st
}
fclose(f);
rehash(name_count);
if (rhash)
rehash(name_count);
my_syslog(LOG_INFO, _("read %s - %d addresses"), filename, addr_count);
@@ -1098,16 +1133,22 @@ void cache_reload(void)
{
if (daemon->cachesize > 0)
my_syslog(LOG_INFO, _("cleared cache"));
return;
}
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);
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);
}
#ifdef HAVE_INOTIFY
set_dynamic_inotify(AH_HOSTS, total_size, (struct crec **)daemon->packet, revhashsz);
#endif
}
#ifdef HAVE_DHCP
@@ -1326,7 +1367,7 @@ int cache_make_stat(struct txt_record *t)
}
port = prettyprint_addr(&serv->addr, daemon->addrbuff);
lenp = p++; /* length */
bytes_avail = (p - buff) + bufflen;
bytes_avail = bufflen - (p - buff );
bytes_needed = snprintf(p, bytes_avail, "%s#%d %u %u", daemon->addrbuff, port, queries, failed_queries);
if (bytes_needed >= bytes_avail)
{
@@ -1340,7 +1381,7 @@ int cache_make_stat(struct txt_record *t)
lenp = p - 1;
buff = new;
bufflen = newlen;
bytes_avail = (p - buff) + bufflen;
bytes_avail = bufflen - (p - buff );
bytes_needed = snprintf(p, bytes_avail, "%s#%d %u %u", daemon->addrbuff, port, queries, failed_queries);
}
*lenp = bytes_needed;
@@ -1358,6 +1399,20 @@ int cache_make_stat(struct txt_record *t)
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)
{
struct server *serv, *serv1;
@@ -1411,9 +1466,9 @@ void dump_cache(time_t now)
*a = 0;
if (strlen(n) == 0 && !(cache->flags & F_REVERSE))
n = "<Root>";
p += sprintf(p, "%-40.40s ", n);
p += sprintf(p, "%-30.30s ", sanitise(n));
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
else if (cache->flags & F_DS)
{
@@ -1454,7 +1509,7 @@ void dump_cache(time_t now)
else if (cache->flags & F_DNSKEY)
t = "K";
#endif
p += sprintf(p, "%-30.30s %s%s%s%s%s%s%s%s%s ", a, t,
p += sprintf(p, "%-40.40s %s%s%s%s%s%s%s%s%s ", a, t,
cache->flags & F_FORWARD ? "F" : " ",
cache->flags & F_REVERSE ? "R" : " ",
cache->flags & F_IMMORTAL ? "I" : " ",
@@ -1487,7 +1542,13 @@ char *record_source(unsigned int index)
for (ah = daemon->addn_hosts; ah; ah = ah->next)
if (ah->index == index)
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>";
}
@@ -1540,6 +1601,8 @@ void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
if (!option_bool(OPT_LOG))
return;
name = sanitise(name);
if (addr)
{
if (flags & F_KEYTAG)
@@ -1622,7 +1685,16 @@ void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
if (strlen(name) == 0)
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);
}
+15 -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
it under the terms of the GNU General Public License as published by
@@ -17,6 +17,7 @@
#define FTABSIZ 150 /* max number of outstanding requests (default) */
#define MAX_PROCS 20 /* max no children for TCP requests */
#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 KEYBLOCK_LEN 40 /* choose to mininise fragmentation when storing DNSSEC keys */
#define DNSSEC_WORK 50 /* Max number of queries to validate one question */
@@ -26,6 +27,7 @@
#define RANDOM_SOCKS 64 /* max simultaneous random ports */
#define LEASE_RETRY 60 /* on error, retry writing leasefile after LEASE_RETRY seconds */
#define CACHESIZ 150 /* default cache size */
#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 PING_WAIT 3 /* wait for ping address-in-use test */
#define PING_CACHE_TIME 30 /* Ping test assumed to be valid this long. */
@@ -113,6 +115,8 @@ HAVE_DNSSEC
HAVE_LOOP
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_TFTP
@@ -121,6 +125,7 @@ NO_DHCP6
NO_SCRIPT
NO_LARGEFILE
NO_AUTH
NO_INOTIFY
these are avilable to explictly disable compile time options which would
otherwise be enabled automatically (HAVE_IPV6, >2Gb file sizes) or
which are enabled by default in the distributed source tree. Building dnsmasq
@@ -353,6 +358,10 @@ HAVE_SOCKADDR_SA_LEN
#undef HAVE_LOOP
#endif
#if defined (HAVE_LINUX_NETWORK) && !defined(NO_INOTIFY)
#define HAVE_INOTIFY
#endif
/* Define a string indicating which options are in use.
DNSMASQP_COMPILE_OPTS is only defined in dnsmasq.c */
@@ -426,7 +435,11 @@ static char *compile_opts =
#ifndef HAVE_LOOP
"no-"
#endif
"loop-detect";
"loop-detect "
#ifndef HAVE_INOTIFY
"no-"
#endif
"inotify";
#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
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
it under the terms of the GNU General Public License as published by
@@ -426,7 +426,7 @@ static DBusMessage *dbus_set_bool(DBusMessage *message, int flag, char *name)
}
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);
}
+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
it under the terms of the GNU General Public License as published by
+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
it under the terms of the GNU General Public License as published by
+27 -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
it under the terms of the GNU General Public License as published by
@@ -376,10 +376,9 @@ void dhcp_packet(time_t now, int pxe_fd)
}
}
#if defined(HAVE_LINUX_NETWORK)
else 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) */
/* fill cmsg for outbound interface (both broadcast & unicast) */
struct in_pktinfo *pkt;
msg.msg_control = control_u.control;
msg.msg_controllen = sizeof(control_u);
@@ -389,23 +388,29 @@ void dhcp_packet(time_t now, int pxe_fd)
pkt->ipi_spec_dst.s_addr = 0;
msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
cmptr->cmsg_level = IPPROTO_IP;
cmptr->cmsg_type = IP_PKTINFO;
dest.sin_addr.s_addr = INADDR_BROADCAST;
dest.sin_port = htons(daemon->dhcp_client_port);
}
else
{
/* unicast to unconfigured client. Inject mac address direct into ARP cache.
struct sockaddr limits size to 14 bytes. */
dest.sin_addr = mess->yiaddr;
dest.sin_port = htons(daemon->dhcp_client_port);
memcpy(&arp_req.arp_pa, &dest, sizeof(struct sockaddr_in));
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));
cmptr->cmsg_type = IP_PKTINFO;
if ((ntohs(mess->flags) & 0x8000) || mess->hlen == 0 ||
mess->hlen > sizeof(ifr.ifr_addr.sa_data) || mess->htype == 0)
{
/* broadcast to 255.255.255.255 (or mac address invalid) */
dest.sin_addr.s_addr = INADDR_BROADCAST;
dest.sin_port = htons(daemon->dhcp_client_port);
}
else
{
/* unicast to unconfigured client. Inject mac address direct into ARP cache.
struct sockaddr limits size to 14 bytes. */
dest.sin_addr = mess->yiaddr;
dest.sin_port = htons(daemon->dhcp_client_port);
memcpy(&arp_req.arp_pa, &dest, sizeof(struct sockaddr_in));
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)
else if ((ntohs(mess->flags) & 0x8000) || mess->hlen != ETHER_ADDR_LEN || mess->htype != ARPHRD_ETHER)
@@ -443,7 +448,7 @@ void dhcp_packet(time_t now, int pxe_fd)
setsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &iface_index, sizeof(iface_index));
#endif
while(sendmsg(fd, &msg, 0) == -1 && retry_send());
while(retry_send(sendmsg(fd, &msg, 0)));
}
/* 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
it under the terms of the GNU General Public License as published by
+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
it under the terms of the GNU General Public License as published by
@@ -225,9 +225,9 @@ void dhcp6_packet(time_t now)
if (port != 0)
{
from.sin6_port = htons(port);
while (sendto(daemon->dhcp6fd, daemon->outpacket.iov_base, save_counter(0),
0, (struct sockaddr *)&from, sizeof(from)) == -1 &&
retry_send());
while (retry_send(sendto(daemon->dhcp6fd, daemon->outpacket.iov_base,
save_counter(0), 0, (struct sockaddr *)&from,
sizeof(from))));
}
}
@@ -246,7 +246,9 @@ void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsi
neigh.code = 0;
neigh.reserved = 0;
neigh.target = *client;
/* RFC4443 section-2.3: checksum has to be zero to be calculated */
neigh.checksum = 0;
memset(&addr, 0, sizeof(addr));
#ifdef HAVE_SOCKADDR_SA_LEN
addr.sin6_len = sizeof(struct sockaddr_in6);
+9 -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
it under the terms of the GNU General Public License as published by
@@ -142,3 +142,11 @@ struct dns_header {
#define ADD_RDLEN(header, pp, plen, len) \
(!CHECK_LEN(header, pp, plen, len) ? 0 : (((pp) += (len)), 1))
/* Escape character in our presentation format for names.
Cannot be '.' or /000 and must be !isprint().
Note that escaped chars are stored as
<NAME_ESCAPE> <orig-char+1>
to ensure that the escaped form of /000 doesn't include /000
*/
#define NAME_ESCAPE 1
+137 -46
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
it under the terms of the GNU General Public License as published by
@@ -58,6 +58,9 @@ int main (int argc, char **argv)
struct dhcp_context *context;
struct dhcp_relay *relay;
#endif
#ifdef HAVE_TFTP
int tftp_prefix_missing = 0;
#endif
#ifdef LOCALEDIR
setlocale(LC_ALL, "");
@@ -87,23 +90,31 @@ int main (int argc, char **argv)
if (daemon->edns_pktsz < PACKETSZ)
daemon->edns_pktsz = PACKETSZ;
#ifdef HAVE_DNSSEC
/* Enforce min packet big enough for DNSSEC */
if (option_bool(OPT_DNSSEC_VALID) && daemon->edns_pktsz < EDNS_PKTSZ)
daemon->edns_pktsz = EDNS_PKTSZ;
#endif
daemon->packet_buff_sz = daemon->edns_pktsz > DNSMASQ_PACKETSZ ?
daemon->edns_pktsz : DNSMASQ_PACKETSZ;
daemon->packet = safe_malloc(daemon->packet_buff_sz);
daemon->addrbuff = safe_malloc(ADDRSTRLEN);
if (option_bool(OPT_EXTRALOG))
daemon->addrbuff2 = safe_malloc(ADDRSTRLEN);
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID))
{
daemon->keyname = safe_malloc(MAXDNAME);
daemon->workspacename = safe_malloc(MAXDNAME);
/* Note that both /000 and '.' are allowed within labels. These get
represented in presentation format using NAME_ESCAPE as an escape
character when in DNSSEC mode.
In theory, if all the characters in a name were /000 or
'.' or NAME_ESCAPE then all would have to be escaped, so the
presentation format would be twice as long as the spec.
daemon->namebuff was previously allocated by the option-reading
code before we knew if we're in DNSSEC mode, so reallocate here. */
free(daemon->namebuff);
daemon->namebuff = safe_malloc(MAXDNAME * 2);
daemon->keyname = safe_malloc(MAXDNAME * 2);
daemon->workspacename = safe_malloc(MAXDNAME * 2);
}
#endif
@@ -146,15 +157,20 @@ int main (int argc, char **argv)
reset_option_bool(OPT_CLEVERBIND);
}
#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))
{
#ifdef HAVE_DNSSEC
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)
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
die(_("DNSSEC not available: set HAVE_DNSSEC in src/config.h"), NULL, EC_BADCONF);
#endif
@@ -167,10 +183,10 @@ int main (int argc, char **argv)
#ifdef HAVE_CONNTRACK
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
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
#ifdef HAVE_SOLARIS_NETWORK
@@ -190,7 +206,7 @@ int main (int argc, char **argv)
#ifndef HAVE_LOOP
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
now = dnsmasq_time();
@@ -315,11 +331,19 @@ int main (int argc, char **argv)
if (daemon->port != 0)
{
cache_init();
#ifdef HAVE_DNSSEC
blockdata_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))
#ifdef HAVE_DBUS
{
@@ -332,7 +356,7 @@ int main (int argc, char **argv)
#else
die(_("DBus not available: set HAVE_DBUS in src/config.h"), NULL, EC_BADCONF);
#endif
if (daemon->port != 0)
pre_allocate_sfds();
@@ -359,7 +383,7 @@ int main (int argc, char **argv)
if (baduser)
die(_("unknown user or group: %s"), baduser, EC_BADCONF);
/* implement group defaults, "dip" if available, or group associated with uid */
if (!daemon->group_set && !gp)
{
@@ -434,7 +458,7 @@ int main (int argc, char **argv)
char *msg;
/* close our copy of write-end */
close(err_pipe[1]);
while (retry_send(close(err_pipe[1])));
/* check for errors after the fork */
if (read_event(err_pipe[0], &ev, &msg))
@@ -443,7 +467,7 @@ int main (int argc, char **argv)
_exit(EC_GOOD);
}
close(err_pipe[0]);
while (retry_send(close(err_pipe[0])));
/* NO calls to die() from here on. */
@@ -495,10 +519,12 @@ int main (int argc, char **argv)
{
if (!read_write(fd, (unsigned char *)daemon->namebuff, strlen(daemon->namebuff), 0))
err = 1;
while (!err && close(fd) == -1)
if (!retry_send())
err = 1;
else
{
while (retry_send(close(fd)));
if (errno != 0)
err = 1;
}
}
if (err)
@@ -617,12 +643,14 @@ int main (int argc, char **argv)
}
#ifdef HAVE_LINUX_NETWORK
free(hdr);
free(data);
if (option_bool(OPT_DEBUG))
prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
#endif
#ifdef HAVE_TFTP
if (option_bool(OPT_TFTP))
if (option_bool(OPT_TFTP))
{
DIR *dir;
struct tftp_prefix *p;
@@ -631,20 +659,31 @@ int main (int argc, char **argv)
{
if (!((dir = opendir(daemon->tftp_prefix))))
{
send_event(err_pipe[1], EVENT_TFTP_ERR, errno, daemon->tftp_prefix);
_exit(0);
tftp_prefix_missing = 1;
if (!option_bool(OPT_TFTP_NO_FAIL))
{
send_event(err_pipe[1], EVENT_TFTP_ERR, errno, daemon->tftp_prefix);
_exit(0);
}
}
closedir(dir);
else
closedir(dir);
}
for (p = daemon->if_prefix; p; p = p->next)
{
p->missing = 0;
if (!((dir = opendir(p->prefix))))
{
send_event(err_pipe[1], EVENT_TFTP_ERR, errno, p->prefix);
_exit(0);
}
closedir(dir);
{
p->missing = 1;
if (!option_bool(OPT_TFTP_NO_FAIL))
{
send_event(err_pipe[1], EVENT_TFTP_ERR, errno, p->prefix);
_exit(0);
}
}
else
closedir(dir);
}
}
#endif
@@ -674,9 +713,24 @@ int main (int argc, char **argv)
#ifdef HAVE_DNSSEC
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"));
if (option_bool(OPT_DNSSEC_TIME))
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
@@ -742,8 +796,9 @@ int main (int argc, char **argv)
#endif
#ifdef HAVE_TFTP
if (option_bool(OPT_TFTP))
if (option_bool(OPT_TFTP))
{
struct tftp_prefix *p;
#ifdef FD_SETSIZE
if (FD_SETSIZE < (unsigned)max_fd)
max_fd = FD_SETSIZE;
@@ -753,7 +808,14 @@ int main (int argc, char **argv)
daemon->tftp_prefix ? _("root is ") : _("enabled"),
daemon->tftp_prefix ? daemon->tftp_prefix: "",
option_bool(OPT_TFTP_SECURE) ? _("secure mode") : "");
if (tftp_prefix_missing)
my_syslog(MS_TFTP | LOG_WARNING, _("warning: %s inaccessible"), daemon->tftp_prefix);
for (p = daemon->if_prefix; p; p = p->next)
if (p->missing)
my_syslog(MS_TFTP | LOG_WARNING, _("warning: TFTP directory %s inaccessible"), p->prefix);
/* This is a guess, it assumes that for small limits,
disjoint files might be served, but for large limits,
a single file will be sent to may clients (the file only needs
@@ -786,13 +848,18 @@ int main (int argc, char **argv)
/* finished start-up - release original process */
if (err_pipe[1] != -1)
close(err_pipe[1]);
while (retry_send(close(err_pipe[1])));
if (daemon->port != 0)
check_servers();
pid = getpid();
#ifdef HAVE_INOTIFY
/* Using inotify, have to select a resolv file at startup */
poll_resolv(1, 0, now);
#endif
while (1)
{
int maxfd = -1;
@@ -858,6 +925,14 @@ int main (int argc, char **argv)
bump_maxfd(daemon->icmp6fd, &maxfd);
}
#endif
#ifdef HAVE_INOTIFY
if (daemon->inotifyfd != -1)
{
FD_SET(daemon->inotifyfd, &rset);
bump_maxfd(daemon->inotifyfd, &maxfd);
}
#endif
#if defined(HAVE_LINUX_NETWORK)
FD_SET(daemon->netlinkfd, &rset);
@@ -866,7 +941,7 @@ int main (int argc, char **argv)
FD_SET(daemon->routefd, &rset);
bump_maxfd(daemon->routefd, &maxfd);
#endif
FD_SET(piperead, &rset);
bump_maxfd(piperead, &maxfd);
@@ -929,6 +1004,13 @@ int main (int argc, char **argv)
route_sock();
#endif
#ifdef HAVE_INOTIFY
if (daemon->inotifyfd != -1 && FD_ISSET(daemon->inotifyfd, &rset) && inotify_check(now))
{
if (daemon->port != 0 && !option_bool(OPT_NO_POLL))
poll_resolv(1, 1, now);
}
#else
/* Check for changes to resolv files once per second max. */
/* Don't go silent for long periods if the clock goes backwards. */
if (daemon->last_resolv == 0 ||
@@ -941,7 +1023,8 @@ int main (int argc, char **argv)
poll_resolv(0, daemon->last_resolv != 0, now);
daemon->last_resolv = now;
}
#endif
if (FD_ISSET(piperead, &rset))
async_event(piperead, now);
@@ -1128,6 +1211,9 @@ static void fatal_event(struct event_desc *ev, char *msg)
case EVENT_TFTP_ERR:
die(_("TFTP directory %s inaccessible: %s"), msg, EC_FILE);
case EVENT_TIME_ERR:
die(_("cannot create timestamp file %s: %s" ), msg, EC_BADCONF);
}
}
@@ -1268,7 +1354,7 @@ static void async_event(int pipe, time_t now)
do {
helper_write();
} while (!helper_buf_empty() || do_script_run(now));
close(daemon->helperfd);
while (retry_send(close(daemon->helperfd)));
}
#endif
@@ -1367,6 +1453,9 @@ void clear_cache_and_reload(time_t now)
if (option_bool(OPT_ETHERS))
dhcp_read_ethers();
reread_dhcp();
#ifdef HAVE_INOTIFY
set_dynamic_inotify(AH_DHCP_HST | AH_DHCP_OPT, 0, NULL, 0);
#endif
dhcp_update_configs(daemon->dhcp_conf);
lease_update_from_configs();
lease_update_file(now);
@@ -1490,7 +1579,7 @@ static void check_dns_listeners(fd_set *set, time_t now)
if (getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) == -1)
{
close(confd);
while (retry_send(close(confd)));
continue;
}
@@ -1555,7 +1644,7 @@ static void check_dns_listeners(fd_set *set, time_t now)
if (!client_ok)
{
shutdown(confd, SHUT_RDWR);
close(confd);
while (retry_send(close(confd)));
}
#ifndef NO_FORK
else if (!option_bool(OPT_DEBUG) && (p = fork()) != 0)
@@ -1570,7 +1659,10 @@ static void check_dns_listeners(fd_set *set, time_t now)
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
else
@@ -1612,7 +1704,7 @@ static void check_dns_listeners(fd_set *set, time_t now)
buff = tcp_request(confd, now, &tcp_addr, netmask, auth_dns);
shutdown(confd, SHUT_RDWR);
close(confd);
while (retry_send(close(confd)));
if (buff)
free(buff);
@@ -1621,7 +1713,7 @@ static void check_dns_listeners(fd_set *set, time_t now)
if (s->tcpfd != -1)
{
shutdown(s->tcpfd, SHUT_RDWR);
close(s->tcpfd);
while (retry_send(close(s->tcpfd)));
}
#ifndef NO_FORK
if (!option_bool(OPT_DEBUG))
@@ -1699,9 +1791,8 @@ int icmp_ping(struct in_addr addr)
j = (j & 0xffff) + (j >> 16);
packet.icmp.icmp_cksum = (j == 0xffff) ? j : ~j;
while (sendto(fd, (char *)&packet.icmp, sizeof(struct icmp), 0,
(struct sockaddr *)&saddr, sizeof(saddr)) == -1 &&
retry_send());
while (retry_send(sendto(fd, (char *)&packet.icmp, sizeof(struct icmp), 0,
(struct sockaddr *)&saddr, sizeof(saddr))));
for (now = start = dnsmasq_time();
difftime(now, start) < (float)PING_WAIT;)
@@ -1763,7 +1854,7 @@ int icmp_ping(struct in_addr addr)
}
#if defined(HAVE_LINUX_NETWORK) || defined(HAVE_SOLARIS_NETWORK)
close(fd);
while (retry_send(close(fd)));
#else
opt = 1;
setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt));
+52 -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
it under the terms of the GNU General Public License as published by
@@ -14,7 +14,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define COPYRIGHT "Copyright (c) 2000-2014 Simon Kelley"
#define COPYRIGHT "Copyright (c) 2000-2015 Simon Kelley"
#ifndef NO_LARGEFILE
/* Ensure we can use files >2GB (log files may grow this big) */
@@ -167,6 +167,7 @@ struct event_desc {
#define EVENT_INIT 21
#define EVENT_NEWADDR 22
#define EVENT_NEWROUTE 23
#define EVENT_TIME_ERR 24
/* Exit codes. */
#define EC_GOOD 0
@@ -238,7 +239,9 @@ struct event_desc {
#define OPT_DNSSEC_NO_SIGN 48
#define OPT_LOCAL_SERVICE 49
#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
not to occupy the same bits as priorities, no matter how syslog.h is set up. */
@@ -318,6 +321,7 @@ struct ds_config {
#define ADDRLIST_LITERAL 1
#define ADDRLIST_IPV6 2
#define ADDRLIST_REVONLY 4
struct addrlist {
struct all_addr addr;
@@ -441,6 +445,7 @@ struct crec {
#define F_NO_RR (1u<<25)
#define F_IPSET (1u<<26)
#define F_NSIGMATCH (1u<<27)
#define F_NOEXTRA (1u<<28)
/* Values of uid in crecs with F_CONFIG bit set. */
#define SRC_INTERFACE 0
@@ -541,15 +546,26 @@ struct resolvc {
int is_default, logged;
time_t mtime;
char *name;
#ifdef HAVE_INOTIFY
int wd; /* inotify watch descriptor */
char *file; /* pointer to file part if path */
#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_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 *next;
int flags;
char *fname;
#ifdef HAVE_INOTIFY
int wd; /* inotify watch descriptor */
#endif
unsigned int index; /* matches to cache entries for logging */
};
@@ -564,8 +580,10 @@ struct hostsfile {
#define STAT_SECURE_WILDCARD 7
#define STAT_NO_SIG 8
#define STAT_NO_DS 9
#define STAT_NEED_DS_NEG 10
#define STAT_CHASE_CNAME 11
#define STAT_NO_NS 10
#define STAT_NEED_DS_NEG 11
#define STAT_CHASE_CNAME 12
#define STAT_INSECURE_DS 13
#define FREC_NOREBIND 1
#define FREC_CHECKING_DISABLED 2
@@ -593,13 +611,15 @@ struct frec {
#endif
unsigned int iface;
unsigned short orig_id, new_id;
int fd, forwardall, flags;
int log_id, fd, forwardall, flags;
time_t time;
unsigned char *hash[HASH_SIZE];
#ifdef HAVE_DNSSEC
int class, work_counter;
struct blockdata *stash; /* Saved reply, whilst we validate */
size_t stash_len;
struct blockdata *orig_domain; /* domain of original query, whilst
we're seeing is if in unsigned domain */
size_t stash_len, name_start, name_len;
struct frec *dependent; /* Query awaiting internally-generated DNSKEY or DS query */
struct frec *blocking_query; /* Query which is blocking us. */
#endif
@@ -883,6 +903,7 @@ struct addr_list {
struct tftp_prefix {
char *interface;
char *prefix;
int missing;
struct tftp_prefix *next;
};
@@ -925,7 +946,7 @@ extern struct daemon {
char *runfile;
char *lease_change_command;
struct iname *if_names, *if_addrs, *if_except, *dhcp_except, *auth_peers, *tftp_interfaces;
struct bogus_addr *bogus_addr;
struct bogus_addr *bogus_addr, *ignore_addr;
struct server *servers;
struct ipsets *ipsets;
int log_fac; /* log facility */
@@ -933,7 +954,7 @@ extern struct daemon {
int max_logs; /* queue limit */
int cachesize, ftabsize;
int port, query_port, min_port;
unsigned long local_ttl, neg_ttl, max_ttl, 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 dhcp_context *dhcp, *dhcp6;
struct ra_interface *ra_interfaces;
@@ -951,7 +972,7 @@ extern struct daemon {
int doing_ra, doing_dhcp6;
struct dhcp_netid_list *dhcp_ignore, *dhcp_ignore_names, *dhcp_gen_names;
struct dhcp_netid_list *force_broadcast, *bootp_dynamic;
struct hostsfile *dhcp_hosts_file, *dhcp_opts_file;
struct hostsfile *dhcp_hosts_file, *dhcp_opts_file, *dynamic_dirs;
int dhcp_max, tftp_max;
int dhcp_server_port, dhcp_client_port;
int start_tftp_port, end_tftp_port;
@@ -969,6 +990,7 @@ extern struct daemon {
#endif
#ifdef HAVE_DNSSEC
struct ds_config *ds;
char *timestamp_file;
#endif
/* globally used stuff for DNS */
@@ -994,9 +1016,14 @@ extern struct daemon {
struct randfd randomsocks[RANDOM_SOCKS];
int v6pktinfo;
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 */
int dhcpfd, helperfd, pxefd;
#ifdef HAVE_INOTIFY
int inotifyfd;
#endif
#if defined(HAVE_LINUX_NETWORK)
int netlinkfd;
#elif defined(HAVE_BSD_NETWORK)
@@ -1025,6 +1052,7 @@ extern struct daemon {
/* utility string buffer, hold max sized IP address as string */
char *addrbuff;
char *addrbuff2; /* only allocated when OPT_EXTRALOG */
} *daemon;
@@ -1051,6 +1079,8 @@ int cache_make_stat(struct txt_record *t);
char *cache_get_name(struct crec *crecp);
char *cache_get_cname_target(struct crec *crecp);
struct crec *cache_enumerate(int init);
int read_hostsfile(char *filename, unsigned int index, int cache_size,
struct crec **rhash, int hashsz);
/* blockdata.c */
#ifdef HAVE_DNSSEC
@@ -1088,6 +1118,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
time_t now, int *ad_reqd, int *do_bit);
int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
struct bogus_addr *addr, time_t now);
int check_for_ignored_address(struct dns_header *header, size_t qlen, struct bogus_addr *baddr);
unsigned char *find_pseudoheader(struct dns_header *header, size_t plen,
size_t *len, unsigned char **p, int *is_sign);
int check_for_local_domain(char *name, time_t now);
@@ -1120,11 +1151,12 @@ int in_zone(struct auth_zone *zone, char *name, char **cut);
size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, int class, int type, union mysockaddr *addr);
int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t n, char *name, char *keyname, int class);
int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class);
int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class, int *neganswer);
int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class, int *neganswer, int *nons);
int dnssec_chase_cname(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname);
int dnskey_keytag(int alg, int flags, unsigned char *rdata, int rdlen);
size_t filter_rrsigs(struct dns_header *header, size_t plen);
unsigned char* hash_questions(struct dns_header *header, size_t plen, char *name);
int setup_timestamp(void);
/* util.c */
void rand_init(void);
@@ -1148,7 +1180,7 @@ int is_same_net6(struct in6_addr *a, struct in6_addr *b, int prefixlen);
u64 addr6part(struct in6_addr *addr);
void setaddr6part(struct in6_addr *addr, u64 host);
#endif
int retry_send(void);
int retry_send(ssize_t rc);
void prettyprint_time(char *buf, unsigned int t);
int prettyprint_addr(union mysockaddr *addr, char *buf);
int parse_hex(char *in, unsigned char *out, int maxlen,
@@ -1183,6 +1215,7 @@ void reset_option_bool(unsigned int opt);
struct hostsfile *expand_filelist(struct hostsfile *list);
char *parse_server(char *arg, union mysockaddr *addr,
union mysockaddr *source_addr, char *interface, int *flags);
int option_read_dynfile(char *file, int flags);
/* forward.c */
void reply_query(int fd, int family, time_t now);
@@ -1469,3 +1502,9 @@ void loop_send_probes();
int detect_loop(char *query, int type);
#endif
/* inotify.c */
#ifdef HAVE_INOTIFY
void inotify_dnsmasq_init();
int inotify_check(time_t now);
void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revhashsz);
#endif
+333 -185
View File
@@ -1,5 +1,5 @@
/* 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
it under the terms of the GNU General Public License as published by
@@ -34,6 +34,7 @@
#include <nettle/dsa-compat.h>
#endif
#include <utime.h>
#define SERIAL_UNDEF -100
#define SERIAL_EQ 0
@@ -275,7 +276,7 @@ static int dnsmasq_ecdsa_verify(struct blockdata *key_data, unsigned int key_len
}
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;
mpz_import(x, t , 1, 1, 0, 0, p);
@@ -320,10 +321,18 @@ static int verify(struct blockdata *key_data, unsigned int key_len, unsigned cha
thus generating names in canonical form.
Calling to_wire followed by from_wire is almost an identity,
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)
{
unsigned char *l, *p, term;
unsigned char *l, *p, *q, term;
int len;
for (l = (unsigned char*)name; *l != 0; l = p)
@@ -331,7 +340,12 @@ static int to_wire(char *name)
for (p = l; *p != '.' && *p != 0; p++)
if (*p >= 'A' && *p <= 'Z')
*p = *p - 'A' + 'a';
else if (*p == NAME_ESCAPE)
{
for (q = p; *q; q++)
*q = *(q+1);
(*p)--;
}
term = *p;
if ((len = p - l) != 0)
@@ -350,13 +364,24 @@ static int to_wire(char *name)
/* Note: no compression allowed in input. */
static void from_wire(char *name)
{
unsigned char *l;
unsigned char *l, *p, *last;
int len;
for (last = (unsigned char *)name; *last != 0; last += *last+1);
for (l = (unsigned char *)name; *l != 0; l += len+1)
{
len = *l;
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] = '.';
}
@@ -394,17 +419,90 @@ static int serial_compare_32(unsigned long s1, unsigned long s2)
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;
static int back_to_the_future;
int setup_timestamp(void)
{
struct stat statbuf;
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));
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 */
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 */
if (option_bool(OPT_DNSSEC_TIME))
/* 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 (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));
back_to_the_future = 1;
set_option_bool(OPT_DNSSEC_TIME);
queue_event(EVENT_RELOAD); /* purge cache */
}
if (back_to_the_future == 0)
return 1;
}
else if (option_bool(OPT_DNSSEC_TIME))
return 1;
curtime = time(0);
/* We must explicitly check against wanted values, because of SERIAL_UNDEF */
return serial_compare_32(curtime, date_start) == SERIAL_GT
&& serial_compare_32(curtime, date_end) == SERIAL_LT;
@@ -456,16 +554,27 @@ static u16 *get_desc(int type)
/* Return bytes of canonicalised rdata, when the return value is zero, the remaining
data, pointed to by *p, should be used raw. */
static int get_rdata(struct dns_header *header, size_t plen, unsigned char *end, char *buff,
static int get_rdata(struct dns_header *header, size_t plen, unsigned char *end, char *buff, int bufflen,
unsigned char **p, u16 **desc)
{
int d = **desc;
(*desc)++;
/* No more data needs mangling */
if (d == (u16)-1)
return 0;
{
/* If there's more data than we have space for, just return what fits,
we'll get called again for more chunks */
if (end - *p > bufflen)
{
memcpy(buff, *p, bufflen);
*p += bufflen;
return bufflen;
}
return 0;
}
(*desc)++;
if (d == 0 && extract_name(header, plen, p, buff, 1, 0))
/* domain-name, canonicalise */
@@ -560,7 +669,7 @@ static void sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int
if (left1 != 0)
memmove(buff1, buff1 + len1 - left1, left1);
if ((len1 = get_rdata(header, plen, end1, buff1 + left1, &p1, &dp1)) == 0)
if ((len1 = get_rdata(header, plen, end1, buff1 + left1, (MAXDNAME * 2) - left1, &p1, &dp1)) == 0)
{
quit = 1;
len1 = end1 - p1;
@@ -571,7 +680,7 @@ static void sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int
if (left2 != 0)
memmove(buff2, buff2 + len2 - left2, left2);
if ((len2 = get_rdata(header, plen, end2, buff2 + left2, &p2, &dp2)) == 0)
if ((len2 = get_rdata(header, plen, end2, buff2 + left2, (MAXDNAME *2) - left2, &p2, &dp2)) == 0)
{
quit = 1;
len2 = end2 - p2;
@@ -604,6 +713,7 @@ static void sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int
Return code:
STAT_SECURE if it validates.
STAT_SECURE_WILDCARD if it validates and is the result of wildcard expansion.
(In this case *wildcard_out points to the "body" of the wildcard within name.)
STAT_NO_SIG no RRsigs found.
STAT_INSECURE RRset empty.
STAT_BOGUS signature is wrong, bad packet.
@@ -614,8 +724,8 @@ static void sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int
name is unchanged on exit. keyname is used as workspace and trashed.
*/
static int validate_rrset(time_t now, struct dns_header *header, size_t plen, int class,
int type, char *name, char *keyname, struct blockdata *key, int keylen, int algo_in, int keytag_in)
static int validate_rrset(time_t now, struct dns_header *header, size_t plen, int class, int type,
char *name, char *keyname, char **wildcard_out, struct blockdata *key, int keylen, int algo_in, int keytag_in)
{
static unsigned char **rrset = NULL, **sigs = NULL;
static int rrset_sz = 0, sig_sz = 0;
@@ -625,10 +735,13 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
struct crec *crecp = NULL;
int type_covered, algo, labels, orig_ttl, sig_expiration, sig_inception, key_tag;
u16 *rr_desc = get_desc(type);
if (wildcard_out)
*wildcard_out = NULL;
if (!(p = skip_questions(header, plen)))
return STAT_BOGUS;
name_labels = count_labels(name); /* For 4035 5.3.2 check */
/* look for RRSIGs for this RRset and get pointers to each RR in the set. */
@@ -787,8 +900,16 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
{
int k;
for (k = name_labels - labels; k != 0; k--)
while (*name_start != '.' && *name_start != 0)
name_start++;
{
while (*name_start != '.' && *name_start != 0)
name_start++;
if (k != 1 && *name_start == '.')
name_start++;
}
if (wildcard_out)
*wildcard_out = name_start+1;
name_start--;
*name_start = '*';
}
@@ -805,10 +926,11 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
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;
dp = rr_desc;
for (len = 0; (seg = get_rdata(header, plen, end, name, &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 = htons(len);
hash->update(ctx, 2, (unsigned char *)&len);
@@ -816,7 +938,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
/* Now canonicalise again and digest. */
cp = p;
dp = rr_desc;
while ((seg = get_rdata(header, plen, end, name, &cp, &dp)))
while ((seg = get_rdata(header, plen, end, name, MAXDNAME * 2, &cp, &dp)))
hash->update(ctx, seg, (unsigned char *)name);
if (cp != end)
hash->update(ctx, end - cp, cp);
@@ -852,8 +974,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
/* The DNS packet is expected to contain the answer to a DNSKEY query.
Put all DNSKEYs in the answer which are valid into the cache.
return codes:
STAT_INSECURE No DNSKEYs in reply.
STAT_SECURE At least one valid DNSKEY found and in cache.
STAT_SECURE At least one valid DNSKEY found and in cache.
STAT_BOGUS No DNSKEYs found, which can be validated with DS,
or self-sign for DNSKEY RRset is not valid, bad packet.
STAT_NEED_DS DS records to validate a key not found, name in keyname
@@ -873,11 +994,8 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
GETSHORT(qtype, p);
GETSHORT(qclass, p);
if (qtype != T_DNSKEY || qclass != class)
if (qtype != T_DNSKEY || qclass != class || ntohs(header->ancount) == 0)
return STAT_BOGUS;
if (ntohs(header->ancount) == 0)
return STAT_INSECURE;
/* See if we have cached a DS record which validates this key */
if (!(crecp = cache_find_by_name(NULL, name, now, F_DS)))
@@ -888,7 +1006,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 (crecp->flags & F_NEG)
return STAT_INSECURE;
return STAT_INSECURE_DS;
/* NOTE, we need to find ONE DNSKEY which matches the DS */
for (valid = 0, j = ntohs(header->ancount); j != 0 && !valid; j--)
@@ -963,7 +1081,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
if (recp1->addr.ds.keylen == (int)hash->digest_size &&
(ds_digest = blockdata_retrieve(recp1->addr.key.keydata, recp1->addr.ds.keylen, NULL)) &&
memcmp(ds_digest, digest, recp1->addr.ds.keylen) == 0 &&
validate_rrset(now, header, plen, class, T_DNSKEY, name, keyname, key, rdlen - 4, algo, keytag) == STAT_SECURE)
validate_rrset(now, header, plen, class, T_DNSKEY, name, keyname, NULL, key, rdlen - 4, algo, keytag) == STAT_SECURE)
{
valid = 1;
break;
@@ -1019,7 +1137,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
else
{
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.keydata = key;
@@ -1073,24 +1191,24 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
return STAT_SECURE;
}
log_query(F_UPSTREAM, name, NULL, "BOGUS DNSKEY");
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DNSKEY");
return STAT_BOGUS;
}
/* The DNS packet is expected to contain the answer to a DS query
Put all DSs in the answer which are valid into the cache.
return codes:
STAT_INSECURE no DS in reply or not signed.
STAT_SECURE At least one valid DS found and in cache.
STAT_NO_DS It's proved there's no DS here.
STAT_BOGUS At least one DS found, which fails validation, bad packet.
STAT_NEED_DNSKEY DNSKEY records to validate a DS not found, name in keyname
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_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)
{
unsigned char *p = (unsigned char *)(header+1);
int qtype, qclass, val, i, neganswer;
int qtype, qclass, val, i, neganswer, nons;
if (ntohs(header->qdcount) != 1 ||
!(p = skip_name(p, header, plen, 4)))
@@ -1102,32 +1220,42 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
if (qtype != T_DS || qclass != class)
val = STAT_BOGUS;
else
val = dnssec_validate_reply(now, header, plen, name, keyname, NULL, &neganswer);
if (val == STAT_NO_SIG)
val = STAT_INSECURE;
val = dnssec_validate_reply(now, header, plen, name, keyname, NULL, &neganswer, &nons);
/* Note dnssec_validate_reply() will have cached positive answers */
if (val == STAT_NO_SIG || val == STAT_INSECURE)
val = STAT_BOGUS;
p = (unsigned char *)(header+1);
extract_name(header, plen, &p, name, 1, 4);
p += 4; /* qtype, qclass */
if (!(p = skip_section(p, ntohs(header->ancount), header, plen)))
return STAT_BOGUS;
val = STAT_BOGUS;
if (val == STAT_BOGUS)
log_query(F_UPSTREAM, name, NULL, "BOGUS DS");
if ((val == STAT_SECURE || val == STAT_INSECURE) && neganswer)
/* 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)))
{
int rdlen, flags = F_FORWARD | F_DS | F_NEG;
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS");
return STAT_BOGUS;
}
/* By here, the answer is proved secure, and a positive answer has been cached. */
if (val == STAT_SECURE && neganswer)
{
int rdlen, flags = F_FORWARD | F_DS | F_NEG | F_DNSSECOK;
unsigned long ttl, minttl = ULONG_MAX;
struct all_addr a;
if (RCODE(header) == NXDOMAIN)
flags |= F_NXDOMAIN;
if (val == STAT_SECURE)
flags |= F_DNSSECOK;
/* We only cache validated DS records, DNSSECOK flag hijacked
to store presence/absence of NS. */
if (nons)
flags &= ~F_DNSSECOK;
for (i = ntohs(header->nscount); i != 0; i--)
{
@@ -1173,10 +1301,12 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
a.addr.dnssec.class = class;
cache_insert(name, &a, now, ttl, flags);
cache_end_insert();
cache_end_insert();
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, nons ? "no delegation" : "no DS");
}
return (val == STAT_SECURE) ? STAT_NO_DS : STAT_INSECURE;
return nons ? STAT_NO_NS : STAT_NO_DS;
}
return val;
@@ -1300,12 +1430,15 @@ static int find_nsec_records(struct dns_header *header, size_t plen, unsigned ch
}
static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsigned char **nsecs, int nsec_count,
char *workspace1, char *workspace2, char *name, int type)
char *workspace1, char *workspace2, char *name, int type, int *nons)
{
int i, rc, rdlen;
unsigned char *p, *psave;
int offset = (type & 0xff) >> 3;
int mask = 0x80 >> (type & 0x07);
if (nons)
*nons = 0;
/* Find NSEC record that proves name doesn't exist */
for (i = 0; i < nsec_count; i++)
@@ -1332,6 +1465,10 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
rdlen -= p - psave;
/* rdlen is now length of type map, and p points to it */
/* If we can prove that there's no NS record, return that information. */
if (nons && rdlen >= 2 && p[0] == 0 && (p[2] & (0x80 >> T_NS)) == 0)
*nons = 1;
while (rdlen >= 2)
{
if (!CHECK_LEN(header, p, plen, rdlen))
@@ -1432,14 +1569,98 @@ static int base32_decode(char *in, unsigned char *out)
return p - out;
}
static int check_nsec3_coverage(struct dns_header *header, size_t plen, int digest_len, unsigned char *digest, int type,
char *workspace1, char *workspace2, unsigned char **nsecs, int nsec_count, int *nons)
{
int i, hash_len, salt_len, base32_len, rdlen;
unsigned char *p, *psave;
for (i = 0; i < nsec_count; i++)
if ((p = nsecs[i]))
{
if (!extract_name(header, plen, &p, workspace1, 1, 0) ||
!(base32_len = base32_decode(workspace1, (unsigned char *)workspace2)))
return 0;
p += 8; /* class, type, TTL */
GETSHORT(rdlen, p);
psave = p;
p += 4; /* algo, flags, iterations */
salt_len = *p++; /* salt_len */
p += salt_len; /* salt */
hash_len = *p++; /* p now points to next hashed name */
if (!CHECK_LEN(header, p, plen, hash_len))
return 0;
if (digest_len == base32_len && hash_len == base32_len)
{
int rc = memcmp(workspace2, digest, digest_len);
if (rc == 0)
{
/* We found an NSEC3 whose hashed name exactly matches the query, so
we just need to check the type map. p points to the RR data for the record. */
int offset = (type & 0xff) >> 3;
int mask = 0x80 >> (type & 0x07);
p += hash_len; /* skip next-domain hash */
rdlen -= p - psave;
if (!CHECK_LEN(header, p, plen, rdlen))
return 0;
/* If we can prove that there's no NS record, return that information. */
if (nons && rdlen >= 2 && p[0] == 0 && (p[2] & (0x80 >> T_NS)) == 0)
*nons = 1;
while (rdlen >= 2)
{
if (p[0] == type >> 8)
{
/* Does the NSEC3 say our type exists? */
if (offset < p[1] && (p[offset+2] & mask) != 0)
return STAT_BOGUS;
break; /* finshed checking */
}
rdlen -= p[1];
p += p[1];
}
return 1;
}
else if (rc <= 0)
{
/* 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 */
if (memcmp(p, digest, digest_len) > 0 || memcmp(workspace2, p, digest_len) > 0)
return 1;
}
else
{
/* wrap around case, name falls between start and next domain name */
if (memcmp(workspace2, p, digest_len) > 0 && memcmp(p, digest, digest_len) > 0)
return 1;
}
}
}
return 0;
}
static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, unsigned char **nsecs, int nsec_count,
char *workspace1, char *workspace2, char *name, int type)
char *workspace1, char *workspace2, char *name, int type, char *wildname, int *nons)
{
unsigned char *salt, *p, *digest;
int digest_len, i, iterations, salt_len, hash_len, base32_len, algo = 0;
int digest_len, i, iterations, salt_len, base32_len, algo = 0;
struct nettle_hash const *hash;
char *closest_encloser, *next_closest, *wildcard;
if (nons)
*nons = 0;
/* Look though the NSEC3 records to find the first one with
an algorithm we support (currently only algo == 1).
@@ -1509,7 +1730,14 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
if (!(hash = hash_find("sha1")))
return STAT_BOGUS;
/* Now, we need the "closest encloser NSEC3" */
if ((digest_len = hash_name(name, &digest, hash, salt, salt_len, iterations)) == 0)
return STAT_BOGUS;
if (check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count, nons))
return STAT_SECURE;
/* Can't find an NSEC3 which covers the name directly, we need the "closest encloser NSEC3"
or an answer inferred from a wildcard record. */
closest_encloser = name;
next_closest = NULL;
@@ -1518,6 +1746,9 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
if (*closest_encloser == '.')
closest_encloser++;
if (wildname && hostname_isequal(closest_encloser, wildname))
break;
if ((digest_len = hash_name(closest_encloser, &digest, hash, salt, salt_len, iterations)) == 0)
return STAT_BOGUS;
@@ -1540,132 +1771,39 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
}
while ((closest_encloser = strchr(closest_encloser, '.')));
/* No usable NSEC3s */
if (i == nsec_count)
if (!closest_encloser)
return STAT_BOGUS;
if (!next_closest)
{
/* We found an NSEC3 whose hashed name exactly matches the query, so
Now we just need to check the type map. p points to the RR data for the record. */
int rdlen;
unsigned char *psave;
int offset = (type & 0xff) >> 3;
int mask = 0x80 >> (type & 0x07);
p += 8; /* class, type, TTL */
GETSHORT(rdlen, p);
psave = p;
p += 5 + salt_len; /* algo, flags, iterations, salt_len, salt */
hash_len = *p++;
if (!CHECK_LEN(header, p, plen, hash_len))
return STAT_BOGUS; /* bad packet */
p += hash_len;
rdlen -= p - psave;
while (rdlen >= 2)
{
if (!CHECK_LEN(header, p, plen, rdlen))
return STAT_BOGUS;
if (p[0] == type >> 8)
{
/* Does the NSEC3 say our type exists? */
if (offset < p[1] && (p[offset+2] & mask) != 0)
return STAT_BOGUS;
break; /* finshed checking */
}
rdlen -= p[1];
p += p[1];
}
return STAT_SECURE;
}
/* Look for NSEC3 that proves the non-existence of the next-closest encloser */
if ((digest_len = hash_name(next_closest, &digest, hash, salt, salt_len, iterations)) == 0)
return STAT_BOGUS;
for (i = 0; i < nsec_count; i++)
if ((p = nsecs[i]))
{
if (!extract_name(header, plen, &p, workspace1, 1, 0) ||
!(base32_len = base32_decode(workspace1, (unsigned char *)workspace2)))
return STAT_BOGUS;
p += 15 + salt_len; /* class, type, TTL, rdlen, algo, flags, iterations, salt_len, salt */
hash_len = *p++; /* p now points to next hashed name */
if (!CHECK_LEN(header, p, plen, hash_len))
return STAT_BOGUS;
if (digest_len == base32_len && hash_len == base32_len)
{
if (memcmp(workspace2, digest, digest_len) <= 0)
{
/* 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 */
if (memcmp(p, digest, digest_len) > 0 || memcmp(workspace2, p, digest_len) > 0)
return STAT_SECURE;
}
else
{
/* wrap around case, name falls between start and next domain name */
if (memcmp(workspace2, p, digest_len) > 0 && memcmp(p, digest, digest_len) > 0)
return STAT_SECURE;
}
}
}
if (!check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count, NULL))
return STAT_BOGUS;
/* Finally, check that there's no seat of wildcard synthesis */
if (!(wildcard = strchr(next_closest, '.')) || wildcard == next_closest)
return STAT_BOGUS;
if (!wildname)
{
if (!(wildcard = strchr(next_closest, '.')) || wildcard == next_closest)
return STAT_BOGUS;
wildcard--;
*wildcard = '*';
if ((digest_len = hash_name(wildcard, &digest, hash, salt, salt_len, iterations)) == 0)
return STAT_BOGUS;
if (!check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count, NULL))
return STAT_BOGUS;
}
wildcard--;
*wildcard = '*';
if ((digest_len = hash_name(wildcard, &digest, hash, salt, salt_len, iterations)) == 0)
return STAT_BOGUS;
for (i = 0; i < nsec_count; i++)
if ((p = nsecs[i]))
{
if (!extract_name(header, plen, &p, workspace1, 1, 0) ||
!(base32_len = base32_decode(workspace1, (unsigned char *)workspace2)))
return STAT_BOGUS;
p += 15 + salt_len; /* class, type, TTL, rdlen, algo, flags, iterations, salt_len, salt */
hash_len = *p++; /* p now points to next hashed name */
if (!CHECK_LEN(header, p, plen, hash_len))
return STAT_BOGUS;
if (digest_len == base32_len && hash_len == base32_len)
{
if (memcmp(workspace2, digest, digest_len) <= 0)
{
/* 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 */
if (memcmp(p, digest, digest_len) > 0 || memcmp(workspace2, p, digest_len) > 0)
return STAT_SECURE;
}
else
{
/* wrap around case, name falls between start and next domain name */
if (memcmp(workspace2, p, digest_len) > 0 && memcmp(p, digest, digest_len) > 0)
return STAT_SECURE;
}
}
}
return STAT_BOGUS;
return STAT_SECURE;
}
/* Validate all the RRsets in the answer and authority sections of the reply (4035:3.2.3) */
/* Returns are the same as validate_rrset, plus the class if the missing key is in *class */
int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class, int *neganswer)
int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname,
int *class, int *neganswer, int *nons)
{
unsigned char *ans_start, *qname, *p1, *p2, **nsecs;
int type1, class1, rdlen1, type2, class2, rdlen2, qclass, qtype;
@@ -1781,11 +1919,15 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
struct all_addr a;
struct blockdata *key;
struct crec *crecp;
rc = validate_rrset(now, header, plen, class1, type1, name, keyname, NULL, 0, 0, 0);
char *wildname;
int have_wildcard = 0;
rc = validate_rrset(now, header, plen, class1, type1, name, keyname, &wildname, NULL, 0, 0, 0);
if (rc == STAT_SECURE_WILDCARD)
{
have_wildcard = 1;
/* An attacker replay a wildcard answer with a different
answer and overlay a genuine RR. To prove this
hasn't happened, the answer must prove that
@@ -1794,10 +1936,11 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
return STAT_BOGUS; /* No NSECs or bad packet */
if (nsec_type == T_NSEC)
rc = prove_non_existence_nsec(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, type1);
rc = prove_non_existence_nsec(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, type1, NULL);
else
rc = prove_non_existence_nsec3(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, type1);
rc = prove_non_existence_nsec3(header, plen, nsecs, nsec_count, daemon->workspacename,
keyname, name, type1, wildname, NULL);
if (rc != STAT_SECURE)
return rc;
}
@@ -1847,7 +1990,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
else
{
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.keydata = key;
crecp->addr.ds.algo = algo;
@@ -1875,7 +2018,11 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
p2 += 13; /* labels, orig_ttl, expiration, inception */
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)))
blockdata_free(key);
@@ -1913,16 +2060,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 */
/* 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)))
return STAT_BOGUS; /* No NSECs */
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 */
if (!extract_name(header, plen, &qname, name, 1, 0))
return STAT_BOGUS;
if (nsec_type == T_NSEC)
return prove_non_existence_nsec(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, qtype);
return prove_non_existence_nsec(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, qtype, nons);
else
return prove_non_existence_nsec3(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, qtype);
return prove_non_existence_nsec3(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, qtype, NULL, nons);
}
/* Chase the CNAME chain in the packet until the first record which _doesn't validate.
@@ -1969,7 +2117,7 @@ int dnssec_chase_cname(time_t now, struct dns_header *header, size_t plen, char
return STAT_INSECURE;
/* validate CNAME chain, return if insecure or need more data */
rc = validate_rrset(now, header, plen, class, type, name, keyname, NULL, 0, 0, 0);
rc = validate_rrset(now, header, plen, class, type, name, keyname, NULL, NULL, 0, 0, 0);
if (rc != STAT_SECURE)
{
if (rc == STAT_NO_SIG)
@@ -2020,10 +2168,10 @@ size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, i
char *types = querystr("dnssec-query", type);
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
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
header->qdcount = htons(1);
+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
it under the terms of the GNU General Public License as published by
+340 -209
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
it under the terms of the GNU General Public License as published by
@@ -26,8 +26,9 @@ static void free_frec(struct frec *f);
#ifdef HAVE_DNSSEC
static int tcp_key_recurse(time_t now, int status, struct dns_header *header, size_t n,
int class, char *name, char *keyname, struct server *server, int *keycount);
static int do_check_sign(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class);
static int send_check_sign(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname);
static int do_check_sign(struct frec *forward, int status, time_t now, char *name, char *keyname);
static int send_check_sign(struct frec *forward, time_t now, struct dns_header *header, size_t plen,
char *name, char *keyname);
#endif
@@ -102,15 +103,11 @@ int send_from(int fd, int nowild, char *packet, size_t len,
#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));
return 0;
}
@@ -278,10 +275,10 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
plen = forward->stash_len;
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
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
if (forward->sentto->sfd)
@@ -296,9 +293,9 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
fd = forward->rfd4->fd;
}
while (sendto(fd, (char *)header, plen, 0,
&forward->sentto->addr.sa,
sa_len(&forward->sentto->addr)) == -1 && retry_send());
while (retry_send( sendto(fd, (char *)header, plen, 0,
&forward->sentto->addr.sa,
sa_len(&forward->sentto->addr))));
return 1;
}
@@ -388,6 +385,9 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
struct server *firstsentto = start;
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))
plen = add_mac(header, plen, ((char *) header) + daemon->packet_buff_sz, &forward->source);
@@ -465,14 +465,12 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
#endif
}
if (sendto(fd, (char *)header, plen, 0,
&start->addr.sa,
sa_len(&start->addr)) == -1)
{
if (retry_send())
continue;
}
else
if (retry_send(sendto(fd, (char *)header, plen, 0,
&start->addr.sa,
sa_len(&start->addr))))
continue;
if (errno == 0)
{
/* Keep info in case we want to re-send this packet */
daemon->srv_save = start;
@@ -523,7 +521,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,
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;
char **sets = 0;
@@ -531,7 +530,8 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
size_t plen;
(void)ad_reqd;
(void) do_bit;
(void)do_bit;
(void)bogusanswer;
#ifdef HAVE_IPSET
if (daemon->ipsets && extract_request(header, n, daemon->namebuff, NULL))
@@ -636,7 +636,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
}
#ifdef HAVE_DNSSEC
if (no_cache && !(header->hb4 & HB4_CD))
if (bogusanswer && !(header->hb4 & HB4_CD))
{
if (!option_bool(OPT_DNSSEC_DEBUG))
{
@@ -665,6 +665,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
header->ancount = htons(0);
header->nscount = htons(0);
header->arcount = htons(0);
header->hb3 &= ~HB3_TC;
}
/* the bogus-nxdomain stuff, doctor and NXDOMAIN->NODATA munging can all elide
@@ -724,7 +725,16 @@ void reply_query(int fd, int family, time_t now)
if (!(forward = lookup_frec(ntohs(header->id), hash)))
return;
if ((RCODE(header) == SERVFAIL || RCODE(header) == REFUSED) &&
/* 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 &&
check_for_ignored_address(header, n, daemon->ignore_addr))
return;
if (RCODE(header) == REFUSED &&
!option_bool(OPT_ORDER) &&
forward->forwardall == 0)
/* for broken servers, attempt to send to another one. */
@@ -778,7 +788,7 @@ void reply_query(int fd, int family, time_t now)
everything is broken */
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))
check_rebind = !(forward->flags & FREC_NOREBIND);
@@ -811,18 +821,28 @@ void reply_query(int fd, int family, time_t now)
else if (forward->flags & FREC_DS_QUERY)
{
status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
/* Provably no DS, everything below is insecure, even if signatures are offered */
if (status == STAT_NO_DS)
status = STAT_INSECURE;
/* 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_NS)
status = STAT_BOGUS;
}
else if (forward->flags & FREC_CHECK_NOSIGN)
status = do_check_sign(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_NEED_KEY)
status = do_check_sign(forward, status, now, daemon->namebuff, daemon->keyname);
}
else
{
status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class, NULL);
status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class, NULL, NULL);
if (status == STAT_NO_SIG)
{
if (option_bool(OPT_DNSSEC_NO_SIGN))
status = send_check_sign(now, header, n, daemon->namebuff, daemon->keyname);
status = send_check_sign(forward, now, header, n, daemon->namebuff, daemon->keyname);
else
status = STAT_INSECURE;
}
@@ -857,6 +877,7 @@ void reply_query(int fd, int family, time_t now)
new->blocking_query = NULL;
new->sentto = server;
new->rfd4 = NULL;
new->orig_domain = NULL;
#ifdef HAVE_IPV6
new->rfd6 = NULL;
#endif
@@ -885,7 +906,9 @@ void reply_query(int fd, int family, time_t now)
new->new_id = get_id();
header->id = htons(new->new_id);
/* Save query for retransmission */
new->stash = blockdata_alloc((char *)header, nn);
if (!(new->stash = blockdata_alloc((char *)header, nn)))
return;
new->stash_len = nn;
/* Don't resend this. */
@@ -912,7 +935,9 @@ void reply_query(int fd, int family, time_t now)
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++;
}
@@ -942,18 +967,28 @@ void reply_query(int fd, int family, time_t now)
else if (forward->flags & FREC_DS_QUERY)
{
status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
/* Provably no DS, everything below is insecure, even if signatures are offered */
if (status == STAT_NO_DS)
status = STAT_INSECURE;
/* 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_NS)
status = STAT_BOGUS;
}
else if (forward->flags & FREC_CHECK_NOSIGN)
status = do_check_sign(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_NEED_KEY)
status = do_check_sign(forward, status, now, daemon->namebuff, daemon->keyname);
}
else
{
status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class, NULL);
status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class, NULL, NULL);
if (status == STAT_NO_SIG)
{
if (option_bool(OPT_DNSSEC_NO_SIGN))
status = send_check_sign(now, header, n, daemon->namebuff, daemon->keyname);
status = send_check_sign(forward, now, header, n, daemon->namebuff, daemon->keyname);
else
status = STAT_INSECURE;
}
@@ -964,26 +999,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)
header->hb3 |= HB3_TC;
else
{
char *result;
char *result, *domain = "result";
if (forward->work_counter == 0)
result = "ABANDONED";
{
result = "ABANDONED";
status = STAT_BOGUS;
}
else
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;
log_query(F_KEYTAG | F_SECSTAT, domain, NULL, result);
}
no_cache_dnssec = 0;
if (status == STAT_SECURE)
cache_secure = 1;
else if (status == STAT_BOGUS)
no_cache_dnssec = 1;
{
no_cache_dnssec = 1;
bogusanswer = 1;
}
}
#endif
@@ -993,7 +1046,7 @@ void reply_query(int fd, int family, time_t now)
else
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_ADDED_PHEADER, forward->flags & FREC_HAS_SUBNET, &forward->source)))
{
@@ -1048,7 +1101,7 @@ void receive_query(struct listener *listen, time_t now)
/* packet buffer overwritten */
daemon->srv_save = NULL;
dst_addr_4.s_addr = 0;
dst_addr_4.s_addr = dst_addr.addr.addr4.s_addr = 0;
netmask.s_addr = 0;
if (option_bool(OPT_NOWILD) && listen->iface)
@@ -1057,7 +1110,7 @@ void receive_query(struct listener *listen, time_t now)
if (listen->family == AF_INET)
{
dst_addr_4 = listen->iface->addr.in.sin_addr;
dst_addr_4 = dst_addr.addr.addr4 = listen->iface->addr.in.sin_addr;
netmask = listen->iface->netmask;
}
}
@@ -1242,6 +1295,11 @@ void receive_query(struct listener *listen, time_t now)
dst_addr_4.s_addr = 0;
}
}
/* 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))
{
@@ -1315,73 +1373,83 @@ void receive_query(struct listener *listen, time_t now)
/* UDP: we've got an unsigned answer, return STAT_INSECURE if we can prove there's no DS
and therefore the answer shouldn't be signed, or STAT_BOGUS if it should be, or
STAT_NEED_DS_NEG and keyname if we need to do the query. */
static int send_check_sign(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname)
static int send_check_sign(struct frec *forward, time_t now, struct dns_header *header, size_t plen,
char *name, char *keyname)
{
struct crec *crecp;
char *name_start = name;
int status = dnssec_chase_cname(now, header, plen, name, keyname);
if (status != STAT_INSECURE)
return status;
/* Store the domain we're trying to check. */
forward->name_start = strlen(name);
forward->name_len = forward->name_start + 1;
if (!(forward->orig_domain = blockdata_alloc(name, forward->name_len)))
return STAT_BOGUS;
return do_check_sign(forward, 0, now, name, keyname);
}
/* We either have a a reply (header non-NULL, or we need to start by looking in the cache */
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. */
while (forward->dependent)
forward = forward->dependent;
blockdata_retrieve(forward->orig_domain, forward->name_len, name);
while (1)
{
crecp = cache_find_by_name(NULL, name_start, now, F_DS);
if (crecp && (crecp->flags & F_DNSSECOK))
return (crecp->flags & F_NEG) ? STAT_INSECURE : STAT_BOGUS;
if (crecp && (crecp->flags & F_NEG) && (name_start = strchr(name_start, '.')))
{
name_start++; /* chop a label off and try again */
continue;
}
char *p;
/* Reached the root */
if (!name_start)
if (status == 0)
{
struct crec *crecp;
/* Haven't received answer, see if in cache */
if (!(crecp = cache_find_by_name(NULL, &name[forward->name_start], now, F_DS)))
{
/* put name of DS record we're missing into keyname */
strcpy(keyname, &name[forward->name_start]);
/* and wait for reply to arrive */
return STAT_NEED_DS_NEG;
}
/* F_DNSSECOK misused in DS cache records to non-existance of NS record */
if (!(crecp->flags & F_NEG))
status = STAT_SECURE;
else if (crecp->flags & F_DNSSECOK)
status = STAT_NO_DS;
else
status = STAT_NO_NS;
}
/* Have entered non-signed part of DNS tree. */
if (status == STAT_NO_DS)
return STAT_INSECURE;
if (status == STAT_BOGUS)
return STAT_BOGUS;
strcpy(keyname, name_start);
return STAT_NEED_DS_NEG;
}
}
/* 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.
If we've already tried the whole name, then fail */
/* Got answer to DS query from send_check_sign, check for proven non-existence, or make the next DS query to try. */
static int do_check_sign(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class)
{
char *name_start;
unsigned char *p;
int status;
/* In this case only, a SERVFAIL reply allows us to continue up the tree, looking for a
suitable NSEC reply to DS queries. */
if (RCODE(header) != SERVFAIL)
{
status = dnssec_validate_ds(now, header, plen, name, keyname, class);
if (forward->name_start == 0)
return STAT_BOGUS;
if (status != STAT_INSECURE)
{
if (status == STAT_NO_DS)
status = STAT_INSECURE;
return status;
}
for (p = &name[forward->name_start-2]; (*p != '.') && (p != name); p--);
if (p != name)
p++;
forward->name_start = p - name;
status = 0; /* force to cache when we iterate. */
}
p = (unsigned char *)(header+1);
if (extract_name(header, plen, &p, name, 1, 4) &&
(name_start = strchr(name, '.')))
{
name_start++; /* chop a label off and try again */
strcpy(keyname, name_start);
return STAT_NEED_DS_NEG;
}
return STAT_BOGUS;
}
/* 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
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,
@@ -1390,9 +1458,10 @@ static int tcp_check_for_unsigned_zone(time_t now, struct dns_header *header, s
size_t m;
unsigned char *packet, *payload;
u16 *length;
unsigned char *p = (unsigned char *)(header+1);
int status;
char *name_start = name;
int status, name_len;
struct blockdata *block;
char *name_start;
/* Get first insecure entry in CNAME chain */
status = tcp_key_recurse(now, STAT_CHASE_CNAME, header, plen, class, name, keyname, server, keycount);
@@ -1405,95 +1474,113 @@ static int tcp_check_for_unsigned_zone(time_t now, struct dns_header *header, s
payload = &packet[2];
header = (struct dns_header *)payload;
length = (u16 *)packet;
/* Stash the name away, since the buffer will be trashed when we recurse */
name_len = strlen(name) + 1;
name_start = name + name_len - 1;
if (!(block = blockdata_alloc(name, name_len)))
{
free(packet);
return STAT_BOGUS;
}
while (1)
{
unsigned char *newhash, hash[HASH_SIZE];
unsigned char c1, c2;
struct crec *crecp = cache_find_by_name(NULL, name_start, now, F_DS);
struct crec *crecp;
if (--(*keycount) == 0)
{
free(packet);
blockdata_free(block);
return STAT_BOGUS;
}
if (crecp && (crecp->flags & F_DNSSECOK))
{
free(packet);
return (crecp->flags & F_NEG) ? STAT_INSECURE : STAT_BOGUS;
while ((crecp = cache_find_by_name(NULL, name_start, now, F_DS)))
{
if ((crecp->flags & F_NEG) && (crecp->flags & F_DNSSECOK))
{
/* Found a secure denial of DS - delegation is indeed insecure */
free(packet);
blockdata_free(block);
return STAT_INSECURE;
}
/* Here, either there's a secure DS, or no NS and no DS, and therefore no delegation.
Add another label and continue. */
if (name_start == name)
{
free(packet);
blockdata_free(block);
return STAT_BOGUS; /* run out of labels */
}
name_start -= 2;
while (*name_start != '.' && name_start != name)
name_start--;
if (name_start != name)
name_start++;
}
/* If we have cached insecurely that a DS doesn't exist,
ise that is a hit for where to start looking for the secure one */
if (crecp && (crecp->flags & F_NEG) && (name_start = strchr(name_start, '.')))
{
name_start++; /* chop a label off and try again */
continue;
}
/* reached the root */
if (!name_start)
{
free(packet);
return STAT_BOGUS;
}
/* 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);
/* We rely on the question section coming back unchanged, ensure it is with the hash. */
if ((newhash = hash_questions(header, (unsigned int)m, name)))
*length = htons(m);
if (read_write(server->tcpfd, packet, m + sizeof(u16), 0) &&
read_write(server->tcpfd, &c1, 1, 1) &&
read_write(server->tcpfd, &c2, 1, 1) &&
read_write(server->tcpfd, payload, (c1 << 8) | c2, 1))
{
memcpy(hash, newhash, HASH_SIZE);
*length = htons(m);
m = (c1 << 8) | c2;
if (read_write(server->tcpfd, packet, m + sizeof(u16), 0) &&
read_write(server->tcpfd, &c1, 1, 1) &&
read_write(server->tcpfd, &c2, 1, 1) &&
read_write(server->tcpfd, payload, (c1 << 8) | c2, 1))
/* Note this trashes all three name workspaces */
status = tcp_key_recurse(now, STAT_NEED_DS_NEG, header, m, class, name, keyname, server, keycount);
if (status == STAT_NO_DS)
{
m = (c1 << 8) | c2;
newhash = hash_questions(header, (unsigned int)m, name);
if (newhash && memcmp(hash, newhash, HASH_SIZE) == 0)
{
/* In this case only, a SERVFAIL reply allows us to continue up the tree, looking for a
suitable NSEC reply to DS queries. */
if (RCODE(header) == SERVFAIL)
status = STAT_INSECURE;
else
/* Note this trashes all three name workspaces */
status = tcp_key_recurse(now, STAT_NEED_DS_NEG, header, m, class, name, keyname, server, keycount);
/* We've found a DS which proves the bit of the DNS where the
original query is, is unsigned, so the answer is OK,
if unvalidated. */
if (status == STAT_NO_DS)
{
free(packet);
return STAT_INSECURE;
}
/* No DS, not got to DNSSEC-land yet, go up. */
if (status == STAT_INSECURE)
{
p = (unsigned char *)(header+1);
if (extract_name(header, plen, &p, name, 1, 4) &&
(name_start = strchr(name, '.')))
{
name_start++; /* chop a label off and try again */
continue;
}
}
}
/* Found a secure denial of DS - delegation is indeed insecure */
free(packet);
blockdata_free(block);
return STAT_INSECURE;
}
if (status == STAT_BOGUS)
{
free(packet);
blockdata_free(block);
return STAT_BOGUS;
}
/* Here, either there's a secure DS, or no NS and no DS, and therefore no delegation.
Add another label and continue. */
/* Get name we're checking back. */
blockdata_retrieve(block, name_len, name);
if (name_start == name)
{
free(packet);
blockdata_free(block);
return STAT_BOGUS; /* run out of labels */
}
name_start -= 2;
while (*name_start != '.' && name_start != name)
name_start--;
if (name_start != name)
name_start++;
}
else
{
/* IO failure */
free(packet);
blockdata_free(block);
return STAT_BOGUS; /* run out of labels */
}
free(packet);
return STAT_BOGUS;
}
}
@@ -1512,14 +1599,19 @@ 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)
{
new_status = dnssec_validate_ds(now, header, n, name, keyname, class);
if (status == STAT_NEED_DS && new_status == STAT_NO_DS)
new_status = STAT_INSECURE;
if (status == STAT_NEED_DS)
{
if (new_status == STAT_NO_DS)
new_status = STAT_INSECURE_DS;
else if (new_status == STAT_NO_NS)
new_status = STAT_BOGUS;
}
}
else if (status == STAT_CHASE_CNAME)
new_status = dnssec_chase_cname(now, header, n, name, keyname);
else
{
new_status = dnssec_validate_reply(now, header, n, name, keyname, &class, NULL);
new_status = dnssec_validate_reply(now, header, n, name, keyname, &class, NULL, NULL);
if (new_status == STAT_NO_SIG)
{
@@ -1572,14 +1664,19 @@ 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)
{
new_status = dnssec_validate_ds(now, header, n, name, keyname, class);
if (status == STAT_NEED_DS && new_status == STAT_NO_DS)
new_status = STAT_INSECURE; /* Validated no DS */
if (status == STAT_NEED_DS)
{
if (new_status == STAT_NO_DS)
new_status = STAT_INSECURE_DS;
else if (new_status == STAT_NO_NS)
new_status = STAT_BOGUS; /* Validated no DS */
}
}
else if (status == STAT_CHASE_CNAME)
new_status = dnssec_chase_cname(now, header, n, name, keyname);
else
{
new_status = dnssec_validate_reply(now, header, n, name, keyname, &class, NULL);
new_status = dnssec_validate_reply(now, header, n, name, keyname, &class, NULL, NULL);
if (new_status == STAT_NO_SIG)
{
@@ -1594,7 +1691,7 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
goto another_tcp_key;
}
}
free(packet);
}
return new_status;
@@ -1615,7 +1712,7 @@ unsigned char *tcp_request(int confd, time_t now,
int local_auth = 0;
#endif
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;
unsigned short qtype;
unsigned int gotname;
@@ -1630,7 +1727,8 @@ unsigned char *tcp_request(int confd, time_t now,
struct in_addr dst_addr_4;
union mysockaddr peer_addr;
socklen_t peer_len = sizeof(union mysockaddr);
int query_count = 0;
if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) == -1)
return packet;
@@ -1667,7 +1765,8 @@ unsigned char *tcp_request(int confd, time_t now,
while (1)
{
if (!packet ||
if (query_count == TCP_MAX_QUERIES ||
!packet ||
!read_write(confd, &c1, 1, 1) || !read_write(confd, &c2, 1, 1) ||
!(size = c1 << 8 | c2) ||
!read_write(confd, payload, size, 1))
@@ -1676,6 +1775,13 @@ unsigned char *tcp_request(int confd, time_t now,
if (size < (int)sizeof(struct dns_header))
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;
/* save state of "cd" flag in query */
@@ -1796,6 +1902,24 @@ unsigned char *tcp_request(int confd, time_t now,
if ((last_server->tcpfd = socket(last_server->addr.sa.sa_family, SOCK_STREAM, 0)) == -1)
continue;
#ifdef HAVE_CONNTRACK
/* Copy connection mark of incoming query to outgoing connection. */
if (option_bool(OPT_CONNTRACK))
{
unsigned int mark;
struct all_addr local;
#ifdef HAVE_IPV6
if (local_addr->sa.sa_family == AF_INET6)
local.addr.addr6 = local_addr->in6.sin6_addr;
else
#endif
local.addr.addr4 = local_addr->in.sin_addr;
if (get_incoming_mark(&peer_addr, &local, 1, &mark))
setsockopt(last_server->tcpfd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
}
#endif
if ((!local_bind(last_server->tcpfd, &last_server->source_addr, last_server->interface, 1) ||
connect(last_server->tcpfd, &last_server->addr.sa, sa_len(&last_server->addr)) == -1))
{
@@ -1820,24 +1944,6 @@ unsigned char *tcp_request(int confd, time_t now,
size = new_size;
}
#endif
#ifdef HAVE_CONNTRACK
/* Copy connection mark of incoming query to outgoing connection. */
if (option_bool(OPT_CONNTRACK))
{
unsigned int mark;
struct all_addr local;
#ifdef HAVE_IPV6
if (local_addr->sa.sa_family == AF_INET6)
local.addr.addr6 = local_addr->in6.sin6_addr;
else
#endif
local.addr.addr4 = local_addr->in.sin_addr;
if (get_incoming_mark(&peer_addr, &local, 1, &mark))
setsockopt(last_server->tcpfd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
}
#endif
}
*length = htons(size);
@@ -1872,18 +1978,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 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)
result = "ABANDONED";
{
result = "ABANDONED";
status = STAT_BOGUS;
}
else
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)
no_cache_dnssec = 1;
{
no_cache_dnssec = 1;
bogusanswer = 1;
}
if (status == STAT_SECURE)
cache_secure = 1;
}
@@ -1917,7 +2040,7 @@ unsigned char *tcp_request(int confd, time_t now,
#endif
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, bogusanswer,
cache_secure, ad_question, do_bit, added_pheader, check_subnet, &peer_addr);
break;
@@ -1957,6 +2080,7 @@ static struct frec *allocate_frec(time_t now)
f->dependent = NULL;
f->blocking_query = NULL;
f->stash = NULL;
f->orig_domain = NULL;
#endif
daemon->frec_list = f;
}
@@ -2025,6 +2149,12 @@ static void free_frec(struct frec *f)
f->stash = NULL;
}
if (f->orig_domain)
{
blockdata_free(f->orig_domain);
f->orig_domain = NULL;
}
/* Anything we're waiting on is pointless now, too */
if (f->blocking_query)
free_frec(f->blocking_query);
@@ -2154,8 +2284,9 @@ void resend_query()
else
return;
while(sendto(fd, daemon->packet, daemon->packet_len, 0,
&daemon->srv_save->addr.sa, sa_len(&daemon->srv_save->addr)) == -1 && retry_send());
while(retry_send(sendto(fd, daemon->packet, daemon->packet_len, 0,
&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
it under the terms of the GNU General Public License as published by
+238
View File
@@ -0,0 +1,238 @@
/* 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"
#ifdef HAVE_INOTIFY
#include <sys/inotify.h>
/* the strategy is to set a inotify on the directories containing
resolv files, for any files in the directory which are close-write
or moved into the directory.
When either of those happen, we look to see if the file involved
is actually a resolv-file, and if so, call poll-resolv with
the "force" argument, to ensure it's read.
This adds one new error condition: the directories containing
all specified resolv-files must exist at start-up, even if the actual
files don't.
*/
static char *inotify_buffer;
#define INOTIFY_SZ (sizeof(struct inotify_event) + NAME_MAX + 1)
void inotify_dnsmasq_init()
{
struct resolvc *res;
inotify_buffer = safe_malloc(INOTIFY_SZ);
daemon->inotifyfd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
if (daemon->inotifyfd == -1)
die(_("failed to create inotify: %s"), NULL, EC_MISC);
for (res = daemon->resolv_files; res; res = res->next)
{
char *d = NULL, *path;
if (!(path = realpath(res->name, NULL)))
{
/* realpath will fail if the file doesn't exist, but
dnsmasq copes with missing files, so fall back
and assume that symlinks are not in use in that case. */
if (errno == ENOENT)
path = res->name;
else
die(_("cannot cannonicalise resolv-file %s: %s"), res->name, EC_MISC);
}
if ((d = strrchr(path, '/')))
{
*d = 0; /* make path just directory */
res->wd = inotify_add_watch(daemon->inotifyfd, path, IN_CLOSE_WRITE | IN_MOVED_TO);
res->file = d+1; /* pointer to filename */
*d = '/';
if (res->wd == -1 && errno == ENOENT)
die(_("directory %s for resolv-file is missing, cannot poll"), res->name, EC_MISC);
if (res->wd == -1)
die(_("failed to create inotify for %s: %s"), res->name, EC_MISC);
}
}
}
/* 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;
struct hostsfile *ah;
while (1)
{
int rc;
char *p;
struct resolvc *res;
struct inotify_event *in;
while ((rc = read(daemon->inotifyfd, inotify_buffer, INOTIFY_SZ)) == -1 && errno == EINTR);
if (rc <= 0)
break;
for (p = inotify_buffer; rc - (p - inotify_buffer) >= (int)sizeof(struct inotify_event); p += sizeof(struct inotify_event) + in->len)
{
in = (struct inotify_event*)p;
for (res = daemon->resolv_files; res; res = res->next)
if (res->wd == in->wd && in->len != 0 && strcmp(res->file, in->name) == 0)
hit = 1;
/* ignore emacs backups and dotfiles */
if (in->len == 0 ||
in->name[in->len - 1] == '~' ||
(in->name[0] == '#' && in->name[in->len - 1] == '#') ||
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;
}
#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
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];
uint8_t proto;
int addrsz = INADDRSZ;
ssize_t rc;
#ifdef HAVE_IPV6
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[0]->nla_len = (void *)buffer + NL_ALIGN(nlh->nlmsg_len) - (void *)nested[0];
while ((rc = sendto(ipset_sock, buffer, nlh->nlmsg_len, 0,
(struct sockaddr *)&snl, sizeof(snl))) == -1 && retry_send());
return rc;
while (retry_send(sendto(ipset_sock, buffer, nlh->nlmsg_len, 0,
(struct sockaddr *)&snl, sizeof(snl))));
return errno == 0 ? 0 : -1;
}
+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
it under the terms of the GNU General Public License as published by
+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
it under the terms of the GNU General Public License as published by
+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
it under the terms of the GNU General Public License as published by
@@ -45,8 +45,9 @@ void loop_send_probes()
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);
}
}
+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
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;
/* Don't block in recvfrom if send fails */
while((len = sendto(daemon->netlinkfd, (void *)&req, sizeof(req), 0,
(struct sockaddr *)&addr, sizeof(addr))) == -1 && retry_send());
if (len == -1)
while(retry_send(sendto(daemon->netlinkfd, (void *)&req, sizeof(req), 0,
(struct sockaddr *)&addr, sizeof(addr))));
if (errno != 0)
return 0;
while (1)
+11 -11
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
it under the terms of the GNU General Public License as published by
@@ -16,10 +16,6 @@
#include "dnsmasq.h"
#ifndef IN6_IS_ADDR_ULA
#define IN6_IS_ADDR_ULA(a) ((((__const uint32_t *) (a))[0] & htonl (0xfe00000)) == htonl (0xfc000000))
#endif
#ifdef HAVE_LINUX_NETWORK
int indextoname(int fd, int index, char *name)
@@ -240,7 +236,7 @@ struct iface_param {
};
static int iface_allowed(struct iface_param *param, int if_index, char *label,
union mysockaddr *addr, struct in_addr netmask, int prefixlen, int dad)
union mysockaddr *addr, struct in_addr netmask, int prefixlen, int iface_flags)
{
struct irec *iface;
int mtu = 0, loopback;
@@ -392,6 +388,10 @@ static int iface_allowed(struct iface_param *param, int if_index, char *label,
{
al->addr.addr.addr6 = addr->in6.sin6_addr;
al->flags = ADDRLIST_IPV6;
/* Privacy addresses and addresses still undergoing DAD and deprecated addresses
don't appear in forward queries, but will in reverse ones. */
if (!(iface_flags & IFACE_PERMANENT) || (iface_flags & (IFACE_DEPRECATED | IFACE_TENTATIVE)))
al->flags |= ADDRLIST_REVONLY;
}
#endif
}
@@ -403,7 +403,7 @@ static int iface_allowed(struct iface_param *param, int if_index, char *label,
for (iface = daemon->interfaces; iface; iface = iface->next)
if (sockaddr_isequal(&iface->addr, addr))
{
iface->dad = dad;
iface->dad = !!(iface_flags & IFACE_TENTATIVE);
iface->found = 1; /* for garbage collection */
return 1;
}
@@ -478,7 +478,7 @@ static int iface_allowed(struct iface_param *param, int if_index, char *label,
iface->dhcp_ok = dhcp_ok;
iface->dns_auth = auth_dns;
iface->mtu = mtu;
iface->dad = dad;
iface->dad = !!(iface_flags & IFACE_TENTATIVE);
iface->found = 1;
iface->done = iface->multicast_done = iface->warned = 0;
iface->index = if_index;
@@ -523,7 +523,7 @@ static int iface_allowed_v6(struct in6_addr *local, int prefix,
else
addr.in6.sin6_scope_id = 0;
return iface_allowed((struct iface_param *)vparam, if_index, NULL, &addr, netmask, prefix, !!(flags & IFACE_TENTATIVE));
return iface_allowed((struct iface_param *)vparam, if_index, NULL, &addr, netmask, prefix, flags);
}
#endif
@@ -1459,7 +1459,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))
{
@@ -1475,7 +1475,7 @@ void check_servers(void)
my_syslog(LOG_INFO, _("using local addresses only for %s %s"), s1, s2);
else if (serv->flags & SERV_USE_RESOLV)
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);
}
#ifdef HAVE_LOOP
+135 -31
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
it under the terms of the GNU General Public License as published by
@@ -147,6 +147,13 @@ struct myoption {
#define LOPT_LOCAL_SERVICE 335
#define LOPT_DNSSEC_TIME 336
#define LOPT_LOOP_DETECT 337
#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
static const struct option opts[] =
@@ -159,7 +166,7 @@ static const struct myoption opts[] =
{ "no-poll", 0, 0, 'n' },
{ "help", 0, 0, 'w' },
{ "no-daemon", 0, 0, 'd' },
{ "log-queries", 0, 0, 'q' },
{ "log-queries", 2, 0, 'q' },
{ "user", 2, 0, 'u' },
{ "group", 2, 0, 'g' },
{ "resolv-file", 2, 0, 'r' },
@@ -181,6 +188,7 @@ static const struct myoption opts[] =
{ "local-service", 0, 0, LOPT_LOCAL_SERVICE },
{ "bogus-priv", 0, 0, 'b' },
{ "bogus-nxdomain", 1, 0, 'B' },
{ "ignore-address", 1, 0, LOPT_IGNORE_ADDR },
{ "selfmx", 0, 0, 'e' },
{ "filterwin2k", 0, 0, 'f' },
{ "pid-file", 2, 0, 'x' },
@@ -196,6 +204,7 @@ static const struct myoption opts[] =
{ "local-ttl", 1, 0, 'T' },
{ "no-negcache", 0, 0, 'N' },
{ "addn-hosts", 1, 0, 'H' },
{ "hostsdir", 1, 0, LOPT_HOST_INOTIFY },
{ "query-port", 1, 0, 'Q' },
{ "except-interface", 1, 0, 'I' },
{ "no-dhcp-interface", 1, 0, '2' },
@@ -227,6 +236,7 @@ static const struct myoption opts[] =
{ "dhcp-ignore-names", 2, 0, LOPT_NO_NAMES },
{ "enable-tftp", 2, 0, LOPT_TFTP },
{ "tftp-secure", 0, 0, LOPT_SECURE },
{ "tftp-no-fail", 0, 0, LOPT_TFTP_NO_FAIL },
{ "tftp-unique-root", 0, 0, LOPT_APREF },
{ "tftp-root", 1, 0, LOPT_PREFIX },
{ "tftp-max", 1, 0, LOPT_TFTP_MAX },
@@ -244,6 +254,8 @@ static const struct myoption opts[] =
{ "interface-name", 1, 0, LOPT_INTNAME },
{ "dhcp-hostsfile", 1, 0, LOPT_DHCP_HOST },
{ "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 },
{ "tftp-port-range", 1, 0, LOPT_TFTPPORTS },
{ "stop-dns-rebind", 0, 0, LOPT_REBIND },
@@ -253,6 +265,7 @@ static const struct myoption opts[] =
{ "dhcp-broadcast", 2, 0, LOPT_BROADCAST },
{ "neg-ttl", 1, 0, LOPT_NEGTTL },
{ "max-ttl", 1, 0, LOPT_MAXTTL },
{ "min-cache-ttl", 1, 0, LOPT_MINCTTL },
{ "max-cache-ttl", 1, 0, LOPT_MAXCTTL },
{ "dhcp-alternate-port", 2, 0, LOPT_ALTPORT },
{ "dhcp-scriptuser", 1, 0, LOPT_SCRIPTUSR },
@@ -290,6 +303,7 @@ static const struct myoption opts[] =
{ "dnssec-debug", 0, 0, LOPT_DNSSEC_DEBUG },
{ "dnssec-check-unsigned", 0, 0, LOPT_DNSSEC_CHECK },
{ "dnssec-no-timecheck", 0, 0, LOPT_DNSSEC_TIME },
{ "dnssec-timestamp", 1, 0, LOPT_DNSSEC_STAMP },
#ifdef OPTION6_PREFIX_CLASS
{ "dhcp-prefix-class", 1, 0, LOPT_PREF_CLSS },
#endif
@@ -331,9 +345,12 @@ static struct {
{ '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_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 },
{ '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 },
{ 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) NOT to listen on.") , NULL },
{ 'j', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP user class to tag."), NULL },
@@ -355,7 +372,7 @@ static struct {
{ 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("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 },
{ '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 },
@@ -368,6 +385,8 @@ static struct {
{ '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_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_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 },
@@ -402,6 +421,7 @@ static struct {
{ LOPT_PREFIX, ARG_DUP, "<dir>[,<iface>]", gettext_noop("Export files by TFTP only from the specified subtree."), NULL },
{ LOPT_APREF, OPT_TFTP_APREF, NULL, gettext_noop("Add client IP address to tftp-root."), NULL },
{ LOPT_SECURE, OPT_TFTP_SECURE, NULL, gettext_noop("Allow access only to files owned by the user running dnsmasq."), NULL },
{ LOPT_TFTP_NO_FAIL, OPT_TFTP_NO_FAIL, NULL, gettext_noop("Do not terminate the service if TFTP directories are inaccessible."), NULL },
{ LOPT_TFTP_MAX, ARG_ONE, "<integer>", gettext_noop("Maximum number of conncurrent TFTP transfers (defaults to %s)."), "#" },
{ LOPT_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 },
@@ -448,6 +468,7 @@ static struct {
{ 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_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
{ LOPT_PREF_CLSS, ARG_DUP, "set:tag,<class>", gettext_noop("Specify DHCPv6 prefix class"), NULL },
#endif
@@ -457,6 +478,7 @@ static struct {
{ LOPT_QUIET_RA, OPT_QUIET_RA, NULL, gettext_noop("Do not log RA."), NULL },
{ LOPT_LOCAL_SERVICE, OPT_LOCAL_SERVICE, NULL, gettext_noop("Accept queries only from directly-connected networks"), NULL },
{ LOPT_LOOP_DETECT, OPT_LOOP_DETECT, NULL, gettext_noop("Detect and remove DNS forwarding loops"), NULL },
{ LOPT_IGNORE_ADDR, ARG_DUP, "<ipaddr>", gettext_noop("Ignore DNS responses containing ipaddr."), NULL },
{ 0, 0, NULL, NULL, NULL }
};
@@ -1474,22 +1496,25 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
for (arg = comma; arg; arg = comma)
{
comma = split(arg);
li = opt_malloc(sizeof(struct list));
if (*arg == '*')
if (strlen(arg) != 0)
{
li->next = match_suffix;
match_suffix = li;
/* Have to copy: buffer is overwritten */
li->suffix = opt_string_alloc(arg+1);
li = opt_malloc(sizeof(struct list));
if (*arg == '*')
{
li->next = match_suffix;
match_suffix = li;
/* Have to copy: buffer is overwritten */
li->suffix = opt_string_alloc(arg+1);
}
else
{
li->next = ignore_suffix;
ignore_suffix = li;
/* Have to copy: buffer is overwritten */
li->suffix = opt_string_alloc(arg);
}
}
else
{
li->next = ignore_suffix;
ignore_suffix = li;
/* Have to copy: buffer is overwritten */
li->suffix = opt_string_alloc(arg);
}
};
}
if (!(dir_stream = opendir(directory)))
die(_("cannot access directory %s: %s"), directory, EC_FILE);
@@ -1555,7 +1580,12 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
free(ignore_suffix->suffix);
free(ignore_suffix);
}
for(; match_suffix; match_suffix = li)
{
li = match_suffix->next;
free(match_suffix->suffix);
free(match_suffix);
}
break;
}
@@ -1694,9 +1724,12 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
break;
#endif /* HAVE_DHCP */
case LOPT_DHCP_HOST: /* --dhcp-hostfile */
case LOPT_DHCP_OPTS: /* --dhcp-optsfile */
case 'H': /* --addn-hosts */
case LOPT_DHCP_HOST: /* --dhcp-hostsfile */
case LOPT_DHCP_OPTS: /* --dhcp-optsfile */
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));
static unsigned int hosts_index = SRC_AH;
@@ -1718,6 +1751,18 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
new->next = daemon->dhcp_opts_file;
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;
}
@@ -1933,10 +1978,17 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
else
{
/* generate the equivalent of
local=/<domain>/
local=/xxx.yyy.zzz.in-addr.arpa/ */
struct server *serv = add_rev4(new->start, msize);
serv->flags |= SERV_NO_ADDR;
/* local=/<domain>/ */
serv = opt_malloc(sizeof(struct server));
memset(serv, 0, sizeof(struct server));
serv->domain = d;
serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
serv->next = daemon->servers;
daemon->servers = serv;
}
}
}
@@ -1970,10 +2022,17 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
else
{
/* generate the equivalent of
local=/<domain>/
local=/xxx.yyy.zzz.ip6.arpa/ */
struct server *serv = add_rev6(&new->start6, msize);
serv->flags |= SERV_NO_ADDR;
/* local=/<domain>/ */
serv = opt_malloc(sizeof(struct server));
memset(serv, 0, sizeof(struct server));
serv->domain = d;
serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
serv->next = daemon->servers;
daemon->servers = serv;
}
}
}
@@ -2097,14 +2156,23 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
break;
case 'B': /* --bogus-nxdomain */
{
case LOPT_IGNORE_ADDR: /* --ignore-address */
{
struct in_addr addr;
unhide_metas(arg);
if (arg && (inet_pton(AF_INET, arg, &addr) > 0))
{
struct bogus_addr *baddr = opt_malloc(sizeof(struct bogus_addr));
baddr->next = daemon->bogus_addr;
daemon->bogus_addr = baddr;
if (option == 'B')
{
baddr->next = daemon->bogus_addr;
daemon->bogus_addr = baddr;
}
else
{
baddr->next = daemon->ignore_addr;
daemon->ignore_addr = baddr;
}
baddr->addr = addr;
}
else
@@ -2219,8 +2287,6 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
{
if (!(newlist->flags & SERV_NO_REBIND))
newlist->flags |= SERV_NO_ADDR; /* no server */
if (newlist->flags & SERV_LITERAL_ADDRESS)
ret_err(gen_err);
}
else if (strcmp(arg, "#") == 0)
@@ -2387,6 +2453,12 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
ret_err(gen_err);
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 */
daemon->max_logs = LOG_MAX; /* default */
if (arg && !atoi_check(arg, &daemon->max_logs))
@@ -2416,6 +2488,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
case 'T': /* --local-ttl */
case LOPT_NEGTTL: /* --neg-ttl */
case LOPT_MAXTTL: /* --max-ttl */
case LOPT_MINCTTL: /* --min-cache-ttl */
case LOPT_MAXCTTL: /* --max-cache-ttl */
case LOPT_AUTHTTL: /* --auth-ttl */
{
@@ -2426,6 +2499,12 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
daemon->neg_ttl = (unsigned long)ttl;
else if (option == LOPT_MAXTTL)
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)
daemon->max_cache_ttl = (unsigned long)ttl;
else if (option == LOPT_AUTHTTL)
@@ -3792,6 +3871,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
}
#ifdef HAVE_DNSSEC
case LOPT_DNSSEC_STAMP:
daemon->timestamp_file = opt_string_alloc(arg);
break;
case LOPT_TRUST_ANCHOR:
{
struct ds_config *new = opt_malloc(sizeof(struct ds_config));
@@ -3990,6 +4073,20 @@ static void read_file(char *file, FILE *f, int hard_opt)
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)
{
FILE *f;
@@ -4087,7 +4184,7 @@ struct hostsfile *expand_filelist(struct hostsfile *list)
/* don't read this as a file */
ah->flags |= AH_INACTIVE;
if (!(dir_stream = opendir(ah->fname)))
my_syslog(LOG_ERR, _("cannot access directory %s: %s"),
ah->fname, strerror(errno));
@@ -4272,7 +4369,7 @@ void read_opts(int argc, char **argv, char *compile_opts)
{
char *buff = opt_malloc(MAXDNAME);
int option, conffile_opt = '7', testmode = 0;
char *arg, *conffile = CONFFILE;
char *arg, *conffile = NULL;
opterr = 0;
@@ -4387,7 +4484,14 @@ void read_opts(int argc, char **argv, char *compile_opts)
}
if (conffile)
one_file(conffile, conffile_opt);
{
one_file(conffile, conffile_opt);
free(conffile);
}
else
{
one_file(CONFFILE, conffile_opt);
}
/* port might not be known when the address is parsed - fill in here */
if (daemon->servers)
+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
it under the terms of the GNU General Public License as published by
+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
it under the terms of the GNU General Public License as published by
+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
it under the terms of the GNU General Public License as published by
@@ -479,8 +479,9 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
setsockopt(daemon->icmp6fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &iface, sizeof(iface));
}
while (sendto(daemon->icmp6fd, daemon->outpacket.iov_base, save_counter(0), 0,
(struct sockaddr *)&addr, sizeof(addr)) == -1 && retry_send());
while (retry_send(sendto(daemon->icmp6fd, daemon->outpacket.iov_base,
save_counter(0), 0, (struct sockaddr *)&addr,
sizeof(addr))));
}
+87 -19
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
it under the terms of the GNU General Public License as published by
@@ -20,7 +20,7 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
char *name, int isExtract, int extrabytes)
{
unsigned char *cp = (unsigned char *)name, *p = *pp, *p1 = NULL;
unsigned int j, l, hops = 0;
unsigned int j, l, namelen = 0, hops = 0;
int retvalue = 1;
if (isExtract)
@@ -94,9 +94,15 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
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)
/* output is \[x<hex>/siz]. which is digs+6/7/8 chars */
namelen += digs+6;
if (count > 9)
namelen++;
if (count > 99)
namelen++;
if (namelen+1 >= MAXDNAME)
return 0;
if (!CHECK_LEN(header, p, plen, (count-1)>>3))
return 0;
@@ -119,7 +125,8 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
}
else
{ /* label_type = 0 -> label. */
if (cp - (unsigned char *)name + l + 1 >= MAXDNAME)
namelen += l;
if (namelen+1 >= MAXDNAME)
return 0;
if (!CHECK_LEN(header, p, plen, l))
return 0;
@@ -128,8 +135,21 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
if (isExtract)
{
unsigned char c = *p;
if (isascii(c) && !iscntrl(c) && c != '.')
*cp++ = *p;
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID))
{
if (c == 0 || c == '.' || c == NAME_ESCAPE)
{
*cp++ = NAME_ESCAPE;
*cp++ = c+1;
}
else
*cp++ = c;
}
else
#endif
if (c != 0 && c != '.')
*cp++ = c;
else
return 0;
}
@@ -144,9 +164,14 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
cp++;
if (c1 >= 'A' && c1 <= 'Z')
c1 += 'a' - 'A';
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && c1 == NAME_ESCAPE)
c1 = (*cp++)-1;
#endif
if (c2 >= 'A' && c2 <= 'Z')
c2 += 'a' - 'A';
if (c1 != c2)
retvalue = 2;
}
@@ -1198,7 +1223,10 @@ unsigned int extract_request(struct dns_header *header, size_t qlen, char *name,
size_t setup_reply(struct dns_header *header, size_t qlen,
struct all_addr *addrp, unsigned int flags, unsigned long ttl)
{
unsigned char *p = skip_questions(header, qlen);
unsigned char *p;
if (!(p = skip_questions(header, qlen)))
return 0;
/* clear authoritative and truncated flags, set QR flag */
header->hb3 = (header->hb3 & ~(HB3_AA | HB3_TC)) | HB3_QR;
@@ -1214,7 +1242,7 @@ size_t setup_reply(struct dns_header *header, size_t qlen,
SET_RCODE(header, NOERROR); /* empty domain */
else if (flags == F_NXDOMAIN)
SET_RCODE(header, NXDOMAIN);
else if (p && flags == F_IPV4)
else if (flags == F_IPV4)
{ /* we know the address */
SET_RCODE(header, NOERROR);
header->ancount = htons(1);
@@ -1222,7 +1250,7 @@ size_t setup_reply(struct dns_header *header, size_t qlen,
add_resource_record(header, NULL, NULL, sizeof(struct dns_header), &p, ttl, NULL, T_A, C_IN, "4", addrp);
}
#ifdef HAVE_IPV6
else if (p && flags == F_IPV6)
else if (flags == F_IPV6)
{
SET_RCODE(header, NOERROR);
header->ancount = htons(1);
@@ -1328,6 +1356,43 @@ int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
return 0;
}
int check_for_ignored_address(struct dns_header *header, size_t qlen, struct bogus_addr *baddr)
{
unsigned char *p;
int i, qtype, qclass, rdlen;
struct bogus_addr *baddrp;
/* skip over questions */
if (!(p = skip_questions(header, qlen)))
return 0; /* bad packet */
for (i = ntohs(header->ancount); i != 0; i--)
{
if (!(p = skip_name(p, header, qlen, 10)))
return 0; /* bad packet */
GETSHORT(qtype, p);
GETSHORT(qclass, p);
p += 4; /* TTL */
GETSHORT(rdlen, p);
if (qclass == C_IN && qtype == T_A)
{
if (!CHECK_LEN(header, p, qlen, INADDRSZ))
return 0;
for (baddrp = baddr; baddrp; baddrp = baddrp->next)
if (memcmp(&baddrp->addr, p, INADDRSZ) == 0)
return 1;
}
if (!ADD_RDLEN(header, p, qlen, rdlen))
return 0;
}
return 0;
}
int add_resource_record(struct dns_header *header, char *limit, int *truncp, int nameoffset, unsigned char **pp,
unsigned long ttl, int *offset, unsigned short type, unsigned short class, char *format, ...)
{
@@ -1606,7 +1671,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
{
if (crecp->flags & F_NXDOMAIN)
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)))
{
@@ -1923,14 +1988,17 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
for (intr = daemon->int_names; intr; intr = intr->next)
if (hostname_isequal(name, intr->name))
{
ans = 1;
if (!dryrun)
{
for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
#ifdef HAVE_IPV6
if (((addrlist->flags & ADDRLIST_IPV6) ? T_AAAA : T_A) == type)
if (((addrlist->flags & ADDRLIST_IPV6) ? T_AAAA : T_A) == type)
#endif
{
#ifdef HAVE_IPV6
if (addrlist->flags & ADDRLIST_REVONLY)
continue;
#endif
ans = 1;
if (!dryrun)
{
gotit = 1;
log_query(F_FORWARD | F_CONFIG | flag, name, &addrlist->addr, NULL);
@@ -1939,7 +2007,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
type == T_A ? "4" : "6", &addrlist->addr))
anscount++;
}
}
}
}
if (!dryrun && !gotit)
+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
it under the terms of the GNU General Public License as published by
+35 -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
it under the terms of the GNU General Public License as published by
@@ -691,6 +691,8 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
#endif
o = build_ia(state, &t1cntr);
if (address_assigned)
address_assigned = 2;
for (ia_counter = 0; ia_option; ia_counter++, ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
{
@@ -781,6 +783,27 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
address_assigned = 1;
}
if (address_assigned != 1)
{
/* If the server will not assign any addresses to any IAs in a
subsequent Request from the client, the server MUST send an Advertise
message to the client that doesn't include any IA options. */
if (!state->lease_allocate)
{
save_counter(o);
continue;
}
/* If the server cannot assign any addresses to an IA in the message
from the client, the server MUST include the IA in the Reply message
with no addresses in the IA and a Status Code option in the IA
containing status code NoAddrsAvail. */
o1 = new_opt6(OPTION6_STATUS_CODE);
put_opt6_short(DHCP6NOADDRS);
put_opt6_string(_("address unavailable"));
end_opt6(o1);
}
end_ia(t1cntr, min_time, 0);
end_opt6(o);
}
@@ -806,7 +829,16 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
put_opt6_short(DHCP6NOADDRS);
put_opt6_string(_("no addresses available"));
end_opt6(o1);
log6_packet(state, "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;
@@ -862,7 +894,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
{
/* Static range, not configured. */
o1 = new_opt6(OPTION6_STATUS_CODE);
put_opt6_short(DHCP6UNSPEC);
put_opt6_short(DHCP6NOADDRS);
put_opt6_string(_("address unavailable"));
end_opt6(o1);
}
+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
it under the terms of the GNU General Public License as published by
+5 -1
View File
@@ -20,6 +20,10 @@
#if defined(HAVE_IPSET) && defined(HAVE_BSD_NETWORK)
#ifndef __FreeBSD__
#include <string.h>
#endif
#include <sys/types.h>
#include <sys/ioctl.h>
@@ -136,7 +140,7 @@ int add_to_ipset(const char *setname, const struct all_addr *ipaddr,
return -1;
}
if (rc = pfr_add_tables(&table, 1, &n, 0))
if ((rc = pfr_add_tables(&table, 1, &n, 0)))
{
my_syslog(LOG_WARNING, _("warning: pfr_add_tables: %s(%d)"),
pfr_strerror(errno),rc);
+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
it under the terms of the GNU General Public License as published by
@@ -236,7 +236,7 @@ void tftp_request(struct listener *listen, time_t now)
if (ioctl(listen->tftpfd, SIOCGIFMTU, &ifr) != -1)
mtu = ifr.ifr_mtu;
}
if (name)
{
/* check for per-interface prefix */
+45 -28
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
it under the terms of the GNU General Public License as published by
@@ -226,7 +226,14 @@ unsigned char *do_rfc1035_name(unsigned char *p, char *sval)
{
unsigned char *cp = p++;
for (j = 0; *sval && (*sval != '.'); sval++, j++)
*p++ = *sval;
{
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && *sval == NAME_ESCAPE)
*p++ = (*(++sval))-1;
else
#endif
*p++ = *sval;
}
*cp = j;
if (*sval)
sval++;
@@ -274,6 +281,7 @@ int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2)
#ifdef HAVE_IPV6
if (s1->sa.sa_family == AF_INET6 &&
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))
return 1;
#endif
@@ -568,17 +576,27 @@ void bump_maxfd(int fd, int *max)
*max = fd;
}
int retry_send(void)
/* 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)
{
static int retries = 0;
struct timespec waiter;
if (rc != -1)
{
retries = 0;
errno = 0;
return 0;
}
/* Linux kernels can return EAGAIN in perpetuity when calling
sendmsg() and the relevant interface has gone. Here we loop
retrying in EAGAIN for 1 second max, to avoid this hanging
dnsmasq. */
static int retries = 0;
struct timespec waiter;
if (errno == EAGAIN || errno == EWOULDBLOCK)
if (errno == EAGAIN || errno == EWOULDBLOCK)
{
waiter.tv_sec = 0;
waiter.tv_nsec = 10000;
@@ -586,13 +604,13 @@ int retry_send(void)
if (retries++ < 1000)
return 1;
}
retries = 0;
if (errno == EINTR)
return 1;
return 0;
retries = 0;
if (errno == EINTR)
return 1;
return 0;
}
int read_write(int fd, unsigned char *packet, int size, int rw)
@@ -601,22 +619,21 @@ int read_write(int fd, unsigned char *packet, int size, int rw)
for (done = 0; done < size; done += n)
{
retry:
if (rw)
n = read(fd, &packet[done], (size_t)(size - done));
else
n = write(fd, &packet[done], (size_t)(size - done));
do {
if (rw)
n = read(fd, &packet[done], (size_t)(size - done));
else
n = write(fd, &packet[done], (size_t)(size - done));
if (n == 0)
return 0;
} while (retry_send(n) || errno == ENOMEM || errno == ENOBUFS);
if (n == 0)
return 0;
else if (n == -1)
{
if (retry_send() || errno == ENOMEM || errno == ENOBUFS)
goto retry;
else
return 0;
}
if (errno != 0)
return 0;
}
return 1;
}