Compare commits
189 Commits
v2.68
...
v2.69test8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ee4158678a | ||
|
|
83349b8aa4 | ||
|
|
7fa836e105 | ||
|
|
1633e30834 | ||
|
|
c8ca33f810 | ||
|
|
e243c072b5 | ||
|
|
da4f372271 | ||
|
|
610e782a29 | ||
|
|
854cf26907 | ||
|
|
bb201c211a | ||
|
|
12fae49fff | ||
|
|
fd372273bd | ||
|
|
b98d22c191 | ||
|
|
160f6507c3 | ||
|
|
613d6c5249 | ||
|
|
81a883fda3 | ||
|
|
40b695c1f1 | ||
|
|
5f938534a9 | ||
|
|
8d718cbb3e | ||
|
|
f6a2b79310 | ||
|
|
82e3f45a9f | ||
|
|
072e81b3c5 | ||
|
|
1d97ac4fd2 | ||
|
|
db73746620 | ||
|
|
97bc798b05 | ||
|
|
edc231bc58 | ||
|
|
b85e092e23 | ||
|
|
583043f527 | ||
|
|
8f6213cce9 | ||
|
|
00ec693db8 | ||
|
|
70b4a818ef | ||
|
|
7c28612a59 | ||
|
|
6f4681034e | ||
|
|
6938f3476e | ||
|
|
17fb9ea763 | ||
|
|
7d23a66ff0 | ||
|
|
703c7ff429 | ||
|
|
8a9be9e493 | ||
|
|
c92f0083a2 | ||
|
|
b5dbfd142a | ||
|
|
cbf13a2a6d | ||
|
|
5b3bf92101 | ||
|
|
0744ca66ad | ||
|
|
2d33bda2e6 | ||
|
|
32f90c0fad | ||
|
|
bce6e1bc6d | ||
|
|
824202ef54 | ||
|
|
9ebfca1e84 | ||
|
|
6429e421b3 | ||
|
|
c9bfa948c3 | ||
|
|
e7829aefd8 | ||
|
|
51ea3ca254 | ||
|
|
57ab36e77d | ||
|
|
dd0e0a3995 | ||
|
|
6fd6dacb39 | ||
|
|
39048ad10b | ||
|
|
979cdf9b64 | ||
|
|
dbf721235b | ||
|
|
c979fa04a4 | ||
|
|
c5f4ec7d23 | ||
|
|
5d3b87a484 | ||
|
|
72ae2f3d56 | ||
|
|
6c0cb858c1 | ||
|
|
e0c0ad3b5e | ||
|
|
4619d94622 | ||
|
|
0975a58e9b | ||
|
|
a25720a34a | ||
|
|
cc111e0bab | ||
|
|
86bec2d399 | ||
|
|
a59ff5f3df | ||
|
|
c3a04081ff | ||
|
|
ae76242fdf | ||
|
|
4f04476e3b | ||
|
|
1486a9c7f2 | ||
|
|
5ada888507 | ||
|
|
5f8e58f49b | ||
|
|
b8071a849a | ||
|
|
b6e9e7c32d | ||
|
|
0435d041ea | ||
|
|
795501bc86 | ||
|
|
c2207688c0 | ||
|
|
98c098bfc7 | ||
|
|
c47e3ba446 | ||
|
|
f1668d2786 | ||
|
|
7d7b7b31e5 | ||
|
|
3ddacb86e9 | ||
|
|
60b68069cf | ||
|
|
871417d45d | ||
|
|
65d1e3bb9b | ||
|
|
0fc2f31368 | ||
|
|
c3e0b9b6e7 | ||
|
|
6ea1f23b3f | ||
|
|
963c380d13 | ||
|
|
00238fb019 | ||
|
|
74e6b52011 | ||
|
|
875b8160f6 | ||
|
|
76ff440ebe | ||
|
|
8db957dfbf | ||
|
|
9d633048fe | ||
|
|
a9b55837dc | ||
|
|
c352dd8f1a | ||
|
|
3a2371527f | ||
|
|
1ee9be4c3f | ||
|
|
65c9b48921 | ||
|
|
e597dba7ec | ||
|
|
06e54b823e | ||
|
|
5a4120dbfb | ||
|
|
687bac22db | ||
|
|
8d41ebd8a3 | ||
|
|
4631dbf68c | ||
|
|
4f9aefc753 | ||
|
|
4b5287005f | ||
|
|
5c32841934 | ||
|
|
ccd1d32c3a | ||
|
|
75ffc9bf15 | ||
|
|
3af1ea8cbc | ||
|
|
1f0dc5835b | ||
|
|
ed1fc98595 | ||
|
|
b58fb39f24 | ||
|
|
0304d28f7e | ||
|
|
f5adbb90a1 | ||
|
|
32b826e2a0 | ||
|
|
0937692dc6 | ||
|
|
785ee80b93 | ||
|
|
f119ed382e | ||
|
|
da23c4f960 | ||
|
|
4885d57c58 | ||
|
|
0db0e0c216 | ||
|
|
ec2962eacb | ||
|
|
0ca895f585 | ||
|
|
6299ffbe60 | ||
|
|
7f0485cf53 | ||
|
|
02bff4f109 | ||
|
|
d1ca25ca7e | ||
|
|
23c2176681 | ||
|
|
e83297d0f6 | ||
|
|
41de7442d2 | ||
|
|
0852d76b58 | ||
|
|
a55ce08cc0 | ||
|
|
dd090561bf | ||
|
|
28f04fd647 | ||
|
|
50a96b62f1 | ||
|
|
00b963ab72 | ||
|
|
79333a2498 | ||
|
|
32f82c62c8 | ||
|
|
4e076d746f | ||
|
|
13e435ebca | ||
|
|
4b0eecbb44 | ||
|
|
0360a524df | ||
|
|
262ac85107 | ||
|
|
4c70046d93 | ||
|
|
458824dcb4 | ||
|
|
a7338645d7 | ||
|
|
776fd04754 | ||
|
|
20bccd499f | ||
|
|
708bcd2dd3 | ||
|
|
d0edff7d6e | ||
|
|
ccca70cb33 | ||
|
|
0d829ebc69 | ||
|
|
4137b84e4e | ||
|
|
e6c2a670fe | ||
|
|
47f99dd2b3 | ||
|
|
6759b99e28 | ||
|
|
3471f18130 | ||
|
|
2ef843dd16 | ||
|
|
ce2a0f5a6a | ||
|
|
adca3e9c4b | ||
|
|
366dfcb907 | ||
|
|
28c625572b | ||
|
|
02f9b76418 | ||
|
|
ba8badd6df | ||
|
|
0decc869ae | ||
|
|
b573aebc09 | ||
|
|
d31d057aa3 | ||
|
|
6445c8ed73 | ||
|
|
382e38f494 | ||
|
|
9940aba9f6 | ||
|
|
7e846b9858 | ||
|
|
d322de0613 | ||
|
|
b98f771519 | ||
|
|
c7a93f6e4e | ||
|
|
970ce22b68 | ||
|
|
e292e93d35 | ||
|
|
fa164d459f | ||
|
|
f53c79c01b | ||
|
|
7dbe193bee | ||
|
|
a669f012dd | ||
|
|
237724c0c7 | ||
|
|
53f84c7f62 |
65
CHANGELOG
65
CHANGELOG
@@ -1,3 +1,68 @@
|
||||
version 2.69
|
||||
Implement dynamic interface discovery on *BSD. This allows
|
||||
the contructor: syntax to be used in dhcp-range for DHCPv6
|
||||
on the BSD platform. Thanks to Matthias Andree for
|
||||
valuable research on how to implement this.
|
||||
|
||||
Fix infinite loop associated with some --bogus-nxdomain
|
||||
configs. Thanks fogobogo for the bug report.
|
||||
|
||||
Fix missing RA RDNS option with configuration like
|
||||
--dhcp-option=option6:23,[::] Thanks to Tsachi Kimeldorfer
|
||||
for spotting the problem.
|
||||
|
||||
Add [fd00::] and [fe80::] as special addresses in DHCPv6
|
||||
options, analogous to [::]. [fd00::] is replaced with the
|
||||
actual ULA of the interface on the machine running
|
||||
dnsmasq, [fe80::] with the link-local address.
|
||||
Thanks to Tsachi Kimeldorfer for championing this.
|
||||
|
||||
DNSSEC validation and caching. Dnsmasq needs to be
|
||||
compiled with this enabled, with
|
||||
|
||||
make dnsmasq COPTS=-DHAVE_DNSSEC
|
||||
|
||||
this add dependencies on the nettle crypto library and the
|
||||
gmp maths library. It's possible to have these linked
|
||||
statically with
|
||||
|
||||
make dnsmasq COPTS='-DHAVE_DNSSEC -DHAVE_DNSSEC_STATIC'
|
||||
|
||||
which bloats the dnsmasq binary to over a megabyte, but
|
||||
saves the size of the shared libraries which are five
|
||||
times that size.
|
||||
To enable, DNSSEC, you will need a set of
|
||||
trust-anchors. Now that the TLDs are signed, this can be
|
||||
the keys for the root zone, and for convenience they are
|
||||
included in trust-anchors.conf in the dnsmasq
|
||||
distribution. You should of course check that these are
|
||||
legitimate and up-to-date. So, adding
|
||||
|
||||
conf-file=/path/to/trust-anchors.conf
|
||||
dnssec
|
||||
|
||||
to your config is all thats needed to get things
|
||||
working. The upstream nameservers have to be DNSSEC-capable
|
||||
too, of course. Many ISP nameservers aren't, but the
|
||||
Google public nameservers (8.8.8.8 and 8.8.4.4) are.
|
||||
When DNSSEC is configured, dnsmasq validates any queries
|
||||
for domains which are signed. Query results which are
|
||||
bogus are replaced with SERVFAIL replies, and results
|
||||
which are correctly signed have the AD bit set. In
|
||||
addition, and just as importantly, dnsmasq supplies
|
||||
correct DNSSEC information to clients which are doing
|
||||
their own validation, and caches DNSKEY, DS and RRSIG
|
||||
records, which significantly improve the performance of
|
||||
downstream validators. Setting --log-queries will show
|
||||
DNSSEC in action.
|
||||
|
||||
The development of DNSSEC in dnsmasq was started by
|
||||
Giovanni Bajo, to whom huge thanks are owed. It has been
|
||||
supported by Comcast, whose techfund grant has allowed for
|
||||
an invaluable period of full-time work to get it to
|
||||
a workable state.
|
||||
|
||||
|
||||
version 2.68
|
||||
Use random addresses for DHCPv6 temporary address
|
||||
allocations, instead of algorithmically determined stable
|
||||
|
||||
36
Makefile
36
Makefile
@@ -1,4 +1,4 @@
|
||||
# dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
# dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -51,30 +51,34 @@ top!=pwd
|
||||
# GNU make way.
|
||||
top?=$(CURDIR)
|
||||
|
||||
dbus_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DBUS $(PKG_CONFIG) --cflags dbus-1`
|
||||
dbus_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DBUS $(PKG_CONFIG) --libs dbus-1`
|
||||
idn_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_IDN $(PKG_CONFIG) --cflags libidn`
|
||||
idn_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_IDN $(PKG_CONFIG) --libs libidn`
|
||||
ct_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_CONNTRACK $(PKG_CONFIG) --cflags libnetfilter_conntrack`
|
||||
ct_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_CONNTRACK $(PKG_CONFIG) --libs libnetfilter_conntrack`
|
||||
lua_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LUASCRIPT $(PKG_CONFIG) --cflags lua5.1`
|
||||
lua_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LUASCRIPT $(PKG_CONFIG) --libs lua5.1`
|
||||
sunos_libs = `if uname | grep SunOS >/dev/null 2>&1; then echo -lsocket -lnsl -lposix4; fi`
|
||||
dbus_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DBUS $(PKG_CONFIG) --cflags dbus-1`
|
||||
dbus_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DBUS $(PKG_CONFIG) --libs dbus-1`
|
||||
idn_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_IDN $(PKG_CONFIG) --cflags libidn`
|
||||
idn_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_IDN $(PKG_CONFIG) --libs libidn`
|
||||
ct_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_CONNTRACK $(PKG_CONFIG) --cflags libnetfilter_conntrack`
|
||||
ct_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_CONNTRACK $(PKG_CONFIG) --libs libnetfilter_conntrack`
|
||||
lua_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LUASCRIPT $(PKG_CONFIG) --cflags lua5.1`
|
||||
lua_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LUASCRIPT $(PKG_CONFIG) --libs lua5.1`
|
||||
nettle_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC $(PKG_CONFIG) --cflags nettle hogweed`
|
||||
nettle_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC $(PKG_CONFIG) --libs nettle hogweed`
|
||||
gmp_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC $(PKG_CONFIG) --copy -lgmp`
|
||||
sunos_libs = `if uname | grep SunOS >/dev/null 2>&1; then echo -lsocket -lnsl -lposix4; fi`
|
||||
version = -DVERSION='\"`$(top)/bld/get-version $(top)`\"'
|
||||
|
||||
objs = cache.o rfc1035.o util.o option.o forward.o network.o \
|
||||
dnsmasq.o dhcp.o lease.o rfc2131.o netlink.o dbus.o bpf.o \
|
||||
helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o \
|
||||
dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o domain.o
|
||||
dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o \
|
||||
domain.o dnssec.o blockdata.o
|
||||
|
||||
hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \
|
||||
dns-protocol.h radv-protocol.h
|
||||
dns-protocol.h radv-protocol.h ip6addr.h
|
||||
|
||||
all : $(BUILDDIR)
|
||||
@cd $(BUILDDIR) && $(MAKE) \
|
||||
top="$(top)" \
|
||||
build_cflags="$(version) $(dbus_cflags) $(idn_cflags) $(ct_cflags) $(lua_cflags)" \
|
||||
build_libs="$(dbus_libs) $(idn_libs) $(ct_libs) $(lua_libs) $(sunos_libs)" \
|
||||
build_cflags="$(version) $(dbus_cflags) $(idn_cflags) $(ct_cflags) $(lua_cflags) $(nettle_cflags)" \
|
||||
build_libs="$(dbus_libs) $(idn_libs) $(ct_libs) $(lua_libs) $(sunos_libs) $(nettle_libs) $(gmp_libs)" \
|
||||
-f $(top)/Makefile dnsmasq
|
||||
|
||||
mostly_clean :
|
||||
@@ -97,8 +101,8 @@ all-i18n : $(BUILDDIR)
|
||||
@cd $(BUILDDIR) && $(MAKE) \
|
||||
top="$(top)" \
|
||||
i18n=-DLOCALEDIR=\'\"$(LOCALEDIR)\"\' \
|
||||
build_cflags="$(version) $(dbus_cflags) $(ct_cflags) $(lua_cflags) `$(PKG_CONFIG) --cflags libidn`" \
|
||||
build_libs="$(dbus_libs) $(ct_libs) $(lua_libs) $(sunos_libs) `$(PKG_CONFIG) --libs libidn`" \
|
||||
build_cflags="$(version) $(dbus_cflags) $(ct_cflags) $(lua_cflags) $(nettle_cflags) `$(PKG_CONFIG) --cflags libidn`" \
|
||||
build_libs="$(dbus_libs) $(ct_libs) $(lua_libs) $(sunos_libs) $(nettle_libs) $(gmp_libs) `$(PKG_CONFIG) --libs libidn`" \
|
||||
-f $(top)/Makefile dnsmasq
|
||||
for f in `cd $(PO); echo *.po`; do \
|
||||
cd $(top) && cd $(BUILDDIR) && $(MAKE) top="$(top)" -f $(top)/Makefile $${f%.po}.mo; \
|
||||
|
||||
@@ -8,7 +8,8 @@ LOCAL_SRC_FILES := bpf.c cache.c dbus.c dhcp.c dnsmasq.c \
|
||||
netlink.c network.c option.c rfc1035.c \
|
||||
rfc2131.c tftp.c util.c conntrack.c \
|
||||
dhcp6.c rfc3315.c dhcp-common.c outpacket.c \
|
||||
radv.c slaac.c auth.c ipset.c domain.c
|
||||
radv.c slaac.c auth.c ipset.c domain.c \
|
||||
dnssec.c dnssec-openssl.c blockdata.c
|
||||
|
||||
LOCAL_MODULE := dnsmasq
|
||||
|
||||
|
||||
@@ -2,10 +2,31 @@
|
||||
|
||||
search=$1
|
||||
shift
|
||||
pkg=$1
|
||||
shift
|
||||
op=$1
|
||||
shift
|
||||
|
||||
in=`cat`
|
||||
|
||||
if grep "^\#[[:space:]]*define[[:space:]]*$search" config.h >/dev/null 2>&1 || \
|
||||
grep $search >/dev/null 2>&1; then
|
||||
exec $*
|
||||
echo $in | grep $search >/dev/null 2>&1; then
|
||||
|
||||
if [ $op = "--copy" ]; then
|
||||
pkg="$*"
|
||||
else
|
||||
pkg=`$pkg $op $*`
|
||||
fi
|
||||
|
||||
if grep "^\#[[:space:]]*define[[:space:]]*${search}_STATIC" config.h >/dev/null 2>&1 || \
|
||||
echo $in | grep ${search}_STATIC >/dev/null 2>&1; then
|
||||
if [ $op = "--libs" ] || [ $op = "--copy" ]; then
|
||||
echo "-Wl,-Bstatic $pkg -Wl,-Bdynamic"
|
||||
else
|
||||
echo "$pkg"
|
||||
fi
|
||||
else
|
||||
echo "$pkg"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
|
||||
29
contrib/try-all-ns/dnsmasq-2.68-try-all-ns
Normal file
29
contrib/try-all-ns/dnsmasq-2.68-try-all-ns
Normal file
@@ -0,0 +1,29 @@
|
||||
From: Jesse Glick <jglick@cloudbees.com>
|
||||
To: dnsmasq-discuss@lists.thekelleys.org.uk
|
||||
Subject: Re: [Dnsmasq-discuss] Ability to delegate to one server but fall
|
||||
back to another after NXDOMAIN?
|
||||
|
||||
|
||||
On Wed, Jan 15, 2014 at 12:30 PM, Simon Kelley <simon@thekelleys.org.uk> wrote:
|
||||
> > There's a (very old) patch in contrib/try-all-ns that would make a starting point
|
||||
This does not apply against trunk, so I tried to rework it. The
|
||||
following appears to do what I expect:
|
||||
|
||||
diff --git a/src/forward.c b/src/forward.c
|
||||
index 8167229..76070b5 100644
|
||||
--- a/src/forward.c
|
||||
+++ b/src/forward.c
|
||||
@@ -610,7 +610,11 @@ void reply_query(int fd, int family, time_t now)
|
||||
|
||||
if ((RCODE(header) == SERVFAIL || RCODE(header) == REFUSED) &&
|
||||
!option_bool(OPT_ORDER) &&
|
||||
- forward->forwardall == 0)
|
||||
+ forward->forwardall == 0 ||
|
||||
+ /* try each in turn */
|
||||
+ RCODE(header) == NXDOMAIN &&
|
||||
+ option_bool(OPT_ORDER) &&
|
||||
+ server->next != NULL)
|
||||
/* for broken servers, attempt to send to another one. */
|
||||
{
|
||||
unsigned char *pheader;
|
||||
|
||||
8
debian/changelog
vendored
8
debian/changelog
vendored
@@ -1,7 +1,13 @@
|
||||
dnsmasq (2.69-1) unstable; urgency=low
|
||||
|
||||
* New upstream.
|
||||
|
||||
-- Simon Kelley <simon@thekelleys.org.uk> Tue, 4 Feb 2014 16:28:12 +0000
|
||||
|
||||
dnsmasq (2.68-1) unstable; urgency=low
|
||||
|
||||
* New upstream. (closes: #730553)
|
||||
|
||||
|
||||
-- Simon Kelley <simon@thekelleys.org.uk> Sun, 8 Dec 2013 15:57:32 +0000
|
||||
|
||||
dnsmasq (2.67-1) unstable; urgency=low
|
||||
|
||||
3
debian/control
vendored
3
debian/control
vendored
@@ -1,7 +1,8 @@
|
||||
Source: dnsmasq
|
||||
Section: net
|
||||
Priority: optional
|
||||
Build-depends: gettext, libnetfilter-conntrack-dev [linux-any], libidn11-dev, libdbus-1-dev (>=0.61)
|
||||
Build-depends: gettext, libnetfilter-conntrack-dev [linux-any],
|
||||
libidn11-dev, libdbus-1-dev (>=0.61), libgmp-dev, nettle-dev (>=2.4-3)
|
||||
Maintainer: Simon Kelley <simon@thekelleys.org.uk>
|
||||
Standards-Version: 3.9.3
|
||||
|
||||
|
||||
34
debian/rules
vendored
34
debian/rules
vendored
@@ -17,7 +17,7 @@ CFLAGS += -Wall -W
|
||||
|
||||
LDFLAGS = $(shell dpkg-buildflags --get LDFLAGS)
|
||||
|
||||
COPTS =
|
||||
DEB_COPTS = $(COPTS)
|
||||
|
||||
TARGET = install-i18n
|
||||
|
||||
@@ -29,52 +29,56 @@ ifneq (,$(filter gitversion,$(DEB_BUILD_OPTIONS)))
|
||||
endif
|
||||
|
||||
ifeq (,$(filter nodbus,$(DEB_BUILD_OPTIONS)))
|
||||
COPTS += -DHAVE_DBUS
|
||||
DEB_COPTS += -DHAVE_DBUS
|
||||
endif
|
||||
|
||||
ifeq (,$(filter noconntrack,$(DEB_BUILD_OPTIONS)))
|
||||
ifeq ($(DEB_BUILD_ARCH_OS),linux)
|
||||
COPTS += -DHAVE_CONNTRACK
|
||||
DEB_COPTS += -DHAVE_CONNTRACK
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq (,$(filter noipset,$(DEB_BUILD_OPTIONS)))
|
||||
COPTS += -DNO_IPSET
|
||||
DEB_COPTS += -DNO_IPSET
|
||||
endif
|
||||
|
||||
ifneq (,$(filter nodhcp6,$(DEB_BUILD_OPTIONS)))
|
||||
COPTS += -DNO_DHCP6
|
||||
DEB_COPTS += -DNO_DHCP6
|
||||
endif
|
||||
|
||||
ifneq (,$(filter noipv6,$(DEB_BUILD_OPTIONS)))
|
||||
COPTS += -DNO_IPV6
|
||||
DEB_COPTS += -DNO_IPV6
|
||||
endif
|
||||
|
||||
ifneq (,$(filter notftp,$(DEB_BUILD_OPTIONS)))
|
||||
COPTS += -DNO_TFTP
|
||||
DEB_COPTS += -DNO_TFTP
|
||||
endif
|
||||
|
||||
ifneq (,$(filter nodhcp,$(DEB_BUILD_OPTIONS)))
|
||||
COPTS += -DNO_DHCP
|
||||
DEB_COPTS += -DNO_DHCP
|
||||
endif
|
||||
|
||||
ifneq (,$(filter noscript,$(DEB_BUILD_OPTIONS)))
|
||||
COPTS += -DNO_SCRIPT
|
||||
DEB_COPTS += -DNO_SCRIPT
|
||||
endif
|
||||
|
||||
ifneq (,$(filter nortc,$(DEB_BUILD_OPTIONS)))
|
||||
COPTS += -DHAVE_BROKEN_RTC
|
||||
DEB_COPTS += -DHAVE_BROKEN_RTC
|
||||
endif
|
||||
|
||||
ifneq (,$(filter noi18n,$(DEB_BUILD_OPTIONS)))
|
||||
TARGET = install
|
||||
ifeq (,$(filter noidn, $(DEB_BUILD_OPTIONS)))
|
||||
COPTS += -DHAVE_IDN
|
||||
DEB_COPTS += -DHAVE_IDN
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq (,$(filter uselua,$(DEB_BUILD_OPTIONS)))
|
||||
COPTS += -DHAVE_LUASCRIPT
|
||||
DEB_COPTS += -DHAVE_LUASCRIPT
|
||||
endif
|
||||
|
||||
ifeq (,$(filter nodnssec,$(DEB_BUILD_OPTIONS)))
|
||||
DEB_COPTS += -DHAVE_DNSSEC
|
||||
endif
|
||||
|
||||
clean:
|
||||
@@ -122,12 +126,14 @@ binary-arch: checkroot
|
||||
-d debian/base/usr/share/doc/$(package) \
|
||||
-d debian/base/usr/share/doc/$(package)/examples \
|
||||
-d debian/base/var/run \
|
||||
-d debian/base/usr/share/$(package) \
|
||||
-d debian/base/var/lib/misc
|
||||
make $(TARGET) PREFIX=/usr DESTDIR=`pwd`/debian/base CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" COPTS="$(COPTS)" CC=gcc
|
||||
make $(TARGET) PREFIX=/usr DESTDIR=`pwd`/debian/base CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" COPTS="$(DEB_COPTS)" CC=gcc
|
||||
ifeq (,$(findstring nodocs,$(DEB_BUILD_OPTIONS)))
|
||||
install -m 644 doc.html debian/base/usr/share/doc/$(package)/.
|
||||
install -m 644 setup.html debian/base/usr/share/doc/$(package)/.
|
||||
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
|
||||
install -m 644 CHANGELOG debian/base/usr/share/doc/$(package)/changelog
|
||||
@@ -167,7 +173,7 @@ ifeq ($(DEB_BUILD_ARCH_OS),linux)
|
||||
-d debian/utils/usr/share/man/man1 \
|
||||
-d debian/utils/usr/bin \
|
||||
-d debian/utils/usr/share/doc/dnsmasq-utils
|
||||
make -C contrib/wrt PREFIX=/usr DESTDIR=`pwd`/debian/utils CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" COPTS="$(COPTS)" CC=gcc
|
||||
make -C contrib/wrt PREFIX=/usr DESTDIR=`pwd`/debian/utils CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" COPTS="$(DEB_COPTS)" CC=gcc
|
||||
install -m 755 contrib/wrt/dhcp_release debian/utils/usr/bin/dhcp_release
|
||||
install -m 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
|
||||
|
||||
@@ -20,6 +20,10 @@
|
||||
# Never forward addresses in the non-routed address spaces.
|
||||
#bogus-priv
|
||||
|
||||
# Uncomment these to enable DNSSEC validation and caching:
|
||||
# (Requires dnsmasq to be built with DNSSEC option.)
|
||||
#conf-file=%%PREFIX%%/share/dnsmasq/trust-anchors.conf
|
||||
#dnssec
|
||||
|
||||
# Uncomment this to filter useless windows-originated DNS requests
|
||||
# which can trigger dial-on-demand links needlessly.
|
||||
|
||||
@@ -13,7 +13,10 @@ Dnsmasq accepts DNS queries and either answers them from a small, local,
|
||||
cache or forwards them to a real, recursive, DNS server. It loads the
|
||||
contents of /etc/hosts so that local hostnames
|
||||
which do not appear in the global DNS can be resolved and also answers
|
||||
DNS queries for DHCP configured hosts. It can also act as the authoritative DNS server for one or more domains, allowing local names to appear in the global DNS.
|
||||
DNS queries for DHCP configured hosts. It can also act as the
|
||||
authoritative DNS server for one or more domains, allowing local names
|
||||
to appear in the global DNS. It can be configured to do DNSSEC
|
||||
validation.
|
||||
.PP
|
||||
The dnsmasq DHCP server supports static address assignments and multiple
|
||||
networks. It automatically
|
||||
@@ -579,19 +582,35 @@ Set the maximum number of concurrent DNS queries. The default value is
|
||||
where this needs to be increased is when using web-server log file
|
||||
resolvers, which can generate large numbers of concurrent queries.
|
||||
.TP
|
||||
.B --dnssec
|
||||
Validate DNS replies and cache DNSSEC data. When forwarding DNS queries, dnsmasq requests the
|
||||
DNSSEC records needed to validate the replies. The replies are validated and the result returned as
|
||||
the Authenticated Data bit in the DNS packet. In addition the DNSSEC records are stored in the cache, making
|
||||
validation by clients more efficient. Note that validation by clients is the most secure DNSSEC mode, but for
|
||||
clients unable to do validation, use of the AD bit set by dnsmasq is useful, provided that the network between
|
||||
the dnsmasq server and the client is trusted. Dnsmasq must be compiled with HAVE_DNSSEC enabled, and DNSSEC
|
||||
trust anchors provided, see
|
||||
.B --trust-anchor.
|
||||
Because the DNSSEC validation process uses the cache, it is not permitted to reduce the cache size below the default when DNSSEC is enabled.
|
||||
.TP
|
||||
.B --trust-anchor=[<class>],<domain>,<key-tag>,<algorithm>,<digest-type>,<digest>
|
||||
Provide DS records to act a trust anchors for DNSSEC
|
||||
validation. Typically these will be the DS record(s) for Zone Signing
|
||||
key(s) of the root zone,
|
||||
but trust anchors for limited domains are also possible. The current
|
||||
root-zone trust anchors may be donwloaded from https://data.iana.org/root-anchors/root-anchors.xml
|
||||
.TP
|
||||
.B --proxy-dnssec
|
||||
A resolver on a client machine can do DNSSEC validation in two ways: it
|
||||
can perform the cryptograhic operations on the reply it receives, or
|
||||
it can rely on the upstream recursive nameserver to do the validation
|
||||
and set a bit in the reply if it succeeds. Dnsmasq is not a DNSSEC
|
||||
validator, so it cannot perform the validation role of the recursive nameserver,
|
||||
but it can pass through the validation results from its own upstream
|
||||
nameservers. This option enables this behaviour. You should only do
|
||||
this if you trust all the configured upstream nameservers
|
||||
.I and the network between you and them.
|
||||
If you use the first DNSSEC mode, validating resolvers in clients,
|
||||
this option is not required. Dnsmasq always returns all the data
|
||||
needed for a client to do validation itself.
|
||||
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
|
||||
dnsmasq and the upstream servers, and the trustworthiness of the upstream servers.
|
||||
.TP
|
||||
.B --dnssec-debug
|
||||
Set debugging mode for the DNSSEC validation, set the Checking Disabled bit on upstream queries,
|
||||
and don't convert replies which do not validate to responses with
|
||||
a return code of SERVFAIL. Note that
|
||||
setting this may affect DNS behaviour in bad ways, it is not an
|
||||
extra-logging flag and should not be set in production.
|
||||
.TP
|
||||
.B --auth-zone=<domain>[,<subnet>[/<prefix length>][,<subnet>[/<prefix length>].....]]
|
||||
Define a DNS zone for which dnsmasq acts as authoritative server. Locally defined DNS records which are in the domain
|
||||
@@ -917,9 +936,11 @@ and to set the time-server address to 192.168.0.4, do
|
||||
.B --dhcp-option = 42,192.168.0.4
|
||||
or
|
||||
.B --dhcp-option = option:ntp-server, 192.168.0.4
|
||||
The special address 0.0.0.0 (or [::] for DHCPv6) is taken to mean "the address of the
|
||||
machine running dnsmasq". Data types allowed are comma separated
|
||||
dotted-quad IP addresses, a decimal number, colon-separated hex digits
|
||||
The special address 0.0.0.0 is taken to mean "the address of the
|
||||
machine running dnsmasq".
|
||||
|
||||
Data types allowed are comma separated
|
||||
dotted-quad IPv4 addresses, []-wrapped IPv6 addresses, a decimal number, colon-separated hex digits
|
||||
and a text string. If the optional tags are given then
|
||||
this option is only sent when all the tags are matched.
|
||||
|
||||
@@ -935,7 +956,9 @@ keyword, followed by the option number or option name. The IPv6 option
|
||||
name space is disjoint from the IPv4 option name space. IPv6 addresses
|
||||
in options must be bracketed with square brackets, eg.
|
||||
.B --dhcp-option=option6:ntp-server,[1234::56]
|
||||
|
||||
For IPv6, [::] means "the global address of
|
||||
the machine running dnsmasq", whilst [fd00::] is replaced with the
|
||||
ULA, if it exists, and [fe80::] with the link-local address.
|
||||
|
||||
Be careful: no checking is done that the correct type of data for the
|
||||
option number is sent, it is quite possible to
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
146
src/blockdata.c
Normal file
146
src/blockdata.c
Normal file
@@ -0,0 +1,146 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2014 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_DNSSEC
|
||||
|
||||
static struct blockdata *keyblock_free;
|
||||
static unsigned int blockdata_count, blockdata_hwm, blockdata_alloced;
|
||||
|
||||
static void blockdata_expand(int n)
|
||||
{
|
||||
struct blockdata *new = whine_malloc(n * sizeof(struct blockdata));
|
||||
|
||||
if (new)
|
||||
{
|
||||
int i;
|
||||
|
||||
new[n-1].next = keyblock_free;
|
||||
keyblock_free = new;
|
||||
|
||||
for (i = 0; i < n - 1; i++)
|
||||
new[i].next = &new[i+1];
|
||||
|
||||
blockdata_alloced += n;
|
||||
}
|
||||
}
|
||||
|
||||
/* Preallocate some blocks, proportional to cachesize, to reduce heap fragmentation. */
|
||||
void blockdata_init(void)
|
||||
{
|
||||
keyblock_free = NULL;
|
||||
blockdata_alloced = 0;
|
||||
blockdata_count = 0;
|
||||
blockdata_hwm = 0;
|
||||
|
||||
blockdata_expand((daemon->cachesize * 100) / sizeof(struct blockdata));
|
||||
}
|
||||
|
||||
void blockdata_report(void)
|
||||
{
|
||||
my_syslog(LOG_INFO, _("DNSSEC memory in use %u, max %u, allocated %u"),
|
||||
blockdata_count * sizeof(struct blockdata), blockdata_hwm * sizeof(struct blockdata), blockdata_alloced * sizeof(struct blockdata));
|
||||
}
|
||||
|
||||
struct blockdata *blockdata_alloc(char *data, size_t len)
|
||||
{
|
||||
struct blockdata *block, *ret = NULL;
|
||||
struct blockdata **prev = &ret;
|
||||
size_t blen;
|
||||
|
||||
while (len > 0)
|
||||
{
|
||||
if (!keyblock_free)
|
||||
blockdata_expand(50);
|
||||
|
||||
if (keyblock_free)
|
||||
{
|
||||
block = keyblock_free;
|
||||
keyblock_free = block->next;
|
||||
blockdata_count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* failed to alloc, free partial chain */
|
||||
blockdata_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (blockdata_hwm < blockdata_count)
|
||||
blockdata_hwm = blockdata_count;
|
||||
|
||||
blen = len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len;
|
||||
memcpy(block->key, data, blen);
|
||||
data += blen;
|
||||
len -= blen;
|
||||
*prev = block;
|
||||
prev = &block->next;
|
||||
block->next = NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void blockdata_free(struct blockdata *blocks)
|
||||
{
|
||||
struct blockdata *tmp;
|
||||
|
||||
if (blocks)
|
||||
{
|
||||
for (tmp = blocks; tmp->next; tmp = tmp->next)
|
||||
blockdata_count--;
|
||||
tmp->next = keyblock_free;
|
||||
keyblock_free = blocks;
|
||||
blockdata_count--;
|
||||
}
|
||||
}
|
||||
|
||||
/* if data == NULL, return pointer to static block of sufficient size */
|
||||
void *blockdata_retrieve(struct blockdata *block, size_t len, void *data)
|
||||
{
|
||||
size_t blen;
|
||||
struct blockdata *b;
|
||||
void *new, *d;
|
||||
|
||||
static unsigned int buff_len = 0;
|
||||
static unsigned char *buff = NULL;
|
||||
|
||||
if (!data)
|
||||
{
|
||||
if (len > buff_len)
|
||||
{
|
||||
if (!(new = whine_malloc(len)))
|
||||
return NULL;
|
||||
if (buff)
|
||||
free(buff);
|
||||
buff = new;
|
||||
}
|
||||
data = buff;
|
||||
}
|
||||
|
||||
for (d = data, b = block; len > 0 && b; b = b->next)
|
||||
{
|
||||
blen = len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len;
|
||||
memcpy(d, b->key, blen);
|
||||
d += blen;
|
||||
len -= blen;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
#endif
|
||||
108
src/bpf.c
108
src/bpf.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -19,9 +19,9 @@
|
||||
#if defined(HAVE_BSD_NETWORK) || defined(HAVE_SOLARIS_NETWORK)
|
||||
#include <ifaddrs.h>
|
||||
|
||||
#if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
|
||||
#include <sys/param.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <net/if.h>
|
||||
#include <net/route.h>
|
||||
#include <net/if_dl.h>
|
||||
#include <netinet/if_ether.h>
|
||||
@@ -29,7 +29,9 @@
|
||||
# include <net/if_var.h>
|
||||
#endif
|
||||
#include <netinet/in_var.h>
|
||||
#include <netinet6/in6_var.h>
|
||||
#ifdef HAVE_IPV6
|
||||
# include <netinet6/in6_var.h>
|
||||
#endif
|
||||
|
||||
#ifndef SA_SIZE
|
||||
#define SA_SIZE(sa) \
|
||||
@@ -38,6 +40,13 @@
|
||||
1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1) ) )
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_BSD_NETWORK
|
||||
static int del_family = 0;
|
||||
static struct all_addr del_addr;
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
|
||||
|
||||
int arp_enumerate(void *parm, int (*callback)())
|
||||
{
|
||||
int mib[6];
|
||||
@@ -88,7 +97,7 @@ int arp_enumerate(void *parm, int (*callback)())
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
#endif /* defined(HAVE_BSD_NETWORK) && !defined(__APPLE__) */
|
||||
|
||||
|
||||
int iface_enumerate(int family, void *parm, int (*callback)())
|
||||
@@ -129,6 +138,10 @@ int iface_enumerate(int family, void *parm, int (*callback)())
|
||||
{
|
||||
struct in_addr addr, netmask, broadcast;
|
||||
addr = ((struct sockaddr_in *) addrs->ifa_addr)->sin_addr;
|
||||
#ifdef HAVE_BSD_NETWORK
|
||||
if (del_family == AF_INET && del_addr.addr.addr4.s_addr == addr.s_addr)
|
||||
continue;
|
||||
#endif
|
||||
netmask = ((struct sockaddr_in *) addrs->ifa_netmask)->sin_addr;
|
||||
if (addrs->ifa_broadaddr)
|
||||
broadcast = ((struct sockaddr_in *) addrs->ifa_broadaddr)->sin_addr;
|
||||
@@ -146,6 +159,10 @@ int iface_enumerate(int family, void *parm, int (*callback)())
|
||||
int i, j, prefix = 0;
|
||||
u32 valid = 0xffffffff, preferred = 0xffffffff;
|
||||
int flags = 0;
|
||||
#ifdef HAVE_BSD_NETWORK
|
||||
if (del_family == AF_INET6 && IN6_ARE_ADDR_EQUAL(&del_addr.addr.addr6, addr))
|
||||
continue;
|
||||
#endif
|
||||
#if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
|
||||
struct in6_ifreq ifr6;
|
||||
|
||||
@@ -226,7 +243,7 @@ int iface_enumerate(int family, void *parm, int (*callback)())
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
#endif /* defined(HAVE_BSD_NETWORK) || defined(HAVE_SOLARIS_NETWORK) */
|
||||
|
||||
|
||||
#if defined(HAVE_BSD_NETWORK) && defined(HAVE_DHCP)
|
||||
@@ -345,6 +362,87 @@ void send_via_bpf(struct dhcp_packet *mess, size_t len,
|
||||
while (writev(daemon->dhcp_raw_fd, iov, 4) == -1 && retry_send());
|
||||
}
|
||||
|
||||
#endif /* defined(HAVE_BSD_NETWORK) && defined(HAVE_DHCP) */
|
||||
|
||||
|
||||
#ifdef HAVE_BSD_NETWORK
|
||||
|
||||
void route_init(void)
|
||||
{
|
||||
/* AF_UNSPEC: all addr families */
|
||||
daemon->routefd = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC);
|
||||
|
||||
if (daemon->routefd == -1 || !fix_fd(daemon->routefd))
|
||||
die(_("cannot create PF_ROUTE socket: %s"), NULL, EC_BADNET);
|
||||
}
|
||||
|
||||
void route_sock(time_t now)
|
||||
{
|
||||
struct if_msghdr *msg;
|
||||
int rc = recv(daemon->routefd, daemon->packet, daemon->packet_buff_sz, 0);
|
||||
|
||||
if (rc < 4)
|
||||
return;
|
||||
|
||||
msg = (struct if_msghdr *)daemon->packet;
|
||||
|
||||
if (rc < msg->ifm_msglen)
|
||||
return;
|
||||
|
||||
if (msg->ifm_version != RTM_VERSION)
|
||||
{
|
||||
static int warned = 0;
|
||||
if (!warned)
|
||||
{
|
||||
my_syslog(LOG_WARNING, _("Unknown protocol version from route socket"));
|
||||
warned = 1;
|
||||
}
|
||||
}
|
||||
else if (msg->ifm_type == RTM_NEWADDR)
|
||||
{
|
||||
del_family = 0;
|
||||
newaddress(now);
|
||||
}
|
||||
else if (msg->ifm_type == RTM_DELADDR)
|
||||
{
|
||||
/* There's a race in the kernel, such that if we run iface_enumerate() immediately
|
||||
we get a DELADDR event, the deleted address still appears. Here we store the deleted address
|
||||
in a static variable, and omit it from the set returned by iface_enumerate() */
|
||||
int mask = ((struct ifa_msghdr *)msg)->ifam_addrs;
|
||||
int maskvec[] = { RTA_DST, RTA_GATEWAY, RTA_NETMASK, RTA_GENMASK,
|
||||
RTA_IFP, RTA_IFA, RTA_AUTHOR, RTA_BRD };
|
||||
int of;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0, of = sizeof(struct ifa_msghdr); of < rc && i < sizeof(maskvec)/sizeof(maskvec[0]); i++)
|
||||
if (mask & maskvec[i])
|
||||
{
|
||||
struct sockaddr *sa = (struct sockaddr *)((char *)msg + of);
|
||||
size_t diff = (sa->sa_len != 0) ? sa->sa_len : sizeof(long);
|
||||
|
||||
if (maskvec[i] == RTA_IFA)
|
||||
{
|
||||
del_family = sa->sa_family;
|
||||
if (del_family == AF_INET)
|
||||
del_addr.addr.addr4 = ((struct sockaddr_in *)sa)->sin_addr;
|
||||
#ifdef HAVE_IPV6
|
||||
else if (del_family == AF_INET6)
|
||||
del_addr.addr.addr6 = ((struct sockaddr_in6 *)sa)->sin6_addr;
|
||||
#endif
|
||||
else
|
||||
del_family = 0;
|
||||
}
|
||||
|
||||
of += diff;
|
||||
/* round up as needed */
|
||||
if (diff & (sizeof(long) - 1))
|
||||
of += sizeof(long) - (diff & (sizeof(long) - 1));
|
||||
}
|
||||
|
||||
newaddress(now);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* HAVE_BSD_NETWORK */
|
||||
|
||||
|
||||
|
||||
355
src/cache.c
355
src/cache.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -25,9 +25,6 @@ static int cache_inserted = 0, cache_live_freed = 0, insert_error;
|
||||
static union bigname *big_free = NULL;
|
||||
static int bignames_left, hash_size;
|
||||
static int uid = 1;
|
||||
#ifdef HAVE_DNSSEC
|
||||
static struct keydata *keyblock_free = NULL;
|
||||
#endif
|
||||
|
||||
/* type->string mapping: this is also used by the name-hash function as a mixing table. */
|
||||
static const struct {
|
||||
@@ -56,7 +53,11 @@ static const struct {
|
||||
{ 38, "A6" },
|
||||
{ 39, "DNAME" },
|
||||
{ 41, "OPT" },
|
||||
{ 43, "DS" },
|
||||
{ 46, "RRSIG" },
|
||||
{ 47, "NSEC" },
|
||||
{ 48, "DNSKEY" },
|
||||
{ 50, "NSEC3" },
|
||||
{ 249, "TKEY" },
|
||||
{ 250, "TSIG" },
|
||||
{ 251, "IXFR" },
|
||||
@@ -171,7 +172,22 @@ static void cache_hash(struct crec *crecp)
|
||||
crecp->hash_next = *up;
|
||||
*up = crecp;
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
static void cache_blockdata_free(struct crec *crecp)
|
||||
{
|
||||
if (crecp->flags & F_DNSKEY)
|
||||
{
|
||||
if (crecp->flags & F_DS)
|
||||
blockdata_free(crecp->addr.sig.keydata);
|
||||
else
|
||||
blockdata_free(crecp->addr.key.keydata);
|
||||
}
|
||||
else if (crecp->flags & F_DS)
|
||||
blockdata_free(crecp->addr.ds.keydata);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void cache_free(struct crec *crecp)
|
||||
{
|
||||
crecp->flags &= ~F_FORWARD;
|
||||
@@ -196,9 +212,9 @@ static void cache_free(struct crec *crecp)
|
||||
big_free = crecp->name.bname;
|
||||
crecp->flags &= ~F_BIGNAME;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
else if (crecp->flags & (F_DNSKEY | F_DS))
|
||||
keydata_free(crecp->addr.key.keydata);
|
||||
cache_blockdata_free(crecp);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -317,27 +333,52 @@ static int cache_scan_free(char *name, struct all_addr *addr, time_t now, unsign
|
||||
if (flags & F_FORWARD)
|
||||
{
|
||||
for (up = hash_bucket(name), crecp = *up; crecp; crecp = crecp->hash_next)
|
||||
if (is_expired(now, crecp) || is_outdated_cname_pointer(crecp))
|
||||
{
|
||||
*up = crecp->hash_next;
|
||||
if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
|
||||
{
|
||||
cache_unlink(crecp);
|
||||
cache_free(crecp);
|
||||
}
|
||||
}
|
||||
else if ((crecp->flags & F_FORWARD) &&
|
||||
((flags & crecp->flags & F_TYPE) || ((crecp->flags | flags) & F_CNAME)) &&
|
||||
hostname_isequal(cache_get_name(crecp), name))
|
||||
{
|
||||
if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))
|
||||
return 0;
|
||||
*up = crecp->hash_next;
|
||||
cache_unlink(crecp);
|
||||
cache_free(crecp);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (is_expired(now, crecp) || is_outdated_cname_pointer(crecp))
|
||||
{
|
||||
*up = crecp->hash_next;
|
||||
if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
|
||||
{
|
||||
cache_unlink(crecp);
|
||||
cache_free(crecp);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((crecp->flags & F_FORWARD) && hostname_isequal(cache_get_name(crecp), name))
|
||||
{
|
||||
/* Don't delete DNSSEC in favour of a CNAME, they can co-exist */
|
||||
if ((flags & crecp->flags & (F_IPV4 | F_IPV6)) ||
|
||||
(((crecp->flags | flags) & F_CNAME) && !(crecp->flags & (F_DNSKEY | F_DS))))
|
||||
{
|
||||
if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))
|
||||
return 0;
|
||||
*up = crecp->hash_next;
|
||||
cache_unlink(crecp);
|
||||
cache_free(crecp);
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
/* Deletion has to be class-sensitive for DS, DNSKEY, RRSIG, also
|
||||
type-covered sensitive for RRSIG */
|
||||
if ((flags & (F_DNSKEY | F_DS)) &&
|
||||
(flags & (F_DNSKEY | F_DS)) == (crecp->flags & (F_DNSKEY | F_DS)) &&
|
||||
crecp->uid == addr->addr.dnssec.class &&
|
||||
(!((flags & (F_DS | F_DNSKEY)) == (F_DS | F_DNSKEY)) ||
|
||||
crecp->addr.sig.type_covered == addr->addr.dnssec.type))
|
||||
{
|
||||
if (crecp->flags & F_CONFIG)
|
||||
return 0;
|
||||
*up = crecp->hash_next;
|
||||
cache_unlink(crecp);
|
||||
cache_free(crecp);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
up = &crecp->hash_next;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -410,16 +451,16 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
|
||||
if (daemon->max_cache_ttl != 0 && daemon->max_cache_ttl < ttl)
|
||||
ttl = daemon->max_cache_ttl;
|
||||
|
||||
/* Don't log keys */
|
||||
if (flags & (F_IPV4 | F_IPV6))
|
||||
/* Don't log keys here, done elsewhere */
|
||||
if (flags & (F_IPV4 | F_IPV6 | F_CNAME))
|
||||
log_query(flags | F_UPSTREAM, name, addr, NULL);
|
||||
|
||||
/* if previous insertion failed give up now. */
|
||||
if (insert_error)
|
||||
return NULL;
|
||||
|
||||
|
||||
/* First remove any expired entries and entries for the name/address we
|
||||
are currently inserting. Fail is we attempt to delete a name from
|
||||
are currently inserting. Fail if we attempt to delete a name from
|
||||
/etc/hosts or DHCP. */
|
||||
if (!cache_scan_free(name, addr, now, flags))
|
||||
{
|
||||
@@ -447,14 +488,32 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
|
||||
insert. Once in this state, all inserts will probably fail. */
|
||||
if (free_avail)
|
||||
{
|
||||
static int warned = 0;
|
||||
if (!warned)
|
||||
{
|
||||
my_syslog(LOG_ERR, _("Internal error in cache."));
|
||||
warned = 1;
|
||||
}
|
||||
insert_error = 1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (freed_all)
|
||||
{
|
||||
struct all_addr free_addr = new->addr.addr;;
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
/* For DNSSEC records, addr holds class and type_covered for RRSIG */
|
||||
if (new->flags & (F_DS | F_DNSKEY))
|
||||
{
|
||||
free_addr.addr.dnssec.class = new->uid;
|
||||
if ((new->flags & (F_DS | F_DNSKEY)) == (F_DS | F_DNSKEY))
|
||||
free_addr.addr.dnssec.type = new->addr.sig.type_covered;
|
||||
}
|
||||
#endif
|
||||
|
||||
free_avail = 1; /* Must be free space now. */
|
||||
cache_scan_free(cache_get_name(new), &new->addr.addr, now, new->flags);
|
||||
cache_scan_free(cache_get_name(new), &free_addr, now, new->flags);
|
||||
cache_live_freed++;
|
||||
}
|
||||
else
|
||||
@@ -466,7 +525,7 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
|
||||
}
|
||||
|
||||
/* Check if we need to and can allocate extra memory for a long name.
|
||||
If that fails, give up now. */
|
||||
If that fails, give up now, always succeed for DNSSEC records. */
|
||||
if (name && (strlen(name) > SMALLDNAME-1))
|
||||
{
|
||||
if (big_free)
|
||||
@@ -474,13 +533,13 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
|
||||
big_name = big_free;
|
||||
big_free = big_free->next;
|
||||
}
|
||||
else if (!bignames_left ||
|
||||
else if ((bignames_left == 0 && !(flags & (F_DS | F_DNSKEY))) ||
|
||||
!(big_name = (union bigname *)whine_malloc(sizeof(union bigname))))
|
||||
{
|
||||
insert_error = 1;
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
else if (bignames_left != 0)
|
||||
bignames_left--;
|
||||
|
||||
}
|
||||
@@ -535,10 +594,13 @@ void cache_end_insert(void)
|
||||
new_chain = NULL;
|
||||
}
|
||||
|
||||
struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsigned short prot)
|
||||
struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsigned int prot)
|
||||
{
|
||||
struct crec *ans;
|
||||
int no_rr = prot & F_NO_RR;
|
||||
|
||||
prot &= ~F_NO_RR;
|
||||
|
||||
if (crecp) /* iterating */
|
||||
ans = crecp->next;
|
||||
else
|
||||
@@ -555,6 +617,9 @@ struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsi
|
||||
if (!is_expired(now, crecp) && !is_outdated_cname_pointer(crecp))
|
||||
{
|
||||
if ((crecp->flags & F_FORWARD) &&
|
||||
#ifdef HAVE_DNSSEC
|
||||
((crecp->flags & (F_DNSKEY | F_DS)) == (prot & (F_DNSKEY | F_DS))) &&
|
||||
#endif
|
||||
(crecp->flags & prot) &&
|
||||
hostname_isequal(cache_get_name(crecp), name))
|
||||
{
|
||||
@@ -583,7 +648,7 @@ struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsi
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!insert)
|
||||
if (!insert && !no_rr)
|
||||
{
|
||||
insert = up;
|
||||
ins_flags = crecp->flags & (F_REVERSE | F_IMMORTAL);
|
||||
@@ -612,7 +677,10 @@ struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsi
|
||||
|
||||
if (ans &&
|
||||
(ans->flags & F_FORWARD) &&
|
||||
(ans->flags & prot) &&
|
||||
#ifdef HAVE_DNSSEC
|
||||
((ans->flags & (F_DNSKEY | F_DS)) == (prot & (F_DNSKEY | F_DS))) &&
|
||||
#endif
|
||||
(ans->flags & prot) &&
|
||||
hostname_isequal(cache_get_name(ans), name))
|
||||
return ans;
|
||||
|
||||
@@ -620,7 +688,7 @@ struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsi
|
||||
}
|
||||
|
||||
struct crec *cache_find_by_addr(struct crec *crecp, struct all_addr *addr,
|
||||
time_t now, unsigned short prot)
|
||||
time_t now, unsigned int prot)
|
||||
{
|
||||
struct crec *ans;
|
||||
#ifdef HAVE_IPV6
|
||||
@@ -916,12 +984,18 @@ void cache_reload(void)
|
||||
struct name_list *nl;
|
||||
struct cname *a;
|
||||
struct interface_name *intr;
|
||||
#ifdef HAVE_DNSSEC
|
||||
struct ds_config *ds;
|
||||
#endif
|
||||
|
||||
cache_inserted = cache_live_freed = 0;
|
||||
|
||||
for (i=0; i<hash_size; i++)
|
||||
for (cache = hash_table[i], up = &hash_table[i]; cache; cache = tmp)
|
||||
{
|
||||
#ifdef HAVE_DNSSEC
|
||||
cache_blockdata_free(cache);
|
||||
#endif
|
||||
tmp = cache->hash_next;
|
||||
if (cache->flags & (F_HOSTS | F_CONFIG))
|
||||
{
|
||||
@@ -955,6 +1029,22 @@ void cache_reload(void)
|
||||
cache_hash(cache);
|
||||
add_hosts_cname(cache); /* handle chains */
|
||||
}
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
for (ds = daemon->ds; ds; ds = ds->next)
|
||||
if ((cache = whine_malloc(sizeof(struct crec))) &&
|
||||
(cache->addr.ds.keydata = blockdata_alloc(ds->digest, ds->digestlen)))
|
||||
{
|
||||
cache->flags = F_FORWARD | F_IMMORTAL | F_DS | F_CONFIG | F_NAMEP;
|
||||
cache->name.namep = ds->name;
|
||||
cache->addr.ds.keylen = ds->digestlen;
|
||||
cache->addr.ds.algo = ds->algo;
|
||||
cache->addr.ds.keytag = ds->keytag;
|
||||
cache->addr.ds.digest = ds->digest_type;
|
||||
cache->uid = ds->class;
|
||||
cache_hash(cache);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* borrow the packet buffer for a temporary by-address hash */
|
||||
memset(daemon->packet, 0, daemon->packet_buff_sz);
|
||||
@@ -1155,6 +1245,7 @@ void cache_add_dhcp_entry(char *host_name, int prot,
|
||||
void dump_cache(time_t now)
|
||||
{
|
||||
struct server *serv, *serv1;
|
||||
char *t = "";
|
||||
|
||||
my_syslog(LOG_INFO, _("time %lu"), (unsigned long)now);
|
||||
my_syslog(LOG_INFO, _("cache size %d, %d/%d cache insertions re-used unexpired cache entries."),
|
||||
@@ -1164,6 +1255,9 @@ void dump_cache(time_t now)
|
||||
#ifdef HAVE_AUTH
|
||||
my_syslog(LOG_INFO, _("queries for authoritative zones %u"), daemon->auth_answer);
|
||||
#endif
|
||||
#ifdef HAVE_DNSSEC
|
||||
blockdata_report();
|
||||
#endif
|
||||
|
||||
/* sum counts from different records for same server */
|
||||
for (serv = daemon->servers; serv; serv = serv->next)
|
||||
@@ -1192,35 +1286,43 @@ void dump_cache(time_t now)
|
||||
{
|
||||
struct crec *cache ;
|
||||
int i;
|
||||
my_syslog(LOG_INFO, "Host Address Flags Expires");
|
||||
my_syslog(LOG_INFO, "Host Address Flags Expires");
|
||||
|
||||
for (i=0; i<hash_size; i++)
|
||||
for (cache = hash_table[i]; cache; cache = cache->hash_next)
|
||||
{
|
||||
char *a, *p = daemon->namebuff;
|
||||
p += sprintf(p, "%-40.40s ", cache_get_name(cache));
|
||||
if ((cache->flags & F_NEG) && (cache->flags & F_FORWARD))
|
||||
a = "";
|
||||
else if (cache->flags & F_CNAME)
|
||||
{
|
||||
a = "";
|
||||
if (!is_outdated_cname_pointer(cache))
|
||||
a = cache_get_cname_target(cache);
|
||||
}
|
||||
char *a = daemon->addrbuff, *p = daemon->namebuff, *n = cache_get_name(cache);
|
||||
*a = 0;
|
||||
if (strlen(n) == 0 && !(cache->flags & F_REVERSE))
|
||||
n = "<Root>";
|
||||
p += sprintf(p, "%-40.40s ", n);
|
||||
if ((cache->flags & F_CNAME) && !is_outdated_cname_pointer(cache))
|
||||
a = cache_get_cname_target(cache);
|
||||
#ifdef HAVE_DNSSEC
|
||||
else if (cache->flags & F_DS)
|
||||
{
|
||||
if (cache->flags & F_DNSKEY)
|
||||
{
|
||||
/* RRSIG */
|
||||
a = daemon->addrbuff;
|
||||
sprintf(a, "%5u %3u %s", cache->addr.sig.keytag,
|
||||
cache->addr.sig.algo, querystr("", cache->addr.sig.type_covered));
|
||||
}
|
||||
else
|
||||
{
|
||||
a = daemon->addrbuff;
|
||||
sprintf(a, "%5u %3u %3u", cache->addr.ds.keytag,
|
||||
cache->addr.ds.algo, cache->addr.ds.digest);
|
||||
}
|
||||
}
|
||||
else if (cache->flags & F_DNSKEY)
|
||||
{
|
||||
a = daemon->addrbuff;
|
||||
sprintf(a, "%3u %u", cache->addr.key.algo, cache->uid);
|
||||
}
|
||||
else if (cache->flags & F_DS)
|
||||
{
|
||||
a = daemon->addrbuff;
|
||||
sprintf(a, "%5u %3u %3u %u", cache->addr.key.flags_or_keyid,
|
||||
cache->addr.key.algo, cache->addr.key.digest, cache->uid);
|
||||
sprintf(a, "%5u %3u %3u", cache->addr.key.keytag,
|
||||
cache->addr.key.algo, cache->addr.key.flags);
|
||||
}
|
||||
#endif
|
||||
else
|
||||
else if (!(cache->flags & F_NEG) || !(cache->flags & F_FORWARD))
|
||||
{
|
||||
a = daemon->addrbuff;
|
||||
if (cache->flags & F_IPV4)
|
||||
@@ -1231,12 +1333,21 @@ void dump_cache(time_t now)
|
||||
#endif
|
||||
}
|
||||
|
||||
p += sprintf(p, "%-30.30s %s%s%s%s%s%s%s%s%s%s%s%s%s ", a,
|
||||
cache->flags & F_IPV4 ? "4" : "",
|
||||
cache->flags & F_IPV6 ? "6" : "",
|
||||
cache->flags & F_DNSKEY ? "K" : "",
|
||||
cache->flags & F_DS ? "S" : "",
|
||||
cache->flags & F_CNAME ? "C" : "",
|
||||
if (cache->flags & F_IPV4)
|
||||
t = "4";
|
||||
else if (cache->flags & F_IPV6)
|
||||
t = "6";
|
||||
else if (cache->flags & F_CNAME)
|
||||
t = "C";
|
||||
#ifdef HAVE_DNSSEC
|
||||
else if ((cache->flags & (F_DS | F_DNSKEY)) == (F_DS | F_DNSKEY))
|
||||
t = "G"; /* DNSKEY and DS set -> RRISG */
|
||||
else if (cache->flags & F_DS)
|
||||
t = "S";
|
||||
else if (cache->flags & F_DNSKEY)
|
||||
t = "K";
|
||||
#endif
|
||||
p += sprintf(p, "%-30.30s %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" : " ",
|
||||
@@ -1271,14 +1382,45 @@ char *record_source(int index)
|
||||
return "<unknown>";
|
||||
}
|
||||
|
||||
void querystr(char *desc, char *str, unsigned short type)
|
||||
char *querystr(char *desc, unsigned short type)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
sprintf(str, "%s[type=%d]", desc, type);
|
||||
int len = 10; /* strlen("type=xxxxx") */
|
||||
const char *types = NULL;
|
||||
static char *buff = NULL;
|
||||
static int bufflen = 0;
|
||||
|
||||
for (i = 0; i < (sizeof(typestr)/sizeof(typestr[0])); i++)
|
||||
if (typestr[i].type == type)
|
||||
sprintf(str,"%s[%s]", desc, typestr[i].name);
|
||||
{
|
||||
types = typestr[i].name;
|
||||
len = strlen(types);
|
||||
break;
|
||||
}
|
||||
|
||||
len += 3; /* braces, terminator */
|
||||
len += strlen(desc);
|
||||
|
||||
if (!buff || bufflen < len)
|
||||
{
|
||||
if (buff)
|
||||
free(buff);
|
||||
else if (len < 20)
|
||||
len = 20;
|
||||
|
||||
buff = whine_malloc(len);
|
||||
bufflen = len;
|
||||
}
|
||||
|
||||
if (buff)
|
||||
{
|
||||
if (types)
|
||||
sprintf(buff, "%s[%s]", desc, types);
|
||||
else
|
||||
sprintf(buff, "%s[type=%d]", desc, type);
|
||||
}
|
||||
|
||||
return buff ? buff : "";
|
||||
}
|
||||
|
||||
void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
|
||||
@@ -1291,13 +1433,20 @@ void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
|
||||
|
||||
if (addr)
|
||||
{
|
||||
if (flags & F_KEYTAG)
|
||||
sprintf(daemon->addrbuff, arg, addr->addr.keytag);
|
||||
else
|
||||
{
|
||||
#ifdef HAVE_IPV6
|
||||
inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
|
||||
addr, daemon->addrbuff, ADDRSTRLEN);
|
||||
inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
|
||||
addr, daemon->addrbuff, ADDRSTRLEN);
|
||||
#else
|
||||
strncpy(daemon->addrbuff, inet_ntoa(addr->addr.addr4), ADDRSTRLEN);
|
||||
strncpy(daemon->addrbuff, inet_ntoa(addr->addr.addr4), ADDRSTRLEN);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
dest = arg;
|
||||
|
||||
if (flags & F_REVERSE)
|
||||
{
|
||||
@@ -1308,14 +1457,7 @@ void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
|
||||
if (flags & F_NEG)
|
||||
{
|
||||
if (flags & F_NXDOMAIN)
|
||||
{
|
||||
if (flags & F_IPV4)
|
||||
dest = "NXDOMAIN-IPv4";
|
||||
else if (flags & F_IPV6)
|
||||
dest = "NXDOMAIN-IPv6";
|
||||
else
|
||||
dest = "NXDOMAIN";
|
||||
}
|
||||
dest = "NXDOMAIN";
|
||||
else
|
||||
{
|
||||
if (flags & F_IPV4)
|
||||
@@ -1339,6 +1481,8 @@ void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
|
||||
source = arg;
|
||||
else if (flags & F_UPSTREAM)
|
||||
source = "reply";
|
||||
else if (flags & F_SECSTAT)
|
||||
source = "validation";
|
||||
else if (flags & F_AUTH)
|
||||
source = "auth";
|
||||
else if (flags & F_SERVER)
|
||||
@@ -1351,6 +1495,11 @@ void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
|
||||
source = arg;
|
||||
verb = "from";
|
||||
}
|
||||
else if (flags & F_DNSSEC)
|
||||
{
|
||||
source = arg;
|
||||
verb = "to";
|
||||
}
|
||||
else
|
||||
source = "cached";
|
||||
|
||||
@@ -1360,50 +1509,4 @@ void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
|
||||
my_syslog(LOG_INFO, "%s %s %s %s", source, name, verb, dest);
|
||||
}
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
struct keydata *keydata_alloc(char *data, size_t len)
|
||||
{
|
||||
struct keydata *block, *ret = NULL;
|
||||
struct keydata **prev = &ret;
|
||||
while (len > 0)
|
||||
{
|
||||
if (keyblock_free)
|
||||
{
|
||||
block = keyblock_free;
|
||||
keyblock_free = block->next;
|
||||
}
|
||||
else
|
||||
block = whine_malloc(sizeof(struct keydata));
|
||||
|
||||
if (!block)
|
||||
{
|
||||
/* failed to alloc, free partial chain */
|
||||
keydata_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(block->key, data, len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len);
|
||||
data += KEYBLOCK_LEN;
|
||||
len -= KEYBLOCK_LEN;
|
||||
*prev = block;
|
||||
prev = &block->next;
|
||||
block->next = NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void keydata_free(struct keydata *blocks)
|
||||
{
|
||||
struct keydata *tmp;
|
||||
|
||||
if (blocks)
|
||||
{
|
||||
for (tmp = blocks; tmp->next; tmp = tmp->next);
|
||||
tmp->next = keyblock_free;
|
||||
keyblock_free = blocks;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
27
src/config.h
27
src/config.h
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -18,7 +18,8 @@
|
||||
#define MAX_PROCS 20 /* max no children for TCP requests */
|
||||
#define CHILD_LIFETIME 150 /* secs 'till terminated (RFC1035 suggests > 120s) */
|
||||
#define EDNS_PKTSZ 4096 /* default max EDNS.0 UDP packet from RFC5625 */
|
||||
#define KEYBLOCK_LEN 140 /* choose to mininise fragmentation when storing DNSSEC keys */
|
||||
#define KEYBLOCK_LEN 35 /* choose to mininise fragmentation when storing DNSSEC keys */
|
||||
#define DNSSEC_WORK 50 /* Max number of queries to validate one question */
|
||||
#define TIMEOUT 10 /* drop UDP queries after TIMEOUT seconds */
|
||||
#define FORWARD_TEST 50 /* try all servers every 50 queries */
|
||||
#define FORWARD_TIME 20 /* or 20 seconds */
|
||||
@@ -124,6 +125,11 @@ RESOLVFILE
|
||||
|
||||
*/
|
||||
|
||||
/* Defining this builds a binary which handles time differently and works better on a system without a
|
||||
stable RTC (it uses uptime, not epoch time) and writes the DHCP leases file less often to avoid flash wear.
|
||||
*/
|
||||
|
||||
/* #define HAVE_BROKEN_RTC */
|
||||
|
||||
/* The default set of options to build. Built with these options, dnsmasq
|
||||
has no library dependencies other than libc */
|
||||
@@ -134,11 +140,19 @@ RESOLVFILE
|
||||
#define HAVE_SCRIPT
|
||||
#define HAVE_AUTH
|
||||
#define HAVE_IPSET
|
||||
|
||||
/* Build options which require external libraries.
|
||||
|
||||
Defining HAVE_<opt>_STATIC as _well_ as HAVE_<opt> will link the library statically.
|
||||
|
||||
You can use "make COPTS=-DHAVE_<opt>" instead of editing these.
|
||||
*/
|
||||
|
||||
/* #define HAVE_LUASCRIPT */
|
||||
/* #define HAVE_BROKEN_RTC */
|
||||
/* #define HAVE_DBUS */
|
||||
/* #define HAVE_IDN */
|
||||
/* #define HAVE_CONNTRACK */
|
||||
/* #define HAVE_DNSSEC */
|
||||
|
||||
|
||||
/* Default locations for important system files. */
|
||||
@@ -384,7 +398,12 @@ static char *compile_opts =
|
||||
#ifndef HAVE_AUTH
|
||||
"no-"
|
||||
#endif
|
||||
"auth";
|
||||
"auth "
|
||||
#ifndef HAVE_DNSSEC
|
||||
"no-"
|
||||
#endif
|
||||
"DNSSEC";
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 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
|
||||
@@ -399,13 +399,13 @@ void dhcp_update_configs(struct dhcp_config *configs)
|
||||
if (cache_find_by_name(crec, config->hostname, 0, cacheflags))
|
||||
{
|
||||
/* use primary (first) address */
|
||||
while (crec && !(crec->flags & F_REVERSE))
|
||||
crec = cache_find_by_name(crec, config->hostname, 0, cacheflags);
|
||||
if (!crec)
|
||||
continue; /* should be never */
|
||||
inet_ntop(prot, &crec->addr.addr, daemon->addrbuff, ADDRSTRLEN);
|
||||
my_syslog(MS_DHCP | LOG_WARNING, _("%s has more than one address in hostsfile, using %s for DHCP"),
|
||||
config->hostname, daemon->addrbuff);
|
||||
while (crec && !(crec->flags & F_REVERSE))
|
||||
crec = cache_find_by_name(crec, config->hostname, 0, cacheflags);
|
||||
if (!crec)
|
||||
continue; /* should be never */
|
||||
inet_ntop(prot, &crec->addr.addr, daemon->addrbuff, ADDRSTRLEN);
|
||||
my_syslog(MS_DHCP | LOG_WARNING, _("%s has more than one address in hostsfile, using %s for DHCP"),
|
||||
config->hostname, daemon->addrbuff);
|
||||
}
|
||||
|
||||
if (prot == AF_INET &&
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 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
src/dhcp6.c
14
src/dhcp6.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 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
|
||||
@@ -23,7 +23,7 @@
|
||||
struct iface_param {
|
||||
struct dhcp_context *current;
|
||||
struct dhcp_relay *relay;
|
||||
struct in6_addr fallback, relay_local;
|
||||
struct in6_addr fallback, relay_local, ll_addr, ula_addr;
|
||||
int ind, addr_match;
|
||||
};
|
||||
|
||||
@@ -158,6 +158,8 @@ void dhcp6_packet(time_t now)
|
||||
parm.ind = if_index;
|
||||
parm.addr_match = 0;
|
||||
memset(&parm.fallback, 0, IN6ADDRSZ);
|
||||
memset(&parm.ll_addr, 0, IN6ADDRSZ);
|
||||
memset(&parm.ula_addr, 0, IN6ADDRSZ);
|
||||
|
||||
for (context = daemon->dhcp6; context; context = context->next)
|
||||
if (IN6_IS_ADDR_UNSPECIFIED(&context->start6) && context->prefix == 0)
|
||||
@@ -210,7 +212,7 @@ void dhcp6_packet(time_t now)
|
||||
lease_prune(NULL, now); /* lose any expired leases */
|
||||
|
||||
port = dhcp6_reply(parm.current, if_index, ifr.ifr_name, &parm.fallback,
|
||||
sz, &from.sin6_addr, now);
|
||||
&parm.ll_addr, &parm.ula_addr, sz, &from.sin6_addr, now);
|
||||
|
||||
lease_update_file(now);
|
||||
lease_update_dns(0);
|
||||
@@ -309,6 +311,11 @@ static int complete_context6(struct in6_addr *local, int prefix,
|
||||
|
||||
if (if_index == param->ind)
|
||||
{
|
||||
if (IN6_IS_ADDR_LINKLOCAL(local))
|
||||
param->ll_addr = *local;
|
||||
else if (IN6_IS_ADDR_ULA(local))
|
||||
param->ula_addr = *local;
|
||||
|
||||
if (!IN6_IS_ADDR_LOOPBACK(local) &&
|
||||
!IN6_IS_ADDR_LINKLOCAL(local) &&
|
||||
!IN6_IS_ADDR_MULTICAST(local))
|
||||
@@ -720,7 +727,6 @@ void dhcp_construct_contexts(time_t now)
|
||||
|
||||
if (context->flags & CONTEXT_GC && !(context->flags & CONTEXT_OLD))
|
||||
{
|
||||
|
||||
if ((context->flags & (CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS)) ||
|
||||
option_bool(OPT_RA))
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 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
|
||||
@@ -36,20 +36,38 @@
|
||||
|
||||
#define C_IN 1 /* the arpa internet */
|
||||
#define C_CHAOS 3 /* for chaos net (MIT) */
|
||||
#define C_HESIOD 4 /* hesiod */
|
||||
#define C_ANY 255 /* wildcard match */
|
||||
|
||||
#define T_A 1
|
||||
#define T_NS 2
|
||||
#define T_NS 2
|
||||
#define T_MD 3
|
||||
#define T_MF 4
|
||||
#define T_CNAME 5
|
||||
#define T_SOA 6
|
||||
#define T_MB 7
|
||||
#define T_MG 8
|
||||
#define T_MR 9
|
||||
#define T_PTR 12
|
||||
#define T_MINFO 14
|
||||
#define T_MX 15
|
||||
#define T_TXT 16
|
||||
#define T_RP 17
|
||||
#define T_AFSDB 18
|
||||
#define T_RT 21
|
||||
#define T_SIG 24
|
||||
#define T_PX 26
|
||||
#define T_AAAA 28
|
||||
#define T_NXT 30
|
||||
#define T_SRV 33
|
||||
#define T_NAPTR 35
|
||||
#define T_KX 36
|
||||
#define T_DNAME 39
|
||||
#define T_OPT 41
|
||||
#define T_DS 43
|
||||
#define T_RRSIG 46
|
||||
#define T_NSEC 47
|
||||
#define T_DNSKEY 48
|
||||
#define T_TKEY 249
|
||||
#define T_TSIG 250
|
||||
#define T_AXFR 252
|
||||
@@ -59,7 +77,6 @@
|
||||
#define EDNS0_OPTION_MAC 65001 /* dyndns.org temporary assignment */
|
||||
#define EDNS0_OPTION_CLIENT_SUBNET 8 /* IANA */
|
||||
|
||||
|
||||
struct dns_header {
|
||||
u16 id;
|
||||
u8 hb3,hb4;
|
||||
@@ -78,6 +95,8 @@ struct dns_header {
|
||||
#define HB4_RCODE 0x0f
|
||||
|
||||
#define OPCODE(x) (((x)->hb3 & HB3_OPCODE) >> 3)
|
||||
#define SET_OPCODE(x, code) (x)->hb3 = ((x)->hb3 & ~HB3_OPCODE) | code
|
||||
|
||||
#define RCODE(x) ((x)->hb4 & HB4_RCODE)
|
||||
#define SET_RCODE(x, code) (x)->hb4 = ((x)->hb4 & ~HB4_RCODE) | code
|
||||
|
||||
@@ -117,3 +136,8 @@ struct dns_header {
|
||||
(cp) += 4; \
|
||||
}
|
||||
|
||||
#define CHECK_LEN(header, pp, plen, len) \
|
||||
((size_t)((pp) - (unsigned char *)(header) + (len)) <= (plen))
|
||||
|
||||
#define ADD_RDLEN(header, pp, plen, len) \
|
||||
(!CHECK_LEN(header, pp, plen, len) ? 0 : (((pp) += (len)), 1))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -81,15 +81,25 @@ int main (int argc, char **argv)
|
||||
umask(022); /* known umask, create leases and pid files as 0644 */
|
||||
|
||||
read_opts(argc, argv, compile_opts);
|
||||
|
||||
|
||||
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);
|
||||
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID))
|
||||
daemon->keyname = safe_malloc(MAXDNAME);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DHCP
|
||||
if (!daemon->lease_file)
|
||||
@@ -131,6 +141,19 @@ int main (int argc, char **argv)
|
||||
}
|
||||
#endif
|
||||
|
||||
if (option_bool(OPT_DNSSEC_VALID))
|
||||
{
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (!daemon->ds)
|
||||
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);
|
||||
#else
|
||||
die(_("DNSSEC not available: set HAVE_DNSSEC in src/config.h"), NULL, EC_BADCONF);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef HAVE_TFTP
|
||||
if (option_bool(OPT_TFTP))
|
||||
die(_("TFTP server not available: set HAVE_TFTP in src/config.h"), NULL, EC_BADCONF);
|
||||
@@ -182,7 +205,7 @@ int main (int argc, char **argv)
|
||||
daemon->doing_dhcp6 = 1;
|
||||
if (context->flags & CONTEXT_RA)
|
||||
daemon->doing_ra = 1;
|
||||
#ifndef HAVE_LINUX_NETWORK
|
||||
#if !defined(HAVE_LINUX_NETWORK) && !defined(HAVE_BSD_NETWORK)
|
||||
if (context->flags & CONTEXT_TEMPLATE)
|
||||
die (_("dhcp-range constructor not available on this platform"), NULL, EC_BADCONF);
|
||||
#endif
|
||||
@@ -220,13 +243,15 @@ int main (int argc, char **argv)
|
||||
ipset_init();
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
#if defined(HAVE_LINUX_NETWORK)
|
||||
netlink_init();
|
||||
|
||||
if (option_bool(OPT_NOWILD) && option_bool(OPT_CLEVERBIND))
|
||||
die(_("cannot set --bind-interfaces and --bind-dynamic"), NULL, EC_BADCONF);
|
||||
#elif defined(HAVE_BSD_NETWORK)
|
||||
route_init();
|
||||
#endif
|
||||
|
||||
if (option_bool(OPT_NOWILD) && option_bool(OPT_CLEVERBIND))
|
||||
die(_("cannot set --bind-interfaces and --bind-dynamic"), NULL, EC_BADCONF);
|
||||
|
||||
if (!enumerate_interfaces(1) || !enumerate_interfaces(0))
|
||||
die(_("failed to find list of interfaces: %s"), NULL, EC_MISC);
|
||||
|
||||
@@ -279,7 +304,12 @@ int main (int argc, char **argv)
|
||||
#endif
|
||||
|
||||
if (daemon->port != 0)
|
||||
cache_init();
|
||||
{
|
||||
cache_init();
|
||||
#ifdef HAVE_DNSSEC
|
||||
blockdata_init();
|
||||
#endif
|
||||
}
|
||||
|
||||
if (option_bool(OPT_DBUS))
|
||||
#ifdef HAVE_DBUS
|
||||
@@ -628,11 +658,16 @@ int main (int argc, char **argv)
|
||||
my_syslog(LOG_INFO, _("DBus support enabled: bus connection pending"));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID))
|
||||
my_syslog(LOG_INFO, _("DNSSEC validation enabled"));
|
||||
#endif
|
||||
|
||||
if (log_err != 0)
|
||||
my_syslog(LOG_WARNING, _("warning: failed to change owner of %s: %s"),
|
||||
daemon->log_file, strerror(log_err));
|
||||
|
||||
|
||||
if (bind_fallback)
|
||||
my_syslog(LOG_WARNING, _("setting --bind-interfaces option because of OS limitations"));
|
||||
|
||||
@@ -808,11 +843,14 @@ int main (int argc, char **argv)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
#if defined(HAVE_LINUX_NETWORK)
|
||||
FD_SET(daemon->netlinkfd, &rset);
|
||||
bump_maxfd(daemon->netlinkfd, &maxfd);
|
||||
#elif defined(HAVE_BSD_NETWORK)
|
||||
FD_SET(daemon->routefd, &rset);
|
||||
bump_maxfd(daemon->routefd, &maxfd);
|
||||
#endif
|
||||
|
||||
|
||||
FD_SET(piperead, &rset);
|
||||
bump_maxfd(piperead, &maxfd);
|
||||
|
||||
@@ -867,9 +905,12 @@ int main (int argc, char **argv)
|
||||
warn_bound_listeners();
|
||||
}
|
||||
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
#if defined(HAVE_LINUX_NETWORK)
|
||||
if (FD_ISSET(daemon->netlinkfd, &rset))
|
||||
netlink_multicast(now);
|
||||
#elif defined(HAVE_BSD_NETWORK)
|
||||
if (FD_ISSET(daemon->routefd, &rset))
|
||||
route_sock(now);
|
||||
#endif
|
||||
|
||||
/* Check for changes to resolv files once per second max. */
|
||||
@@ -1304,7 +1345,7 @@ static int set_dns_listeners(time_t now, fd_set *set, int *maxfdp)
|
||||
|
||||
/* will we be able to get memory? */
|
||||
if (daemon->port != 0)
|
||||
get_new_frec(now, &wait);
|
||||
get_new_frec(now, &wait, 0);
|
||||
|
||||
for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
|
||||
{
|
||||
|
||||
142
src/dnsmasq.h
142
src/dnsmasq.h
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 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-2013 Simon Kelley"
|
||||
#define COPYRIGHT "Copyright (c) 2000-2014 Simon Kelley"
|
||||
|
||||
#ifndef NO_LARGEFILE
|
||||
/* Ensure we can use files >2GB (log files may grow this big) */
|
||||
@@ -50,12 +50,16 @@
|
||||
#include <getopt.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "ip6addr.h"
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned long long u64;
|
||||
|
||||
#define countof(x) (long)(sizeof(x) / sizeof(x[0]))
|
||||
#define MIN(a,b) ((a) < (b) ? (a) : (b))
|
||||
|
||||
#include "dns-protocol.h"
|
||||
#include "dhcp-protocol.h"
|
||||
#ifdef HAVE_DHCP6
|
||||
@@ -213,7 +217,7 @@ struct event_desc {
|
||||
#define OPT_NO_OVERRIDE 30
|
||||
#define OPT_NO_REBIND 31
|
||||
#define OPT_ADD_MAC 32
|
||||
#define OPT_DNSSEC 33
|
||||
#define OPT_DNSSEC_PROXY 33
|
||||
#define OPT_CONSEC_ADDR 34
|
||||
#define OPT_CONNTRACK 35
|
||||
#define OPT_FQDN_UPDATE 36
|
||||
@@ -225,7 +229,10 @@ struct event_desc {
|
||||
#define OPT_QUIET_DHCP 42
|
||||
#define OPT_QUIET_DHCP6 43
|
||||
#define OPT_QUIET_RA 44
|
||||
#define OPT_LAST 45
|
||||
#define OPT_DNSSEC_VALID 45
|
||||
#define OPT_DNSSEC_PERMISS 46
|
||||
#define OPT_DNSSEC_DEBUG 47
|
||||
#define OPT_LAST 48
|
||||
|
||||
/* 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. */
|
||||
@@ -238,6 +245,12 @@ struct all_addr {
|
||||
#ifdef HAVE_IPV6
|
||||
struct in6_addr addr6;
|
||||
#endif
|
||||
/* for log_query */
|
||||
unsigned int keytag;
|
||||
/* for cache_insert if RRSIG, DNSKEY, DS */
|
||||
struct {
|
||||
unsigned short class, type;
|
||||
} dnssec;
|
||||
} addr;
|
||||
};
|
||||
|
||||
@@ -282,6 +295,12 @@ struct cname {
|
||||
struct cname *next;
|
||||
};
|
||||
|
||||
struct ds_config {
|
||||
char *name, *digest;
|
||||
int digestlen, class, algo, keytag, digest_type;
|
||||
struct ds_config *next;
|
||||
};
|
||||
|
||||
#define ADDRLIST_LITERAL 1
|
||||
#define ADDRLIST_IPV6 2
|
||||
|
||||
@@ -331,8 +350,8 @@ union bigname {
|
||||
union bigname *next; /* freelist */
|
||||
};
|
||||
|
||||
struct keydata {
|
||||
struct keydata *next;
|
||||
struct blockdata {
|
||||
struct blockdata *next;
|
||||
unsigned char key[KEYBLOCK_LEN];
|
||||
};
|
||||
|
||||
@@ -349,14 +368,24 @@ struct crec {
|
||||
int uid; /* -1 if union is interface-name */
|
||||
} cname;
|
||||
struct {
|
||||
struct keydata *keydata;
|
||||
struct blockdata *keydata;
|
||||
unsigned short keylen, flags, keytag;
|
||||
unsigned char algo;
|
||||
unsigned char digest; /* DS only */
|
||||
unsigned short flags_or_keyid; /* flags for DNSKEY, keyid for DS */
|
||||
} key;
|
||||
} key;
|
||||
struct {
|
||||
struct blockdata *keydata;
|
||||
unsigned short keylen, keytag;
|
||||
unsigned char algo;
|
||||
unsigned char digest;
|
||||
} ds;
|
||||
struct {
|
||||
struct blockdata *keydata;
|
||||
unsigned short keylen, type_covered, keytag;
|
||||
char algo;
|
||||
} sig;
|
||||
} addr;
|
||||
time_t ttd; /* time to die */
|
||||
/* used as keylen if F_DS or F_DNSKEY, index to source for F_HOSTS */
|
||||
/* used as class if DNSKEY/DS/RRSIG, index to source for F_HOSTS */
|
||||
int uid;
|
||||
unsigned short flags;
|
||||
union {
|
||||
@@ -391,10 +420,10 @@ struct crec {
|
||||
#define F_QUERY (1u<<19)
|
||||
#define F_NOERR (1u<<20)
|
||||
#define F_AUTH (1u<<21)
|
||||
|
||||
/* composites */
|
||||
#define F_TYPE (F_IPV4 | F_IPV6 | F_DNSKEY | F_DS) /* Only one may be set */
|
||||
|
||||
#define F_DNSSEC (1u<<22)
|
||||
#define F_KEYTAG (1u<<23)
|
||||
#define F_SECSTAT (1u<<24)
|
||||
#define F_NO_RR (1u<<25)
|
||||
|
||||
|
||||
/* struct sockaddr is not large enough to hold any address,
|
||||
@@ -496,9 +525,27 @@ struct hostsfile {
|
||||
int index; /* matches to cache entries for logging */
|
||||
};
|
||||
|
||||
|
||||
/* DNSSEC status values. */
|
||||
#define STAT_SECURE 1
|
||||
#define STAT_INSECURE 2
|
||||
#define STAT_BOGUS 3
|
||||
#define STAT_NEED_DS 4
|
||||
#define STAT_NEED_KEY 5
|
||||
#define STAT_TRUNCATED 6
|
||||
|
||||
#define FREC_NOREBIND 1
|
||||
#define FREC_CHECKING_DISABLED 2
|
||||
#define FREC_HAS_SUBNET 4
|
||||
#define FREC_DNSKEY_QUERY 8
|
||||
#define FREC_DS_QUERY 16
|
||||
#define FREC_AD_QUESTION 32
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
#define HASH_SIZE 20 /* SHA-1 digest size */
|
||||
#else
|
||||
#define HASH_SIZE sizeof(int)
|
||||
#endif
|
||||
|
||||
struct frec {
|
||||
union mysockaddr source;
|
||||
@@ -511,8 +558,15 @@ struct frec {
|
||||
unsigned int iface;
|
||||
unsigned short orig_id, new_id;
|
||||
int fd, forwardall, flags;
|
||||
unsigned int crc;
|
||||
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 frec *dependent; /* Query awaiting internally-generated DNSKEY or DS query */
|
||||
struct frec *blocking_query; /* Query which is blocking us. */
|
||||
#endif
|
||||
struct frec *next;
|
||||
};
|
||||
|
||||
@@ -561,7 +615,7 @@ struct dhcp_lease {
|
||||
struct in6_addr addr6;
|
||||
int iaid;
|
||||
struct slaac_address {
|
||||
struct in6_addr addr, local;
|
||||
struct in6_addr addr;
|
||||
time_t ping_time;
|
||||
int backoff; /* zero -> confirmed */
|
||||
struct slaac_address *next;
|
||||
@@ -875,11 +929,17 @@ extern struct daemon {
|
||||
#ifdef OPTION6_PREFIX_CLASS
|
||||
struct prefix_class *prefix_classes;
|
||||
#endif
|
||||
#ifdef HAVE_DNSSEC
|
||||
struct ds_config *ds;
|
||||
#endif
|
||||
|
||||
/* globally used stuff for DNS */
|
||||
char *packet; /* packet buffer */
|
||||
int packet_buff_sz; /* size of above */
|
||||
char *namebuff; /* MAXDNAME size buffer */
|
||||
#ifdef HAVE_DNSSEC
|
||||
char *keyname; /* MAXDNAME size buffer */
|
||||
#endif
|
||||
unsigned int local_answer, queries_forwarded, auth_answer;
|
||||
struct frec *frec_list;
|
||||
struct serverfd *sfds;
|
||||
@@ -900,7 +960,7 @@ extern struct daemon {
|
||||
#if defined(HAVE_LINUX_NETWORK)
|
||||
int netlinkfd;
|
||||
#elif defined(HAVE_BSD_NETWORK)
|
||||
int dhcp_raw_fd, dhcp_icmp_fd;
|
||||
int dhcp_raw_fd, dhcp_icmp_fd, routefd;
|
||||
#endif
|
||||
struct iovec dhcp_packet;
|
||||
char *dhcp_buff, *dhcp_buff2, *dhcp_buff3;
|
||||
@@ -932,12 +992,12 @@ extern struct daemon {
|
||||
void cache_init(void);
|
||||
void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg);
|
||||
char *record_source(int index);
|
||||
void querystr(char *desc, char *str, unsigned short type);
|
||||
char *querystr(char *desc, unsigned short type);
|
||||
struct crec *cache_find_by_addr(struct crec *crecp,
|
||||
struct all_addr *addr, time_t now,
|
||||
unsigned short prot);
|
||||
unsigned int prot);
|
||||
struct crec *cache_find_by_name(struct crec *crecp,
|
||||
char *name, time_t now, unsigned short prot);
|
||||
char *name, time_t now, unsigned int prot);
|
||||
void cache_end_insert(void);
|
||||
void cache_start_insert(void);
|
||||
struct crec *cache_insert(char *name, struct all_addr *addr,
|
||||
@@ -950,9 +1010,14 @@ void dump_cache(time_t now);
|
||||
char *cache_get_name(struct crec *crecp);
|
||||
char *cache_get_cname_target(struct crec *crecp);
|
||||
struct crec *cache_enumerate(int init);
|
||||
|
||||
/* blockdata.c */
|
||||
#ifdef HAVE_DNSSEC
|
||||
struct keydata *keydata_alloc(char *data, size_t len);
|
||||
void keydata_free(struct keydata *blocks);
|
||||
void blockdata_init(void);
|
||||
void blockdata_report(void);
|
||||
struct blockdata *blockdata_alloc(char *data, size_t len);
|
||||
void *blockdata_retrieve(struct blockdata *block, size_t len, void *data);
|
||||
void blockdata_free(struct blockdata *blocks);
|
||||
#endif
|
||||
|
||||
/* domain.c */
|
||||
@@ -964,6 +1029,10 @@ int is_name_synthetic(int flags, char *name, struct all_addr *addr);
|
||||
int is_rev_synth(int flag, struct all_addr *addr, char *name);
|
||||
|
||||
/* rfc1035.c */
|
||||
int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
|
||||
char *name, int isExtract, int extrabytes);
|
||||
unsigned char *skip_name(unsigned char *ansp, struct dns_header *header, size_t plen, int extrabytes);
|
||||
unsigned char *skip_questions(struct dns_header *header, size_t plen);
|
||||
unsigned int extract_request(struct dns_header *header, size_t qlen,
|
||||
char *name, unsigned short *typep);
|
||||
size_t setup_reply(struct dns_header *header, size_t qlen,
|
||||
@@ -971,9 +1040,10 @@ size_t setup_reply(struct dns_header *header, size_t qlen,
|
||||
unsigned long local_ttl);
|
||||
int extract_addresses(struct dns_header *header, size_t qlen, char *namebuff,
|
||||
time_t now, char **ipsets, int is_sign, int checkrebind,
|
||||
int checking_disabled);
|
||||
int no_cache, int secure, int *doctored);
|
||||
size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
struct in_addr local_addr, struct in_addr local_netmask, time_t now);
|
||||
struct in_addr local_addr, struct in_addr local_netmask,
|
||||
time_t now, int *ad_reqd);
|
||||
int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
|
||||
struct bogus_addr *addr, time_t now);
|
||||
unsigned char *find_pseudoheader(struct dns_header *header, size_t plen,
|
||||
@@ -984,6 +1054,9 @@ size_t resize_packet(struct dns_header *header, size_t plen,
|
||||
unsigned char *pheader, size_t hlen);
|
||||
size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysockaddr *l3);
|
||||
size_t add_source_addr(struct dns_header *header, size_t plen, char *limit, union mysockaddr *source);
|
||||
#ifdef HAVE_DNSSEC
|
||||
size_t add_do_bit(struct dns_header *header, size_t plen, char *limit);
|
||||
#endif
|
||||
int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer);
|
||||
int add_resource_record(struct dns_header *header, char *limit, int *truncp,
|
||||
int nameoffset, unsigned char **pp, unsigned long ttl,
|
||||
@@ -1001,6 +1074,14 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen,
|
||||
int in_zone(struct auth_zone *zone, char *name, char **cut);
|
||||
#endif
|
||||
|
||||
/* dnssec.c */
|
||||
size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, int class, int type, union mysockaddr *addr);
|
||||
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 dnskey_keytag(int alg, int flags, unsigned char *rdata, int rdlen);
|
||||
unsigned char* hash_questions(struct dns_header *header, size_t plen, char *name);
|
||||
|
||||
/* util.c */
|
||||
void rand_init(void);
|
||||
unsigned short rand16(void);
|
||||
@@ -1061,7 +1142,7 @@ void receive_query(struct listener *listen, time_t now);
|
||||
unsigned char *tcp_request(int confd, time_t now,
|
||||
union mysockaddr *local_addr, struct in_addr netmask, int auth_dns);
|
||||
void server_gone(struct server *server);
|
||||
struct frec *get_new_frec(time_t now, int *wait);
|
||||
struct frec *get_new_frec(time_t now, int *wait, int force);
|
||||
int send_from(int fd, int nowild, char *packet, size_t len,
|
||||
union mysockaddr *to, struct all_addr *source,
|
||||
unsigned int iface);
|
||||
@@ -1090,6 +1171,10 @@ int set_ipv6pktinfo(int fd);
|
||||
#ifdef HAVE_DHCP6
|
||||
void join_multicast(int dienow);
|
||||
#endif
|
||||
#if defined(HAVE_LINUX_NETWORK) || defined(HAVE_BSD_NETWORK)
|
||||
void newaddress(time_t now);
|
||||
#endif
|
||||
|
||||
|
||||
/* dhcp.c */
|
||||
#ifdef HAVE_DHCP
|
||||
@@ -1177,6 +1262,8 @@ void netlink_multicast(time_t now);
|
||||
void init_bpf(void);
|
||||
void send_via_bpf(struct dhcp_packet *mess, size_t len,
|
||||
struct in_addr iface_addr, struct ifreq *ifr);
|
||||
void route_init(void);
|
||||
void route_sock(time_t now);
|
||||
#endif
|
||||
|
||||
/* bpf.c or netlink.c */
|
||||
@@ -1249,7 +1336,8 @@ void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac,
|
||||
/* rfc3315.c */
|
||||
#ifdef HAVE_DHCP6
|
||||
unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name,
|
||||
struct in6_addr *fallback, size_t sz, struct in6_addr *client_addr, time_t now);
|
||||
struct in6_addr *fallback, struct in6_addr *ll_addr, struct in6_addr *ula_addr,
|
||||
size_t sz, struct in6_addr *client_addr, time_t now);
|
||||
void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, struct in6_addr *peer_address, u32 scope_id);
|
||||
|
||||
unsigned short relay_reply6( struct sockaddr_in6 *peer, ssize_t sz, char *arrival_interface);
|
||||
|
||||
1454
src/dnssec.c
Normal file
1454
src/dnssec.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 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
|
||||
|
||||
586
src/forward.c
586
src/forward.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 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,11 +16,11 @@
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
static struct frec *lookup_frec(unsigned short id, unsigned int crc);
|
||||
static struct frec *lookup_frec(unsigned short id, void *hash);
|
||||
static struct frec *lookup_frec_by_sender(unsigned short id,
|
||||
union mysockaddr *addr,
|
||||
unsigned int crc);
|
||||
static unsigned short get_id(unsigned int crc);
|
||||
void *hash);
|
||||
static unsigned short get_id(void);
|
||||
static void free_frec(struct frec *f);
|
||||
static struct randfd *allocate_rfd(int family);
|
||||
|
||||
@@ -234,24 +234,67 @@ static unsigned int search_servers(time_t now, struct all_addr **addrpp,
|
||||
|
||||
static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
struct all_addr *dst_addr, unsigned int dst_iface,
|
||||
struct dns_header *header, size_t plen, time_t now, struct frec *forward)
|
||||
struct dns_header *header, size_t plen, time_t now,
|
||||
struct frec *forward, int ad_reqd)
|
||||
{
|
||||
char *domain = NULL;
|
||||
int type = 0, norebind = 0;
|
||||
struct all_addr *addrp = NULL;
|
||||
unsigned int crc = questions_crc(header, plen, daemon->namebuff);
|
||||
unsigned int flags = 0;
|
||||
unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL);
|
||||
struct server *start = NULL;
|
||||
|
||||
/* RFC 4035: sect 4.6 para 2 */
|
||||
header->hb4 &= ~HB4_AD;
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
void *hash = hash_questions(header, plen, daemon->namebuff);
|
||||
#else
|
||||
unsigned int crc = questions_crc(header, plen, daemon->namebuff);
|
||||
void *hash = &crc;
|
||||
#endif
|
||||
unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL);
|
||||
|
||||
/* may be no servers available. */
|
||||
if (!daemon->servers)
|
||||
forward = NULL;
|
||||
else if (forward || (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, crc)))
|
||||
else if (forward || (hash && (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, hash))))
|
||||
{
|
||||
#ifdef HAVE_DNSSEC
|
||||
/* If we've already got an answer to this query, but we're awaiting keys for vaildation,
|
||||
there's no point retrying the query, retry the key query instead...... */
|
||||
if (forward->blocking_query)
|
||||
{
|
||||
int fd;
|
||||
|
||||
while (forward->blocking_query)
|
||||
forward = forward->blocking_query;
|
||||
|
||||
blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
|
||||
plen = forward->stash_len;
|
||||
|
||||
if (forward->sentto->addr.sa.sa_family)
|
||||
log_query(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");
|
||||
#endif
|
||||
|
||||
if (forward->sentto->sfd)
|
||||
fd = forward->sentto->sfd->fd;
|
||||
else
|
||||
{
|
||||
#ifdef HAVE_IPV6
|
||||
if (forward->sentto->addr.sa.sa_family == AF_INET6)
|
||||
fd = forward->rfd6->fd;
|
||||
else
|
||||
#endif
|
||||
fd = forward->rfd4->fd;
|
||||
}
|
||||
|
||||
while (sendto(fd, (char *)header, plen, 0,
|
||||
&forward->sentto->addr.sa,
|
||||
sa_len(&forward->sentto->addr)) == -1 && retry_send());
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* retry on existing query, send to all available servers */
|
||||
domain = forward->sentto->domain;
|
||||
forward->sentto->failed_queries++;
|
||||
@@ -270,7 +313,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
if (gotname)
|
||||
flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);
|
||||
|
||||
if (!flags && !(forward = get_new_frec(now, NULL)))
|
||||
if (!flags && !(forward = get_new_frec(now, NULL, 0)))
|
||||
/* table full - server failure. */
|
||||
flags = F_NEG;
|
||||
|
||||
@@ -280,15 +323,20 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
forward->dest = *dst_addr;
|
||||
forward->iface = dst_iface;
|
||||
forward->orig_id = ntohs(header->id);
|
||||
forward->new_id = get_id(crc);
|
||||
forward->new_id = get_id();
|
||||
forward->fd = udpfd;
|
||||
forward->crc = crc;
|
||||
memcpy(forward->hash, hash, HASH_SIZE);
|
||||
forward->forwardall = 0;
|
||||
forward->flags = 0;
|
||||
if (norebind)
|
||||
forward->flags |= FREC_NOREBIND;
|
||||
if (header->hb4 & HB4_CD)
|
||||
forward->flags |= FREC_CHECKING_DISABLED;
|
||||
if (ad_reqd)
|
||||
forward->flags |= FREC_AD_QUESTION;
|
||||
#ifdef HAVE_DNSSEC
|
||||
forward->work_counter = DNSSEC_WORK;
|
||||
#endif
|
||||
|
||||
header->id = htons(forward->new_id);
|
||||
|
||||
@@ -330,11 +378,11 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
int forwarded = 0;
|
||||
|
||||
if (option_bool(OPT_ADD_MAC))
|
||||
plen = add_mac(header, plen, ((char *) header) + PACKETSZ, &forward->source);
|
||||
plen = add_mac(header, plen, ((char *) header) + daemon->packet_buff_sz, &forward->source);
|
||||
|
||||
if (option_bool(OPT_CLIENT_SUBNET))
|
||||
{
|
||||
size_t new = add_source_addr(header, plen, ((char *) header) + PACKETSZ, &forward->source);
|
||||
size_t new = add_source_addr(header, plen, ((char *) header) + daemon->packet_buff_sz, &forward->source);
|
||||
if (new != plen)
|
||||
{
|
||||
plen = new;
|
||||
@@ -342,6 +390,17 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID))
|
||||
{
|
||||
plen = add_do_bit(header, plen, ((char *) header) + daemon->packet_buff_sz);
|
||||
/* For debugging, set Checking Disabled, otherwise, have the upstream check too,
|
||||
this allows it to select auth servers when one is returning bad data. */
|
||||
if (option_bool(OPT_DNSSEC_DEBUG))
|
||||
header->hb4 |= HB4_CD;
|
||||
}
|
||||
#endif
|
||||
|
||||
while (1)
|
||||
{
|
||||
/* only send to servers dealing with our domain.
|
||||
@@ -447,13 +506,15 @@ 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 checking_disabled, int check_subnet, union mysockaddr *query_source)
|
||||
int no_cache, int cache_secure, int ad_reqd, int check_subnet, union mysockaddr *query_source)
|
||||
{
|
||||
unsigned char *pheader, *sizep;
|
||||
char **sets = 0;
|
||||
int munged = 0, is_sign;
|
||||
size_t plen;
|
||||
|
||||
(void)ad_reqd;
|
||||
|
||||
#ifdef HAVE_IPSET
|
||||
/* Similar algorithm to search_servers. */
|
||||
struct ipsets *ipset_pos;
|
||||
@@ -465,10 +526,11 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
||||
char *matchstart = daemon->namebuff + namelen - domainlen;
|
||||
if (namelen >= domainlen && hostname_isequal(matchstart, ipset_pos->domain) &&
|
||||
(domainlen == 0 || namelen == domainlen || *(matchstart - 1) == '.' ) &&
|
||||
domainlen >= matchlen) {
|
||||
matchlen = domainlen;
|
||||
sets = ipset_pos->sets;
|
||||
}
|
||||
domainlen >= matchlen)
|
||||
{
|
||||
matchlen = domainlen;
|
||||
sets = ipset_pos->sets;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -478,15 +540,13 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
||||
|
||||
if ((pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign)))
|
||||
{
|
||||
if (!is_sign)
|
||||
{
|
||||
unsigned short udpsz;
|
||||
unsigned char *psave = sizep;
|
||||
|
||||
GETSHORT(udpsz, sizep);
|
||||
if (udpsz > daemon->edns_pktsz)
|
||||
PUTSHORT(daemon->edns_pktsz, psave);
|
||||
}
|
||||
unsigned short udpsz;
|
||||
unsigned char *psave = sizep;
|
||||
|
||||
GETSHORT(udpsz, sizep);
|
||||
|
||||
if (!is_sign && udpsz > daemon->edns_pktsz)
|
||||
PUTSHORT(daemon->edns_pktsz, psave);
|
||||
|
||||
if (check_subnet && !check_source(header, plen, pheader, query_source))
|
||||
{
|
||||
@@ -494,12 +554,11 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* RFC 4035 sect 4.6 para 3 */
|
||||
if (!is_sign && !option_bool(OPT_DNSSEC))
|
||||
header->hb4 &= ~HB4_AD;
|
||||
|
||||
if (!is_sign && !option_bool(OPT_DNSSEC_PROXY))
|
||||
header->hb4 &= ~HB4_AD;
|
||||
|
||||
if (OPCODE(header) != QUERY || (RCODE(header) != NOERROR && RCODE(header) != NXDOMAIN))
|
||||
return n;
|
||||
|
||||
@@ -512,16 +571,19 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
||||
if (!option_bool(OPT_LOG))
|
||||
server->flags |= SERV_WARNED_RECURSIVE;
|
||||
}
|
||||
|
||||
|
||||
if (daemon->bogus_addr && RCODE(header) != NXDOMAIN &&
|
||||
check_for_bogus_wildcard(header, n, daemon->namebuff, daemon->bogus_addr, now))
|
||||
{
|
||||
munged = 1;
|
||||
SET_RCODE(header, NXDOMAIN);
|
||||
header->hb3 &= ~HB3_AA;
|
||||
cache_secure = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
int doctored = 0;
|
||||
|
||||
if (RCODE(header) == NXDOMAIN &&
|
||||
extract_request(header, n, daemon->namebuff, NULL) &&
|
||||
check_for_local_domain(daemon->namebuff, now))
|
||||
@@ -532,15 +594,38 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
||||
munged = 1;
|
||||
header->hb3 |= HB3_AA;
|
||||
SET_RCODE(header, NOERROR);
|
||||
cache_secure = 0;
|
||||
}
|
||||
|
||||
if (extract_addresses(header, n, daemon->namebuff, now, sets, is_sign, check_rebind, checking_disabled))
|
||||
if (extract_addresses(header, n, daemon->namebuff, now, sets, is_sign, check_rebind, no_cache, cache_secure, &doctored))
|
||||
{
|
||||
my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff);
|
||||
munged = 1;
|
||||
cache_secure = 0;
|
||||
}
|
||||
|
||||
if (doctored)
|
||||
cache_secure = 0;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (no_cache && !(header->hb4 & HB4_CD))
|
||||
{
|
||||
if (!option_bool(OPT_DNSSEC_DEBUG))
|
||||
{
|
||||
/* Bogus reply, turn into SERVFAIL */
|
||||
SET_RCODE(header, SERVFAIL);
|
||||
munged = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (option_bool(OPT_DNSSEC_VALID))
|
||||
header->hb4 &= ~HB4_AD;
|
||||
|
||||
if (!(header->hb4 & HB4_CD) && ad_reqd && cache_secure)
|
||||
header->hb4 |= HB4_AD;
|
||||
#endif
|
||||
|
||||
/* do this after extract_addresses. Ensure NODATA reply and remove
|
||||
nameserver info. */
|
||||
|
||||
@@ -566,10 +651,14 @@ void reply_query(int fd, int family, time_t now)
|
||||
union mysockaddr serveraddr;
|
||||
struct frec *forward;
|
||||
socklen_t addrlen = sizeof(serveraddr);
|
||||
ssize_t n = recvfrom(fd, daemon->packet, daemon->edns_pktsz, 0, &serveraddr.sa, &addrlen);
|
||||
ssize_t n = recvfrom(fd, daemon->packet, daemon->packet_buff_sz, 0, &serveraddr.sa, &addrlen);
|
||||
size_t nn;
|
||||
struct server *server;
|
||||
|
||||
void *hash;
|
||||
#ifndef HAVE_DNSSEC
|
||||
unsigned int crc;
|
||||
#endif
|
||||
|
||||
/* packet buffer overwritten */
|
||||
daemon->srv_save = NULL;
|
||||
|
||||
@@ -587,14 +676,19 @@ void reply_query(int fd, int family, time_t now)
|
||||
break;
|
||||
|
||||
header = (struct dns_header *)daemon->packet;
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
hash = hash_questions(header, n, daemon->namebuff);
|
||||
#else
|
||||
hash = &crc;
|
||||
crc = questions_crc(header, n, daemon->namebuff);
|
||||
#endif
|
||||
|
||||
if (!server ||
|
||||
n < (int)sizeof(struct dns_header) || !(header->hb3 & HB3_QR) ||
|
||||
!(forward = lookup_frec(ntohs(header->id), questions_crc(header, n, daemon->namebuff))))
|
||||
!(forward = lookup_frec(ntohs(header->id), hash)))
|
||||
return;
|
||||
|
||||
server = forward->sentto;
|
||||
|
||||
|
||||
if ((RCODE(header) == SERVFAIL || RCODE(header) == REFUSED) &&
|
||||
!option_bool(OPT_ORDER) &&
|
||||
forward->forwardall == 0)
|
||||
@@ -614,11 +708,13 @@ void reply_query(int fd, int family, time_t now)
|
||||
if ((nn = resize_packet(header, (size_t)n, pheader, plen)))
|
||||
{
|
||||
header->hb3 &= ~(HB3_QR | HB3_TC);
|
||||
forward_query(-1, NULL, NULL, 0, header, nn, now, forward);
|
||||
forward_query(-1, NULL, NULL, 0, header, nn, now, forward, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
server = forward->sentto;
|
||||
|
||||
if ((forward->sentto->flags & SERV_TYPE) == 0)
|
||||
{
|
||||
@@ -640,7 +736,7 @@ void reply_query(int fd, int family, time_t now)
|
||||
if (!option_bool(OPT_ALL_SERVERS))
|
||||
daemon->last_server = server;
|
||||
}
|
||||
|
||||
|
||||
/* If the answer is an error, keep the forward record in place in case
|
||||
we get a good reply from another server. Kill it when we've
|
||||
had replies from all to avoid filling the forwarding table when
|
||||
@@ -648,13 +744,190 @@ void reply_query(int fd, int family, time_t now)
|
||||
if (forward->forwardall == 0 || --forward->forwardall == 1 ||
|
||||
(RCODE(header) != REFUSED && RCODE(header) != SERVFAIL))
|
||||
{
|
||||
int check_rebind = !(forward->flags & FREC_NOREBIND);
|
||||
int check_rebind = 0, no_cache_dnssec = 0, cache_secure = 0;
|
||||
|
||||
if (!option_bool(OPT_NO_REBIND))
|
||||
check_rebind = 0;
|
||||
if (option_bool(OPT_NO_REBIND))
|
||||
check_rebind = !(forward->flags & FREC_NOREBIND);
|
||||
|
||||
if ((nn = process_reply(header, now, server, (size_t)n, check_rebind, forward->flags & FREC_CHECKING_DISABLED,
|
||||
forward->flags & FREC_HAS_SUBNET, &forward->source)))
|
||||
/* Don't cache replies where DNSSEC validation was turned off, either
|
||||
the upstream server told us so, or the original query specified it. */
|
||||
if ((header->hb4 & HB4_CD) || (forward->flags & FREC_CHECKING_DISABLED))
|
||||
no_cache_dnssec = 1;
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID) && !(forward->flags & FREC_CHECKING_DISABLED))
|
||||
{
|
||||
int status;
|
||||
|
||||
/* We've had a reply already, which we're validating. Ignore this duplicate */
|
||||
if (forward->blocking_query)
|
||||
return;
|
||||
|
||||
if (header->hb3 & HB3_TC)
|
||||
{
|
||||
/* Truncated answer can't be validated.
|
||||
If this is an answer to a DNSSEC-generated query, we still
|
||||
need to get the client to retry over TCP, so return
|
||||
an answer with the TC bit set, even if the actual answer fits.
|
||||
*/
|
||||
status = STAT_TRUNCATED;
|
||||
}
|
||||
else if (forward->flags & FREC_DNSKEY_QUERY)
|
||||
status = dnssec_validate_by_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
|
||||
else if (forward->flags & FREC_DS_QUERY)
|
||||
status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
|
||||
else
|
||||
status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class);
|
||||
|
||||
/* Can't validate, as we're missing key data. Put this
|
||||
answer aside, whilst we get that. */
|
||||
if (status == STAT_NEED_DS || status == STAT_NEED_KEY)
|
||||
{
|
||||
struct frec *new, *orig;
|
||||
|
||||
/* Free any saved query */
|
||||
if (forward->stash)
|
||||
blockdata_free(forward->stash);
|
||||
|
||||
/* Now save reply pending receipt of key data */
|
||||
if (!(forward->stash = blockdata_alloc((char *)header, n)))
|
||||
return;
|
||||
forward->stash_len = n;
|
||||
|
||||
anotherkey:
|
||||
/* Find the original query that started it all.... */
|
||||
for (orig = forward; orig->dependent; orig = orig->dependent);
|
||||
|
||||
if (--orig->work_counter == 0 || !(new = get_new_frec(now, NULL, 1)))
|
||||
status = STAT_INSECURE;
|
||||
else
|
||||
{
|
||||
int fd;
|
||||
struct frec *next = new->next;
|
||||
*new = *forward; /* copy everything, then overwrite */
|
||||
new->next = next;
|
||||
new->blocking_query = NULL;
|
||||
new->rfd4 = NULL;
|
||||
#ifdef HAVE_IPV6
|
||||
new->rfd6 = NULL;
|
||||
#endif
|
||||
new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY);
|
||||
|
||||
new->dependent = forward; /* to find query awaiting new one. */
|
||||
forward->blocking_query = new; /* for garbage cleaning */
|
||||
/* validate routines leave name of required record in daemon->keyname */
|
||||
if (status == STAT_NEED_KEY)
|
||||
{
|
||||
new->flags |= FREC_DNSKEY_QUERY;
|
||||
nn = dnssec_generate_query(header, ((char *) header) + daemon->packet_buff_sz,
|
||||
daemon->keyname, forward->class, T_DNSKEY, &server->addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
new->flags |= FREC_DS_QUERY;
|
||||
nn = dnssec_generate_query(header,((char *) header) + daemon->packet_buff_sz,
|
||||
daemon->keyname, forward->class, T_DS, &server->addr);
|
||||
}
|
||||
if ((hash = hash_questions(header, nn, daemon->namebuff)))
|
||||
memcpy(new->hash, hash, HASH_SIZE);
|
||||
new->new_id = get_id();
|
||||
header->id = htons(new->new_id);
|
||||
/* Save query for retransmission */
|
||||
new->stash = blockdata_alloc((char *)header, nn);
|
||||
new->stash_len = nn;
|
||||
|
||||
/* Don't resend this. */
|
||||
daemon->srv_save = NULL;
|
||||
|
||||
if (server->sfd)
|
||||
fd = server->sfd->fd;
|
||||
else
|
||||
{
|
||||
fd = -1;
|
||||
#ifdef HAVE_IPV6
|
||||
if (server->addr.sa.sa_family == AF_INET6)
|
||||
{
|
||||
if (new->rfd6 || (new->rfd6 = allocate_rfd(AF_INET6)))
|
||||
fd = new->rfd6->fd;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
if (new->rfd4 || (new->rfd4 = allocate_rfd(AF_INET)))
|
||||
fd = new->rfd4->fd;
|
||||
}
|
||||
}
|
||||
|
||||
if (fd != -1)
|
||||
{
|
||||
while (sendto(fd, (char *)header, nn, 0, &server->addr.sa, sa_len(&server->addr)) == -1 && retry_send());
|
||||
server->queries++;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Ok, we reached far enough up the chain-of-trust that we can validate something.
|
||||
Now wind back down, pulling back answers which wouldn't previously validate
|
||||
and validate them with the new data. Note that if an answer needs multiple
|
||||
keys to validate, we may find another key is needed, in which case we set off
|
||||
down another branch of the tree. Once we get to the original answer
|
||||
(FREC_DNSSEC_QUERY not set) and it validates, return it to the original requestor. */
|
||||
while (forward->dependent)
|
||||
{
|
||||
struct frec *prev = forward->dependent;
|
||||
free_frec(forward);
|
||||
forward = prev;
|
||||
forward->blocking_query = NULL; /* already gone */
|
||||
blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
|
||||
n = forward->stash_len;
|
||||
|
||||
if (status == STAT_SECURE)
|
||||
{
|
||||
if (forward->flags & FREC_DNSKEY_QUERY)
|
||||
status = dnssec_validate_by_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
|
||||
else if (forward->flags & FREC_DS_QUERY)
|
||||
status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
|
||||
else
|
||||
status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class);
|
||||
|
||||
if (status == STAT_NEED_DS || status == STAT_NEED_KEY)
|
||||
goto anotherkey;
|
||||
}
|
||||
}
|
||||
|
||||
if (status == STAT_TRUNCATED)
|
||||
header->hb3 |= HB3_TC;
|
||||
else
|
||||
{
|
||||
char *result;
|
||||
|
||||
if (forward->work_counter == 0)
|
||||
result = "ABANDONED";
|
||||
else
|
||||
result = (status == STAT_SECURE ? "SECURE" : (status == STAT_INSECURE ? "INSECURE" : "BOGUS"));
|
||||
|
||||
log_query(F_KEYTAG | F_SECSTAT, "result", NULL, result);
|
||||
}
|
||||
|
||||
no_cache_dnssec = 0;
|
||||
|
||||
if (status == STAT_SECURE)
|
||||
cache_secure = 1;
|
||||
else if (status == STAT_BOGUS)
|
||||
no_cache_dnssec = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* restore CD bit to the value in the query */
|
||||
if (forward->flags & FREC_CHECKING_DISABLED)
|
||||
header->hb4 |= HB4_CD;
|
||||
else
|
||||
header->hb4 &= ~HB4_CD;
|
||||
|
||||
if ((nn = process_reply(header, now, server, (size_t)n, check_rebind, no_cache_dnssec, cache_secure,
|
||||
forward->flags & FREC_AD_QUESTION, forward->flags & FREC_HAS_SUBNET, &forward->source)))
|
||||
{
|
||||
header->id = htons(forward->orig_id);
|
||||
header->hb4 |= HB4_RA; /* recursion if available */
|
||||
@@ -855,13 +1128,11 @@ void receive_query(struct listener *listen, time_t now)
|
||||
|
||||
if (extract_request(header, (size_t)n, daemon->namebuff, &type))
|
||||
{
|
||||
char types[20];
|
||||
#ifdef HAVE_AUTH
|
||||
struct auth_zone *zone;
|
||||
#endif
|
||||
|
||||
querystr(auth_dns ? "auth" : "query", types, type);
|
||||
|
||||
char *types = querystr(auth_dns ? "auth" : "query", type);
|
||||
|
||||
if (listen->family == AF_INET)
|
||||
log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
|
||||
(struct all_addr *)&source_addr.in.sin_addr, types);
|
||||
@@ -887,7 +1158,7 @@ void receive_query(struct listener *listen, time_t now)
|
||||
#ifdef HAVE_AUTH
|
||||
if (auth_dns)
|
||||
{
|
||||
m = answer_auth(header, ((char *) header) + PACKETSZ, (size_t)n, now, &source_addr, local_auth);
|
||||
m = answer_auth(header, ((char *) header) + daemon->packet_buff_sz, (size_t)n, now, &source_addr, local_auth);
|
||||
if (m >= 1)
|
||||
{
|
||||
send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND),
|
||||
@@ -898,8 +1169,9 @@ void receive_query(struct listener *listen, time_t now)
|
||||
else
|
||||
#endif
|
||||
{
|
||||
m = answer_request(header, ((char *) header) + PACKETSZ, (size_t)n,
|
||||
dst_addr_4, netmask, now);
|
||||
int ad_reqd;
|
||||
m = answer_request(header, ((char *) header) + daemon->packet_buff_sz, (size_t)n,
|
||||
dst_addr_4, netmask, now, &ad_reqd);
|
||||
|
||||
if (m >= 1)
|
||||
{
|
||||
@@ -908,13 +1180,86 @@ void receive_query(struct listener *listen, time_t now)
|
||||
daemon->local_answer++;
|
||||
}
|
||||
else if (forward_query(listen->fd, &source_addr, &dst_addr, if_index,
|
||||
header, (size_t)n, now, NULL))
|
||||
header, (size_t)n, now, NULL, ad_reqd))
|
||||
daemon->queries_forwarded++;
|
||||
else
|
||||
daemon->local_answer++;
|
||||
}
|
||||
}
|
||||
|
||||
#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)
|
||||
{
|
||||
/* Recurse up the key heirarchy */
|
||||
int new_status;
|
||||
|
||||
/* limit the amount of work we do, to avoid cycling forever on loops in the DNS */
|
||||
if (--(*keycount) == 0)
|
||||
return STAT_INSECURE;
|
||||
|
||||
if (status == STAT_NEED_KEY)
|
||||
new_status = dnssec_validate_by_ds(now, header, n, name, keyname, class);
|
||||
else if (status == STAT_NEED_DS)
|
||||
new_status = dnssec_validate_ds(now, header, n, name, keyname, class);
|
||||
else
|
||||
new_status = dnssec_validate_reply(now, header, n, name, keyname, &class);
|
||||
|
||||
/* Can't validate because we need a key/DS whose name now in keyname.
|
||||
Make query for same, and recurse to validate */
|
||||
if (new_status == STAT_NEED_DS || new_status == STAT_NEED_KEY)
|
||||
{
|
||||
size_t m;
|
||||
unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ + sizeof(u16));
|
||||
unsigned char *payload = &packet[2];
|
||||
struct dns_header *new_header = (struct dns_header *)payload;
|
||||
u16 *length = (u16 *)packet;
|
||||
unsigned char c1, c2;
|
||||
|
||||
if (!packet)
|
||||
return STAT_INSECURE;
|
||||
|
||||
another_tcp_key:
|
||||
m = dnssec_generate_query(new_header, ((char *) new_header) + 65536, keyname, class,
|
||||
new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS, &server->addr);
|
||||
|
||||
*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))
|
||||
new_status = STAT_INSECURE;
|
||||
else
|
||||
{
|
||||
m = (c1 << 8) | c2;
|
||||
|
||||
if (tcp_key_recurse(now, new_status, new_header, m, class, name, keyname, server, keycount) == STAT_SECURE)
|
||||
{
|
||||
/* Reached a validated record, now try again at this level.
|
||||
Note that we may get ANOTHER NEED_* if an answer needs more than one key.
|
||||
If so, go round again. */
|
||||
|
||||
if (status == STAT_NEED_KEY)
|
||||
new_status = dnssec_validate_by_ds(now, header, n, name, keyname, class);
|
||||
else if (status == STAT_NEED_DS)
|
||||
new_status = dnssec_validate_ds(now, header, n, name, keyname, class);
|
||||
else
|
||||
new_status = dnssec_validate_reply(now, header, n, name, keyname, &class);
|
||||
|
||||
if (new_status == STAT_NEED_DS || new_status == STAT_NEED_KEY)
|
||||
goto another_tcp_key;
|
||||
}
|
||||
}
|
||||
|
||||
free(packet);
|
||||
}
|
||||
|
||||
return new_status;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* The daemon forks before calling this: it should deal with one connection,
|
||||
blocking as neccessary, and then return. Note, need to be a bit careful
|
||||
about resources for debug mode, when the fork is suppressed: that's
|
||||
@@ -927,7 +1272,7 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
#ifdef HAVE_AUTH
|
||||
int local_auth = 0;
|
||||
#endif
|
||||
int checking_disabled, check_subnet;
|
||||
int checking_disabled, ad_question, check_subnet, no_cache_dnssec = 0, cache_secure = 0;
|
||||
size_t m;
|
||||
unsigned short qtype;
|
||||
unsigned int gotname;
|
||||
@@ -960,18 +1305,15 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
check_subnet = 0;
|
||||
|
||||
/* save state of "cd" flag in query */
|
||||
checking_disabled = header->hb4 & HB4_CD;
|
||||
if ((checking_disabled = header->hb4 & HB4_CD))
|
||||
no_cache_dnssec = 1;
|
||||
|
||||
/* RFC 4035: sect 4.6 para 2 */
|
||||
header->hb4 &= ~HB4_AD;
|
||||
|
||||
if ((gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype)))
|
||||
{
|
||||
char types[20];
|
||||
#ifdef HAVE_AUTH
|
||||
struct auth_zone *zone;
|
||||
#endif
|
||||
querystr(auth_dns ? "auth" : "query", types, qtype);
|
||||
char *types = querystr(auth_dns ? "auth" : "query", qtype);
|
||||
|
||||
if (peer_addr.sa.sa_family == AF_INET)
|
||||
log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
|
||||
@@ -1008,7 +1350,7 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
{
|
||||
/* m > 0 if answered from cache */
|
||||
m = answer_request(header, ((char *) header) + 65536, (size_t)size,
|
||||
dst_addr_4, netmask, now);
|
||||
dst_addr_4, netmask, now, &ad_question);
|
||||
|
||||
/* Do this by steam now we're not in the select() loop */
|
||||
check_log_writer(NULL);
|
||||
@@ -1044,8 +1386,13 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
if (!flags && last_server)
|
||||
{
|
||||
struct server *firstsendto = NULL;
|
||||
#ifdef HAVE_DNSSEC
|
||||
unsigned char *newhash, hash[HASH_SIZE];
|
||||
if ((newhash = hash_questions(header, (unsigned int)size, daemon->keyname)))
|
||||
memcpy(hash, newhash, HASH_SIZE);
|
||||
#else
|
||||
unsigned int crc = questions_crc(header, (unsigned int)size, daemon->namebuff);
|
||||
|
||||
#endif
|
||||
/* Loop round available servers until we succeed in connecting to one.
|
||||
Note that this code subtley ensures that consecutive queries on this connection
|
||||
which can go to the same server, do so. */
|
||||
@@ -1080,6 +1427,14 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID))
|
||||
{
|
||||
size = add_do_bit(header, size, ((char *) header) + 65536);
|
||||
header->hb4 |= HB4_CD;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CONNTRACK
|
||||
/* Copy connection mark of incoming query to outgoing connection. */
|
||||
if (option_bool(OPT_CONNTRACK))
|
||||
@@ -1103,7 +1458,8 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
|
||||
if (!read_write(last_server->tcpfd, packet, size + sizeof(u16), 0) ||
|
||||
!read_write(last_server->tcpfd, &c1, 1, 1) ||
|
||||
!read_write(last_server->tcpfd, &c2, 1, 1))
|
||||
!read_write(last_server->tcpfd, &c2, 1, 1) ||
|
||||
!read_write(last_server->tcpfd, payload, (c1 << 8) | c2, 1))
|
||||
{
|
||||
close(last_server->tcpfd);
|
||||
last_server->tcpfd = -1;
|
||||
@@ -1111,8 +1467,6 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
}
|
||||
|
||||
m = (c1 << 8) | c2;
|
||||
if (!read_write(last_server->tcpfd, payload, m, 1))
|
||||
return packet;
|
||||
|
||||
if (!gotname)
|
||||
strcpy(daemon->namebuff, "query");
|
||||
@@ -1124,6 +1478,34 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff,
|
||||
(struct all_addr *)&last_server->addr.in6.sin6_addr, NULL);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID) && !checking_disabled)
|
||||
{
|
||||
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;
|
||||
|
||||
if (keycount == 0)
|
||||
result = "ABANDONED";
|
||||
else
|
||||
result = (status == STAT_SECURE ? "SECURE" : (status == STAT_INSECURE ? "INSECURE" : "BOGUS"));
|
||||
|
||||
log_query(F_KEYTAG | F_SECSTAT, "result", NULL, result);
|
||||
|
||||
if (status == STAT_BOGUS)
|
||||
no_cache_dnssec = 1;
|
||||
|
||||
if (status == STAT_SECURE)
|
||||
cache_secure = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* restore CD bit to the value in the query */
|
||||
if (checking_disabled)
|
||||
header->hb4 |= HB4_CD;
|
||||
else
|
||||
header->hb4 &= ~HB4_CD;
|
||||
|
||||
/* There's no point in updating the cache, since this process will exit and
|
||||
lose the information after a few queries. We make this call for the alias and
|
||||
@@ -1131,10 +1513,24 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
/* If the crc of the question section doesn't match the crc we sent, then
|
||||
someone might be attempting to insert bogus values into the cache by
|
||||
sending replies containing questions and bogus answers. */
|
||||
if (crc == questions_crc(header, (unsigned int)m, daemon->namebuff))
|
||||
m = process_reply(header, now, last_server, (unsigned int)m,
|
||||
option_bool(OPT_NO_REBIND) && !norebind, checking_disabled,
|
||||
check_subnet, &peer_addr);
|
||||
#ifdef HAVE_DNSSEC
|
||||
newhash = hash_questions(header, (unsigned int)m, daemon->namebuff);
|
||||
if (!newhash || memcmp(hash, newhash, HASH_SIZE) != 0)
|
||||
{
|
||||
m = 0;
|
||||
break;
|
||||
}
|
||||
#else
|
||||
if (crc != questions_crc(header, (unsigned int)m, daemon->namebuff))
|
||||
{
|
||||
m = 0;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
m = process_reply(header, now, last_server, (unsigned int)m,
|
||||
option_bool(OPT_NO_REBIND) && !norebind, no_cache_dnssec,
|
||||
cache_secure, ad_question, check_subnet, &peer_addr);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -1168,6 +1564,11 @@ static struct frec *allocate_frec(time_t now)
|
||||
f->flags = 0;
|
||||
#ifdef HAVE_IPV6
|
||||
f->rfd6 = NULL;
|
||||
#endif
|
||||
#ifdef HAVE_DNSSEC
|
||||
f->dependent = NULL;
|
||||
f->blocking_query = NULL;
|
||||
f->stash = NULL;
|
||||
#endif
|
||||
daemon->frec_list = f;
|
||||
}
|
||||
@@ -1210,7 +1611,6 @@ static struct randfd *allocate_rfd(int family)
|
||||
|
||||
return NULL; /* doom */
|
||||
}
|
||||
|
||||
static void free_frec(struct frec *f)
|
||||
{
|
||||
if (f->rfd4 && --(f->rfd4->refcount) == 0)
|
||||
@@ -1226,13 +1626,29 @@ static void free_frec(struct frec *f)
|
||||
|
||||
f->rfd6 = NULL;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (f->stash)
|
||||
{
|
||||
blockdata_free(f->stash);
|
||||
f->stash = NULL;
|
||||
}
|
||||
|
||||
/* Anything we're waiting on is pointless now, too */
|
||||
if (f->blocking_query)
|
||||
free_frec(f->blocking_query);
|
||||
f->blocking_query = NULL;
|
||||
f->dependent = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* if wait==NULL return a free or older than TIMEOUT record.
|
||||
else return *wait zero if one available, or *wait is delay to
|
||||
when the oldest in-use record will expire. Impose an absolute
|
||||
limit of 4*TIMEOUT before we wipe things (for random sockets) */
|
||||
struct frec *get_new_frec(time_t now, int *wait)
|
||||
limit of 4*TIMEOUT before we wipe things (for random sockets).
|
||||
If force is set, always return a result, even if we have
|
||||
to allocate above the limit. */
|
||||
struct frec *get_new_frec(time_t now, int *wait, int force)
|
||||
{
|
||||
struct frec *f, *oldest, *target;
|
||||
int count;
|
||||
@@ -1281,7 +1697,7 @@ struct frec *get_new_frec(time_t now, int *wait)
|
||||
}
|
||||
|
||||
/* none available, calculate time 'till oldest record expires */
|
||||
if (count > daemon->ftabsize)
|
||||
if (!force && count > daemon->ftabsize)
|
||||
{
|
||||
static time_t last_log = 0;
|
||||
|
||||
@@ -1305,13 +1721,13 @@ struct frec *get_new_frec(time_t now, int *wait)
|
||||
}
|
||||
|
||||
/* crc is all-ones if not known. */
|
||||
static struct frec *lookup_frec(unsigned short id, unsigned int crc)
|
||||
static struct frec *lookup_frec(unsigned short id, void *hash)
|
||||
{
|
||||
struct frec *f;
|
||||
|
||||
for(f = daemon->frec_list; f; f = f->next)
|
||||
if (f->sentto && f->new_id == id &&
|
||||
(f->crc == crc || crc == 0xffffffff))
|
||||
(!hash || memcmp(hash, f->hash, HASH_SIZE) == 0))
|
||||
return f;
|
||||
|
||||
return NULL;
|
||||
@@ -1319,14 +1735,14 @@ static struct frec *lookup_frec(unsigned short id, unsigned int crc)
|
||||
|
||||
static struct frec *lookup_frec_by_sender(unsigned short id,
|
||||
union mysockaddr *addr,
|
||||
unsigned int crc)
|
||||
void *hash)
|
||||
{
|
||||
struct frec *f;
|
||||
|
||||
for(f = daemon->frec_list; f; f = f->next)
|
||||
if (f->sentto &&
|
||||
f->orig_id == id &&
|
||||
f->crc == crc &&
|
||||
memcmp(hash, f->hash, HASH_SIZE) == 0 &&
|
||||
sockaddr_isequal(&f->source, addr))
|
||||
return f;
|
||||
|
||||
@@ -1350,13 +1766,13 @@ void server_gone(struct server *server)
|
||||
}
|
||||
|
||||
/* return unique random ids. */
|
||||
static unsigned short get_id(unsigned int crc)
|
||||
static unsigned short get_id(void)
|
||||
{
|
||||
unsigned short ret = 0;
|
||||
|
||||
do
|
||||
ret = rand16();
|
||||
while (lookup_frec(ret, crc));
|
||||
while (lookup_frec(ret, NULL));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 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
src/ip6addr.h
Normal file
34
src/ip6addr.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2014 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/>.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#define IN6_IS_ADDR_ULA(a) \
|
||||
((((__const uint32_t *) (a))[0] & htonl (0xff000000)) \
|
||||
== htonl (0xfd000000))
|
||||
|
||||
#define IN6_IS_ADDR_ULA_ZERO(a) \
|
||||
(((__const uint32_t *) (a))[0] == htonl (0xfd000000) \
|
||||
&& ((__const uint32_t *) (a))[1] == 0 \
|
||||
&& ((__const uint32_t *) (a))[2] == 0 \
|
||||
&& ((__const uint32_t *) (a))[3] == 0)
|
||||
|
||||
#define IN6_IS_ADDR_LINK_LOCAL_ZERO(a) \
|
||||
(((__const uint32_t *) (a))[0] == htonl (0xfe800000) \
|
||||
&& ((__const uint32_t *) (a))[1] == 0 \
|
||||
&& ((__const uint32_t *) (a))[2] == 0 \
|
||||
&& ((__const uint32_t *) (a))[3] == 0)
|
||||
|
||||
23
src/lease.c
23
src/lease.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 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
|
||||
@@ -306,7 +306,7 @@ void lease_update_file(time_t now)
|
||||
file_dirty = 0;
|
||||
}
|
||||
|
||||
/* Set alarm for when the first lease expires + slop. */
|
||||
/* Set alarm for when the first lease expires. */
|
||||
next_event = 0;
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
@@ -331,8 +331,8 @@ void lease_update_file(time_t now)
|
||||
|
||||
for (lease = leases; lease; lease = lease->next)
|
||||
if (lease->expires != 0 &&
|
||||
(next_event == 0 || difftime(next_event, lease->expires + 10) > 0.0))
|
||||
next_event = lease->expires + 10;
|
||||
(next_event == 0 || difftime(next_event, lease->expires) > 0.0))
|
||||
next_event = lease->expires;
|
||||
|
||||
if (err)
|
||||
{
|
||||
@@ -745,14 +745,23 @@ struct dhcp_lease *lease6_allocate(struct in6_addr *addrp, int lease_type)
|
||||
|
||||
void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now)
|
||||
{
|
||||
time_t exp = now + (time_t)len;
|
||||
|
||||
time_t exp;
|
||||
|
||||
if (len == 0xffffffff)
|
||||
{
|
||||
exp = 0;
|
||||
len = 0;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
exp = now + (time_t)len;
|
||||
/* Check for 2038 overflow. Make the lease
|
||||
inifinite in that case, as the least disruptive
|
||||
thing we can do. */
|
||||
if (difftime(exp, now) <= 0.0)
|
||||
exp = 0;
|
||||
}
|
||||
|
||||
if (exp != lease->expires)
|
||||
{
|
||||
dns_dirty = 1;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -39,7 +39,6 @@ static struct iovec iov;
|
||||
static u32 netlink_pid;
|
||||
|
||||
static int nl_async(struct nlmsghdr *h);
|
||||
static void nl_newaddress(time_t now);
|
||||
|
||||
void netlink_init(void)
|
||||
{
|
||||
@@ -203,7 +202,7 @@ int iface_enumerate(int family, void *parm, int (*callback)())
|
||||
/* handle async new interface address arrivals, these have to be done
|
||||
after we complete as we're not re-entrant */
|
||||
if (newaddr)
|
||||
nl_newaddress(dnsmasq_time());
|
||||
newaddress(dnsmasq_time());
|
||||
|
||||
return callback_ok;
|
||||
}
|
||||
@@ -265,10 +264,10 @@ int iface_enumerate(int family, void *parm, int (*callback)())
|
||||
|
||||
if (ifa->ifa_flags & IFA_F_DEPRECATED)
|
||||
flags |= IFACE_DEPRECATED;
|
||||
|
||||
if (ifa->ifa_flags & IFA_F_PERMANENT)
|
||||
flags |= IFACE_PERMANENT;
|
||||
|
||||
if (!(ifa->ifa_flags & IFA_F_TEMPORARY))
|
||||
flags |= IFACE_PERMANENT;
|
||||
|
||||
if (addrp && callback_ok)
|
||||
if (!((*callback)(addrp, (int)(ifa->ifa_prefixlen), (int)(ifa->ifa_scope),
|
||||
(int)(ifa->ifa_index), flags,
|
||||
@@ -351,7 +350,7 @@ void netlink_multicast(time_t now)
|
||||
fcntl(daemon->netlinkfd, F_SETFL, flags);
|
||||
|
||||
if (newaddr)
|
||||
nl_newaddress(now);
|
||||
newaddress(now);
|
||||
}
|
||||
|
||||
static int nl_async(struct nlmsghdr *h)
|
||||
@@ -399,30 +398,6 @@ static int nl_async(struct nlmsghdr *h)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nl_newaddress(time_t now)
|
||||
{
|
||||
(void)now;
|
||||
|
||||
if (option_bool(OPT_CLEVERBIND) || daemon->doing_dhcp6 || daemon->relay6 || daemon->doing_ra)
|
||||
enumerate_interfaces(0);
|
||||
|
||||
if (option_bool(OPT_CLEVERBIND))
|
||||
create_bound_listeners(0);
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
if (daemon->doing_dhcp6 || daemon->relay6 || daemon->doing_ra)
|
||||
join_multicast(0);
|
||||
|
||||
if (daemon->doing_dhcp6 || daemon->doing_ra)
|
||||
dhcp_construct_contexts(now);
|
||||
|
||||
if (daemon->doing_dhcp6)
|
||||
lease_find_interfaces(now);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 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
|
||||
@@ -489,7 +489,7 @@ static int iface_allowed_v6(struct in6_addr *local, int prefix,
|
||||
addr.in6.sin6_scope_id = if_index;
|
||||
else
|
||||
addr.in6.sin6_scope_id = 0;
|
||||
|
||||
|
||||
return iface_allowed((struct iface_param *)vparam, if_index, NULL, &addr, netmask, prefix, !!(flags & IFACE_TENTATIVE));
|
||||
}
|
||||
#endif
|
||||
@@ -681,7 +681,7 @@ static int make_sock(union mysockaddr *addr, int type, int dienow)
|
||||
close (fd);
|
||||
|
||||
errno = errsav;
|
||||
|
||||
|
||||
if (dienow)
|
||||
{
|
||||
/* failure to bind addresses given by --listen-address at this point
|
||||
@@ -1470,7 +1470,31 @@ int reload_servers(char *fname)
|
||||
return gotone;
|
||||
}
|
||||
|
||||
|
||||
#if defined(HAVE_LINUX_NETWORK) || defined(HAVE_BSD_NETWORK)
|
||||
/* Called when addresses are added or deleted from an interface */
|
||||
void newaddress(time_t now)
|
||||
{
|
||||
(void)now;
|
||||
|
||||
if (option_bool(OPT_CLEVERBIND) || daemon->doing_dhcp6 || daemon->relay6 || daemon->doing_ra)
|
||||
enumerate_interfaces(0);
|
||||
|
||||
if (option_bool(OPT_CLEVERBIND))
|
||||
create_bound_listeners(0);
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
if (daemon->doing_dhcp6 || daemon->relay6 || daemon->doing_ra)
|
||||
join_multicast(0);
|
||||
|
||||
if (daemon->doing_dhcp6 || daemon->doing_ra)
|
||||
dhcp_construct_contexts(now);
|
||||
|
||||
if (daemon->doing_dhcp6)
|
||||
lease_find_interfaces(now);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
93
src/option.c
93
src/option.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 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
|
||||
@@ -138,7 +138,9 @@ struct myoption {
|
||||
#define LOPT_QUIET_DHCP 326
|
||||
#define LOPT_QUIET_DHCP6 327
|
||||
#define LOPT_QUIET_RA 328
|
||||
|
||||
#define LOPT_SEC_VALID 329
|
||||
#define LOPT_TRUST_ANCHOR 330
|
||||
#define LOPT_DNSSEC_DEBUG 331
|
||||
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
static const struct option opts[] =
|
||||
@@ -274,6 +276,9 @@ static const struct myoption opts[] =
|
||||
{ "auth-peer", 1, 0, LOPT_AUTHPEER },
|
||||
{ "ipset", 1, 0, LOPT_IPSET },
|
||||
{ "synth-domain", 1, 0, LOPT_SYNTH },
|
||||
{ "dnssec", 0, 0, LOPT_SEC_VALID },
|
||||
{ "trust-anchor", 1, 0, LOPT_TRUST_ANCHOR },
|
||||
{ "dnssec-debug", 0, 0, LOPT_DNSSEC_DEBUG },
|
||||
#ifdef OPTION6_PREFIX_CLASS
|
||||
{ "dhcp-prefix-class", 1, 0, LOPT_PREF_CLSS },
|
||||
#endif
|
||||
@@ -407,7 +412,7 @@ static struct {
|
||||
{ LOPT_TEST, 0, NULL, gettext_noop("Check configuration syntax."), NULL },
|
||||
{ LOPT_ADD_MAC, OPT_ADD_MAC, NULL, gettext_noop("Add requestor's MAC address to forwarded DNS queries."), NULL },
|
||||
{ LOPT_ADD_SBNET, ARG_ONE, "<v4 pref>[,<v6 pref>]", gettext_noop("Add requestor's IP subnet to forwarded DNS queries."), NULL },
|
||||
{ LOPT_DNSSEC, OPT_DNSSEC, NULL, gettext_noop("Proxy DNSSEC validation results from upstream nameservers."), NULL },
|
||||
{ LOPT_DNSSEC, OPT_DNSSEC_PROXY, NULL, gettext_noop("Proxy DNSSEC validation results from upstream nameservers."), NULL },
|
||||
{ LOPT_INCR_ADDR, OPT_CONSEC_ADDR, NULL, gettext_noop("Attempt to allocate sequential IP addresses to DHCP clients."), NULL },
|
||||
{ LOPT_CONNTRACK, OPT_CONNTRACK, NULL, gettext_noop("Copy connection-track mark from queries to upstream connections."), NULL },
|
||||
{ LOPT_FQDN, OPT_FQDN_UPDATE, NULL, gettext_noop("Allow DHCP clients to do their own DDNS updates."), NULL },
|
||||
@@ -424,6 +429,9 @@ static struct {
|
||||
{ LOPT_AUTHPEER, ARG_DUP, "<ipaddr>[,<ipaddr>...]", gettext_noop("Peers which are allowed to do zone transfer"), NULL },
|
||||
{ LOPT_IPSET, ARG_DUP, "/<domain>/<ipset>[,<ipset>...]", gettext_noop("Specify ipsets to which matching domains should be added"), NULL },
|
||||
{ LOPT_SYNTH, ARG_DUP, "<domain>,<range>,[<prefix>]", gettext_noop("Specify a domain and address range for synthesised names"), NULL },
|
||||
{ LOPT_SEC_VALID, OPT_DNSSEC_VALID, NULL, gettext_noop("Activate DNSSEC validation"), NULL },
|
||||
{ LOPT_TRUST_ANCHOR, ARG_DUP, "<domain>,[<class>],...", gettext_noop("Specify trust anchor key digest."), NULL },
|
||||
{ LOPT_DNSSEC_DEBUG, OPT_DNSSEC_DEBUG, NULL, gettext_noop("Disable upstream checking for DNSSEC debugging."), NULL },
|
||||
#ifdef OPTION6_PREFIX_CLASS
|
||||
{ LOPT_PREF_CLSS, ARG_DUP, "set:tag,<class>", gettext_noop("Specify DHCPv6 prefix class"), NULL },
|
||||
#endif
|
||||
@@ -582,6 +590,16 @@ static int atoi_check16(char *a, int *res)
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int atoi_check8(char *a, int *res)
|
||||
{
|
||||
if (!(atoi_check(a, res)) ||
|
||||
*res < 0 ||
|
||||
*res > 0xff)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void add_txt(char *name, char *txt)
|
||||
{
|
||||
@@ -2732,7 +2750,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
else
|
||||
{
|
||||
char *cp, *lastp = NULL, last = 0;
|
||||
int fac = 1;
|
||||
int fac = 1, isdig = 0;
|
||||
|
||||
if (strlen(a[j]) > 1)
|
||||
{
|
||||
@@ -2763,9 +2781,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
}
|
||||
|
||||
for (cp = a[j]; *cp; cp++)
|
||||
if (!isdigit((unsigned char)*cp) && *cp != ' ')
|
||||
if (isdigit((unsigned char)*cp))
|
||||
isdig = 1;
|
||||
else if (*cp != ' ')
|
||||
break;
|
||||
|
||||
|
||||
if (*cp)
|
||||
{
|
||||
if (lastp)
|
||||
@@ -2787,7 +2807,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
new->domain = strip_hostname(new->hostname);
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (isdig)
|
||||
{
|
||||
new->lease_time = atoi(a[j]) * fac;
|
||||
/* Leases of a minute or less confuse
|
||||
@@ -3663,9 +3683,64 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
daemon->host_records_tail = new;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
case LOPT_TRUST_ANCHOR:
|
||||
{
|
||||
struct ds_config *new = opt_malloc(sizeof(struct ds_config));
|
||||
char *cp, *cp1, *keyhex, *digest, *algo = NULL;
|
||||
int len;
|
||||
|
||||
new->class = C_IN;
|
||||
|
||||
if ((comma = split(arg)) && (algo = split(comma)))
|
||||
{
|
||||
int class = 0;
|
||||
if (strcmp(comma, "IN") == 0)
|
||||
class = C_IN;
|
||||
else if (strcmp(comma, "CH") == 0)
|
||||
class = C_CHAOS;
|
||||
else if (strcmp(comma, "HS") == 0)
|
||||
class = C_HESIOD;
|
||||
|
||||
if (class != 0)
|
||||
{
|
||||
new->class = class;
|
||||
comma = algo;
|
||||
algo = split(comma);
|
||||
}
|
||||
}
|
||||
|
||||
if (!comma || !algo || !(digest = split(algo)) || !(keyhex = split(digest)) ||
|
||||
!atoi_check16(comma, &new->keytag) ||
|
||||
!atoi_check8(algo, &new->algo) ||
|
||||
!atoi_check8(digest, &new->digest_type) ||
|
||||
!(new->name = canonicalise_opt(arg)))
|
||||
ret_err(_("bad trust anchor"));
|
||||
|
||||
/* Upper bound on length */
|
||||
len = (2*strlen(keyhex))+1;
|
||||
new->digest = opt_malloc(len);
|
||||
unhide_metas(keyhex);
|
||||
/* 4034: "Whitespace is allowed within digits" */
|
||||
for (cp = keyhex; *cp; )
|
||||
if (isspace(*cp))
|
||||
for (cp1 = cp; *cp1; cp1++)
|
||||
*cp1 = *(cp1+1);
|
||||
else
|
||||
cp++;
|
||||
if ((new->digestlen = parse_hex(keyhex, (unsigned char *)new->digest, len, NULL, NULL)) == -1)
|
||||
ret_err(_("bad HEX in trust anchor"));
|
||||
|
||||
new->next = daemon->ds;
|
||||
daemon->ds = new;
|
||||
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
ret_err(_("unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DBus support)"));
|
||||
ret_err(_("unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DNSSEC/DBus support)"));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 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
|
||||
|
||||
132
src/radv.c
132
src/radv.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 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
|
||||
@@ -31,8 +31,8 @@ struct ra_param {
|
||||
int ind, managed, other, found_context, first;
|
||||
char *if_name;
|
||||
struct dhcp_netid *tags;
|
||||
struct in6_addr link_local, link_global;
|
||||
unsigned int pref_time, adv_interval;
|
||||
struct in6_addr link_local, link_global, ula;
|
||||
unsigned int glob_pref_time, link_pref_time, ula_pref_time, adv_interval;
|
||||
};
|
||||
|
||||
struct search_param {
|
||||
@@ -206,6 +206,7 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
|
||||
struct dhcp_opt *opt_cfg;
|
||||
struct ra_interface *ra_param = find_iface_param(iface_name);
|
||||
int done_dns = 0, old_prefix = 0;
|
||||
unsigned int min_pref_time;
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
FILE *f;
|
||||
#endif
|
||||
@@ -228,7 +229,7 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
|
||||
parm.if_name = iface_name;
|
||||
parm.first = 1;
|
||||
parm.now = now;
|
||||
parm.pref_time = 0;
|
||||
parm.glob_pref_time = parm.link_pref_time = parm.ula_pref_time = 0;
|
||||
parm.adv_interval = calc_interval(ra_param);
|
||||
|
||||
/* set tag with name == interface */
|
||||
@@ -245,6 +246,18 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
|
||||
if (!iface_enumerate(AF_INET6, &parm, add_prefixes))
|
||||
return;
|
||||
|
||||
/* Find smallest preferred time within address classes,
|
||||
to use as lifetime for options. This is a rather arbitrary choice. */
|
||||
min_pref_time = 0xffffffff;
|
||||
if (parm.glob_pref_time != 0 && parm.glob_pref_time < min_pref_time)
|
||||
min_pref_time = parm.glob_pref_time;
|
||||
|
||||
if (parm.ula_pref_time != 0 && parm.ula_pref_time < min_pref_time)
|
||||
min_pref_time = parm.ula_pref_time;
|
||||
|
||||
if (parm.link_pref_time != 0 && parm.link_pref_time < min_pref_time)
|
||||
min_pref_time = parm.link_pref_time;
|
||||
|
||||
/* Look for constructed contexts associated with addresses which have gone,
|
||||
and advertise them with preferred_time == 0 RFC 6204 4.3 L-13 */
|
||||
for (up = &daemon->dhcp6, context = daemon->dhcp6; context; context = tmp)
|
||||
@@ -272,10 +285,28 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
|
||||
/* zero net part of address */
|
||||
setaddr6part(&local, addr6part(&local) & ~((context->prefix == 64) ? (u64)-1LL : (1LLU << (128 - context->prefix)) - 1LLU));
|
||||
|
||||
|
||||
if ((context->flags &
|
||||
(CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS)))
|
||||
do_slaac = 1;
|
||||
|
||||
{
|
||||
do_slaac = 1;
|
||||
if (context->flags & CONTEXT_DHCP)
|
||||
{
|
||||
parm.other = 1;
|
||||
if (!(context->flags & CONTEXT_RA_STATELESS))
|
||||
parm.managed = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* don't do RA for non-ra-only unless --enable-ra is set */
|
||||
if (option_bool(OPT_RA))
|
||||
{
|
||||
parm.managed = 1;
|
||||
parm.other = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ((opt = expand(sizeof(struct prefix_opt))))
|
||||
{
|
||||
opt->type = ICMP6_OPT_PREFIX;
|
||||
@@ -340,22 +371,48 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
|
||||
|
||||
if (opt_cfg->opt == OPTION6_DNS_SERVER)
|
||||
{
|
||||
struct in6_addr *a = (struct in6_addr *)opt_cfg->val;
|
||||
|
||||
struct in6_addr *a;
|
||||
int len;
|
||||
|
||||
done_dns = 1;
|
||||
if (opt_cfg->len == 0 || (IN6_IS_ADDR_UNSPECIFIED(a) && parm.pref_time != 0))
|
||||
|
||||
if (opt_cfg->len == 0)
|
||||
continue;
|
||||
|
||||
put_opt6_char(ICMP6_OPT_RDNSS);
|
||||
put_opt6_char((opt_cfg->len/8) + 1);
|
||||
put_opt6_short(0);
|
||||
put_opt6_long(parm.pref_time);
|
||||
/* zero means "self" */
|
||||
for (i = 0; i < opt_cfg->len; i += IN6ADDRSZ, a++)
|
||||
if (IN6_IS_ADDR_UNSPECIFIED(a))
|
||||
put_opt6(&parm.link_global, IN6ADDRSZ);
|
||||
else
|
||||
put_opt6(a, IN6ADDRSZ);
|
||||
/* reduce len for any addresses we can't substitute */
|
||||
for (a = (struct in6_addr *)opt_cfg->val, len = opt_cfg->len, i = 0;
|
||||
i < opt_cfg->len; i += IN6ADDRSZ, a++)
|
||||
if ((IN6_IS_ADDR_UNSPECIFIED(a) && parm.glob_pref_time == 0) ||
|
||||
(IN6_IS_ADDR_ULA_ZERO(a) && parm.ula_pref_time == 0) ||
|
||||
(IN6_IS_ADDR_LINK_LOCAL_ZERO(a) && parm.link_pref_time == 0))
|
||||
len -= IN6ADDRSZ;
|
||||
|
||||
if (len != 0)
|
||||
{
|
||||
put_opt6_char(ICMP6_OPT_RDNSS);
|
||||
put_opt6_char((len/8) + 1);
|
||||
put_opt6_short(0);
|
||||
put_opt6_long(min_pref_time);
|
||||
|
||||
for (a = (struct in6_addr *)opt_cfg->val, i = 0; i < opt_cfg->len; i += IN6ADDRSZ, a++)
|
||||
if (IN6_IS_ADDR_UNSPECIFIED(a))
|
||||
{
|
||||
if (parm.glob_pref_time != 0)
|
||||
put_opt6(&parm.link_global, IN6ADDRSZ);
|
||||
}
|
||||
else if (IN6_IS_ADDR_ULA_ZERO(a))
|
||||
{
|
||||
if (parm.ula_pref_time != 0)
|
||||
put_opt6(&parm.ula, IN6ADDRSZ);
|
||||
}
|
||||
else if (IN6_IS_ADDR_LINK_LOCAL_ZERO(a))
|
||||
{
|
||||
if (parm.link_pref_time != 0)
|
||||
put_opt6(&parm.link_local, IN6ADDRSZ);
|
||||
}
|
||||
else
|
||||
put_opt6(a, IN6ADDRSZ);
|
||||
}
|
||||
}
|
||||
|
||||
if (opt_cfg->opt == OPTION6_DOMAIN_SEARCH && opt_cfg->len != 0)
|
||||
@@ -365,7 +422,7 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
|
||||
put_opt6_char(ICMP6_OPT_DNSSL);
|
||||
put_opt6_char(len + 1);
|
||||
put_opt6_short(0);
|
||||
put_opt6_long(parm.pref_time);
|
||||
put_opt6_long(min_pref_time);
|
||||
put_opt6(opt_cfg->val, opt_cfg->len);
|
||||
|
||||
/* pad */
|
||||
@@ -374,13 +431,13 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
|
||||
}
|
||||
}
|
||||
|
||||
if (daemon->port == NAMESERVER_PORT && !done_dns && parm.pref_time != 0)
|
||||
if (daemon->port == NAMESERVER_PORT && !done_dns && parm.link_pref_time != 0)
|
||||
{
|
||||
/* default == us, as long as we are supplying DNS service. */
|
||||
put_opt6_char(ICMP6_OPT_RDNSS);
|
||||
put_opt6_char(3);
|
||||
put_opt6_short(0);
|
||||
put_opt6_long(parm.pref_time);
|
||||
put_opt6_long(min_pref_time);
|
||||
put_opt6(&parm.link_local, IN6ADDRSZ);
|
||||
}
|
||||
|
||||
@@ -426,7 +483,16 @@ static int add_prefixes(struct in6_addr *local, int prefix,
|
||||
if (if_index == param->ind)
|
||||
{
|
||||
if (IN6_IS_ADDR_LINKLOCAL(local))
|
||||
param->link_local = *local;
|
||||
{
|
||||
/* Can there be more than one LL address?
|
||||
Select the one with the longest preferred time
|
||||
if there is. */
|
||||
if (preferred > param->link_pref_time)
|
||||
{
|
||||
param->link_pref_time = preferred;
|
||||
param->link_local = *local;
|
||||
}
|
||||
}
|
||||
else if (!IN6_IS_ADDR_LOOPBACK(local) &&
|
||||
!IN6_IS_ADDR_MULTICAST(local))
|
||||
{
|
||||
@@ -516,11 +582,22 @@ static int add_prefixes(struct in6_addr *local, int prefix,
|
||||
/* configured time is ceiling */
|
||||
if (!constructed || preferred > time)
|
||||
preferred = time;
|
||||
|
||||
if (preferred > param->pref_time)
|
||||
|
||||
if (IN6_IS_ADDR_ULA(local))
|
||||
{
|
||||
param->pref_time = preferred;
|
||||
param->link_global = *local;
|
||||
if (preferred > param->ula_pref_time)
|
||||
{
|
||||
param->ula_pref_time = preferred;
|
||||
param->ula = *local;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (preferred > param->glob_pref_time)
|
||||
{
|
||||
param->glob_pref_time = preferred;
|
||||
param->link_global = *local;
|
||||
}
|
||||
}
|
||||
|
||||
if (real_prefix != 0)
|
||||
@@ -546,7 +623,6 @@ static int add_prefixes(struct in6_addr *local, int prefix,
|
||||
if (!option_bool(OPT_QUIET_RA))
|
||||
my_syslog(MS_DHCP | LOG_INFO, "RTR-ADVERT(%s) %s", param->if_name, daemon->addrbuff);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
524
src/rfc1035.c
524
src/rfc1035.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 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,13 +16,6 @@
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
|
||||
#define CHECK_LEN(header, pp, plen, len) \
|
||||
((size_t)((pp) - (unsigned char *)(header) + (len)) <= (plen))
|
||||
|
||||
#define ADD_RDLEN(header, pp, plen, len) \
|
||||
(!CHECK_LEN(header, pp, plen, len) ? 0 : (((pp) += (len)), 1))
|
||||
|
||||
int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
|
||||
char *name, int isExtract, int extrabytes)
|
||||
{
|
||||
@@ -274,7 +267,7 @@ int in_arpa_name_2_addr(char *namein, struct all_addr *addrp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned char *skip_name(unsigned char *ansp, struct dns_header *header, size_t plen, int extrabytes)
|
||||
unsigned char *skip_name(unsigned char *ansp, struct dns_header *header, size_t plen, int extrabytes)
|
||||
{
|
||||
while(1)
|
||||
{
|
||||
@@ -367,6 +360,7 @@ static unsigned char *skip_section(unsigned char *ansp, int count, struct dns_he
|
||||
than CRC the raw bytes, since replies might be compressed differently.
|
||||
We ignore case in the names for the same reason. Return all-ones
|
||||
if there is not question section. */
|
||||
#ifndef HAVE_DNSSEC
|
||||
unsigned int questions_crc(struct dns_header *header, size_t plen, char *name)
|
||||
{
|
||||
int q;
|
||||
@@ -407,7 +401,7 @@ unsigned int questions_crc(struct dns_header *header, size_t plen, char *name)
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
size_t resize_packet(struct dns_header *header, size_t plen, unsigned char *pheader, size_t hlen)
|
||||
{
|
||||
@@ -500,7 +494,7 @@ unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t
|
||||
else if (is_sign &&
|
||||
i == arcount - 1 &&
|
||||
class == C_ANY &&
|
||||
(type == T_SIG || type == T_TSIG))
|
||||
type == T_TSIG)
|
||||
*is_sign = 1;
|
||||
}
|
||||
|
||||
@@ -515,50 +509,64 @@ struct macparm {
|
||||
};
|
||||
|
||||
static size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit,
|
||||
int optno, unsigned char *opt, size_t optlen)
|
||||
int optno, unsigned char *opt, size_t optlen, int set_do)
|
||||
{
|
||||
unsigned char *lenp, *datap, *p;
|
||||
int rdlen;
|
||||
int rdlen, is_sign;
|
||||
|
||||
if (ntohs(header->arcount) == 0)
|
||||
if (!(p = find_pseudoheader(header, plen, NULL, NULL, &is_sign)))
|
||||
{
|
||||
if (is_sign)
|
||||
return plen;
|
||||
|
||||
/* We are adding the pseudoheader */
|
||||
if (!(p = skip_questions(header, plen)) ||
|
||||
!(p = skip_section(p,
|
||||
ntohs(header->ancount) + ntohs(header->nscount),
|
||||
ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount),
|
||||
header, plen)))
|
||||
return plen;
|
||||
*p++ = 0; /* empty name */
|
||||
PUTSHORT(T_OPT, p);
|
||||
PUTSHORT(daemon->edns_pktsz, p); /* max packet length */
|
||||
PUTLONG(0, p); /* extended RCODE */
|
||||
PUTSHORT(0, p); /* extended RCODE and version */
|
||||
PUTSHORT(set_do ? 0x8000 : 0, p); /* DO flag */
|
||||
lenp = p;
|
||||
PUTSHORT(0, p); /* RDLEN */
|
||||
rdlen = 0;
|
||||
if (((ssize_t)optlen) > (limit - (p + 4)))
|
||||
return plen; /* Too big */
|
||||
header->arcount = htons(1);
|
||||
header->arcount = htons(ntohs(header->arcount) + 1);
|
||||
datap = p;
|
||||
}
|
||||
else
|
||||
{
|
||||
int i, is_sign;
|
||||
unsigned short code, len;
|
||||
int i;
|
||||
unsigned short code, len, flags;
|
||||
|
||||
/* Must be at the end, if exists */
|
||||
if (ntohs(header->arcount) != 1 ||
|
||||
!(p = find_pseudoheader(header, plen, NULL, NULL, &is_sign)) ||
|
||||
is_sign ||
|
||||
(!(p = skip_name(p, header, plen, 10))))
|
||||
return plen;
|
||||
|
||||
p += 8; /* skip UDP length and RCODE */
|
||||
|
||||
p += 6; /* skip UDP length and RCODE */
|
||||
GETSHORT(flags, p);
|
||||
if (set_do)
|
||||
{
|
||||
p -=2;
|
||||
PUTSHORT(flags | 0x8000, p);
|
||||
}
|
||||
|
||||
lenp = p;
|
||||
GETSHORT(rdlen, p);
|
||||
if (!CHECK_LEN(header, p, plen, rdlen))
|
||||
return plen; /* bad packet */
|
||||
datap = p;
|
||||
|
||||
/* no option to add */
|
||||
if (optno == 0)
|
||||
return plen;
|
||||
|
||||
/* check if option already there */
|
||||
for (i = 0; i + 4 < rdlen; i += len + 4)
|
||||
{
|
||||
@@ -573,10 +581,13 @@ static size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned
|
||||
return plen; /* Too big */
|
||||
}
|
||||
|
||||
PUTSHORT(optno, p);
|
||||
PUTSHORT(optlen, p);
|
||||
memcpy(p, opt, optlen);
|
||||
p += optlen;
|
||||
if (optno != 0)
|
||||
{
|
||||
PUTSHORT(optno, p);
|
||||
PUTSHORT(optlen, p);
|
||||
memcpy(p, opt, optlen);
|
||||
p += optlen;
|
||||
}
|
||||
|
||||
PUTSHORT(p - datap, lenp);
|
||||
return p - (unsigned char *)header;
|
||||
@@ -602,7 +613,7 @@ static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *p
|
||||
if (!match)
|
||||
return 1; /* continue */
|
||||
|
||||
parm->plen = add_pseudoheader(parm->header, parm->plen, parm->limit, EDNS0_OPTION_MAC, (unsigned char *)mac, maclen);
|
||||
parm->plen = add_pseudoheader(parm->header, parm->plen, parm->limit, EDNS0_OPTION_MAC, (unsigned char *)mac, maclen, 0);
|
||||
|
||||
return 0; /* done */
|
||||
}
|
||||
@@ -681,9 +692,16 @@ size_t add_source_addr(struct dns_header *header, size_t plen, char *limit, unio
|
||||
struct subnet_opt opt;
|
||||
|
||||
len = calc_subnet_opt(&opt, source);
|
||||
return add_pseudoheader(header, plen, (unsigned char *)limit, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len);
|
||||
return add_pseudoheader(header, plen, (unsigned char *)limit, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0);
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
size_t add_do_bit(struct dns_header *header, size_t plen, char *limit)
|
||||
{
|
||||
return add_pseudoheader(header, plen, (unsigned char *)limit, 0, NULL, 0, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer)
|
||||
{
|
||||
/* Section 9.2, Check that subnet option in reply matches. */
|
||||
@@ -736,7 +754,7 @@ int private_net(struct in_addr addr, int ban_localhost)
|
||||
((ip_addr & 0xFFFF0000) == 0xA9FE0000) /* 169.254.0.0/16 (zeroconf) */ ;
|
||||
}
|
||||
|
||||
static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *header, size_t qlen, char *name)
|
||||
static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *header, size_t qlen, char *name, int *doctored)
|
||||
{
|
||||
int i, qtype, qclass, rdlen;
|
||||
|
||||
@@ -781,6 +799,7 @@ static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *
|
||||
addr.s_addr |= (doctor->out.s_addr & doctor->mask.s_addr);
|
||||
/* Since we munged the data, the server it came from is no longer authoritative */
|
||||
header->hb3 &= ~HB3_AA;
|
||||
*doctored = 1;
|
||||
memcpy(p, &addr, INADDRSZ);
|
||||
break;
|
||||
}
|
||||
@@ -819,7 +838,7 @@ static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *
|
||||
return p;
|
||||
}
|
||||
|
||||
static int find_soa(struct dns_header *header, size_t qlen, char *name)
|
||||
static int find_soa(struct dns_header *header, size_t qlen, char *name, int *doctored)
|
||||
{
|
||||
unsigned char *p;
|
||||
int qtype, qclass, rdlen;
|
||||
@@ -828,7 +847,7 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name)
|
||||
|
||||
/* first move to NS section and find TTL from any SOA section */
|
||||
if (!(p = skip_questions(header, qlen)) ||
|
||||
!(p = do_doctor(p, ntohs(header->ancount), header, qlen, name)))
|
||||
!(p = do_doctor(p, ntohs(header->ancount), header, qlen, name, doctored)))
|
||||
return 0; /* bad packet */
|
||||
|
||||
for (i = ntohs(header->nscount); i != 0; i--)
|
||||
@@ -863,8 +882,8 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name)
|
||||
return 0; /* bad packet */
|
||||
}
|
||||
|
||||
/* rewrite addresses in additioal section too */
|
||||
if (!do_doctor(p, ntohs(header->arcount), header, qlen, NULL))
|
||||
/* rewrite addresses in additional section too */
|
||||
if (!do_doctor(p, ntohs(header->arcount), header, qlen, NULL, doctored))
|
||||
return 0;
|
||||
|
||||
if (!found_soa)
|
||||
@@ -878,7 +897,7 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name)
|
||||
expired and cleaned out that way.
|
||||
Return 1 if we reject an address because it look like part of dns-rebinding attack. */
|
||||
int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t now,
|
||||
char **ipsets, int is_sign, int check_rebind, int checking_disabled)
|
||||
char **ipsets, int is_sign, int check_rebind, int no_cache_dnssec, int secure, int *doctored)
|
||||
{
|
||||
unsigned char *p, *p1, *endrr, *namep;
|
||||
int i, j, qtype, qclass, aqtype, aqclass, ardlen, res, searched_soa = 0;
|
||||
@@ -893,10 +912,14 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
cache_start_insert();
|
||||
|
||||
/* find_soa is needed for dns_doctor and logging side-effects, so don't call it lazily if there are any. */
|
||||
if (daemon->doctors || option_bool(OPT_LOG))
|
||||
if (daemon->doctors || option_bool(OPT_LOG) || option_bool(OPT_DNSSEC_VALID))
|
||||
{
|
||||
searched_soa = 1;
|
||||
ttl = find_soa(header, qlen, name);
|
||||
ttl = find_soa(header, qlen, name, doctored);
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (*doctored)
|
||||
secure = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* go through the questions. */
|
||||
@@ -907,8 +930,9 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
int found = 0, cname_count = 5;
|
||||
struct crec *cpp = NULL;
|
||||
int flags = RCODE(header) == NXDOMAIN ? F_NXDOMAIN : 0;
|
||||
int secflag = secure ? F_DNSSECOK : 0;
|
||||
unsigned long cttl = ULONG_MAX, attl;
|
||||
|
||||
|
||||
namep = p;
|
||||
if (!extract_name(header, qlen, &p, name, 1, 4))
|
||||
return 0; /* bad packet */
|
||||
@@ -966,10 +990,11 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
{
|
||||
if (!cname_count--)
|
||||
return 0; /* looped CNAMES */
|
||||
secflag = 0; /* no longer DNSSEC */
|
||||
goto cname_loop;
|
||||
}
|
||||
|
||||
cache_insert(name, &addr, now, cttl, name_encoding | F_REVERSE);
|
||||
cache_insert(name, &addr, now, cttl, name_encoding | secflag | F_REVERSE);
|
||||
found = 1;
|
||||
}
|
||||
|
||||
@@ -984,10 +1009,10 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
if (!searched_soa)
|
||||
{
|
||||
searched_soa = 1;
|
||||
ttl = find_soa(header, qlen, NULL);
|
||||
ttl = find_soa(header, qlen, NULL, doctored);
|
||||
}
|
||||
if (ttl)
|
||||
cache_insert(NULL, &addr, now, ttl, name_encoding | F_REVERSE | F_NEG | flags);
|
||||
cache_insert(NULL, &addr, now, ttl, name_encoding | F_REVERSE | F_NEG | flags | secflag);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1037,7 +1062,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
{
|
||||
if (!cname_count--)
|
||||
return 0; /* looped CNAMES */
|
||||
newc = cache_insert(name, NULL, now, attl, F_CNAME | F_FORWARD);
|
||||
newc = cache_insert(name, NULL, now, attl, F_CNAME | F_FORWARD | secflag);
|
||||
if (newc)
|
||||
{
|
||||
newc->addr.cname.target.cache = NULL;
|
||||
@@ -1080,7 +1105,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
}
|
||||
#endif
|
||||
|
||||
newc = cache_insert(name, &addr, now, attl, flags | F_FORWARD);
|
||||
newc = cache_insert(name, &addr, now, attl, flags | F_FORWARD | secflag);
|
||||
if (newc && cpp)
|
||||
{
|
||||
cpp->addr.cname.target.cache = newc;
|
||||
@@ -1100,13 +1125,13 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
if (!searched_soa)
|
||||
{
|
||||
searched_soa = 1;
|
||||
ttl = find_soa(header, qlen, NULL);
|
||||
ttl = find_soa(header, qlen, NULL, doctored);
|
||||
}
|
||||
/* If there's no SOA to get the TTL from, but there is a CNAME
|
||||
pointing at this, inherit its TTL */
|
||||
if (ttl || cpp)
|
||||
{
|
||||
newc = cache_insert(name, NULL, now, ttl ? ttl : cttl, F_FORWARD | F_NEG | flags);
|
||||
newc = cache_insert(name, NULL, now, ttl ? ttl : cttl, F_FORWARD | F_NEG | flags | secflag);
|
||||
if (newc && cpp)
|
||||
{
|
||||
cpp->addr.cname.target.cache = newc;
|
||||
@@ -1118,15 +1143,13 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
}
|
||||
|
||||
/* Don't put stuff from a truncated packet into the cache.
|
||||
Don't cache replies where DNSSEC validation was turned off, either
|
||||
the upstream server told us so, or the original query specified it.
|
||||
Don't cache replies from non-recursive nameservers, since we may get a
|
||||
reply containing a CNAME but not its target, even though the target
|
||||
does exist. */
|
||||
if (!(header->hb3 & HB3_TC) &&
|
||||
!(header->hb4 & HB4_CD) &&
|
||||
(header->hb4 & HB4_RA) &&
|
||||
!checking_disabled)
|
||||
!no_cache_dnssec)
|
||||
cache_end_insert();
|
||||
|
||||
return 0;
|
||||
@@ -1134,7 +1157,6 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
|
||||
/* If the packet holds exactly one query
|
||||
return F_IPV4 or F_IPV6 and leave the name from the query in name */
|
||||
|
||||
unsigned int extract_request(struct dns_header *header, size_t qlen, char *name, unsigned short *typep)
|
||||
{
|
||||
unsigned char *p = (unsigned char *)(header+1);
|
||||
@@ -1220,7 +1242,7 @@ int check_for_local_domain(char *name, time_t now)
|
||||
struct ptr_record *ptr;
|
||||
struct naptr *naptr;
|
||||
|
||||
if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_CNAME)) &&
|
||||
if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_CNAME | F_NO_RR)) &&
|
||||
(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
|
||||
return 1;
|
||||
|
||||
@@ -1283,7 +1305,7 @@ int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
|
||||
/* Found a bogus address. Insert that info here, since there no SOA record
|
||||
to get the ttl from in the normal processing */
|
||||
cache_start_insert();
|
||||
cache_insert(name, NULL, now, ttl, F_IPV4 | F_FORWARD | F_NEG | F_NXDOMAIN | F_CONFIG);
|
||||
cache_insert(name, NULL, now, ttl, F_IPV4 | F_FORWARD | F_NEG | F_NXDOMAIN);
|
||||
cache_end_insert();
|
||||
|
||||
return 1;
|
||||
@@ -1353,6 +1375,11 @@ int add_resource_record(struct dns_header *header, char *limit, int *truncp, int
|
||||
p += INADDRSZ;
|
||||
break;
|
||||
|
||||
case 'b':
|
||||
usval = va_arg(ap, int);
|
||||
*p++ = usval;
|
||||
break;
|
||||
|
||||
case 's':
|
||||
usval = va_arg(ap, int);
|
||||
PUTSHORT(usval, p);
|
||||
@@ -1425,7 +1452,8 @@ static unsigned long crec_ttl(struct crec *crecp, time_t now)
|
||||
|
||||
/* return zero if we can't answer from cache, or packet size if we can */
|
||||
size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
struct in_addr local_addr, struct in_addr local_netmask, time_t now)
|
||||
struct in_addr local_addr, struct in_addr local_netmask,
|
||||
time_t now, int *ad_reqd)
|
||||
{
|
||||
char *name = daemon->namebuff;
|
||||
unsigned char *p, *ansp, *pheader;
|
||||
@@ -1434,28 +1462,39 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
int nameoffset;
|
||||
unsigned short flag;
|
||||
int q, ans, anscount = 0, addncount = 0;
|
||||
int dryrun = 0, sec_reqd = 0;
|
||||
int dryrun = 0, sec_reqd = 0, have_pseudoheader = 0;
|
||||
int is_sign;
|
||||
struct crec *crecp;
|
||||
int nxdomain = 0, auth = 1, trunc = 0;
|
||||
int nxdomain = 0, auth = 1, trunc = 0, sec_data = 1;
|
||||
struct mx_srv_record *rec;
|
||||
size_t len;
|
||||
|
||||
/* Don't return AD set if checking disabled. */
|
||||
if (header->hb4 & HB4_CD)
|
||||
sec_data = 0;
|
||||
|
||||
/* RFC 6840 5.7 */
|
||||
*ad_reqd = header->hb4 & HB4_AD;
|
||||
|
||||
/* If there is an RFC2671 pseudoheader then it will be overwritten by
|
||||
partial replies, so we have to do a dry run to see if we can answer
|
||||
the query. We check to see if the do bit is set, if so we always
|
||||
forward rather than answering from the cache, which doesn't include
|
||||
security information. */
|
||||
security information, unless we're in DNSSEC validation mode. */
|
||||
|
||||
if (find_pseudoheader(header, qlen, NULL, &pheader, &is_sign))
|
||||
{
|
||||
unsigned short udpsz, flags;
|
||||
unsigned char *psave = pheader;
|
||||
|
||||
have_pseudoheader = 1;
|
||||
|
||||
GETSHORT(udpsz, pheader);
|
||||
pheader += 2; /* ext_rcode */
|
||||
GETSHORT(flags, pheader);
|
||||
|
||||
sec_reqd = flags & 0x8000; /* do bit */
|
||||
*ad_reqd = 1;
|
||||
|
||||
/* If our client is advertising a larger UDP packet size
|
||||
than we allow, trim it so that we don't get an overlarge
|
||||
@@ -1516,6 +1555,88 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID) && (qtype == T_DNSKEY || qtype == T_DS))
|
||||
{
|
||||
int gotone = 0;
|
||||
struct blockdata *keydata;
|
||||
|
||||
/* Do we have RRSIG? Can't do DS or DNSKEY otherwise. */
|
||||
if (sec_reqd)
|
||||
{
|
||||
crecp = NULL;
|
||||
while ((crecp = cache_find_by_name(crecp, name, now, F_DNSKEY | F_DS)))
|
||||
if (crecp->uid == qclass && crecp->addr.sig.type_covered == qtype)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!sec_reqd || crecp)
|
||||
{
|
||||
if (qtype == T_DS)
|
||||
{
|
||||
crecp = NULL;
|
||||
while ((crecp = cache_find_by_name(crecp, name, now, F_DS)))
|
||||
if (crecp->uid == qclass)
|
||||
{
|
||||
gotone = 1;
|
||||
if (!dryrun && (keydata = blockdata_retrieve(crecp->addr.ds.keydata, crecp->addr.ds.keylen, NULL)))
|
||||
{
|
||||
struct all_addr a;
|
||||
a.addr.keytag = crecp->addr.ds.keytag;
|
||||
log_query(F_KEYTAG | (crecp->flags & F_CONFIG), name, &a, "DS keytag %u");
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
crec_ttl(crecp, now), &nameoffset,
|
||||
T_DS, qclass, "sbbt",
|
||||
crecp->addr.ds.keytag, crecp->addr.ds.algo, crecp->addr.ds.digest, crecp->addr.ds.keylen, keydata))
|
||||
anscount++;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
else /* DNSKEY */
|
||||
{
|
||||
crecp = NULL;
|
||||
while ((crecp = cache_find_by_name(crecp, name, now, F_DNSKEY)))
|
||||
if (crecp->uid == qclass)
|
||||
{
|
||||
gotone = 1;
|
||||
if (!dryrun && (keydata = blockdata_retrieve(crecp->addr.key.keydata, crecp->addr.key.keylen, NULL)))
|
||||
{
|
||||
struct all_addr a;
|
||||
a.addr.keytag = crecp->addr.key.keytag;
|
||||
log_query(F_KEYTAG | (crecp->flags & F_CONFIG), name, &a, "DNSKEY keytag %u");
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
crec_ttl(crecp, now), &nameoffset,
|
||||
T_DNSKEY, qclass, "sbbt",
|
||||
crecp->addr.key.flags, 3, crecp->addr.key.algo, crecp->addr.key.keylen, keydata))
|
||||
anscount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Now do RRSIGs */
|
||||
if (gotone)
|
||||
{
|
||||
ans = 1;
|
||||
auth = 0;
|
||||
if (!dryrun && sec_reqd)
|
||||
{
|
||||
crecp = NULL;
|
||||
while ((crecp = cache_find_by_name(crecp, name, now, F_DNSKEY | F_DS)))
|
||||
if (crecp->uid == qclass && crecp->addr.sig.type_covered == qtype &&
|
||||
(keydata = blockdata_retrieve(crecp->addr.sig.keydata, crecp->addr.sig.keylen, NULL)))
|
||||
{
|
||||
add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
crec_ttl(crecp, now), &nameoffset,
|
||||
T_RRSIG, qclass, "t", crecp->addr.sig.keylen, keydata);
|
||||
anscount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (qclass == C_IN)
|
||||
{
|
||||
struct txt_record *t;
|
||||
@@ -1606,38 +1727,77 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
}
|
||||
}
|
||||
else if ((crecp = cache_find_by_addr(NULL, &addr, now, is_arpa)))
|
||||
do
|
||||
{
|
||||
/* don't answer wildcard queries with data not from /etc/hosts or dhcp leases */
|
||||
if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP)))
|
||||
continue;
|
||||
|
||||
if (crecp->flags & F_NEG)
|
||||
{
|
||||
ans = 1;
|
||||
auth = 0;
|
||||
if (crecp->flags & F_NXDOMAIN)
|
||||
nxdomain = 1;
|
||||
if (!dryrun)
|
||||
log_query(crecp->flags & ~F_FORWARD, name, &addr, NULL);
|
||||
}
|
||||
else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd)
|
||||
{
|
||||
ans = 1;
|
||||
if (!(crecp->flags & (F_HOSTS | F_DHCP)))
|
||||
auth = 0;
|
||||
if (!dryrun)
|
||||
{
|
||||
log_query(crecp->flags & ~F_FORWARD, cache_get_name(crecp), &addr,
|
||||
record_source(crecp->uid));
|
||||
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
crec_ttl(crecp, now), NULL,
|
||||
T_PTR, C_IN, "d", cache_get_name(crecp)))
|
||||
anscount++;
|
||||
}
|
||||
}
|
||||
} while ((crecp = cache_find_by_addr(crecp, &addr, now, is_arpa)));
|
||||
{
|
||||
if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) && sec_reqd)
|
||||
{
|
||||
if (!option_bool(OPT_DNSSEC_VALID) || ((crecp->flags & F_NEG) && (crecp->flags & F_DNSSECOK)))
|
||||
crecp = NULL;
|
||||
#ifdef HAVE_DNSSEC
|
||||
else if (crecp->flags & F_DNSSECOK)
|
||||
{
|
||||
int gotsig = 0;
|
||||
struct crec *rr_crec = NULL;
|
||||
|
||||
while ((rr_crec = cache_find_by_name(rr_crec, name, now, F_DS | F_DNSKEY)))
|
||||
{
|
||||
if (rr_crec->addr.sig.type_covered == T_PTR && rr_crec->uid == C_IN)
|
||||
{
|
||||
char *sigdata = blockdata_retrieve(rr_crec->addr.sig.keydata, rr_crec->addr.sig.keylen, NULL);
|
||||
gotsig = 1;
|
||||
|
||||
if (!dryrun &&
|
||||
add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
rr_crec->ttd - now, &nameoffset,
|
||||
T_RRSIG, C_IN, "t", crecp->addr.sig.keylen, sigdata))
|
||||
anscount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!gotsig)
|
||||
crecp = NULL;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (crecp)
|
||||
{
|
||||
do
|
||||
{
|
||||
/* don't answer wildcard queries with data not from /etc/hosts or dhcp leases */
|
||||
if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP)))
|
||||
continue;
|
||||
|
||||
if (!(crecp->flags & F_DNSSECOK))
|
||||
sec_data = 0;
|
||||
|
||||
if (crecp->flags & F_NEG)
|
||||
{
|
||||
ans = 1;
|
||||
auth = 0;
|
||||
if (crecp->flags & F_NXDOMAIN)
|
||||
nxdomain = 1;
|
||||
if (!dryrun)
|
||||
log_query(crecp->flags & ~F_FORWARD, name, &addr, NULL);
|
||||
}
|
||||
else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd || option_bool(OPT_DNSSEC_VALID))
|
||||
{
|
||||
ans = 1;
|
||||
if (!(crecp->flags & (F_HOSTS | F_DHCP)))
|
||||
auth = 0;
|
||||
if (!dryrun)
|
||||
{
|
||||
log_query(crecp->flags & ~F_FORWARD, cache_get_name(crecp), &addr,
|
||||
record_source(crecp->uid));
|
||||
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
crec_ttl(crecp, now), NULL,
|
||||
T_PTR, C_IN, "d", cache_get_name(crecp)))
|
||||
anscount++;
|
||||
}
|
||||
}
|
||||
} while ((crecp = cache_find_by_addr(crecp, &addr, now, is_arpa)));
|
||||
}
|
||||
}
|
||||
else if (is_rev_synth(is_arpa, &addr, name))
|
||||
{
|
||||
ans = 1;
|
||||
@@ -1758,7 +1918,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
}
|
||||
|
||||
cname_restart:
|
||||
if ((crecp = cache_find_by_name(NULL, name, now, flag | F_CNAME)))
|
||||
if ((crecp = cache_find_by_name(NULL, name, now, flag | F_CNAME | (dryrun ? F_NO_RR : 0))))
|
||||
{
|
||||
int localise = 0;
|
||||
|
||||
@@ -1777,69 +1937,119 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
} while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME)));
|
||||
crecp = save;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
/* don't answer wildcard queries with data not from /etc/hosts
|
||||
or DHCP leases */
|
||||
if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
|
||||
break;
|
||||
|
||||
if (crecp->flags & F_CNAME)
|
||||
|
||||
/* If the client asked for DNSSEC and we can't provide RRSIGs, either
|
||||
because we've not doing DNSSEC or the cached answer is signed by negative,
|
||||
don't answer from the cache, forward instead. */
|
||||
if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) && sec_reqd)
|
||||
{
|
||||
if (!option_bool(OPT_DNSSEC_VALID) || ((crecp->flags & F_NEG) && (crecp->flags & F_DNSSECOK)))
|
||||
crecp = NULL;
|
||||
#ifdef HAVE_DNSSEC
|
||||
else if (crecp->flags & F_DNSSECOK)
|
||||
{
|
||||
char *cname_target = cache_get_cname_target(crecp);
|
||||
/* We're returning validated data, need to return the RRSIG too. */
|
||||
struct crec *rr_crec = NULL;
|
||||
int sigtype = type;
|
||||
/* The signature may have expired even though the data is still in cache,
|
||||
forward instead of answering from cache if so. */
|
||||
int gotsig = 0;
|
||||
|
||||
if (!dryrun)
|
||||
if (crecp->flags & F_CNAME)
|
||||
sigtype = T_CNAME;
|
||||
|
||||
while ((rr_crec = cache_find_by_name(rr_crec, name, now, F_DS | F_DNSKEY)))
|
||||
{
|
||||
log_query(crecp->flags, name, NULL, record_source(crecp->uid));
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
crec_ttl(crecp, now), &nameoffset,
|
||||
T_CNAME, C_IN, "d", cname_target))
|
||||
anscount++;
|
||||
if (rr_crec->addr.sig.type_covered == sigtype && rr_crec->uid == C_IN)
|
||||
{
|
||||
char *sigdata = blockdata_retrieve(rr_crec->addr.sig.keydata, rr_crec->addr.sig.keylen, NULL);
|
||||
gotsig = 1;
|
||||
|
||||
if (!dryrun &&
|
||||
add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
rr_crec->ttd - now, &nameoffset,
|
||||
T_RRSIG, C_IN, "t", rr_crec->addr.sig.keylen, sigdata))
|
||||
anscount++;
|
||||
}
|
||||
}
|
||||
|
||||
strcpy(name, cname_target);
|
||||
/* check if target interface_name */
|
||||
if (crecp->addr.cname.uid == -1)
|
||||
goto intname_restart;
|
||||
else
|
||||
goto cname_restart;
|
||||
if (!gotsig)
|
||||
crecp = NULL;
|
||||
}
|
||||
|
||||
if (crecp->flags & F_NEG)
|
||||
{
|
||||
ans = 1;
|
||||
auth = 0;
|
||||
if (crecp->flags & F_NXDOMAIN)
|
||||
nxdomain = 1;
|
||||
if (!dryrun)
|
||||
log_query(crecp->flags, name, NULL, NULL);
|
||||
}
|
||||
else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd)
|
||||
{
|
||||
/* If we are returning local answers depending on network,
|
||||
filter here. */
|
||||
if (localise &&
|
||||
(crecp->flags & F_HOSTS) &&
|
||||
!is_same_net(*((struct in_addr *)&crecp->addr), local_addr, local_netmask))
|
||||
continue;
|
||||
|
||||
if (!(crecp->flags & (F_HOSTS | F_DHCP)))
|
||||
auth = 0;
|
||||
|
||||
ans = 1;
|
||||
if (!dryrun)
|
||||
{
|
||||
log_query(crecp->flags & ~F_REVERSE, name, &crecp->addr.addr,
|
||||
record_source(crecp->uid));
|
||||
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
crec_ttl(crecp, now), NULL, type, C_IN,
|
||||
type == T_A ? "4" : "6", &crecp->addr))
|
||||
anscount++;
|
||||
}
|
||||
}
|
||||
} while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME)));
|
||||
#endif
|
||||
}
|
||||
|
||||
if (crecp)
|
||||
do
|
||||
{
|
||||
/* don't answer wildcard queries with data not from /etc/hosts
|
||||
or DHCP leases */
|
||||
if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
|
||||
break;
|
||||
|
||||
if (!(crecp->flags & F_DNSSECOK))
|
||||
sec_data = 0;
|
||||
|
||||
if (crecp->flags & F_CNAME)
|
||||
{
|
||||
char *cname_target = cache_get_cname_target(crecp);
|
||||
|
||||
if (!dryrun)
|
||||
{
|
||||
log_query(crecp->flags, name, NULL, record_source(crecp->uid));
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
crec_ttl(crecp, now), &nameoffset,
|
||||
T_CNAME, C_IN, "d", cname_target))
|
||||
anscount++;
|
||||
}
|
||||
|
||||
strcpy(name, cname_target);
|
||||
/* check if target interface_name */
|
||||
if (crecp->addr.cname.uid == -1)
|
||||
goto intname_restart;
|
||||
else
|
||||
goto cname_restart;
|
||||
}
|
||||
|
||||
if (crecp->flags & F_NEG)
|
||||
{
|
||||
/* We don't cache NSEC records, so if a DNSSEC-validated negative answer
|
||||
is cached and the client wants DNSSEC, forward rather than answering from the cache */
|
||||
if (!sec_reqd || !(crecp->flags & F_DNSSECOK))
|
||||
{
|
||||
ans = 1;
|
||||
auth = 0;
|
||||
if (crecp->flags & F_NXDOMAIN)
|
||||
nxdomain = 1;
|
||||
if (!dryrun)
|
||||
log_query(crecp->flags, name, NULL, NULL);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If we are returning local answers depending on network,
|
||||
filter here. */
|
||||
if (localise &&
|
||||
(crecp->flags & F_HOSTS) &&
|
||||
!is_same_net(*((struct in_addr *)&crecp->addr), local_addr, local_netmask))
|
||||
continue;
|
||||
|
||||
if (!(crecp->flags & (F_HOSTS | F_DHCP)))
|
||||
auth = 0;
|
||||
|
||||
ans = 1;
|
||||
if (!dryrun)
|
||||
{
|
||||
log_query(crecp->flags & ~F_REVERSE, name, &crecp->addr.addr,
|
||||
record_source(crecp->uid));
|
||||
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
crec_ttl(crecp, now), NULL, type, C_IN,
|
||||
type == T_A ? "4" : "6", &crecp->addr))
|
||||
anscount++;
|
||||
}
|
||||
}
|
||||
} while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME)));
|
||||
}
|
||||
else if (is_name_synthetic(flag, name, &addr))
|
||||
{
|
||||
@@ -1857,8 +2067,11 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
if (qtype == T_CNAME || qtype == T_ANY)
|
||||
{
|
||||
if ((crecp = cache_find_by_name(NULL, name, now, F_CNAME)) &&
|
||||
(qtype == T_CNAME || (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))))
|
||||
(qtype == T_CNAME || (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG | (dryrun ? F_NO_RR : 0)))))
|
||||
{
|
||||
if (!(crecp->flags & F_DNSSECOK))
|
||||
sec_data = 0;
|
||||
|
||||
ans = 1;
|
||||
if (!dryrun)
|
||||
{
|
||||
@@ -1870,7 +2083,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (qtype == T_MX || qtype == T_ANY)
|
||||
{
|
||||
int found = 0;
|
||||
@@ -1893,7 +2106,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
}
|
||||
|
||||
if (!found && (option_bool(OPT_SELFMX) || option_bool(OPT_LOCALMX)) &&
|
||||
cache_find_by_name(NULL, name, now, F_HOSTS | F_DHCP))
|
||||
cache_find_by_name(NULL, name, now, F_HOSTS | F_DHCP | F_NO_RR))
|
||||
{
|
||||
ans = 1;
|
||||
if (!dryrun)
|
||||
@@ -2037,7 +2250,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
/* truncation */
|
||||
if (trunc)
|
||||
header->hb3 |= HB3_TC;
|
||||
|
||||
|
||||
if (nxdomain)
|
||||
SET_RCODE(header, NXDOMAIN);
|
||||
else
|
||||
@@ -2045,6 +2258,17 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
header->ancount = htons(anscount);
|
||||
header->nscount = htons(0);
|
||||
header->arcount = htons(addncount);
|
||||
return ansp - (unsigned char *)header;
|
||||
|
||||
len = ansp - (unsigned char *)header;
|
||||
|
||||
if (have_pseudoheader)
|
||||
len = add_pseudoheader(header, len, (unsigned char *)limit, 0, NULL, 0, sec_reqd);
|
||||
|
||||
if (*ad_reqd && sec_data)
|
||||
header->hb4 |= HB4_AD;
|
||||
else
|
||||
header->hb4 &= ~HB4_AD;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -24,7 +24,7 @@ struct state {
|
||||
int clid_len, iaid, ia_type, interface, hostname_auth, lease_allocate;
|
||||
char *client_hostname, *hostname, *domain, *send_domain;
|
||||
struct dhcp_context *context;
|
||||
struct in6_addr *link_address, *fallback;
|
||||
struct in6_addr *link_address, *fallback, *ll_addr, *ula_addr;
|
||||
unsigned int xid, fqdn_flags;
|
||||
char *iface_name;
|
||||
void *packet_options, *end;
|
||||
@@ -73,7 +73,8 @@ static void calculate_times(struct dhcp_context *context, unsigned int *min_time
|
||||
|
||||
|
||||
unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name,
|
||||
struct in6_addr *fallback, size_t sz, struct in6_addr *client_addr, time_t now)
|
||||
struct in6_addr *fallback, struct in6_addr *ll_addr, struct in6_addr *ula_addr,
|
||||
size_t sz, struct in6_addr *client_addr, time_t now)
|
||||
{
|
||||
struct dhcp_vendor *vendor;
|
||||
int msg_type;
|
||||
@@ -93,6 +94,8 @@ unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *if
|
||||
state.interface = interface;
|
||||
state.iface_name = iface_name;
|
||||
state.fallback = fallback;
|
||||
state.ll_addr = ll_addr;
|
||||
state.ula_addr = ula_addr;
|
||||
state.mac_len = 0;
|
||||
state.tags = NULL;
|
||||
state.link_address = NULL;
|
||||
@@ -1269,36 +1272,59 @@ static struct dhcp_netid *add_options(struct state *state, int do_refresh)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (opt_cfg->opt == OPTION6_DNS_SERVER)
|
||||
{
|
||||
done_dns = 1;
|
||||
if (opt_cfg->len == 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (opt_cfg->opt == OPTION6_REFRESH_TIME)
|
||||
done_refresh = 1;
|
||||
|
||||
o = new_opt6(opt_cfg->opt);
|
||||
if (opt_cfg->flags & DHOPT_ADDR6)
|
||||
{
|
||||
int j;
|
||||
struct in6_addr *a = (struct in6_addr *)opt_cfg->val;
|
||||
for (j = 0; j < opt_cfg->len; j+=IN6ADDRSZ, a++)
|
||||
{
|
||||
/* zero means "self" (but not in vendorclass options.) */
|
||||
if (IN6_IS_ADDR_UNSPECIFIED(a))
|
||||
{
|
||||
if (!add_local_addrs(state->context))
|
||||
put_opt6(state->fallback, IN6ADDRSZ);
|
||||
int len, j;
|
||||
struct in6_addr *a;
|
||||
|
||||
if (opt_cfg->opt == OPTION6_DNS_SERVER)
|
||||
done_dns = 1;
|
||||
|
||||
for (a = (struct in6_addr *)opt_cfg->val, len = opt_cfg->len, j = 0;
|
||||
j < opt_cfg->len; j += IN6ADDRSZ, a++)
|
||||
if ((IN6_IS_ADDR_ULA_ZERO(a) && IN6_IS_ADDR_UNSPECIFIED(state->ula_addr)) ||
|
||||
(IN6_IS_ADDR_LINK_LOCAL_ZERO(a) && IN6_IS_ADDR_UNSPECIFIED(state->ll_addr)))
|
||||
len -= IN6ADDRSZ;
|
||||
|
||||
if (len != 0)
|
||||
{
|
||||
|
||||
o = new_opt6(opt_cfg->opt);
|
||||
|
||||
for (a = (struct in6_addr *)opt_cfg->val, j = 0; j < opt_cfg->len; j+=IN6ADDRSZ, a++)
|
||||
{
|
||||
if (IN6_IS_ADDR_UNSPECIFIED(a))
|
||||
{
|
||||
if (!add_local_addrs(state->context))
|
||||
put_opt6(state->fallback, IN6ADDRSZ);
|
||||
}
|
||||
else if (IN6_IS_ADDR_ULA_ZERO(a))
|
||||
{
|
||||
if (!IN6_IS_ADDR_UNSPECIFIED(state->ula_addr))
|
||||
put_opt6(state->ula_addr, IN6ADDRSZ);
|
||||
}
|
||||
else if (IN6_IS_ADDR_LINK_LOCAL_ZERO(a))
|
||||
{
|
||||
if (!IN6_IS_ADDR_UNSPECIFIED(state->ll_addr))
|
||||
put_opt6(state->ll_addr, IN6ADDRSZ);
|
||||
}
|
||||
else
|
||||
put_opt6(a, IN6ADDRSZ);
|
||||
}
|
||||
else
|
||||
put_opt6(a, IN6ADDRSZ);
|
||||
}
|
||||
|
||||
end_opt6(o);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
o = new_opt6(opt_cfg->opt);
|
||||
if (opt_cfg->val)
|
||||
put_opt6(opt_cfg->val, opt_cfg->len);
|
||||
end_opt6(o);
|
||||
}
|
||||
else if (opt_cfg->val)
|
||||
put_opt6(opt_cfg->val, opt_cfg->len);
|
||||
end_opt6(o);
|
||||
}
|
||||
|
||||
if (daemon->port == NAMESERVER_PORT && !done_dns)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 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
|
||||
@@ -93,7 +93,6 @@ void slaac_add_addrs(struct dhcp_lease *lease, time_t now, int force)
|
||||
slaac->ping_time = now;
|
||||
slaac->backoff = 1;
|
||||
slaac->addr = addr;
|
||||
slaac->local = context->local6;
|
||||
/* Do RA's to prod it */
|
||||
ra_start_unsolicted(now, context);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 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
|
||||
@@ -109,10 +109,10 @@ static int check_name(char *in)
|
||||
|
||||
if (in[l-1] == '.')
|
||||
{
|
||||
if (l == 1) return 0;
|
||||
in[l-1] = 0;
|
||||
nowhite = 1;
|
||||
}
|
||||
|
||||
|
||||
for (; (c = *in); in++)
|
||||
{
|
||||
if (c == '.')
|
||||
|
||||
9
trust-anchors.conf
Normal file
9
trust-anchors.conf
Normal file
@@ -0,0 +1,9 @@
|
||||
# The root DNSSEC trust anchor, valid as at 30/01/2014
|
||||
|
||||
# Note that this is a DS record (ie a hash of the root Zone Signing Key)
|
||||
# If was downloaded from https://data.iana.org/root-anchors/root-anchors.xml
|
||||
|
||||
trust-anchor=.,19036,8,2,49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user