Compare commits

..
Author SHA1 Message Date
Simon Kelley 8707019237 Mass edit of INSECURE->BOGUS returns for server failure/bad input. 2014-03-01 20:48:24 +00:00
Simon Kelley d1fbb77e0f Don't cache secure replies which we've messsed with. 2014-03-01 20:08:58 +00:00
Simon Kelley 1fbe4d2f5f Tweak tuning params. 2014-03-01 20:03:47 +00:00
Simon Kelley 0575610fa1 Handle replies with no answers and no NS in validate_reply. 2014-03-01 18:07:57 +00:00
Simon Kelley e3f1455850 Don't free blockdata for negative DS cache entries. 2014-03-01 17:58:28 +00:00
Simon Kelley bd9b3cf55b Fix off-by-one overwrite. 2014-03-01 16:12:28 +00:00
Simon Kelley 14db4212ab Tidy. 2014-03-01 15:35:50 +00:00
Simon Kelley 00a5b5d477 Check that unsigned replies come from unsigned zones if --dnssec-check-unsigned set. 2014-02-28 18:10:55 +00:00
Simon Kelley b8eac19177 Negative caching for DS records. 2014-02-27 14:30:03 +00:00
Simon Kelley b47b04c846 Return INSECURE when validation fails with proved non-existent DS. 2014-02-25 23:13:28 +00:00
Simon Kelley 613ad15d02 Strip DNSSEC RRs when query doesn't have DO bit set. 2014-02-25 23:02:28 +00:00
Simon Kelley 24187530fb Speeling. 2014-02-24 21:46:44 +00:00
Simon Kelley a857daa351 Code cleanup. 2014-02-24 21:01:09 +00:00
Simon Kelley f01d7be6c6 An NSEC record cannot attest to its own non-existance! 2014-02-24 20:20:00 +00:00
Simon Kelley d387380a25 Check signer name in RRSIGs. 2014-02-23 16:20:46 +00:00
Simon Kelley f2e4c277c4 Bugfix for last commit. 2014-02-23 15:24:26 +00:00
Simon Kelley 5107ace14a NSEC3 validation. First pass. 2014-02-23 10:48:32 +00:00
Simon Kelley 7b1eae4f50 Add --servers-file option. 2014-02-20 13:43:28 +00:00
Simon Kelley c152dc8492 Omit ECC from DNSSEC if nettle library is old. 2014-02-19 18:14:33 +00:00
Simon Kelley 7bcca0060f More server cleanup. 2014-02-19 17:45:17 +00:00
Simon Kelley d68c2ca2b7 Cleanup of server reading code, preparation, for dynamic reading from files. 2014-02-18 22:30:30 +00:00
Simon Kelley de73a497ca --rev-server option. Syntactic sugar for PTR queries. 2014-02-17 21:43:27 +00:00
Simon Kelley e3ec15af10 Log BOGUS validation result when upstream sends SERVFAIL. 2014-02-13 16:56:30 +00:00
Simon Kelley dac74312da TYpo. 2014-02-13 16:43:49 +00:00
Simon Kelley 2ecd9bd5c0 No CD in forwarded queries unless dnssec-debug for TCP too. 2014-02-13 16:42:02 +00:00
Simon Kelley a0ab18f6eb Don't mess with the TTL of DNSSEC RRs. 2014-02-13 16:38:23 +00:00
Simon Kelley ebe95a831f Add RFC-6605 ECDSA DNSSEC verification. 2014-02-13 14:56:10 +00:00
Simon Kelley ee4158678a Use DS records as trust anchors, not DNSKEYs.
This allows us to query for the root zone DNSKEY RRset and validate
it, thus automatically handling KSK rollover.
2014-02-11 11:07:22 +00:00
Simon Kelley 83349b8aa4 Further tidying of AD and DO bit handling. 2014-02-10 21:02:01 +00:00
Simon Kelley 7fa836e105 Handle validation when more one key is needed. 2014-02-10 20:11:24 +00:00
Simon Kelley 1633e30834 Fix Byte-order botch: broke DNSSEC on big-endian platforms. 2014-02-10 16:42:46 +00:00
Simon Kelley c8ca33f810 Fix DNSSEC caching problems: incomplete RRSIG RRsets. 2014-02-10 10:35:42 +00:00
Simon Kelley e243c072b5 AD bit in queries handled as RFC6840 p5.7 2014-02-06 18:14:09 +00:00
Simon Kelley da4f372271 Add trust-anchors file to Debian package. 2014-02-06 15:21:37 +00:00
Simon Kelley 610e782a29 Fix stack-smashing crash in DNSSEC. Thanks to Henk Jan Agteresch. 2014-02-06 14:45:17 +00:00
Simon Kelley 854cf26907 DNSSEC config in example file. 2014-02-06 12:07:10 +00:00
Simon Kelley bb201c211a Protect against malicious DNS replies with very large RRsets. 2014-02-06 12:01:05 +00:00
Simon Kelley 12fae49fff Make RR work when returning A/AAAA records and an RRSIG. 2014-02-04 22:03:06 +00:00
Jesse Glick fd372273bd Updated version of contrib/try-all-ns 2014-02-04 20:20:35 +00:00
Simon Kelley b98d22c191 Linking stuff. Latest Debian/Ubuntu don't automatically link gmp. 2014-02-04 18:09:30 +00:00
Simon Kelley 160f6507c3 Make DNSEC default, add build-depends for same, bump version. 2014-02-04 16:49:41 +00:00
22 changed files with 2265 additions and 970 deletions

No files matched your search

+5
View File
@@ -62,7 +62,12 @@ version 2.69
an invaluable period of full-time work to get it to
a workable state.
Add --rev-server. Thanks to Dave Taht for suggesting this.
Add --servers-file. Allows dynamic update of upstream servers
full access to configuration.
version 2.68
Use random addresses for DHCPv6 temporary address
allocations, instead of algorithmically determined stable
+3 -2
View File
@@ -61,6 +61,7 @@ lua_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LUASCRIPT $(PKG_CON
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)`\"'
@@ -77,7 +78,7 @@ all : $(BUILDDIR)
@cd $(BUILDDIR) && $(MAKE) \
top="$(top)" \
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)" \
build_libs="$(dbus_libs) $(idn_libs) $(ct_libs) $(lua_libs) $(sunos_libs) $(nettle_libs) $(gmp_libs)" \
-f $(top)/Makefile dnsmasq
mostly_clean :
@@ -101,7 +102,7 @@ all-i18n : $(BUILDDIR)
top="$(top)" \
i18n=-DLOCALEDIR=\'\"$(LOCALEDIR)\"\' \
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) `$(PKG_CONFIG) --libs 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; \
+17 -12
View File
@@ -9,19 +9,24 @@ shift
in=`cat`
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" ]; then
pkg=`$pkg --static $op $*`
echo "-Wl,-Bstatic $pkg -Wl,-Bdynamic"
exit 0
fi
fi
if grep "^\#[[:space:]]*define[[:space:]]*$search" config.h >/dev/null 2>&1 || \
echo $in | grep $search >/dev/null 2>&1; then
pkg=`$pkg $op $*`
echo "$pkg"
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
@@ -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;
+7 -1
View File
@@ -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
+2 -1
View File
@@ -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
+7 -1
View File
@@ -19,6 +19,10 @@ LDFLAGS = $(shell dpkg-buildflags --get LDFLAGS)
DEB_COPTS = $(COPTS)
# The nettle library in Debian is too old to include
# ECC support.
DEB_COPTS += -DNO_NETTLE_ECC
TARGET = install-i18n
DEB_BUILD_ARCH_OS := $(shell dpkg-architecture -qDEB_BUILD_ARCH_OS)
@@ -77,7 +81,7 @@ ifneq (,$(filter uselua,$(DEB_BUILD_OPTIONS)))
DEB_COPTS += -DHAVE_LUASCRIPT
endif
ifneq (,$(filter usednssec,$(DEB_BUILD_OPTIONS)))
ifeq (,$(filter nodnssec,$(DEB_BUILD_OPTIONS)))
DEB_COPTS += -DHAVE_DNSSEC
endif
@@ -126,12 +130,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="$(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
+4
View File
@@ -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.
+39 -6
View File
@@ -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
@@ -412,6 +415,14 @@ source address specified but the port may be specified directly as
part of the source address. Forcing queries to an interface is not
implemented on all platforms supported by dnsmasq.
.TP
.B --rev-server=<ip-address>/<prefix-len>,<ipaddr>[#<port>][@<source-ip>|<interface>[#<port>]]
This is functionally the same as
.B --server,
but provides some syntactic sugar to make specifying address-to-name queries easier. For example
.B --rev-server=1.2.3.0/24,192.168.0.1
is exactly equivalent to
.B --server=/3.2.1.in-addr.arpa/192.168.0.1
.TP
.B \-A, --address=/<domain>/[domain/]<ipaddr>
Specify an IP address to return for any host in the given domains.
Queries in the domains are never forwarded and always replied to
@@ -587,12 +598,24 @@ validation by clients more efficient. Note that validation by clients is the mos
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 --dnsskey.
.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 --dnskey=[<class>],<domain>,<flags>,<algorithm>,<base64-key>
Provide DNSKEY records to act a trust anchors for DNSSEC validation. Typically these will be the keys for root zone,
but trust anchors for limited domains are also possible.
.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 --dnssec-check-unsigned
As a default, dnsmasq does not check that unsigned DNS replies are
legitimate: they are assumed to be valid and passed on (without the
"authentic data" bit set, of course). This does not protect against an
attacker forging unsigned replies for signed DNS zones, but it is
fast. If this flag is set, dnsmasq will check the zones of unsigned
replies, to ensure that unsigned replies are allowed in those
zones. The cost of this is more upstream queries and slower performance.
.TP
.B --proxy-dnssec
Copy the DNSSEC Authenticated Data bit from upstream servers to downstream clients and cache it. This is an
@@ -601,7 +624,10 @@ dnsmasq and the upstream servers, and the trustworthiness of the upstream server
.TP
.B --dnssec-debug
Set debugging mode for the DNSSEC validation, set the Checking Disabled bit on upstream queries,
and don't convert BOGUS replies to SERVFAIL responses.
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
@@ -1661,6 +1687,13 @@ files. If extension(s) are given, any files which end in those
extensions are skipped. Any files whose names end in ~ or start with . or start and end
with # are always skipped. This flag may be given on the command
line or in a configuration file.
.TP
.B --servers-file=<file>
A special case of
.B --conf-file
which differs in two respects. Firstly, only --server and --rev-server are allowed
in the configuration file included. Secondly, the file is re-read and the configuration
therein is updated when dnsmasq recieves SIGHUP.
.SH CONFIG FILE
At startup, dnsmasq reads
.I /etc/dnsmasq.conf,
+78 -44
View File
@@ -55,7 +55,9 @@ static const struct {
{ 41, "OPT" },
{ 43, "DS" },
{ 46, "RRSIG" },
{ 47, "NSEC" },
{ 48, "DNSKEY" },
{ 50, "NSEC3" },
{ 249, "TKEY" },
{ 250, "TSIG" },
{ 251, "IXFR" },
@@ -181,7 +183,7 @@ static void cache_blockdata_free(struct crec *crecp)
else
blockdata_free(crecp->addr.key.keydata);
}
else if (crecp->flags & F_DS)
else if ((crecp->flags & F_DS) && !(crecp->flags & F_NEG))
blockdata_free(crecp->addr.ds.keydata);
}
#endif
@@ -446,19 +448,21 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
int freed_all = flags & F_REVERSE;
int free_avail = 0;
if (daemon->max_cache_ttl != 0 && daemon->max_cache_ttl < ttl)
ttl = daemon->max_cache_ttl;
/* Don't log keys here, done elsewhere */
/* Don't log DNSSEC records here, done elsewhere */
if (flags & (F_IPV4 | F_IPV6 | F_CNAME))
log_query(flags | F_UPSTREAM, name, addr, NULL);
{
log_query(flags | F_UPSTREAM, name, addr, NULL);
/* Don;t mess with TTL for DNSSEC records. */
if (daemon->max_cache_ttl != 0 && daemon->max_cache_ttl < ttl)
ttl = daemon->max_cache_ttl;
}
/* 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))
{
@@ -560,7 +564,14 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
*cache_get_name(new) = 0;
if (addr)
new->addr.addr = *addr;
{
#ifdef HAVE_DNSSEC
if (flags & (F_DS | F_DNSKEY))
new->uid = addr->addr.dnssec.class;
else
#endif
new->addr.addr = *addr;
}
new->ttd = now + (time_t)ttl;
new->next = new_chain;
@@ -592,10 +603,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
@@ -643,7 +657,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);
@@ -683,7 +697,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
@@ -980,7 +994,7 @@ void cache_reload(void)
struct cname *a;
struct interface_name *intr;
#ifdef HAVE_DNSSEC
struct dnskey *key;
struct ds_config *ds;
#endif
cache_inserted = cache_live_freed = 0;
@@ -1026,17 +1040,17 @@ void cache_reload(void)
}
#ifdef HAVE_DNSSEC
for (key = daemon->dnskeys; key; key = key->next)
for (ds = daemon->ds; ds; ds = ds->next)
if ((cache = whine_malloc(sizeof(struct crec))) &&
(cache->addr.key.keydata = blockdata_alloc(key->key, key->keylen)))
(cache->addr.ds.keydata = blockdata_alloc(ds->digest, ds->digestlen)))
{
cache->flags = F_FORWARD | F_IMMORTAL | F_DNSKEY | F_CONFIG | F_NAMEP;
cache->name.namep = key->name;
cache->addr.key.keylen = key->keylen;
cache->addr.key.algo = key->algo;
cache->addr.key.flags = key->flags;
cache->addr.key.keytag = dnskey_keytag(key->algo, key->flags, (unsigned char *)key->key, key->keylen);
cache->uid = key->class;
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
@@ -1297,27 +1311,16 @@ void dump_cache(time_t now)
else if (cache->flags & F_DS)
{
if (cache->flags & F_DNSKEY)
{
char tp[20];
/* RRSIG */
querystr("", tp, cache->addr.sig.type_covered);
a = daemon->addrbuff;
sprintf(a, "%5u %3u %s", cache->addr.sig.keytag,
cache->addr.sig.algo, tp);
}
else
{
a = daemon->addrbuff;
sprintf(a, "%5u %3u %3u", cache->addr.ds.keytag,
cache->addr.ds.algo, cache->addr.ds.digest);
}
/* RRSIG */
sprintf(a, "%5u %3u %s", cache->addr.sig.keytag,
cache->addr.sig.algo, querystr("", cache->addr.sig.type_covered));
else if (!(cache->flags & F_NEG))
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, "%5u %3u %3u", cache->addr.key.keytag,
cache->addr.key.algo, cache->addr.key.flags);
}
sprintf(a, "%5u %3u %3u", cache->addr.key.keytag,
cache->addr.key.algo, cache->addr.key.flags);
#endif
else if (!(cache->flags & F_NEG) || !(cache->flags & F_FORWARD))
{
@@ -1379,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)
+3 -1
View File
@@ -19,6 +19,7 @@
#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 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 */
@@ -30,7 +31,8 @@
#define PING_CACHE_TIME 30 /* Ping test assumed to be valid this long. */
#define DECLINE_BACKOFF 600 /* disable DECLINEd static addresses for this long */
#define DHCP_PACKET_MAX 16384 /* hard limit on DHCP packet size */
#define SMALLDNAME 40 /* most domain names are smaller than this */
#define SMALLDNAME 50 /* most domain names are smaller than this */
#define CNAME_CHAIN 10 /* chains longer than this atr dropped for loop protection */
#define HOSTSFILE "/etc/hosts"
#define ETHERSFILE "/etc/ethers"
#define DEFLEASE 3600 /* default lease time, 1 hour */
+17 -115
View File
@@ -108,108 +108,6 @@ static void remove_watch(DBusWatch *watch, void *data)
w = data; /* no warning */
}
static void add_update_server(union mysockaddr *addr,
union mysockaddr *source_addr,
const char *interface,
const char *domain)
{
struct server *serv;
/* See if there is a suitable candidate, and unmark */
for (serv = daemon->servers; serv; serv = serv->next)
if ((serv->flags & SERV_FROM_DBUS) &&
(serv->flags & SERV_MARK))
{
if (domain)
{
if (!(serv->flags & SERV_HAS_DOMAIN) || !hostname_isequal(domain, serv->domain))
continue;
}
else
{
if (serv->flags & SERV_HAS_DOMAIN)
continue;
}
serv->flags &= ~SERV_MARK;
break;
}
if (!serv && (serv = whine_malloc(sizeof (struct server))))
{
/* Not found, create a new one. */
memset(serv, 0, sizeof(struct server));
if (domain && !(serv->domain = whine_malloc(strlen(domain)+1)))
{
free(serv);
serv = NULL;
}
else
{
serv->next = daemon->servers;
daemon->servers = serv;
serv->flags = SERV_FROM_DBUS;
if (domain)
{
strcpy(serv->domain, domain);
serv->flags |= SERV_HAS_DOMAIN;
}
}
}
if (serv)
{
if (interface)
strcpy(serv->interface, interface);
else
serv->interface[0] = 0;
if (source_addr->in.sin_family == AF_INET &&
addr->in.sin_addr.s_addr == 0 &&
serv->domain)
serv->flags |= SERV_NO_ADDR;
else
{
serv->flags &= ~SERV_NO_ADDR;
serv->addr = *addr;
serv->source_addr = *source_addr;
}
}
}
static void mark_dbus(void)
{
struct server *serv;
/* mark everything from DBUS */
for (serv = daemon->servers; serv; serv = serv->next)
if (serv->flags & SERV_FROM_DBUS)
serv->flags |= SERV_MARK;
}
static void cleanup_dbus()
{
struct server *serv, *tmp, **up;
/* unlink and free anything still marked. */
for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp)
{
tmp = serv->next;
if (serv->flags & SERV_MARK)
{
server_gone(serv);
*up = serv->next;
if (serv->domain)
free(serv->domain);
free(serv);
}
else
up = &serv->next;
}
}
static void dbus_read_servers(DBusMessage *message)
{
DBusMessageIter iter;
@@ -218,8 +116,8 @@ static void dbus_read_servers(DBusMessage *message)
dbus_message_iter_init(message, &iter);
mark_dbus();
mark_servers(SERV_FROM_DBUS);
while (1)
{
int skip = 0;
@@ -289,13 +187,13 @@ static void dbus_read_servers(DBusMessage *message)
domain = NULL;
if (!skip)
add_update_server(&addr, &source_addr, NULL, domain);
add_update_server(SERV_FROM_DBUS, &addr, &source_addr, NULL, domain);
} while (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING);
}
/* unlink and free anything still marked. */
cleanup_dbus();
cleanup_servers();
}
static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
@@ -319,7 +217,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
strings ? "Expected array of string" : "Expected array of string arrays");
}
mark_dbus();
mark_servers(SERV_FROM_DBUS);
/* array_iter points to each "as" element in the outer array */
dbus_message_iter_recurse(&iter, &array_iter);
@@ -327,6 +225,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
{
const char *str = NULL;
union mysockaddr addr, source_addr;
int flags = 0;
char interface[IF_NAMESIZE];
char *str_addr, *str_domain = NULL;
@@ -416,16 +315,19 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
memset(&interface, 0, sizeof(interface));
/* parse the IP address */
addr_err = parse_server(str_addr, &addr, &source_addr, (char *) &interface, NULL);
if (addr_err)
{
if ((addr_err = parse_server(str_addr, &addr, &source_addr, (char *) &interface, &flags)))
{
error = dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
"Invalid IP address '%s': %s",
str, addr_err);
break;
}
/* 0.0.0.0 for server address == NULL, for Dbus */
if (addr.in.sin_family == AF_INET &&
addr.in.sin_addr.s_addr == 0)
flags |= SERV_NO_ADDR;
if (strings)
{
char *p;
@@ -439,7 +341,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
else
p = NULL;
add_update_server(&addr, &source_addr, interface, str_domain);
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str_domain);
} while ((str_domain = p));
}
else
@@ -454,7 +356,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
dbus_message_iter_get_basic(&string_iter, &str);
dbus_message_iter_next (&string_iter);
add_update_server(&addr, &source_addr, interface, str);
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str);
} while (dbus_message_iter_get_arg_type(&string_iter) == DBUS_TYPE_STRING);
}
@@ -462,7 +364,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
dbus_message_iter_next(&array_iter);
}
cleanup_dbus();
cleanup_servers();
if (dup)
free(dup);
+1
View File
@@ -68,6 +68,7 @@
#define T_RRSIG 46
#define T_NSEC 47
#define T_DNSKEY 48
#define T_NSEC3 50
#define T_TKEY 249
#define T_TSIG 250
#define T_AXFR 252
+23 -6
View File
@@ -98,7 +98,10 @@ int main (int argc, char **argv)
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID))
daemon->keyname = safe_malloc(MAXDNAME);
{
daemon->keyname = safe_malloc(MAXDNAME);
daemon->workspacename = safe_malloc(MAXDNAME);
}
#endif
#ifdef HAVE_DHCP
@@ -144,7 +147,7 @@ int main (int argc, char **argv)
if (option_bool(OPT_DNSSEC_VALID))
{
#ifdef HAVE_DNSSEC
if (!daemon->dnskeys)
if (!daemon->ds)
die(_("No trust anchors provided for DNSSEC"), NULL, EC_BADCONF);
if (daemon->cachesize < CACHESIZ)
@@ -1114,7 +1117,7 @@ static void async_event(int pipe, time_t now)
{
pid_t p;
struct event_desc ev;
int i;
int i, check = 0;
char *msg;
/* NOTE: the memory used to return msg is leaked: use msgs in events only
@@ -1125,11 +1128,25 @@ static void async_event(int pipe, time_t now)
{
case EVENT_RELOAD:
clear_cache_and_reload(now);
if (daemon->port != 0 && daemon->resolv_files && option_bool(OPT_NO_POLL))
if (daemon->port != 0)
{
reload_servers(daemon->resolv_files->name);
check_servers();
if (daemon->resolv_files && option_bool(OPT_NO_POLL))
{
reload_servers(daemon->resolv_files->name);
check = 1;
}
if (daemon->servers_file)
{
read_servers_file();
check = 1;
}
if (check)
check_servers();
}
#ifdef HAVE_DHCP
rerun_scripts();
#endif
+38 -15
View File
@@ -232,7 +232,8 @@ struct event_desc {
#define OPT_DNSSEC_VALID 45
#define OPT_DNSSEC_PERMISS 46
#define OPT_DNSSEC_DEBUG 47
#define OPT_LAST 48
#define OPT_DNSSEC_NO_SIGN 48
#define OPT_LAST 49
/* 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. */
@@ -295,10 +296,10 @@ struct cname {
struct cname *next;
};
struct dnskey {
char *name, *key;
int keylen, class, algo, flags;
struct dnskey *next;
struct ds_config {
char *name, *digest;
int digestlen, class, algo, keytag, digest_type;
struct ds_config *next;
};
#define ADDRLIST_LITERAL 1
@@ -423,6 +424,7 @@ struct crec {
#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,
@@ -455,6 +457,7 @@ union mysockaddr {
#define SERV_COUNTED 512 /* workspace for log code */
#define SERV_USE_RESOLV 1024 /* forward this domain in the normal way */
#define SERV_NO_REBIND 2048 /* inhibit dns-rebind protection */
#define SERV_FROM_FILE 4096 /* read from --servers-file */
struct serverfd {
int fd;
@@ -532,12 +535,21 @@ struct hostsfile {
#define STAT_NEED_DS 4
#define STAT_NEED_KEY 5
#define STAT_TRUNCATED 6
#define STAT_SECURE_WILDCARD 7
#define STAT_NO_SIG 8
#define STAT_NO_DS 9
#define STAT_NEED_DS_NEG 10
#define STAT_CHASE_CNAME 11
#define 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
#define FREC_DO_QUESTION 64
#define FREC_ADDED_PHEADER 128
#define FREC_CHECK_NOSIGN 256
#ifdef HAVE_DNSSEC
#define HASH_SIZE 20 /* SHA-1 digest size */
@@ -559,7 +571,7 @@ struct frec {
time_t time;
unsigned char *hash[HASH_SIZE];
#ifdef HAVE_DNSSEC
int class;
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 */
@@ -862,6 +874,7 @@ extern struct daemon {
unsigned int options, options2;
struct resolvc default_resolv, *resolv_files;
time_t last_resolv;
char *servers_file;
struct mx_srv_record *mxnames;
struct naptr *naptr;
struct txt_record *txt, *rr;
@@ -928,7 +941,7 @@ extern struct daemon {
struct prefix_class *prefix_classes;
#endif
#ifdef HAVE_DNSSEC
struct dnskey *dnskeys;
struct ds_config *ds;
#endif
/* globally used stuff for DNS */
@@ -937,6 +950,7 @@ extern struct daemon {
char *namebuff; /* MAXDNAME size buffer */
#ifdef HAVE_DNSSEC
char *keyname; /* MAXDNAME size buffer */
char *workspacename; /* ditto */
#endif
unsigned int local_answer, queries_forwarded, auth_answer;
struct frec *frec_list;
@@ -990,12 +1004,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,
@@ -1031,6 +1045,7 @@ 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 char *skip_section(unsigned char *ansp, int count, 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,
@@ -1040,7 +1055,8 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *namebuff,
time_t now, char **ipsets, int is_sign, int checkrebind,
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 *do_bit);
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,
@@ -1075,8 +1091,10 @@ int in_zone(struct auth_zone *zone, char *name, char **cut);
size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, int class, int type, union mysockaddr *addr);
int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t n, char *name, char *keyname, int class);
int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class);
int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class);
int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class, int *neganswer);
int dnssec_chase_cname(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname);
int dnskey_keytag(int alg, int flags, unsigned char *rdata, int rdlen);
size_t filter_rrsigs(struct dns_header *header, size_t plen);
unsigned char* hash_questions(struct dns_header *header, size_t plen, char *name);
/* util.c */
@@ -1104,9 +1122,6 @@ void prettyprint_time(char *buf, unsigned int t);
int prettyprint_addr(union mysockaddr *addr, char *buf);
int parse_hex(char *in, unsigned char *out, int maxlen,
unsigned int *wildcard_mask, int *mac_type);
#ifdef HAVE_DNSSEC
int parse_base64(char *in, char *out);
#endif
int memcmp_masked(unsigned char *a, unsigned char *b, int len,
unsigned int mask);
int expand_buf(struct iovec *iov, size_t size);
@@ -1130,6 +1145,7 @@ void read_opts (int argc, char **argv, char *compile_opts);
char *option_string(int prot, unsigned int opt, unsigned char *val,
int opt_len, char *buf, int buf_len);
void reread_dhcp(void);
void read_servers_file(void);
void set_option_bool(unsigned int opt);
void reset_option_bool(unsigned int opt);
struct hostsfile *expand_filelist(struct hostsfile *list);
@@ -1153,6 +1169,13 @@ int local_bind(int fd, union mysockaddr *addr, char *intname, int is_tcp);
int random_sock(int family);
void pre_allocate_sfds(void);
int reload_servers(char *fname);
void mark_servers(int flag);
void cleanup_servers(void);
void add_update_server(int flags,
union mysockaddr *addr,
union mysockaddr *source_addr,
const char *interface,
const char *domain);
void check_servers(void);
int enumerate_interfaces(int reset);
void create_wildcard_listeners(void);
+1111 -266
View File
File diff suppressed because it is too large. Load diff
+456 -176
View File
@@ -24,6 +24,14 @@ static unsigned short get_id(void);
static void free_frec(struct frec *f);
static struct randfd *allocate_rfd(int family);
#ifdef HAVE_DNSSEC
static int tcp_key_recurse(time_t now, int status, struct dns_header *header, size_t n,
int class, char *name, char *keyname, struct server *server, int *keycount);
static int do_check_sign(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class);
static int send_check_sign(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname);
#endif
/* Send a UDP packet with its source address set as "source"
unless nowild is true, when we just send it with the kernel default */
int send_from(int fd, int nowild, char *packet, size_t len,
@@ -234,7 +242,8 @@ 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, int do_bit)
{
char *domain = NULL;
int type = 0, norebind = 0;
@@ -249,16 +258,15 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
#endif
unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL);
/* RFC 4035: sect 4.6 para 2 */
header->hb4 &= ~HB4_AD;
(void)do_bit;
/* may be no servers available. */
if (!daemon->servers)
forward = NULL;
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,
/* If we've already got an answer to this query, but we're awaiting keys for validation,
there's no point retrying the query, retry the key query instead...... */
if (forward->blocking_query)
{
@@ -334,7 +342,14 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
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;
if (do_bit)
forward->flags |= FREC_DO_QUESTION;
#endif
header->id = htons(forward->new_id);
/* In strict_order mode, always try servers in the order
@@ -390,11 +405,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);
size_t new_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;
if (new_plen != plen)
forward->flags |= FREC_ADDED_PHEADER;
plen = new_plen;
}
#endif
@@ -503,13 +524,16 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
}
static size_t process_reply(struct dns_header *header, time_t now, struct server *server, size_t n, int check_rebind,
int no_cache, int cache_secure, int check_subnet, union mysockaddr *query_source)
int no_cache, int cache_secure, int ad_reqd, int do_bit, int added_pheader, 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;
(void) do_bit;
#ifdef HAVE_IPSET
/* Similar algorithm to search_servers. */
struct ipsets *ipset_pos;
@@ -535,23 +559,27 @@ 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))
{
my_syslog(LOG_WARNING, _("discarding DNS reply: subnet option mismatch"));
return 0;
}
}
if (added_pheader)
{
pheader = 0;
header->arcount = htons(0);
}
}
/* RFC 4035 sect 4.6 para 3 */
if (!is_sign && !option_bool(OPT_DNSSEC_PROXY))
header->hb4 &= ~HB4_AD;
@@ -619,8 +647,12 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
if (option_bool(OPT_DNSSEC_VALID))
header->hb4 &= ~HB4_AD;
if (!(header->hb4 & HB4_CD) && cache_secure)
if (!(header->hb4 & HB4_CD) && ad_reqd && cache_secure)
header->hb4 |= HB4_AD;
/* If the requestor didn't set the DO bit, don't return DNSSEC info. */
if (!do_bit)
n = filter_rrsigs(header, n);
#endif
/* do this after extract_addresses. Ensure NODATA reply and remove
@@ -705,7 +737,7 @@ 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, 0);
return;
}
}
@@ -772,18 +804,48 @@ void reply_query(int fd, int family, time_t now)
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);
{
status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
if (status == STAT_NO_DS)
status = STAT_INSECURE;
}
else if (forward->flags & FREC_CHECK_NOSIGN)
status = do_check_sign(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
else
status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class);
{
status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class, NULL);
if (status == STAT_NO_SIG)
{
if (option_bool(OPT_DNSSEC_NO_SIGN))
status = send_check_sign(now, header, n, daemon->namebuff, daemon->keyname);
else
status = STAT_INSECURE;
}
}
/* 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)
if (status == STAT_NEED_DS || status == STAT_NEED_DS_NEG || status == STAT_NEED_KEY)
{
struct frec *new;
struct frec *new, *orig;
if ((new = get_new_frec(now, NULL, 1)))
/* 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;
@@ -792,82 +854,72 @@ void reply_query(int fd, int family, time_t now)
#ifdef HAVE_IPV6
new->rfd6 = NULL;
#endif
new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY);
new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY | FREC_CHECK_NOSIGN);
/* Free any saved query */
if (forward->stash)
blockdata_free(forward->stash);
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
{
if (status == STAT_NEED_DS_NEG)
new->flags |= FREC_CHECK_NOSIGN;
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;
/* Now save reply pending receipt of key data */
if (!(forward->stash = blockdata_alloc((char *)header, n)))
free_frec(new); /* malloc failure, unwind */
/* Don't resend this. */
daemon->srv_save = NULL;
if (server->sfd)
fd = server->sfd->fd;
else
{
int fd;
forward->stash_len = n;
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;
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)
if (server->addr.sa.sa_family == AF_INET6)
{
while (sendto(fd, (char *)header, nn, 0, &server->addr.sa, sa_len(&server->addr)) == -1 && retry_send());
server->queries++;
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;
}
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. Failure to find needed data here is an internal error.
Once we get to the original answer (FREC_DNSSEC_QUERY not set) and it validates,
return it to the original requestor. */
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;
@@ -882,23 +934,43 @@ void reply_query(int fd, int family, time_t now)
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)
{
my_syslog(LOG_ERR, _("Unexpected missing data for DNSSEC validation"));
status = STAT_INSECURE;
status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
if (status == STAT_NO_DS)
status = STAT_INSECURE;
}
else if (forward->flags & FREC_CHECK_NOSIGN)
status = do_check_sign(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
else
{
status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class, NULL);
if (status == STAT_NO_SIG)
{
if (option_bool(OPT_DNSSEC_NO_SIGN))
status = send_check_sign(now, header, n, daemon->namebuff, daemon->keyname);
else
status = STAT_INSECURE;
}
}
if (status == STAT_NEED_DS || status == STAT_NEED_DS_NEG || status == STAT_NEED_KEY)
goto anotherkey;
}
}
if (status == STAT_TRUNCATED)
header->hb3 |= HB3_TC;
else
log_query(F_KEYTAG | F_SECSTAT, "result", NULL,
status == STAT_SECURE ? "SECURE" : (status == STAT_INSECURE ? "INSECURE" : "BOGUS"));
{
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;
@@ -906,17 +978,18 @@ void reply_query(int fd, int family, time_t now)
cache_secure = 1;
else if (status == STAT_BOGUS)
no_cache_dnssec = 1;
/* restore CD bit to the value in the query */
if (forward->flags & FREC_CHECKING_DISABLED)
header->hb4 |= HB4_CD;
else
header->hb4 &= ~HB4_CD;
}
#endif
#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_HAS_SUBNET, &forward->source)))
forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION,
forward->flags & FREC_ADDED_PHEADER, forward->flags & FREC_HAS_SUBNET, &forward->source)))
{
header->id = htons(forward->orig_id);
header->hb4 |= HB4_RA; /* recursion if available */
@@ -1117,13 +1190,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);
@@ -1160,8 +1231,9 @@ void receive_query(struct listener *listen, time_t now)
else
#endif
{
int ad_reqd, do_bit;
m = answer_request(header, ((char *) header) + daemon->packet_buff_sz, (size_t)n,
dst_addr_4, netmask, now);
dst_addr_4, netmask, now, &ad_reqd, &do_bit);
if (m >= 1)
{
@@ -1170,7 +1242,7 @@ 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, do_bit))
daemon->queries_forwarded++;
else
daemon->local_answer++;
@@ -1178,60 +1250,264 @@ void receive_query(struct listener *listen, time_t now)
}
#ifdef HAVE_DNSSEC
static int tcp_key_recurse(time_t now, int status, int class, char *keyname, struct server *server)
{
/* Recurse up the key heirarchy */
size_t n;
unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ + sizeof(u16));
unsigned char *payload = &packet[2];
struct dns_header *header = (struct dns_header *)payload;
u16 *length = (u16 *)packet;
int new_status;
unsigned char c1, c2;
n = dnssec_generate_query(header, ((char *) header) + 65536, keyname, class,
status == STAT_NEED_KEY ? T_DNSKEY : T_DS, &server->addr);
/* UDP: we've got an unsigned answer, return STAT_INSECURE if we can prove there's no DS
and therefore the answer shouldn't be signed, or STAT_BOGUS if it should be, or
STAT_NEED_DS_NEG and keyname if we need to do the query. */
static int send_check_sign(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname)
{
struct crec *crecp;
char *name_start = name;
int status = dnssec_chase_cname(now, header, plen, name, keyname);
*length = htons(n);
if (!read_write(server->tcpfd, packet, n + 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))
if (status != STAT_INSECURE)
return status;
while (1)
{
close(server->tcpfd);
server->tcpfd = -1;
new_status = STAT_INSECURE;
}
else
{
n = (c1 << 8) | c2;
crecp = cache_find_by_name(NULL, name_start, now, F_DS);
if (status == STAT_NEED_KEY)
new_status = dnssec_validate_by_ds(now, header, n, daemon->namebuff, daemon->keyname, class);
else
new_status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, class);
if (new_status == STAT_NEED_DS || new_status == STAT_NEED_KEY)
if (crecp && (crecp->flags & F_DNSSECOK))
return (crecp->flags & F_NEG) ? STAT_INSECURE : STAT_BOGUS;
if (crecp && (crecp->flags & F_NEG) && (name_start = strchr(name_start, '.')))
{
if ((new_status = tcp_key_recurse(now, new_status, class, daemon->keyname, server) == STAT_SECURE))
name_start++; /* chop a label off and try again */
continue;
}
strcpy(keyname, name_start);
return STAT_NEED_DS_NEG;
}
}
/* Got answer to DS query from send_check_sign, check for proven non-existence, or make the next DS query to try. */
static int do_check_sign(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class)
{
char *name_start;
unsigned char *p;
int status = dnssec_validate_ds(now, header, plen, name, keyname, class);
if (status != STAT_INSECURE)
{
if (status == STAT_NO_DS)
status = STAT_INSECURE;
return status;
}
p = (unsigned char *)(header+1);
if (extract_name(header, plen, &p, name, 1, 4) &&
(name_start = strchr(name, '.')))
{
name_start++; /* chop a label off and try again */
strcpy(keyname, name_start);
return STAT_NEED_DS_NEG;
}
return STAT_BOGUS;
}
/* Move toward the root, until we find a signed non-existance of a DS, in which case
an unsigned answer is OK, or we find a signed DS, in which case there should be
a signature, and the answer is BOGUS */
static int tcp_check_for_unsigned_zone(time_t now, struct dns_header *header, size_t plen, int class, char *name,
char *keyname, struct server *server, int *keycount)
{
size_t m;
unsigned char *packet, *payload;
u16 *length;
unsigned char *p = (unsigned char *)(header+1);
int status;
char *name_start = name;
/* Get first insecure entry in CNAME chain */
status = tcp_key_recurse(now, STAT_CHASE_CNAME, header, plen, class, name, keyname, server, keycount);
if (status == STAT_BOGUS)
return STAT_BOGUS;
if (!(packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ + sizeof(u16))))
return STAT_BOGUS;
payload = &packet[2];
header = (struct dns_header *)payload;
length = (u16 *)packet;
while (1)
{
unsigned char *newhash, hash[HASH_SIZE];
unsigned char c1, c2;
struct crec *crecp = cache_find_by_name(NULL, name_start, now, F_DS);
if (--(*keycount) == 0)
return STAT_BOGUS;
if (crecp && (crecp->flags & F_DNSSECOK))
{
free(packet);
return (crecp->flags & F_NEG) ? STAT_INSECURE : STAT_BOGUS;
}
/* If we have cached insecurely that a DS doesn't exist,
ise that is a hit for where to start looking for the secure one */
if (crecp && (crecp->flags & F_NEG) && (name_start = strchr(name_start, '.')))
{
name_start++; /* chop a label off and try again */
continue;
}
m = dnssec_generate_query(header, ((char *) header) + 65536, name_start, class, T_DS, &server->addr);
/* We rely on the question section coming back unchanged, ensure it is with the hash. */
if ((newhash = hash_questions(header, (unsigned int)m, name)))
memcpy(hash, newhash, HASH_SIZE);
*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))
{
m = (c1 << 8) | c2;
newhash = hash_questions(header, (unsigned int)m, name);
if (newhash && memcmp(hash, newhash, HASH_SIZE) == 0)
{
if (status == STAT_NEED_KEY)
new_status = dnssec_validate_by_ds(now, header, n, daemon->namebuff, daemon->keyname, class);
else
new_status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, class);
if (new_status == STAT_NEED_DS || new_status == STAT_NEED_KEY)
/* Note this trashes all three name workspaces */
status = tcp_key_recurse(now, STAT_NEED_DS_NEG, header, m, class, name, keyname, server, keycount);
/* We've found a DS which proves the bit of the DNS where the
original query is, is unsigned, so the answer is OK,
if unvalidated. */
if (status == STAT_NO_DS)
{
my_syslog(LOG_ERR, _("Unexpected missing data for DNSSEC validation"));
status = STAT_INSECURE;
free(packet);
return STAT_INSECURE;
}
/* No DS, not got to DNSSEC-land yet, go up. */
if (status == STAT_INSECURE)
{
p = (unsigned char *)(header+1);
if (extract_name(header, plen, &p, name, 1, 4) &&
(name_start = strchr(name, '.')))
{
name_start++; /* chop a label off and try again */
continue;
}
}
}
}
free(packet);
return STAT_BOGUS;
}
}
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 || status == STAT_NEED_DS_NEG)
{
new_status = dnssec_validate_ds(now, header, n, name, keyname, class);
if (status == STAT_NEED_DS && new_status == STAT_NO_DS)
new_status = STAT_INSECURE;
}
else if (status == STAT_CHASE_CNAME)
new_status = dnssec_chase_cname(now, header, n, name, keyname);
else
{
new_status = dnssec_validate_reply(now, header, n, name, keyname, &class, NULL);
if (new_status == STAT_NO_SIG)
{
if (option_bool(OPT_DNSSEC_NO_SIGN))
new_status = tcp_check_for_unsigned_zone(now, header, n, class, name, keyname, server, keycount);
else
new_status = STAT_INSECURE;
}
}
free(packet);
/* 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;
new_status = tcp_key_recurse(now, new_status, new_header, m, class, name, keyname, server, keycount);
if (new_status == 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 || status == STAT_NEED_DS_NEG)
{
new_status = dnssec_validate_ds(now, header, n, name, keyname, class);
if (status == STAT_NEED_DS && new_status == STAT_NO_DS)
new_status = STAT_INSECURE; /* Validated no DS */
}
else if (status == STAT_CHASE_CNAME)
new_status = dnssec_chase_cname(now, header, n, name, keyname);
else
{
new_status = dnssec_validate_reply(now, header, n, name, keyname, &class, NULL);
if (new_status == STAT_NO_SIG)
{
if (option_bool(OPT_DNSSEC_NO_SIGN))
new_status = tcp_check_for_unsigned_zone(now, header, n, class, name, keyname, server, keycount);
else
new_status = STAT_INSECURE;
}
}
if (new_status == STAT_NEED_DS || new_status == STAT_NEED_KEY)
goto another_tcp_key;
}
}
free(packet);
}
return new_status;
}
#endif
@@ -1249,7 +1525,8 @@ unsigned char *tcp_request(int confd, time_t now,
#ifdef HAVE_AUTH
int local_auth = 0;
#endif
int checking_disabled, check_subnet, no_cache_dnssec = 0, cache_secure = 0;
int checking_disabled, ad_question, do_bit, added_pheader = 0;
int check_subnet, no_cache_dnssec = 0, cache_secure = 0;
size_t m;
unsigned short qtype;
unsigned int gotname;
@@ -1285,16 +1562,12 @@ unsigned char *tcp_request(int confd, time_t now,
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,
@@ -1331,7 +1604,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_bit);
/* Do this by steam now we're not in the select() loop */
check_log_writer(NULL);
@@ -1411,8 +1684,17 @@ unsigned char *tcp_request(int confd, time_t now,
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID))
{
size = add_do_bit(header, size, ((char *) header) + 65536);
header->hb4 |= HB4_CD;
size_t new_size = add_do_bit(header, size, ((char *) header) + 65536);
/* 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;
if (size != new_size)
added_pheader = 1;
size = new_size;
}
#endif
@@ -1463,22 +1745,20 @@ unsigned char *tcp_request(int confd, time_t now,
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && !checking_disabled)
{
int class, status;
status = dnssec_validate_reply(now, header, m, daemon->namebuff, daemon->keyname, &class);
if (status == STAT_NEED_DS || status == STAT_NEED_KEY)
{
if ((status = tcp_key_recurse(now, status, class, daemon->keyname, last_server)) == STAT_SECURE)
status = dnssec_validate_reply(now, header, m, daemon->namebuff, daemon->keyname, &class);
}
log_query(F_KEYTAG | F_SECSTAT, "result", NULL,
status == STAT_SECURE ? "SECURE" : (status == STAT_INSECURE ? "INSECURE" : "BOGUS"));
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;
}
@@ -1513,7 +1793,7 @@ unsigned char *tcp_request(int confd, time_t now,
m = process_reply(header, now, last_server, (unsigned int)m,
option_bool(OPT_NO_REBIND) && !norebind, no_cache_dnssec,
cache_secure, check_subnet, &peer_addr);
cache_secure, ad_question, do_bit, added_pheader, check_subnet, &peer_addr);
break;
}
+135 -83
View File
@@ -1254,87 +1254,187 @@ void pre_allocate_sfds(void)
}
}
void mark_servers(int flag)
{
struct server *serv;
/* mark everything with argument flag */
for (serv = daemon->servers; serv; serv = serv->next)
if (serv->flags & flag)
serv->flags |= SERV_MARK;
}
void cleanup_servers(void)
{
struct server *serv, *tmp, **up;
/* unlink and free anything still marked. */
for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp)
{
tmp = serv->next;
if (serv->flags & SERV_MARK)
{
server_gone(serv);
*up = serv->next;
if (serv->domain)
free(serv->domain);
free(serv);
}
else
up = &serv->next;
}
}
void add_update_server(int flags,
union mysockaddr *addr,
union mysockaddr *source_addr,
const char *interface,
const char *domain)
{
struct server *serv, *next = NULL;
char *domain_str = NULL;
/* See if there is a suitable candidate, and unmark */
for (serv = daemon->servers; serv; serv = serv->next)
if (serv->flags & SERV_MARK)
{
if (domain)
{
if (!(serv->flags & SERV_HAS_DOMAIN) || !hostname_isequal(domain, serv->domain))
continue;
}
else
{
if (serv->flags & SERV_HAS_DOMAIN)
continue;
}
break;
}
if (serv)
{
domain_str = serv->domain;
next = serv->next;
}
else if ((serv = whine_malloc(sizeof (struct server))))
{
/* Not found, create a new one. */
if (domain && !(domain_str = whine_malloc(strlen(domain)+1)))
{
free(serv);
serv = NULL;
}
else
{
struct server *s;
/* Add to the end of the chain, for order */
if (!daemon->servers)
daemon->servers = serv;
else
{
for (s = daemon->servers; s->next; s = s->next);
s->next = serv;
}
if (domain)
strcpy(domain_str, domain);
}
}
if (serv)
{
memset(serv, 0, sizeof(struct server));
serv->flags = flags;
serv->domain = domain_str;
serv->next = next;
serv->queries = serv->failed_queries = 0;
if (domain)
serv->flags |= SERV_HAS_DOMAIN;
if (interface)
strcpy(serv->interface, interface);
if (addr)
serv->addr = *addr;
if (source_addr)
serv->source_addr = *source_addr;
}
}
void check_servers(void)
{
struct irec *iface;
struct server *new, *tmp, *ret = NULL;
struct server *serv;
int port = 0;
/* interface may be new since startup */
if (!option_bool(OPT_NOWILD))
enumerate_interfaces(0);
for (new = daemon->servers; new; new = tmp)
for (serv = daemon->servers; serv; serv = serv->next)
{
tmp = new->next;
if (!(new->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND)))
if (!(serv->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND)))
{
port = prettyprint_addr(&new->addr, daemon->namebuff);
port = prettyprint_addr(&serv->addr, daemon->namebuff);
/* 0.0.0.0 is nothing, the stack treats it like 127.0.0.1 */
if (new->addr.sa.sa_family == AF_INET &&
new->addr.in.sin_addr.s_addr == 0)
if (serv->addr.sa.sa_family == AF_INET &&
serv->addr.in.sin_addr.s_addr == 0)
{
free(new);
serv->flags |= SERV_MARK;
continue;
}
for (iface = daemon->interfaces; iface; iface = iface->next)
if (sockaddr_isequal(&new->addr, &iface->addr))
if (sockaddr_isequal(&serv->addr, &iface->addr))
break;
if (iface)
{
my_syslog(LOG_WARNING, _("ignoring nameserver %s - local interface"), daemon->namebuff);
free(new);
serv->flags |= SERV_MARK;
continue;
}
/* Do we need a socket set? */
if (!new->sfd &&
!(new->sfd = allocate_sfd(&new->source_addr, new->interface)) &&
if (!serv->sfd &&
!(serv->sfd = allocate_sfd(&serv->source_addr, serv->interface)) &&
errno != 0)
{
my_syslog(LOG_WARNING,
_("ignoring nameserver %s - cannot make/bind socket: %s"),
daemon->namebuff, strerror(errno));
free(new);
serv->flags |= SERV_MARK;
continue;
}
}
/* reverse order - gets it right. */
new->next = ret;
ret = new;
if (!(new->flags & SERV_NO_REBIND))
if (!(serv->flags & SERV_NO_REBIND))
{
if (new->flags & (SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_USE_RESOLV))
if (serv->flags & (SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_USE_RESOLV))
{
char *s1, *s2;
if (!(new->flags & SERV_HAS_DOMAIN))
if (!(serv->flags & SERV_HAS_DOMAIN))
s1 = _("unqualified"), s2 = _("names");
else if (strlen(new->domain) == 0)
else if (strlen(serv->domain) == 0)
s1 = _("default"), s2 = "";
else
s1 = _("domain"), s2 = new->domain;
s1 = _("domain"), s2 = serv->domain;
if (new->flags & SERV_NO_ADDR)
if (serv->flags & SERV_NO_ADDR)
my_syslog(LOG_INFO, _("using local addresses only for %s %s"), s1, s2);
else if (new->flags & SERV_USE_RESOLV)
else if (serv->flags & SERV_USE_RESOLV)
my_syslog(LOG_INFO, _("using standard nameservers for %s %s"), s1, s2);
else if (!(new->flags & SERV_LITERAL_ADDRESS))
else if (!(serv->flags & SERV_LITERAL_ADDRESS))
my_syslog(LOG_INFO, _("using nameserver %s#%d for %s %s"), daemon->namebuff, port, s1, s2);
}
else if (new->interface[0] != 0)
my_syslog(LOG_INFO, _("using nameserver %s#%d(via %s)"), daemon->namebuff, port, new->interface);
else if (serv->interface[0] != 0)
my_syslog(LOG_INFO, _("using nameserver %s#%d(via %s)"), daemon->namebuff, port, serv->interface);
else
my_syslog(LOG_INFO, _("using nameserver %s#%d"), daemon->namebuff, port);
}
}
daemon->servers = ret;
cleanup_servers();
}
/* Return zero if no servers found, in that case we keep polling.
@@ -1343,9 +1443,6 @@ int reload_servers(char *fname)
{
FILE *f;
char *line;
struct server *old_servers = NULL;
struct server *new_servers = NULL;
struct server *serv;
int gotone = 0;
/* buff happens to be MAXDNAME long... */
@@ -1354,28 +1451,9 @@ int reload_servers(char *fname)
my_syslog(LOG_ERR, _("failed to read %s: %s"), fname, strerror(errno));
return 0;
}
/* move old servers to free list - we can reuse the memory
and not risk malloc if there are the same or fewer new servers.
Servers which were specced on the command line go to the new list. */
for (serv = daemon->servers; serv;)
{
struct server *tmp = serv->next;
if (serv->flags & SERV_FROM_RESOLV)
{
serv->next = old_servers;
old_servers = serv;
/* forward table rules reference servers, so have to blow them away */
server_gone(serv);
}
else
{
serv->next = new_servers;
new_servers = serv;
}
serv = tmp;
}
mark_servers(SERV_FROM_RESOLV);
while ((line = fgets(daemon->namebuff, MAXDNAME, f)))
{
union mysockaddr addr, source_addr;
@@ -1434,38 +1512,12 @@ int reload_servers(char *fname)
continue;
#endif
if (old_servers)
{
serv = old_servers;
old_servers = old_servers->next;
}
else if (!(serv = whine_malloc(sizeof (struct server))))
continue;
/* this list is reverse ordered:
it gets reversed again in check_servers */
serv->next = new_servers;
new_servers = serv;
serv->addr = addr;
serv->source_addr = source_addr;
serv->domain = NULL;
serv->interface[0] = 0;
serv->sfd = NULL;
serv->flags = SERV_FROM_RESOLV;
serv->queries = serv->failed_queries = 0;
add_update_server(SERV_FROM_RESOLV, &addr, &source_addr, NULL, NULL);
gotone = 1;
}
/* Free any memory not used. */
while (old_servers)
{
struct server *tmp = old_servers->next;
free(old_servers);
old_servers = tmp;
}
daemon->servers = new_servers;
fclose(f);
cleanup_servers();
return gotone;
}
+176 -73
View File
@@ -139,8 +139,11 @@ struct myoption {
#define LOPT_QUIET_DHCP6 327
#define LOPT_QUIET_RA 328
#define LOPT_SEC_VALID 329
#define LOPT_DNSKEY 330
#define LOPT_TRUST_ANCHOR 330
#define LOPT_DNSSEC_DEBUG 331
#define LOPT_REV_SERV 332
#define LOPT_SERVERS_FILE 333
#define LOPT_DNSSEC_CHECK 334
#ifdef HAVE_GETOPT_LONG
static const struct option opts[] =
@@ -157,6 +160,7 @@ static const struct myoption opts[] =
{ "user", 2, 0, 'u' },
{ "group", 2, 0, 'g' },
{ "resolv-file", 2, 0, 'r' },
{ "servers-file", 1, 0, LOPT_SERVERS_FILE },
{ "mx-host", 1, 0, 'm' },
{ "mx-target", 1, 0, 't' },
{ "cache-size", 2, 0, 'c' },
@@ -178,6 +182,7 @@ static const struct myoption opts[] =
{ "pid-file", 2, 0, 'x' },
{ "strict-order", 0, 0, 'o' },
{ "server", 1, 0, 'S' },
{ "rev-server", 1, 0, LOPT_REV_SERV },
{ "local", 1, 0, LOPT_LOCAL },
{ "address", 1, 0, 'A' },
{ "conf-file", 2, 0, 'C' },
@@ -277,8 +282,9 @@ static const struct myoption opts[] =
{ "ipset", 1, 0, LOPT_IPSET },
{ "synth-domain", 1, 0, LOPT_SYNTH },
{ "dnssec", 0, 0, LOPT_SEC_VALID },
{ "dnskey", 1, 0, LOPT_DNSKEY },
{ "trust-anchor", 1, 0, LOPT_TRUST_ANCHOR },
{ "dnssec-debug", 0, 0, LOPT_DNSSEC_DEBUG },
{ "dnssec-check-unsigned", 0, 0, LOPT_DNSSEC_CHECK },
#ifdef OPTION6_PREFIX_CLASS
{ "dhcp-prefix-class", 1, 0, LOPT_PREF_CLSS },
#endif
@@ -347,7 +353,9 @@ static struct {
{ 'Q', ARG_ONE, "<integer>", gettext_noop("Force the originating port for upstream DNS queries."), NULL },
{ 'R', OPT_NO_RESOLV, NULL, gettext_noop("Do NOT read resolv.conf."), NULL },
{ 'r', ARG_DUP, "<path>", gettext_noop("Specify path to resolv.conf (defaults to %s)."), RESOLVFILE },
{ LOPT_SERVERS_FILE, ARG_ONE, "<path>", gettext_noop("Specify path to file with server= options"), NULL },
{ 'S', ARG_DUP, "/<domain>/<ipaddr>", gettext_noop("Specify address(es) of upstream servers with optional domains."), NULL },
{ LOPT_REV_SERV, ARG_DUP, "<addr>/<prefix>,<ipaddr>", gettext_noop("Specify address of upstream servers for reverse address queries"), NULL },
{ LOPT_LOCAL, ARG_DUP, "/<domain>/", gettext_noop("Never forward queries to specified domains."), NULL },
{ 's', ARG_DUP, "<domain>[,<range>]", gettext_noop("Specify the domain to be assigned in DHCP leases."), NULL },
{ 't', ARG_ONE, "<host_name>", gettext_noop("Specify default target in an MX record."), NULL },
@@ -430,8 +438,9 @@ static struct {
{ 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_DNSKEY, ARG_DUP, "<domain>,<algo>,<key>", gettext_noop("Specify trust anchor DNSKEY"), 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 },
{ LOPT_DNSSEC_CHECK, OPT_DNSSEC_NO_SIGN, NULL, gettext_noop("Ensure answers without DNSSEC are in unsigned zones."), NULL },
#ifdef OPTION6_PREFIX_CLASS
{ LOPT_PREF_CLSS, ARG_DUP, "set:tag,<class>", gettext_noop("Specify DHCPv6 prefix class"), NULL },
#endif
@@ -590,6 +599,18 @@ static int atoi_check16(char *a, int *res)
return 1;
}
#ifdef HAVE_DNSSEC
static int atoi_check8(char *a, int *res)
{
if (!(atoi_check(a, res)) ||
*res < 0 ||
*res > 0xff)
return 0;
return 1;
}
#endif
static void add_txt(char *name, char *txt)
{
@@ -674,6 +695,13 @@ char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_a
char *scope_id;
#endif
if (!arg || strlen(arg) == 0)
{
*flags |= SERV_NO_ADDR;
*interface = 0;
return NULL;
}
if ((source = split_chr(arg, '@')) && /* is there a source. */
(portno = split_chr(source, '#')) &&
!atoi_check16(portno, &source_port))
@@ -752,6 +780,54 @@ char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_a
return NULL;
}
static struct server *add_rev4(struct in_addr addr, int msize)
{
struct server *serv = opt_malloc(sizeof(struct server));
in_addr_t a = ntohl(addr.s_addr) >> 8;
char *p;
memset(serv, 0, sizeof(struct server));
p = serv->domain = opt_malloc(25); /* strlen("xxx.yyy.zzz.in-addr.arpa")+1 */
if (msize == 24)
p += sprintf(p, "%d.", a & 0xff);
a = a >> 8;
if (msize != 8)
p += sprintf(p, "%d.", a & 0xff);
a = a >> 8;
p += sprintf(p, "%d.in-addr.arpa", a & 0xff);
serv->flags = SERV_HAS_DOMAIN;
serv->next = daemon->servers;
daemon->servers = serv;
return serv;
}
static struct server *add_rev6(struct in6_addr *addr, int msize)
{
struct server *serv = opt_malloc(sizeof(struct server));
char *p;
int i;
memset(serv, 0, sizeof(struct server));
p = serv->domain = opt_malloc(73); /* strlen("32*<n.>ip6.arpa")+1 */
for (i = msize-1; i >= 0; i -= 4)
{
int dig = ((unsigned char *)addr)[i>>3];
p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
}
p += sprintf(p, "ip6.arpa");
serv->flags = SERV_HAS_DOMAIN;
serv->next = daemon->servers;
daemon->servers = serv;
return serv;
}
#ifdef HAVE_DHCP
static int is_tag_prefix(char *arg)
@@ -1315,7 +1391,7 @@ void reset_option_bool(unsigned int opt)
daemon->options2 &= ~(1u << (opt - 32));
}
static int one_opt(int option, char *arg, char *errstr, char *gen_err, int command_line)
static int one_opt(int option, char *arg, char *errstr, char *gen_err, int command_line, int servers_only)
{
int i;
char *comma;
@@ -1518,6 +1594,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
daemon->resolv_files = list;
break;
}
case LOPT_SERVERS_FILE:
daemon->servers_file = opt_string_alloc(arg);
break;
case 'm': /* --mx-host */
{
@@ -1814,34 +1894,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
ret_err(gen_err);
else
{
/* generate the equivalent of
local=/<domain>/
local=/xxx.yyy.zzz.in-addr.arpa/ */
struct server *serv = opt_malloc(sizeof(struct server));
in_addr_t a = ntohl(new->start.s_addr) >> 8;
char *p;
memset(serv, 0, sizeof(struct server));
serv->domain = d;
serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
serv->next = daemon->servers;
daemon->servers = serv;
serv = opt_malloc(sizeof(struct server));
memset(serv, 0, sizeof(struct server));
p = serv->domain = opt_malloc(25); /* strlen("xxx.yyy.zzz.in-addr.arpa")+1 */
if (msize == 24)
p += sprintf(p, "%d.", a & 0xff);
a = a >> 8;
if (msize != 8)
p += sprintf(p, "%d.", a & 0xff);
a = a >> 8;
p += sprintf(p, "%d.in-addr.arpa", a & 0xff);
serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
serv->next = daemon->servers;
daemon->servers = serv;
/* generate the equivalent of
local=/<domain>/
local=/xxx.yyy.zzz.in-addr.arpa/ */
struct server *serv = add_rev4(new->start, msize);
serv->flags |= SERV_NO_ADDR;
}
}
}
@@ -1877,29 +1934,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
/* generate the equivalent of
local=/<domain>/
local=/xxx.yyy.zzz.ip6.arpa/ */
struct server *serv = opt_malloc(sizeof(struct server));
char *p;
memset(serv, 0, sizeof(struct server));
serv->domain = d;
serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
serv->next = daemon->servers;
daemon->servers = serv;
serv = opt_malloc(sizeof(struct server));
memset(serv, 0, sizeof(struct server));
p = serv->domain = opt_malloc(73); /* strlen("32*<n.>ip6.arpa")+1 */
for (i = msize-1; i >= 0; i -= 4)
{
int dig = ((unsigned char *)&new->start6)[i>>3];
p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
}
p += sprintf(p, "ip6.arpa");
serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
serv->next = daemon->servers;
daemon->servers = serv;
struct server *serv = add_rev6(&new->start6, msize);
serv->flags |= SERV_NO_ADDR;
}
}
}
@@ -2126,6 +2162,9 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
memset(newlist, 0, sizeof(struct server));
}
if (servers_only && option == 'S')
newlist->flags |= SERV_FROM_FILE;
if (option == 'A')
{
newlist->flags |= SERV_LITERAL_ADDRESS;
@@ -2170,6 +2209,40 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
break;
}
case LOPT_REV_SERV: /* --rev-server */
{
char *string;
int size;
struct server *serv;
struct in_addr addr4;
#ifdef HAVE_IPV6
struct in6_addr addr6;
#endif
unhide_metas(arg);
if (!arg || !(comma=split(arg)) || !(string = split_chr(arg, '/')) || !atoi_check(string, &size))
ret_err(gen_err);
if (inet_pton(AF_INET, arg, &addr4))
serv = add_rev4(addr4, size);
#ifdef HAVE_IPV6
else if (inet_pton(AF_INET6, arg, &addr6))
serv = add_rev6(&addr6, size);
#endif
else
ret_err(gen_err);
string = parse_server(comma, &serv->addr, &serv->source_addr, serv->interface, &serv->flags);
if (string)
ret_err(string);
if (servers_only)
serv->flags |= SERV_FROM_FILE;
break;
}
case LOPT_IPSET: /* --ipset */
#ifndef HAVE_IPSET
ret_err(_("recompile with HAVE_IPSET defined to enable ipset directives"));
@@ -3675,10 +3748,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
}
#ifdef HAVE_DNSSEC
case LOPT_DNSKEY:
case LOPT_TRUST_ANCHOR:
{
struct dnskey *new = opt_malloc(sizeof(struct dnskey));
char *key64, *algo = NULL;
struct ds_config *new = opt_malloc(sizeof(struct ds_config));
char *cp, *cp1, *keyhex, *digest, *algo = NULL;
int len;
new->class = C_IN;
@@ -3700,20 +3774,30 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
}
}
if (!comma || !algo || !(key64 = split(algo)) ||
!atoi_check16(comma, &new->flags) || !atoi_check16(algo, &new->algo) ||
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 DNSKEY"));
ret_err(_("bad trust anchor"));
/* Upper bound on length */
new->key = opt_malloc((3*strlen(key64)/4)+1);
unhide_metas(key64);
if ((new->keylen = parse_base64(key64, new->key)) == -1)
ret_err(_("bad base64 in DNSKEY"));
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;
new->next = daemon->dnskeys;
daemon->dnskeys = new;
break;
}
#endif
@@ -3733,12 +3817,13 @@ static void read_file(char *file, FILE *f, int hard_opt)
while (fgets(buff, MAXDNAME, f))
{
int white, i, option = hard_opt;
int white, i;
volatile int option = (hard_opt == LOPT_REV_SERV) ? 0 : hard_opt;
char *errmess, *p, *arg = NULL, *start;
size_t len;
/* Memory allocation failure longjmps here if mem_recover == 1 */
if (option != 0)
if (option != 0 || hard_opt == LOPT_REV_SERV)
{
if (setjmp(mem_jmp))
continue;
@@ -3839,13 +3924,15 @@ static void read_file(char *file, FILE *f, int hard_opt)
errmess = _("extraneous parameter");
else if (opts[i].has_arg == 1 && !arg)
errmess = _("missing parameter");
else if (hard_opt == LOPT_REV_SERV && option != 'S' && option != LOPT_REV_SERV)
errmess = _("illegal option");
}
oops:
if (errmess)
strcpy(daemon->namebuff, errmess);
if (errmess || !one_opt(option, arg, buff, _("error"), 0))
if (errmess || !one_opt(option, arg, buff, _("error"), 0, hard_opt == LOPT_REV_SERV))
{
sprintf(daemon->namebuff + strlen(daemon->namebuff), _(" at line %d of %s"), lineno, file);
if (hard_opt != 0)
@@ -4027,6 +4114,22 @@ struct hostsfile *expand_filelist(struct hostsfile *list)
return list;
}
void read_servers_file(void)
{
FILE *f;
if (!(f = fopen(daemon->servers_file, "r")))
{
my_syslog(LOG_ERR, _("cannot read %s: %s"), daemon->servers_file, strerror(errno));
return;
}
mark_servers(SERV_FROM_FILE);
cleanup_servers();
read_file(daemon->servers_file, f, LOPT_REV_SERV);
}
#ifdef HAVE_DHCP
void reread_dhcp(void)
@@ -4220,9 +4323,9 @@ void read_opts(int argc, char **argv, char *compile_opts)
else
{
#ifdef HAVE_GETOPT_LONG
if (!one_opt(option, arg, daemon->namebuff, _("try --help"), 1))
if (!one_opt(option, arg, daemon->namebuff, _("try --help"), 1, 0))
#else
if (!one_opt(option, arg, daemon->namebuff, _("try -w"), 1))
if (!one_opt(option, arg, daemon->namebuff, _("try -w"), 1, 0))
#endif
die(_("bad command line options: %s"), daemon->namebuff, EC_BADCONF);
}
+108 -103
View File
@@ -337,7 +337,7 @@ unsigned char *skip_questions(struct dns_header *header, size_t plen)
return ansp;
}
static unsigned char *skip_section(unsigned char *ansp, int count, struct dns_header *header, size_t plen)
unsigned char *skip_section(unsigned char *ansp, int count, struct dns_header *header, size_t plen)
{
int i, rdlen;
@@ -601,11 +601,11 @@ static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *p
if (family == parm->l3->sa.sa_family)
{
if (family == AF_INET && memcmp (&parm->l3->in.sin_addr, addrp, INADDRSZ) == 0)
if (family == AF_INET && memcmp(&parm->l3->in.sin_addr, addrp, INADDRSZ) == 0)
match = 1;
#ifdef HAVE_IPV6
else
if (family == AF_INET6 && memcmp (&parm->l3->in6.sin6_addr, addrp, IN6ADDRSZ) == 0)
if (family == AF_INET6 && memcmp(&parm->l3->in6.sin6_addr, addrp, IN6ADDRSZ) == 0)
match = 1;
#endif
}
@@ -917,8 +917,8 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
searched_soa = 1;
ttl = find_soa(header, qlen, name, doctored);
#ifdef HAVE_DNSSEC
if (*doctored)
secure = 0;
if (*doctored && secure)
return 0;
#endif
}
@@ -927,7 +927,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
for (i = ntohs(header->qdcount); i != 0; i--)
{
int found = 0, cname_count = 5;
int found = 0, cname_count = CNAME_CHAIN;
struct crec *cpp = NULL;
int flags = RCODE(header) == NXDOMAIN ? F_NXDOMAIN : 0;
int secflag = secure ? F_DNSSECOK : 0;
@@ -988,9 +988,8 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
if (aqtype == T_CNAME)
{
if (!cname_count--)
return 0; /* looped CNAMES */
secflag = 0; /* no longer DNSSEC */
if (!cname_count-- || secure)
return 0; /* looped CNAMES, or DNSSEC, which we can't cache. */
goto cname_loop;
}
@@ -1242,7 +1241,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;
@@ -1452,7 +1451,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, int *do_bit)
{
char *name = daemon->namebuff;
unsigned char *p, *ansp, *pheader;
@@ -1468,9 +1468,13 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
struct mx_srv_record *rec;
size_t len;
/* Don't return AD set even for local data if checking disabled. */
/* 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;
*do_bit = 0;
/* 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
@@ -1489,7 +1493,9 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
pheader += 2; /* ext_rcode */
GETSHORT(flags, pheader);
sec_reqd = flags & 0x8000; /* do bit */
if ((sec_reqd = flags & 0x8000))
*do_bit = 1;/* 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
@@ -1551,58 +1557,60 @@ 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 || qtype == T_RRSIG))
if (option_bool(OPT_DNSSEC_VALID) && (qtype == T_DNSKEY || qtype == T_DS))
{
int gotone = 0, have_rrsig = 0;
int gotone = 0;
struct blockdata *keydata;
/* Do we have RRSIG? Can't do DS or DNSKEY otherwise. */
crecp = NULL;
while ((crecp = cache_find_by_name(crecp, name, now, F_DNSKEY | F_DS)))
if (crecp->uid == qclass && (qtype == T_RRSIG || crecp->addr.sig.type_covered == qtype))
{
have_rrsig = 1;
break;
}
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 (qtype == T_RRSIG && have_rrsig)
if (!sec_reqd || crecp)
{
ans = gotone = 1;
auth = 0;
}
else if (qtype == T_DS && have_rrsig)
{
auth = 0;
crecp = NULL;
while ((crecp = cache_find_by_name(crecp, name, now, F_DS)))
if (crecp->uid == qclass)
{
ans = 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 if (qtype == T_DNSKEY)
{
crecp = NULL;
while ((crecp = cache_find_by_name(crecp, name, now, F_DNSKEY)))
if (crecp->uid == qclass)
{
if ((crecp->flags & F_CONFIG) || have_rrsig) /* Return configured keys without an RRISG */
if (qtype == T_DS)
{
crecp = NULL;
while ((crecp = cache_find_by_name(crecp, name, now, F_DS)))
if (crecp->uid == qclass)
{
if (!(crecp->flags & F_CONFIG))
auth = 0, gotone = 1;
ans = 1;
gotone = 1;
if (!dryrun)
{
if (crecp->flags & F_NEG)
{
if (crecp->flags & F_NXDOMAIN)
nxdomain = 1;
log_query(F_UPSTREAM, name, NULL, "secure no DS");
}
else if ((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;
@@ -1615,30 +1623,27 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
anscount++;
}
}
}
}
}
/* Now do RRSIGs */
if (gotone)
{
crecp = NULL;
while ((crecp = cache_find_by_name(crecp, name, now, F_DNSKEY | F_DS)))
if (crecp->uid == qclass && (qtype == T_RRSIG || (sec_reqd && crecp->addr.sig.type_covered == qtype)) &&
!dryrun &&
(keydata = blockdata_retrieve(crecp->addr.sig.keydata, crecp->addr.sig.keylen, NULL)))
{
if (qtype == T_RRSIG)
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)))
{
char types[20];
querystr("rrsig", types, crecp->addr.sig.type_covered);
log_query(F_RRNAME, name, NULL, types);
}
if ((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++;
}
T_RRSIG, qclass, "t", crecp->addr.sig.keylen, keydata);
anscount++;
}
}
}
}
#endif
@@ -1742,24 +1747,25 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
else if (crecp->flags & F_DNSSECOK)
{
int gotsig = 0;
crecp = NULL;
while ((crecp = cache_find_by_name(crecp, name, now, F_DS | F_DNSKEY)))
struct crec *rr_crec = NULL;
while ((rr_crec = cache_find_by_name(rr_crec, name, now, F_DS | F_DNSKEY)))
{
if (crecp->addr.sig.type_covered == T_PTR && crecp->uid == C_IN)
if (rr_crec->addr.sig.type_covered == T_PTR && rr_crec->uid == C_IN)
{
char *sigdata = blockdata_retrieve(crecp->addr.sig.keydata, crecp->addr.sig.keylen, NULL);
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,
crecp->ttd - now, &nameoffset,
rr_crec->ttd - now, &nameoffset,
T_RRSIG, C_IN, "t", crecp->addr.sig.keylen, sigdata))
anscount++;
}
}
/* Need to re-run original cache search */
crecp = gotsig ? cache_find_by_addr(NULL, &addr, now, is_arpa) : NULL;
if (!gotsig)
crecp = NULL;
}
#endif
}
@@ -1923,7 +1929,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;
@@ -1954,7 +1960,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
else if (crecp->flags & F_DNSSECOK)
{
/* 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. */
@@ -1963,23 +1969,23 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (crecp->flags & F_CNAME)
sigtype = T_CNAME;
crecp = NULL;
while ((crecp = cache_find_by_name(crecp, name, now, F_DS | F_DNSKEY)))
while ((rr_crec = cache_find_by_name(rr_crec, name, now, F_DS | F_DNSKEY)))
{
if (crecp->addr.sig.type_covered == sigtype && crecp->uid == C_IN)
if (rr_crec->addr.sig.type_covered == sigtype && rr_crec->uid == C_IN)
{
char *sigdata = blockdata_retrieve(crecp->addr.sig.keydata, crecp->addr.sig.keylen, NULL);
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,
crecp->ttd - now, &nameoffset,
T_RRSIG, C_IN, "t", crecp->addr.sig.keylen, sigdata))
rr_crec->ttd - now, &nameoffset,
T_RRSIG, C_IN, "t", rr_crec->addr.sig.keylen, sigdata))
anscount++;
}
}
/* Need to re-run original cache search */
crecp = gotsig ? cache_find_by_name(NULL, name, now, flag | F_CNAME) : NULL;
if (!gotsig)
crecp = NULL;
}
#endif
}
@@ -2072,7 +2078,7 @@ 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;
@@ -2111,7 +2117,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)
@@ -2263,17 +2269,16 @@ 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);
header->hb4 &= ~HB4_AD;
len = ansp - (unsigned char *)header;
if (have_pseudoheader)
{
len = add_pseudoheader(header, len, (unsigned char *)limit, 0, NULL, 0, sec_reqd);
if (sec_reqd && sec_data)
header->hb4 |= HB4_AD;
}
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;
}
-60
View File
@@ -482,66 +482,6 @@ int parse_hex(char *in, unsigned char *out, int maxlen,
return i;
}
#ifdef HAVE_DNSSEC
static int charval(char c)
{
if (c >= 'A' && c <= 'Z')
return c - 'A';
if (c >= 'a' && c <= 'z')
return c - 'a' + 26;
if (c >= '0' && c <= '9')
return c - '0' + 52;
if (c == '+')
return 62;
if (c == '/')
return 63;
if (c == '=')
return -1;
return -2;
}
int parse_base64(char *in, char *out)
{
char *p = out;
int i, val[4];
while (*in)
{
for (i = 0; i < 4; i++)
{
while (*in == ' ')
in++;
if (*in == 0)
return -1;
if ((val[i] = charval(*in++)) == -2)
return -1;
}
while (*in == ' ')
in++;
if (val[1] == -1)
return -1; /* too much padding */
*p++ = (val[0] << 2) | (val[1] >> 4);
if (val[2] != -1)
*p++ = (val[1] << 4) | ( val[2] >> 2);
if (val[3] != -1)
*p++ = (val[2] << 6) | val[3];
}
return p - out;
}
#endif
/* return 0 for no match, or (no matched octets) + 1 */
int memcmp_masked(unsigned char *a, unsigned char *b, int len, unsigned int mask)
{
+6 -5
View File
@@ -1,8 +1,9 @@
# The root DNSSEC trust anchors, valid as at 30/01/2014
# 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
dnskey=.,257,8,AwEAAagAIKlVZrpC6Ia7gEzahOR+9W29euxhJhVVLOyQbSEW0O8gcCjF FVQUTf6v58fLjwBd0YI0EzrAcQqBGCzh/RStIoO8g0NfnfL2MTJRkxoX bfDaUeVPQuYEhg37NZWAJQ9VnMVDxP/VHL496M/QZxkjf5/Efucp2gaD X6RS6CXpoY68LsvPVjR0ZSwzz1apAzvN9dlzEheX7ICJBBtuA6G3LQpz W5hOA2hzCTMjJPJ8LbqF6dsV6DoBQzgul0sGIcGOYl7OyQdXfZ57relS Qageu+ipAdTTJ25AsRTAoub8ONGcLmqrAmRLKBP1dfwhYB4N7knNnulq QxA+Uk1ihz0=
dnskey=.,256,8,AwEAAb8sU6pbYMWRbkRnEuEZw9NSir707TkOcF+UL1XiK4NDJOvXRyX1 95Am5dQ7bRnnuySZ3daf37vvjUUhuIWUAQ4stht8nJfYxVQXDYjSpGH5 I6Hf/0CZEoNP6cNvrQ7AFmKkmv00xWExKQjbvnRPI4bqpMwtHVzn6Wyb BZ6kuqED
dnskey=.,256,8,AwEAAYRU41/8smgAvuSojEP4jaj5Yll7WPaUKpYvnz2pnX2VIvRn4jsy Jns80bloenG6X9ebJVy2CFtZQLKHP8DcKmIFotdgs2HolyocY1am/+33 4RtzusM2ojkhjn1FRGtuSE9s2TSz1ISv0yVnFyu+EP/ZkiWnDfWeVrJI SEWBEr4V