dnsmasq: 2.76test10 with latest patches (001-004)

This is 'dnsmasq 2.76test10', based on current 'next', containing latest patches.

Signed-off-by: Matthias Fischer <matthias.fischer@ipfire.org>
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
This commit is contained in:
Matthias Fischer
2016-02-26 18:29:28 +01:00
committed by Michael Tremer
parent 43747fae33
commit 3b9815eb87
34 changed files with 542 additions and 6603 deletions

View File

@@ -0,0 +1,265 @@
From df3d54f776a3c9b60735b45c0b7fd88b66a2d5c4 Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Wed, 24 Feb 2016 21:03:38 +0000
Subject: [PATCH] Add TTL parameter to --host-record and --cname.
---
man/dnsmasq.8 | 12 ++++++++++--
src/cache.c | 7 +++++++
src/dnsmasq.h | 2 ++
src/option.c | 46 ++++++++++++++++++++++++++++++++++++++--------
src/rfc1035.c | 6 +++++-
5 files changed, 62 insertions(+), 11 deletions(-)
diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
index b782eaf..7bc1394 100644
--- a/man/dnsmasq.8
+++ b/man/dnsmasq.8
@@ -529,7 +529,7 @@ zone files: the port, weight and priority numbers are in a different
order. More than one SRV record for a given service/domain is allowed,
all that match are returned.
.TP
-.B --host-record=<name>[,<name>....],[<IPv4-address>],[<IPv6-address>]
+.B --host-record=<name>[,<name>....],[<IPv4-address>],[<IPv6-address>][,<TTL>]
Add A, AAAA and PTR records to the DNS. This adds one or more names to
the DNS with associated IPv4 (A) and IPv6 (AAAA) records. A name may
appear in more than one
@@ -546,6 +546,10 @@ is in effect. Short and long names may appear in the same
.B host-record,
eg.
.B --host-record=laptop,laptop.thekelleys.org,192.168.0.1,1234::100
+
+If the time-to-live is given, it overrides the default, which is zero
+or the value of --local-ttl. The value is a positive integer and gives
+the time-to-live in seconds.
.TP
.B \-Y, --txt-record=<name>[[,<text>],<text>]
Return a TXT DNS record. The value of TXT record is a set of strings,
@@ -559,7 +563,7 @@ Return a PTR DNS record.
.B --naptr-record=<name>,<order>,<preference>,<flags>,<service>,<regexp>[,<replacement>]
Return an NAPTR DNS record, as specified in RFC3403.
.TP
-.B --cname=<cname>,<target>
+.B --cname=<cname>,<target>[,<TTL>]
Return a CNAME record which indicates that <cname> is really
<target>. There are significant limitations on the target; it must be a
DNS name which is known to dnsmasq from /etc/hosts (or additional
@@ -568,6 +572,10 @@ hosts files), from DHCP, from --interface-name or from another
If the target does not satisfy this
criteria, the whole cname is ignored. The cname must be unique, but it
is permissable to have more than one cname pointing to the same target.
+
+If the time-to-live is given, it overrides the default, which is zero
+or the value of -local-ttl. The value is a positive integer and gives
+the time-to-live in seconds.
.TP
.B --dns-rr=<name>,<RR-number>,[<hex data>]
Return an arbitrary DNS Resource Record. The number is the type of the
diff --git a/src/cache.c b/src/cache.c
index a9eaa65..4ecd535 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -778,6 +778,7 @@ static void add_hosts_cname(struct crec *target)
(crec = whine_malloc(sizeof(struct crec))))
{
crec->flags = F_FORWARD | F_IMMORTAL | F_NAMEP | F_CONFIG | F_CNAME;
+ crec->ttd = a->ttl;
crec->name.namep = a->alias;
crec->addr.cname.target.cache = target;
crec->addr.cname.uid = target->uid;
@@ -981,6 +982,7 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
strcat(cache->name.sname, ".");
strcat(cache->name.sname, domain_suffix);
cache->flags = flags;
+ cache->ttd = daemon->local_ttl;
add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz);
name_count++;
}
@@ -988,6 +990,7 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
{
strcpy(cache->name.sname, canon);
cache->flags = flags;
+ cache->ttd = daemon->local_ttl;
add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz);
name_count++;
}
@@ -1057,6 +1060,7 @@ void cache_reload(void)
((cache = whine_malloc(sizeof(struct crec)))))
{
cache->flags = F_FORWARD | F_NAMEP | F_CNAME | F_IMMORTAL | F_CONFIG;
+ cache->ttd = a->ttl;
cache->name.namep = a->alias;
cache->addr.cname.target.int_name = intr;
cache->addr.cname.uid = SRC_INTERFACE;
@@ -1071,6 +1075,7 @@ void cache_reload(void)
(cache->addr.ds.keydata = blockdata_alloc(ds->digest, ds->digestlen)))
{
cache->flags = F_FORWARD | F_IMMORTAL | F_DS | F_CONFIG | F_NAMEP;
+ cache->ttd = daemon->local_ttl;
cache->name.namep = ds->name;
cache->addr.ds.keylen = ds->digestlen;
cache->addr.ds.algo = ds->algo;
@@ -1095,6 +1100,7 @@ void cache_reload(void)
(cache = whine_malloc(sizeof(struct crec))))
{
cache->name.namep = nl->name;
+ cache->ttd = hr->ttl;
cache->flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4 | F_NAMEP | F_CONFIG;
add_hosts_entry(cache, (struct all_addr *)&hr->addr, INADDRSZ, SRC_CONFIG, (struct crec **)daemon->packet, revhashsz);
}
@@ -1103,6 +1109,7 @@ void cache_reload(void)
(cache = whine_malloc(sizeof(struct crec))))
{
cache->name.namep = nl->name;
+ cache->ttd = hr->ttl;
cache->flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6 | F_NAMEP | F_CONFIG;
add_hosts_entry(cache, (struct all_addr *)&hr->addr6, IN6ADDRSZ, SRC_CONFIG, (struct crec **)daemon->packet, revhashsz);
}
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index 6d1c5ae..6344df5 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -308,6 +308,7 @@ struct ptr_record {
};
struct cname {
+ int ttl;
char *alias, *target;
struct cname *next;
};
@@ -344,6 +345,7 @@ struct auth_zone {
struct host_record {
+ int ttl;
struct name_list {
char *name;
struct name_list *next;
diff --git a/src/option.c b/src/option.c
index c98bdc9..7c5e6bc 100644
--- a/src/option.c
+++ b/src/option.c
@@ -448,20 +448,20 @@ static struct {
{ LOPT_GEN_NAMES, ARG_DUP, "[=tag:<tag>]", gettext_noop("Generate hostnames based on MAC address for nameless clients."), NULL},
{ LOPT_PROXY, ARG_DUP, "[=<ipaddr>]...", gettext_noop("Use these DHCP relays as full proxies."), NULL },
{ LOPT_RELAY, ARG_DUP, "<local-addr>,<server>[,<interface>]", gettext_noop("Relay DHCP requests to a remote server"), NULL},
- { LOPT_CNAME, ARG_DUP, "<alias>,<target>", gettext_noop("Specify alias name for LOCAL DNS name."), NULL },
+ { LOPT_CNAME, ARG_DUP, "<alias>,<target>[,<ttl>]", gettext_noop("Specify alias name for LOCAL DNS name."), NULL },
{ LOPT_PXE_PROMT, ARG_DUP, "<prompt>,[<timeout>]", gettext_noop("Prompt to send to PXE clients."), NULL },
{ LOPT_PXE_SERV, ARG_DUP, "<service>", gettext_noop("Boot service for PXE menu."), NULL },
{ LOPT_TEST, 0, NULL, gettext_noop("Check configuration syntax."), NULL },
{ LOPT_ADD_MAC, ARG_DUP, "[=base64|text]", gettext_noop("Add requestor's MAC address to forwarded DNS queries."), NULL },
{ LOPT_ADD_SBNET, ARG_ONE, "<v4 pref>[,<v6 pref>]", gettext_noop("Add specified IP subnet to forwarded DNS queries."), NULL },
- { LOPT_CPE_ID, ARG_ONE, "<text>", gettext_noop("Add client identification to forwarded DNS queries."), NULL },
+ { LOPT_CPE_ID, ARG_ONE, "<text>", gettext_noop("Add client identification to forwarded DNS queries."), NULL },
{ LOPT_DNSSEC, OPT_DNSSEC_PROXY, NULL, gettext_noop("Proxy DNSSEC validation results from upstream nameservers."), NULL },
{ LOPT_INCR_ADDR, OPT_CONSEC_ADDR, NULL, gettext_noop("Attempt to allocate sequential IP addresses to DHCP clients."), NULL },
{ LOPT_CONNTRACK, OPT_CONNTRACK, NULL, gettext_noop("Copy connection-track mark from queries to upstream connections."), NULL },
{ LOPT_FQDN, OPT_FQDN_UPDATE, NULL, gettext_noop("Allow DHCP clients to do their own DDNS updates."), NULL },
{ LOPT_RA, OPT_RA, NULL, gettext_noop("Send router-advertisements for interfaces doing DHCPv6"), NULL },
{ LOPT_DUID, ARG_ONE, "<enterprise>,<duid>", gettext_noop("Specify DUID_EN-type DHCPv6 server DUID"), NULL },
- { LOPT_HOST_REC, ARG_DUP, "<name>,<address>", gettext_noop("Specify host (A/AAAA and PTR) records"), NULL },
+ { LOPT_HOST_REC, ARG_DUP, "<name>,<address>[,<ttl>]", gettext_noop("Specify host (A/AAAA and PTR) records"), NULL },
{ LOPT_RR, ARG_DUP, "<name>,<RR-number>,[<data>]", gettext_noop("Specify arbitrary DNS resource record"), NULL },
{ LOPT_CLVERBIND, OPT_CLEVERBIND, NULL, gettext_noop("Bind to interfaces in use - check for new interfaces"), NULL },
{ LOPT_AUTHSERV, ARG_ONE, "<NS>,<interface>", gettext_noop("Export local names to global DNS"), NULL },
@@ -3692,12 +3692,15 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
case LOPT_CNAME: /* --cname */
{
struct cname *new;
- char *alias;
- char *target;
+ char *alias, *target, *ttls;
+ int ttl = -1;
if (!(comma = split(arg)))
ret_err(gen_err);
+ if ((ttls = split(comma)) && !atoi_check(ttls, &ttl))
+ ret_err(_("bad TTL"));
+
alias = canonicalise_opt(arg);
target = canonicalise_opt(comma);
@@ -3713,6 +3716,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
daemon->cnames = new;
new->alias = alias;
new->target = target;
+ new->ttl = ttl;
}
break;
@@ -3913,14 +3917,22 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
{
struct host_record *new = opt_malloc(sizeof(struct host_record));
memset(new, 0, sizeof(struct host_record));
-
+ new->ttl = -1;
+
if (!arg || !(comma = split(arg)))
ret_err(_("Bad host-record"));
while (arg)
{
struct all_addr addr;
- if (inet_pton(AF_INET, arg, &addr))
+ char *dig;
+
+ for (dig = arg; *dig != 0; dig++)
+ if (*dig < '0' || *dig > '9')
+ break;
+ if (*dig == 0)
+ new->ttl = atoi(arg);
+ else if (inet_pton(AF_INET, arg, &addr))
new->addr = addr.addr.addr4;
#ifdef HAVE_IPV6
else if (inet_pton(AF_INET6, arg, &addr))
@@ -4601,7 +4613,25 @@ void read_opts(int argc, char **argv, char *compile_opts)
}
}
}
-
+
+ if (daemon->host_records)
+ {
+ struct host_record *hr;
+
+ for (hr = daemon->host_records; hr; hr = hr->next)
+ if (hr->ttl == -1)
+ hr->ttl = daemon->local_ttl;
+ }
+
+ if (daemon->cnames)
+ {
+ struct cname *cn;
+
+ for (cn = daemon->cnames; cn; cn = cn->next)
+ if (cn->ttl == -1)
+ cn->ttl = daemon->local_ttl;
+ }
+
if (daemon->if_addrs)
{
struct iname *tmp;
diff --git a/src/rfc1035.c b/src/rfc1035.c
index 9c0ddb5..3535a71 100644
--- a/src/rfc1035.c
+++ b/src/rfc1035.c
@@ -1169,9 +1169,13 @@ static unsigned long crec_ttl(struct crec *crecp, time_t now)
/* Return 0 ttl for DHCP entries, which might change
before the lease expires. */
- if (crecp->flags & (F_IMMORTAL | F_DHCP))
+ if (crecp->flags & F_DHCP)
return daemon->local_ttl;
+ /* Immortal entries other than DHCP are local, and hold TTL in TTD field. */
+ if (crecp->flags & F_IMMORTAL)
+ return crecp->ttd;
+
/* Return the Max TTL value if it is lower then the actual TTL */
if (daemon->max_ttl == 0 || ((unsigned)(crecp->ttd - now) < daemon->max_ttl))
return crecp->ttd - now;
--
1.7.10.4

View File

@@ -1,41 +0,0 @@
From d2aa7dfbb6d1088dcbea9fecc61b9293b320eb95 Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Mon, 3 Aug 2015 21:52:12 +0100
Subject: [PATCH] Include 0.0.0.0/8 in DNS rebind checks.
---
CHANGELOG | 7 +++++++
src/rfc1035.c | 3 ++-
2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG b/CHANGELOG
index 901da47..3f4026d 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,10 @@
+version 2.76
+ Include 0.0.0.0/8 in DNS rebind checks. This range
+ translates to hosts on the local network, or, at
+ least, 0.0.0.0 accesses the local host, so could
+ be targets for DNS rebinding. See RFC 5735 section 3
+ for details. Thanks to Stephen Röttger for the bug report.
+
version 2.75
Fix reversion on 2.74 which caused 100% CPU use when a
dhcp-script is configured. Thanks to Adrian Davey for
diff --git a/src/rfc1035.c b/src/rfc1035.c
index 56647b0..29e9e65 100644
--- a/src/rfc1035.c
+++ b/src/rfc1035.c
@@ -728,7 +728,8 @@ int private_net(struct in_addr addr, int ban_localhost)
in_addr_t ip_addr = ntohl(addr.s_addr);
return
- (((ip_addr & 0xFF000000) == 0x7F000000) && ban_localhost) /* 127.0.0.0/8 (loopback) */ ||
+ (((ip_addr & 0xFF000000) == 0x7F000000) && ban_localhost) /* 127.0.0.0/8 (loopback) */ ||
+ ((ip_addr & 0xFF000000) == 0x00000000) /* RFC 5735 section 3. "here" network */ ||
((ip_addr & 0xFFFF0000) == 0xC0A80000) /* 192.168.0.0/16 (private) */ ||
((ip_addr & 0xFF000000) == 0x0A000000) /* 10.0.0.0/8 (private) */ ||
((ip_addr & 0xFFF00000) == 0xAC100000) /* 172.16.0.0/12 (private) */ ||
--
1.7.10.4

View File

@@ -0,0 +1,117 @@
From 832e47beab95c2918b5264f0504f2fe6fe523e4c Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Wed, 24 Feb 2016 21:24:45 +0000
Subject: [PATCH] Add --dhcp-ttl option.
---
man/dnsmasq.8 | 5 ++++-
src/dnsmasq.h | 2 +-
src/option.c | 13 +++++++++++--
src/rfc1035.c | 2 +-
4 files changed, 17 insertions(+), 5 deletions(-)
diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
index 7bc1394..2bcce20 100644
--- a/man/dnsmasq.8
+++ b/man/dnsmasq.8
@@ -60,7 +60,7 @@ in the same way as for DHCP-derived names. Note that this does not
apply to domain names in cnames, PTR records, TXT records etc.
.TP
.B \-T, --local-ttl=<time>
-When replying with information from /etc/hosts or the DHCP leases
+When replying with information from /etc/hosts or configuration or the DHCP leases
file dnsmasq by default sets the time-to-live field to zero, meaning
that the requester should not itself cache the information. This is
the correct thing to do in almost all situations. This option allows a
@@ -68,6 +68,9 @@ time-to-live (in seconds) to be given for these replies. This will
reduce the load on the server at the expense of clients using stale
data under some circumstances.
.TP
+.B --dhcp-ttl=<time>
+As for --local-ttl, but affects only replies with information from DHCP leases. If both are given, --dhcp-ttl applies for DHCP information, and --local-ttl for others. Setting this to zero eliminates the effect of --local-ttl for DHCP.
+.TP
.B --neg-ttl=<time>
Negative replies from upstream servers normally contain time-to-live
information in SOA records which dnsmasq uses for caching. If the
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index 6344df5..9f73c3b 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -955,7 +955,7 @@ extern struct daemon {
int max_logs; /* queue limit */
int cachesize, ftabsize;
int port, query_port, min_port, max_port;
- unsigned long local_ttl, neg_ttl, max_ttl, min_cache_ttl, max_cache_ttl, auth_ttl;
+ unsigned long local_ttl, neg_ttl, max_ttl, min_cache_ttl, max_cache_ttl, auth_ttl, dhcp_ttl, use_dhcp_ttl;
char *dns_client_id;
struct hostsfile *addn_hosts;
struct dhcp_context *dhcp, *dhcp6;
diff --git a/src/option.c b/src/option.c
index 7c5e6bc..3f6d162 100644
--- a/src/option.c
+++ b/src/option.c
@@ -157,6 +157,7 @@ struct myoption {
#define LOPT_MAXPORT 345
#define LOPT_CPE_ID 346
#define LOPT_SCRIPT_ARP 347
+#define LOPT_DHCPTTL 348
#ifdef HAVE_GETOPT_LONG
static const struct option opts[] =
@@ -319,6 +320,7 @@ static const struct myoption opts[] =
{ "quiet-ra", 0, 0, LOPT_QUIET_RA },
{ "dns-loop-detect", 0, 0, LOPT_LOOP_DETECT },
{ "script-arp", 0, 0, LOPT_SCRIPT_ARP },
+ { "dhcp-ttl", 1, 0 , LOPT_DHCPTTL },
{ NULL, 0, 0, 0 }
};
@@ -485,9 +487,10 @@ static struct {
{ LOPT_QUIET_DHCP, OPT_QUIET_DHCP, NULL, gettext_noop("Do not log routine DHCP."), NULL },
{ LOPT_QUIET_DHCP6, OPT_QUIET_DHCP6, NULL, gettext_noop("Do not log routine DHCPv6."), NULL },
{ LOPT_QUIET_RA, OPT_QUIET_RA, NULL, gettext_noop("Do not log RA."), NULL },
- { LOPT_LOCAL_SERVICE, OPT_LOCAL_SERVICE, NULL, gettext_noop("Accept queries only from directly-connected networks"), NULL },
- { LOPT_LOOP_DETECT, OPT_LOOP_DETECT, NULL, gettext_noop("Detect and remove DNS forwarding loops"), NULL },
+ { LOPT_LOCAL_SERVICE, OPT_LOCAL_SERVICE, NULL, gettext_noop("Accept queries only from directly-connected networks."), NULL },
+ { LOPT_LOOP_DETECT, OPT_LOOP_DETECT, NULL, gettext_noop("Detect and remove DNS forwarding loops."), NULL },
{ LOPT_IGNORE_ADDR, ARG_DUP, "<ipaddr>", gettext_noop("Ignore DNS responses containing ipaddr."), NULL },
+ { LOPT_DHCPTTL, ARG_ONE, "<ttl>", gettext_noop("Set TTL in DNS responses with DHCP-derived addresses."), NULL },
{ 0, 0, NULL, NULL, NULL }
};
@@ -2580,6 +2583,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
case LOPT_MINCTTL: /* --min-cache-ttl */
case LOPT_MAXCTTL: /* --max-cache-ttl */
case LOPT_AUTHTTL: /* --auth-ttl */
+ case LOPT_DHCPTTL: /* --dhcp-ttl */
{
int ttl;
if (!atoi_check(arg, &ttl))
@@ -2598,6 +2602,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
daemon->max_cache_ttl = (unsigned long)ttl;
else if (option == LOPT_AUTHTTL)
daemon->auth_ttl = (unsigned long)ttl;
+ else if (option == LOPT_DHCPTTL)
+ {
+ daemon->dhcp_ttl = (unsigned long)ttl;
+ daemon->use_dhcp_ttl = 1;
+ }
else
daemon->local_ttl = (unsigned long)ttl;
break;
diff --git a/src/rfc1035.c b/src/rfc1035.c
index 3535a71..8f1e3b4 100644
--- a/src/rfc1035.c
+++ b/src/rfc1035.c
@@ -1170,7 +1170,7 @@ static unsigned long crec_ttl(struct crec *crecp, time_t now)
before the lease expires. */
if (crecp->flags & F_DHCP)
- return daemon->local_ttl;
+ return daemon->use_dhcp_ttl ? daemon->dhcp_ttl : daemon->local_ttl;
/* Immortal entries other than DHCP are local, and hold TTL in TTD field. */
if (crecp->flags & F_IMMORTAL)
--
1.7.10.4

View File

@@ -1,271 +0,0 @@
From a7369bef8abd241c3d85633fa9c870943f091e76 Mon Sep 17 00:00:00 2001
From: Ed Bardsley <ebardsley@google.com>
Date: Wed, 5 Aug 2015 21:17:18 +0100
Subject: [PATCH] Enhance --add-subnet to allow arbitary subnet addresses.
---
CHANGELOG | 4 ++++
man/dnsmasq.8 | 32 ++++++++++++++++++++-----------
src/dnsmasq.h | 13 ++++++++++---
src/option.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++-----
src/rfc1035.c | 39 +++++++++++++++++++++++++++++++-------
5 files changed, 121 insertions(+), 26 deletions(-)
diff --git a/CHANGELOG b/CHANGELOG
index 3f4026d..bbc2834 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -4,6 +4,10 @@ version 2.76
least, 0.0.0.0 accesses the local host, so could
be targets for DNS rebinding. See RFC 5735 section 3
for details. Thanks to Stephen Röttger for the bug report.
+
+ Enhance --add-subnet to allow arbitrary subnet addresses.
+ Thanks to Ed Barsley for the patch.
+
version 2.75
Fix reversion on 2.74 which caused 100% CPU use when a
diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
index c8913b5..a23c898 100644
--- a/man/dnsmasq.8
+++ b/man/dnsmasq.8
@@ -604,17 +604,27 @@ experimental. Also note that exposing MAC addresses in this way may
have security and privacy implications. The warning about caching
given for --add-subnet applies to --add-mac too.
.TP
-.B --add-subnet[[=<IPv4 prefix length>],<IPv6 prefix length>]
-Add the subnet address of the requestor to the DNS queries which are
-forwarded upstream. The amount of the address forwarded depends on the
-prefix length parameter: 32 (128 for IPv6) forwards the whole address,
-zero forwards none of it but still marks the request so that no
-upstream nameserver will add client address information either. The
-default is zero for both IPv4 and IPv6. Note that upstream nameservers
-may be configured to return different results based on this
-information, but the dnsmasq cache does not take account. If a dnsmasq
-instance is configured such that different results may be encountered,
-caching should be disabled.
+.B --add-subnet[[=[<IPv4 address>/]<IPv4 prefix length>][,[<IPv6 address>/]<IPv6 prefix length>]]
+Add a subnet address to the DNS queries which are forwarded
+upstream. If an address is specified in the flag, it will be used,
+otherwise, the address of the requestor will be used. The amount of
+the address forwarded depends on the prefix length parameter: 32 (128
+for IPv6) forwards the whole address, zero forwards none of it but
+still marks the request so that no upstream nameserver will add client
+address information either. The default is zero for both IPv4 and
+IPv6. Note that upstream nameservers may be configured to return
+different results based on this information, but the dnsmasq cache
+does not take account. If a dnsmasq instance is configured such that
+different results may be encountered, caching should be disabled.
+
+For example,
+.B --add-subnet=24,96
+will add the /24 and /96 subnets of the requestor for IPv4 and IPv6 requestors, respectively.
+.B --add-subnet=1.2.3.4/24
+will add 1.2.3.0/24 for IPv4 requestors and ::/0 for IPv6 requestors.
+.B --add-subnet=1.2.3.4/24,1.2.3.4/24
+will add 1.2.3.0/24 for both IPv4 and IPv6 requestors.
+
.TP
.B \-c, --cache-size=<cachesize>
Set the size of dnsmasq's cache. The default is 150 names. Setting the cache size to zero disables caching.
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index cf1a782..f42acdb 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -541,6 +541,13 @@ struct iname {
struct iname *next;
};
+/* subnet parameters from command line */
+struct mysubnet {
+ union mysockaddr addr;
+ int addr_used;
+ int mask;
+};
+
/* resolv-file parms from command-line */
struct resolvc {
struct resolvc *next;
@@ -935,9 +942,9 @@ extern struct daemon {
struct auth_zone *auth_zones;
struct interface_name *int_names;
char *mxtarget;
- int addr4_netmask;
- int addr6_netmask;
- char *lease_file;
+ struct mysubnet *add_subnet4;
+ struct mysubnet *add_subnet6;
+ char *lease_file;
char *username, *groupname, *scriptuser;
char *luascript;
char *authserver, *hostmaster;
diff --git a/src/option.c b/src/option.c
index ecc2619..746cd11 100644
--- a/src/option.c
+++ b/src/option.c
@@ -445,7 +445,7 @@ static struct {
{ LOPT_PXE_SERV, ARG_DUP, "<service>", gettext_noop("Boot service for PXE menu."), NULL },
{ LOPT_TEST, 0, NULL, gettext_noop("Check configuration syntax."), NULL },
{ LOPT_ADD_MAC, OPT_ADD_MAC, NULL, gettext_noop("Add requestor's MAC address to forwarded DNS queries."), NULL },
- { LOPT_ADD_SBNET, ARG_ONE, "<v4 pref>[,<v6 pref>]", gettext_noop("Add requestor's IP subnet to forwarded DNS queries."), NULL },
+ { LOPT_ADD_SBNET, ARG_ONE, "<v4 pref>[,<v6 pref>]", gettext_noop("Add specified IP subnet to forwarded DNS queries."), NULL },
{ LOPT_DNSSEC, OPT_DNSSEC_PROXY, NULL, gettext_noop("Proxy DNSSEC validation results from upstream nameservers."), NULL },
{ LOPT_INCR_ADDR, OPT_CONSEC_ADDR, NULL, gettext_noop("Attempt to allocate sequential IP addresses to DHCP clients."), NULL },
{ LOPT_CONNTRACK, OPT_CONNTRACK, NULL, gettext_noop("Copy connection-track mark from queries to upstream connections."), NULL },
@@ -722,6 +722,20 @@ static void do_usage(void)
#define ret_err(x) do { strcpy(errstr, (x)); return 0; } while (0)
+static char *parse_mysockaddr(char *arg, union mysockaddr *addr)
+{
+ if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0)
+ addr->sa.sa_family = AF_INET;
+#ifdef HAVE_IPV6
+ else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)
+ addr->sa.sa_family = AF_INET6;
+#endif
+ else
+ return _("bad address");
+
+ return NULL;
+}
+
char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_addr, char *interface, int *flags)
{
int source_port = 0, serv_port = NAMESERVER_PORT;
@@ -1585,7 +1599,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
li = match_suffix->next;
free(match_suffix->suffix);
free(match_suffix);
- }
+ }
break;
}
@@ -1593,10 +1607,45 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
set_option_bool(OPT_CLIENT_SUBNET);
if (arg)
{
+ char *err, *end;
comma = split(arg);
- if (!atoi_check(arg, &daemon->addr4_netmask) ||
- (comma && !atoi_check(comma, &daemon->addr6_netmask)))
- ret_err(gen_err);
+
+ struct mysubnet* new = opt_malloc(sizeof(struct mysubnet));
+ if ((end = split_chr(arg, '/')))
+ {
+ /* has subnet+len */
+ err = parse_mysockaddr(arg, &new->addr);
+ if (err)
+ ret_err(err);
+ if (!atoi_check(end, &new->mask))
+ ret_err(gen_err);
+ new->addr_used = 1;
+ }
+ else if (!atoi_check(arg, &new->mask))
+ ret_err(gen_err);
+
+ daemon->add_subnet4 = new;
+
+ new = opt_malloc(sizeof(struct mysubnet));
+ if (comma)
+ {
+ if ((end = split_chr(comma, '/')))
+ {
+ /* has subnet+len */
+ err = parse_mysockaddr(comma, &new->addr);
+ if (err)
+ ret_err(err);
+ if (!atoi_check(end, &new->mask))
+ ret_err(gen_err);
+ new->addr_used = 1;
+ }
+ else
+ {
+ if (!atoi_check(comma, &new->mask))
+ ret_err(gen_err);
+ }
+ }
+ daemon->add_subnet6 = new;
}
break;
diff --git a/src/rfc1035.c b/src/rfc1035.c
index 29e9e65..6a51b30 100644
--- a/src/rfc1035.c
+++ b/src/rfc1035.c
@@ -629,26 +629,47 @@ struct subnet_opt {
#endif
};
+static void *get_addrp(union mysockaddr *addr, const short family)
+{
+#ifdef HAVE_IPV6
+ if (family == AF_INET6)
+ return &addr->in6.sin6_addr;
+#endif
+
+ return &addr->in.sin_addr;
+}
+
static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source)
{
/* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */
int len;
void *addrp;
+ int sa_family = source->sa.sa_family;
#ifdef HAVE_IPV6
if (source->sa.sa_family == AF_INET6)
{
- opt->family = htons(2);
- opt->source_netmask = daemon->addr6_netmask;
- addrp = &source->in6.sin6_addr;
+ opt->source_netmask = daemon->add_subnet6->mask;
+ if (daemon->add_subnet6->addr_used)
+ {
+ sa_family = daemon->add_subnet6->addr.sa.sa_family;
+ addrp = get_addrp(&daemon->add_subnet6->addr, sa_family);
+ }
+ else
+ addrp = &source->in6.sin6_addr;
}
else
#endif
{
- opt->family = htons(1);
- opt->source_netmask = daemon->addr4_netmask;
- addrp = &source->in.sin_addr;
+ opt->source_netmask = daemon->add_subnet4->mask;
+ if (daemon->add_subnet4->addr_used)
+ {
+ sa_family = daemon->add_subnet4->addr.sa.sa_family;
+ addrp = get_addrp(&daemon->add_subnet4->addr, sa_family);
+ }
+ else
+ addrp = &source->in.sin_addr;
}
opt->scope_netmask = 0;
@@ -656,6 +677,11 @@ static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source)
if (opt->source_netmask != 0)
{
+#ifdef HAVE_IPV6
+ opt->family = htons(sa_family == AF_INET6 ? 2 : 1);
+#else
+ opt->family = htons(1);
+#endif
len = ((opt->source_netmask - 1) >> 3) + 1;
memcpy(opt->addr, addrp, len);
if (opt->source_netmask & 7)
@@ -2335,4 +2361,3 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
return len;
}
-
--
1.7.10.4

View File

@@ -0,0 +1,17 @@
X-Git-Url: http://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=blobdiff_plain;f=CHANGELOG;h=6d9ba490488f80ef565f459cef3c110bdf31212c;hp=14354f2506a7fbf8360cd32c96e1d7ce1bfeb3f9;hb=e06e6e34bffd781b7cefa49b25fb8ae863654ca2;hpb=832e47beab95c2918b5264f0504f2fe6fe523e4c
diff --git a/CHANGELOG b/CHANGELOG
index 14354f2..6d9ba49 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -48,6 +48,10 @@ version 2.76
(ie xx::0 to xx::ffff:ffff:ffff:ffff)
Thanks to Laurent Bendel for spotting this problem.
+ Add support for a TTL parameter in --host-record and
+ --cname.
+
+ Add --dhcp-ttl option.
version 2.75
Fix reversion on 2.74 which caused 100% CPU use when a

View File

@@ -1,34 +0,0 @@
From 3a3965ac21b1b759eab8799b6edb09195b671306 Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Sun, 9 Aug 2015 17:45:06 +0100
Subject: [PATCH] Don't answer non-auth queries for auth zones locally when
--localise-queries set.
---
src/forward.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/forward.c b/src/forward.c
index 2731b90..b76a974 100644
--- a/src/forward.c
+++ b/src/forward.c
@@ -1365,7 +1365,7 @@ void receive_query(struct listener *listen, time_t now)
#ifdef HAVE_AUTH
/* find queries for zones we're authoritative for, and answer them directly */
- if (!auth_dns)
+ if (!auth_dns && !option_bool(OPT_LOCALISE))
for (zone = daemon->auth_zones; zone; zone = zone->next)
if (in_zone(zone, daemon->namebuff, NULL))
{
@@ -1904,7 +1904,7 @@ unsigned char *tcp_request(int confd, time_t now,
#ifdef HAVE_AUTH
/* find queries for zones we're authoritative for, and answer them directly */
- if (!auth_dns)
+ if (!auth_dns && !option_bool(OPT_LOCALISE))
for (zone = daemon->auth_zones; zone; zone = zone->next)
if (in_zone(zone, daemon->namebuff, NULL))
{
--
1.7.10.4

View File

@@ -0,0 +1,136 @@
From bec366b4041df72b559e713f1c924177676e6eb0 Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Wed, 24 Feb 2016 22:03:26 +0000
Subject: [PATCH] Add --tftp-mtu option.
---
CHANGELOG | 4 ++++
man/dnsmasq.8 | 4 ++++
src/dnsmasq.h | 2 +-
src/option.c | 10 +++++++++-
src/tftp.c | 14 ++++++++++++--
5 files changed, 30 insertions(+), 4 deletions(-)
diff --git a/CHANGELOG b/CHANGELOG
index 6d9ba49..9218b8c 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -53,6 +53,10 @@ version 2.76
Add --dhcp-ttl option.
+ Add --tftp-mtu option. Thanks to Patrick McLean for the
+ initial patch.
+
+
version 2.75
Fix reversion on 2.74 which caused 100% CPU use when a
dhcp-script is configured. Thanks to Adrian Davey for
diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
index 2bcce20..3cf48cd 100644
--- a/man/dnsmasq.8
+++ b/man/dnsmasq.8
@@ -1810,6 +1810,10 @@ require about (2*n) + 10 descriptors. If
.B --tftp-port-range
is given, that can affect the number of concurrent connections.
.TP
+.B --tftp-mtu=<mtu size>
+Use size as the ceiling of the MTU supported by the intervening network when
+negotiating TFTP blocksize, overriding the MTU setting of the local interface if it is larger.
+.TP
.B --tftp-no-blocksize
Stop the TFTP server from negotiating the "blocksize" option with a
client. Some buggy clients request this option but then behave badly
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index 9f73c3b..280ad9d 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -975,7 +975,7 @@ extern struct daemon {
struct dhcp_netid_list *dhcp_ignore, *dhcp_ignore_names, *dhcp_gen_names;
struct dhcp_netid_list *force_broadcast, *bootp_dynamic;
struct hostsfile *dhcp_hosts_file, *dhcp_opts_file, *dynamic_dirs;
- int dhcp_max, tftp_max;
+ int dhcp_max, tftp_max, tftp_mtu;
int dhcp_server_port, dhcp_client_port;
int start_tftp_port, end_tftp_port;
unsigned int min_leasetime;
diff --git a/src/option.c b/src/option.c
index 3f6d162..765965f 100644
--- a/src/option.c
+++ b/src/option.c
@@ -158,7 +158,8 @@ struct myoption {
#define LOPT_CPE_ID 346
#define LOPT_SCRIPT_ARP 347
#define LOPT_DHCPTTL 348
-
+#define LOPT_TFTP_MTU 349
+
#ifdef HAVE_GETOPT_LONG
static const struct option opts[] =
#else
@@ -244,6 +245,7 @@ static const struct myoption opts[] =
{ "tftp-unique-root", 0, 0, LOPT_APREF },
{ "tftp-root", 1, 0, LOPT_PREFIX },
{ "tftp-max", 1, 0, LOPT_TFTP_MAX },
+ { "tftp-mtu", 1, 0, LOPT_TFTP_MTU },
{ "tftp-lowercase", 0, 0, LOPT_TFTP_LC },
{ "ptr-record", 1, 0, LOPT_PTR },
{ "naptr-record", 1, 0, LOPT_NAPTR },
@@ -432,6 +434,7 @@ static struct {
{ LOPT_SECURE, OPT_TFTP_SECURE, NULL, gettext_noop("Allow access only to files owned by the user running dnsmasq."), NULL },
{ LOPT_TFTP_NO_FAIL, OPT_TFTP_NO_FAIL, NULL, gettext_noop("Do not terminate the service if TFTP directories are inaccessible."), NULL },
{ LOPT_TFTP_MAX, ARG_ONE, "<integer>", gettext_noop("Maximum number of conncurrent TFTP transfers (defaults to %s)."), "#" },
+ { LOPT_TFTP_MTU, ARG_ONE, "<integer>", gettext_noop("Maximum MTU to use for TFTP transfers."), NULL },
{ LOPT_NOBLOCK, OPT_TFTP_NOBLOCK, NULL, gettext_noop("Disable the TFTP blocksize extension."), NULL },
{ LOPT_TFTP_LC, OPT_TFTP_LC, NULL, gettext_noop("Convert TFTP filenames to lowercase"), NULL },
{ LOPT_TFTPPORTS, ARG_ONE, "<start>,<end>", gettext_noop("Ephemeral port range for use by TFTP transfers."), NULL },
@@ -2625,6 +2628,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
ret_err(gen_err);
break;
+ case LOPT_TFTP_MTU: /* --tftp-mtu */
+ if (!atoi_check(arg, &daemon->tftp_mtu))
+ ret_err(gen_err);
+ break;
+
case LOPT_PREFIX: /* --tftp-prefix */
comma = split(arg);
if (comma)
diff --git a/src/tftp.c b/src/tftp.c
index 00ed2fc..dc4aa85 100644
--- a/src/tftp.c
+++ b/src/tftp.c
@@ -103,8 +103,10 @@ void tftp_request(struct listener *listen, time_t now)
if (listen->iface)
{
addr = listen->iface->addr;
- mtu = listen->iface->mtu;
name = listen->iface->name;
+ mtu = listen->iface->mtu;
+ if (daemon->tftp_mtu != 0 && daemon->tftp_mtu < mtu)
+ mtu = daemon->tftp_mtu;
}
else
{
@@ -234,9 +236,17 @@ void tftp_request(struct listener *listen, time_t now)
strncpy(ifr.ifr_name, name, IF_NAMESIZE);
if (ioctl(listen->tftpfd, SIOCGIFMTU, &ifr) != -1)
- mtu = ifr.ifr_mtu;
+ {
+ mtu = ifr.ifr_mtu;
+ if (daemon->tftp_mtu != 0 && daemon->tftp_mtu < mtu)
+ mtu = daemon->tftp_mtu;
+ }
}
+ /* Failed to get interface mtu - can use configured value. */
+ if (mtu == 0)
+ mtu = daemon->tftp_mtu;
+
if (name)
{
/* check for per-interface prefix */
--
1.7.10.4

View File

@@ -1,38 +0,0 @@
From 5e3e464ac4022ee0b3794513abe510817e2cf3ca Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Tue, 25 Aug 2015 23:08:39 +0100
Subject: [PATCH] Fix behaviour of empty dhcp-option=option6:dns-server, which
should inhibit sending option.
---
src/rfc3315.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/src/rfc3315.c b/src/rfc3315.c
index 2665d0d..3f1f9ee 100644
--- a/src/rfc3315.c
+++ b/src/rfc3315.c
@@ -1320,15 +1320,16 @@ static struct dhcp_netid *add_options(struct state *state, int do_refresh)
if (opt_cfg->opt == OPTION6_REFRESH_TIME)
done_refresh = 1;
+
+ if (opt_cfg->opt == OPTION6_DNS_SERVER)
+ done_dns = 1;
- if (opt_cfg->flags & DHOPT_ADDR6)
+ /* Empty DNS_SERVER option will not set DHOPT_ADDR6 */
+ if ((opt_cfg->flags & DHOPT_ADDR6) || opt_cfg->opt == OPTION6_DNS_SERVER)
{
int len, j;
struct in6_addr *a;
- if (opt_cfg->opt == OPTION6_DNS_SERVER)
- done_dns = 1;
-
for (a = (struct in6_addr *)opt_cfg->val, len = opt_cfg->len, j = 0;
j < opt_cfg->len; j += IN6ADDRSZ, a++)
if ((IN6_IS_ADDR_ULA_ZERO(a) && IN6_IS_ADDR_UNSPECIFIED(state->ula_addr)) ||
--
1.7.10.4

View File

@@ -1,50 +0,0 @@
From 9cdcfe9f19ffd45bac4e5b459879bf7c50a287ed Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Wed, 26 Aug 2015 22:38:08 +0100
Subject: [PATCH] Suggest solution to ENOMEM error with IPv6 multicast.
---
src/network.c | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/src/network.c b/src/network.c
index a1d90c8..819302f 100644
--- a/src/network.c
+++ b/src/network.c
@@ -1076,23 +1076,30 @@ void join_multicast(int dienow)
if ((daemon->doing_dhcp6 || daemon->relay6) &&
setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
- err = 1;
+ err = errno;
inet_pton(AF_INET6, ALL_SERVERS, &mreq.ipv6mr_multiaddr);
if (daemon->doing_dhcp6 &&
setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
- err = 1;
+ err = errno;
inet_pton(AF_INET6, ALL_ROUTERS, &mreq.ipv6mr_multiaddr);
if (daemon->doing_ra &&
setsockopt(daemon->icmp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
- err = 1;
+ err = errno;
if (err)
{
char *s = _("interface %s failed to join DHCPv6 multicast group: %s");
+ errno = err;
+
+#ifdef HAVE_LINUX_NETWORK
+ if (errno == ENOMEM)
+ my_syslog(LOG_ERR, _("try increasing /proc/sys/net/core/optmem_max"));
+#endif
+
if (dienow)
die(s, iface->name, EC_BADNET);
else
--
1.7.10.4

View File

@@ -1,35 +0,0 @@
From 20fd11e11a9d09edcea94de135396ae1541fbbab Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Wed, 26 Aug 2015 22:48:13 +0100
Subject: [PATCH] Clarify man page on RDNSS set in router advertisement.
---
man/dnsmasq.8 | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
index a23c898..d51b10f 100644
--- a/man/dnsmasq.8
+++ b/man/dnsmasq.8
@@ -1687,15 +1687,15 @@ creation are handled by a different protocol. When DHCP is in use,
only a subset of this is needed, and dnsmasq can handle it, using
existing DHCP configuration to provide most data. When RA is enabled,
dnsmasq will advertise a prefix for each dhcp-range, with default
-router and recursive DNS server as the relevant link-local address on
-the machine running dnsmasq. By default, he "managed address" bits are set, and
+router as the relevant link-local address on
+the machine running dnsmasq. By default, the "managed address" bits are set, and
the "use SLAAC" bit is reset. This can be changed for individual
subnets with the mode keywords described in
.B --dhcp-range.
RFC6106 DNS parameters are included in the advertisements. By default,
the relevant link-local address of the machine running dnsmasq is sent
as recursive DNS server. If provided, the DHCPv6 options dns-server and
-domain-search are used for RDNSS and DNSSL.
+domain-search are used for the DNS server (RDNSS) and the domain serach list (DNSSL).
.TP
.B --ra-param=<interface>,[high|low],[[<ra-interval>],<router lifetime>]
Set non-default values for router advertisements sent via an
--
1.7.10.4

View File

@@ -1,30 +0,0 @@
From 6de81f1250fd323c9155de065d5a9dc200a6f20b Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Wed, 9 Sep 2015 22:51:13 +0100
Subject: [PATCH] Handle signed dangling CNAME replies to DS queries.
---
src/dnssec.c | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/src/dnssec.c b/src/dnssec.c
index 4deda24..67ce486 100644
--- a/src/dnssec.c
+++ b/src/dnssec.c
@@ -1232,11 +1232,8 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
/* If we return STAT_NO_SIG, name contains the name of the DS query */
if (val == STAT_NO_SIG)
- {
- *keyname = 0;
- return val;
- }
-
+ return val;
+
/* If the key needed to validate the DS is on the same domain as the DS, we'll
loop getting nowhere. Stop that now. This can happen of the DS answer comes
from the DS's zone, and not the parent zone. */
--
1.7.10.4

View File

@@ -1,25 +0,0 @@
From 102208df695e886a3086754d32bf7f8c541fbe46 Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Thu, 10 Sep 2015 21:50:00 +0100
Subject: [PATCH] DHCPv6 option 56 does not hold an address list. (RFC 5908).
---
src/dhcp-common.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/dhcp-common.c b/src/dhcp-common.c
index bc48f41..8fc171a 100644
--- a/src/dhcp-common.c
+++ b/src/dhcp-common.c
@@ -599,7 +599,7 @@ static const struct opttab_t opttab6[] = {
{ "sntp-server", 31, OT_ADDR_LIST },
{ "information-refresh-time", 32, OT_TIME },
{ "FQDN", 39, OT_INTERNAL | OT_RFC1035_NAME },
- { "ntp-server", 56, OT_ADDR_LIST },
+ { "ntp-server", 56, 0 },
{ "bootfile-url", 59, OT_NAME },
{ "bootfile-param", 60, OT_CSTRING },
{ NULL, 0, 0 }
--
1.7.10.4

View File

@@ -1,47 +0,0 @@
From 77607cbea0ad0f876dfb79c8b2c121ee400d57d0 Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Thu, 10 Sep 2015 23:08:43 +0100
Subject: [PATCH] Respect the --no-resolv flag in inotify code.
---
CHANGELOG | 7 ++++++-
debian/changelog | 6 ++++++
src/inotify.c | 3 +++
3 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG b/CHANGELOG
index bbc2834..d6e309f 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -7,8 +7,13 @@ version 2.76
Enhance --add-subnet to allow arbitrary subnet addresses.
Thanks to Ed Barsley for the patch.
+
+ Respect the --no-resolv flag in inotify code. Fixes bug
+ which caused dnsmasq to fail to start if a resolv-file
+ was a dangling symbolic link, even of --no-resolv set.
+ Thanks to Alexander Kurtz for spotting the problem.
+
-
version 2.75
Fix reversion on 2.74 which caused 100% CPU use when a
dhcp-script is configured. Thanks to Adrian Davey for
diff --git a/src/inotify.c b/src/inotify.c
index 52d412f..ef05c58 100644
--- a/src/inotify.c
+++ b/src/inotify.c
@@ -90,6 +90,9 @@ void inotify_dnsmasq_init()
if (daemon->inotifyfd == -1)
die(_("failed to create inotify: %s"), NULL, EC_MISC);
+
+ if (option_bool(OPT_NO_RESOLV))
+ return;
for (res = daemon->resolv_files; res; res = res->next)
{
--
1.7.10.4

View File

@@ -1,26 +0,0 @@
From 27b78d990b7cd901866ad6f1a17b9d633a95fdce Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Sat, 26 Sep 2015 21:40:45 +0100
Subject: [PATCH] Rationalise 5e3e464ac4022ee0b3794513abe510817e2cf3ca
---
src/rfc3315.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/src/rfc3315.c b/src/rfc3315.c
index 3f1f9ee..3ed8623 100644
--- a/src/rfc3315.c
+++ b/src/rfc3315.c
@@ -1324,8 +1324,7 @@ static struct dhcp_netid *add_options(struct state *state, int do_refresh)
if (opt_cfg->opt == OPTION6_DNS_SERVER)
done_dns = 1;
- /* Empty DNS_SERVER option will not set DHOPT_ADDR6 */
- if ((opt_cfg->flags & DHOPT_ADDR6) || opt_cfg->opt == OPTION6_DNS_SERVER)
+ if (opt_cfg->flags & DHOPT_ADDR6)
{
int len, j;
struct in6_addr *a;
--
1.7.10.4

View File

@@ -1,32 +0,0 @@
From 98079ea89851da1df4966dfdfa1852a98da02912 Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Tue, 13 Oct 2015 20:30:32 +0100
Subject: [PATCH] Catch errors from sendmsg in DHCP code. Logs, eg, iptables
DROPS of dest 255.255.255.255
---
src/dhcp.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/dhcp.c b/src/dhcp.c
index e6fceb1..1c85e42 100644
--- a/src/dhcp.c
+++ b/src/dhcp.c
@@ -452,8 +452,13 @@ void dhcp_packet(time_t now, int pxe_fd)
#endif
while(retry_send(sendmsg(fd, &msg, 0)));
+
+ /* This can fail when, eg, iptables DROPS destination 255.255.255.255 */
+ if (errno != 0)
+ my_syslog(MS_DHCP | LOG_WARNING, _("Error sending DHCP packet to %s: %s"),
+ inet_ntoa(dest.sin_addr), strerror(errno));
}
-
+
/* check against secondary interface addresses */
static int check_listen_addrs(struct in_addr local, int if_index, char *label,
struct in_addr netmask, struct in_addr broadcast, void *vparam)
--
1.7.10.4

View File

@@ -1,48 +0,0 @@
From 90477fb79420a34124b66ebd808c578817a30e4c Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Tue, 20 Oct 2015 21:21:32 +0100
Subject: [PATCH] Update list of subnet for --bogus-priv
RFC6303 specifies & recommends following zones not be forwarded
to globally facing servers.
+------------------------------+-----------------------+
| Zone | Description |
+------------------------------+-----------------------+
| 0.IN-ADDR.ARPA | IPv4 "THIS" NETWORK |
| 127.IN-ADDR.ARPA | IPv4 Loopback NETWORK |
| 254.169.IN-ADDR.ARPA | IPv4 LINK LOCAL |
| 2.0.192.IN-ADDR.ARPA | IPv4 TEST-NET-1 |
| 100.51.198.IN-ADDR.ARPA | IPv4 TEST-NET-2 |
| 113.0.203.IN-ADDR.ARPA | IPv4 TEST-NET-3 |
| 255.255.255.255.IN-ADDR.ARPA | IPv4 BROADCAST |
+------------------------------+-----------------------+
Signed-off-by: Kevin Darbyshire-Bryant <kevin@darbyshire-bryant.me.uk>
---
src/rfc1035.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/src/rfc1035.c b/src/rfc1035.c
index 6a51b30..4eb1772 100644
--- a/src/rfc1035.c
+++ b/src/rfc1035.c
@@ -756,10 +756,14 @@ int private_net(struct in_addr addr, int ban_localhost)
return
(((ip_addr & 0xFF000000) == 0x7F000000) && ban_localhost) /* 127.0.0.0/8 (loopback) */ ||
((ip_addr & 0xFF000000) == 0x00000000) /* RFC 5735 section 3. "here" network */ ||
- ((ip_addr & 0xFFFF0000) == 0xC0A80000) /* 192.168.0.0/16 (private) */ ||
((ip_addr & 0xFF000000) == 0x0A000000) /* 10.0.0.0/8 (private) */ ||
((ip_addr & 0xFFF00000) == 0xAC100000) /* 172.16.0.0/12 (private) */ ||
- ((ip_addr & 0xFFFF0000) == 0xA9FE0000) /* 169.254.0.0/16 (zeroconf) */ ;
+ ((ip_addr & 0xFFFF0000) == 0xC0A80000) /* 192.168.0.0/16 (private) */ ||
+ ((ip_addr & 0xFFFF0000) == 0xA9FE0000) /* 169.254.0.0/16 (zeroconf) */ ||
+ ((ip_addr & 0xFFFFFF00) == 0xC0000200) /* 192.0.2.0/24 (test-net) */ ||
+ ((ip_addr & 0xFFFFFF00) == 0xC6336400) /* 198.51.100.0/24(test-net) */ ||
+ ((ip_addr & 0xFFFFFF00) == 0xCB007100) /* 203.0.113.0/24 (test-net) */ ||
+ ((ip_addr & 0xFFFFFFFF) == 0xFFFFFFFF) /* 255.255.255.255/32 (broadcast)*/ ;
}
static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *header, size_t qlen, char *name, int *doctored)
--
1.7.10.4

View File

@@ -1,43 +0,0 @@
From 41a8d9e99be9f2cc8b02051dd322cb45e0faac87 Mon Sep 17 00:00:00 2001
From: =?utf8?q?Edwin=20T=C3=B6r=C3=B6k?= <edwin+ml-cerowrt@etorok.net>
Date: Sat, 14 Nov 2015 17:45:48 +0000
Subject: [PATCH] Fix crash when empty address from DNS overlays A record from
hosts.
---
CHANGELOG | 5 +++++
src/cache.c | 2 +-
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG b/CHANGELOG
index d6e309f..93c73d0 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -13,6 +13,11 @@ version 2.76
was a dangling symbolic link, even of --no-resolv set.
Thanks to Alexander Kurtz for spotting the problem.
+ Fix crash when an A or AAAA record is defined locally,
+ in a hosts file, and an upstream server sends a reply
+ that the same name is empty. Thanks to Edwin Török for
+ the patch.
+
version 2.75
Fix reversion on 2.74 which caused 100% CPU use when a
diff --git a/src/cache.c b/src/cache.c
index 178d654..1b76b67 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -481,7 +481,7 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
existing record is for an A or AAAA and
the record we're trying to insert is the same,
just drop the insert, but don't error the whole process. */
- if ((flags & (F_IPV4 | F_IPV6)) && (flags & F_FORWARD))
+ if ((flags & (F_IPV4 | F_IPV6)) && (flags & F_FORWARD) && addr)
{
if ((flags & F_IPV4) && (new->flags & F_IPV4) &&
new->addr.addr.addr.addr4.s_addr == addr->addr.addr4.s_addr)
--
1.7.10.4

View File

@@ -1,39 +0,0 @@
From 67ab3285b5d9a1b1e20e034cf272867fdab8a0f9 Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Fri, 20 Nov 2015 23:20:47 +0000
Subject: [PATCH] Handle unknown DS hash algos correctly.
When we can validate a DS RRset, but don't speak the hash algo it
contains, treat that the same as an NSEC/3 proving that the DS
doesn't exist. 4025 5.2
---
src/dnssec.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/src/dnssec.c b/src/dnssec.c
index 67ce486..b4dc14e 100644
--- a/src/dnssec.c
+++ b/src/dnssec.c
@@ -1005,6 +1005,19 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
if (crecp->flags & F_NEG)
return STAT_INSECURE_DS;
+ /* 4035 5.2
+ If the validator does not support any of the algorithms listed in an
+ authenticated DS RRset, then the resolver has no supported
+ authentication path leading from the parent to the child. The
+ resolver should treat this case as it would the case of an
+ authenticated NSEC RRset proving that no DS RRset exists, */
+ for (recp1 = crecp; recp1; recp1 = cache_find_by_name(recp1, name, now, F_DS))
+ if (hash_find(ds_digest_name(recp1->addr.ds.digest)))
+ break;
+
+ if (!recp1)
+ return STAT_INSECURE_DS;
+
/* NOTE, we need to find ONE DNSKEY which matches the DS */
for (valid = 0, j = ntohs(header->ancount); j != 0 && !valid; j--)
{
--
1.7.10.4

View File

@@ -1,38 +0,0 @@
From 0007ee90646a5a78a96ee729932e89d31c69513a Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Sat, 21 Nov 2015 21:47:41 +0000
Subject: [PATCH] Fix crash at start up with conf-dir=/path,*
Thanks to Brian Carpenter and American Fuzzy Lop for finding the bug.
---
src/option.c | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/src/option.c b/src/option.c
index 746cd11..71beb98 100644
--- a/src/option.c
+++ b/src/option.c
@@ -1515,10 +1515,16 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
li = opt_malloc(sizeof(struct list));
if (*arg == '*')
{
- li->next = match_suffix;
- match_suffix = li;
- /* Have to copy: buffer is overwritten */
- li->suffix = opt_string_alloc(arg+1);
+ /* "*" with no suffix is a no-op */
+ if (arg[1] == 0)
+ free(li);
+ else
+ {
+ li->next = match_suffix;
+ match_suffix = li;
+ /* Have to copy: buffer is overwritten */
+ li->suffix = opt_string_alloc(arg+1);
+ }
}
else
{
--
1.7.10.4

View File

@@ -1,612 +0,0 @@
From 93be5b1e023b0c661e1ec2cd6d811a8ec9055c49 Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Tue, 15 Dec 2015 12:04:40 +0000
Subject: [PATCH] Abandon caching RRSIGs and returning them from cache.
The list of exceptions to being able to locally answer
cached data for validated records when DNSSEC data is requested
was getting too long, so don't ever do that. This means
that the cache no longer has to hold RRSIGS and allows
us to lose lots of code. Note that cached validated
answers are still returned as long as do=0
---
src/cache.c | 38 ++---------
src/dnsmasq.h | 10 +--
src/dnssec.c | 94 ++++-----------------------
src/rfc1035.c | 197 ++++++---------------------------------------------------
4 files changed, 42 insertions(+), 297 deletions(-)
diff --git a/src/cache.c b/src/cache.c
index 1b76b67..51ba7cc 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -189,12 +189,7 @@ static void cache_hash(struct crec *crecp)
static void cache_blockdata_free(struct crec *crecp)
{
if (crecp->flags & F_DNSKEY)
- {
- if (crecp->flags & F_DS)
- blockdata_free(crecp->addr.sig.keydata);
- else
- blockdata_free(crecp->addr.key.keydata);
- }
+ blockdata_free(crecp->addr.key.keydata);
else if ((crecp->flags & F_DS) && !(crecp->flags & F_NEG))
blockdata_free(crecp->addr.ds.keydata);
}
@@ -369,13 +364,8 @@ static struct crec *cache_scan_free(char *name, struct all_addr *addr, time_t no
}
#ifdef HAVE_DNSSEC
- /* Deletion has to be class-sensitive for DS, DNSKEY, RRSIG, also
- type-covered sensitive for RRSIG */
- if ((flags & (F_DNSKEY | F_DS)) &&
- (flags & (F_DNSKEY | F_DS)) == (crecp->flags & (F_DNSKEY | F_DS)) &&
- crecp->uid == addr->addr.dnssec.class &&
- (!((flags & (F_DS | F_DNSKEY)) == (F_DS | F_DNSKEY)) ||
- crecp->addr.sig.type_covered == addr->addr.dnssec.type))
+ /* Deletion has to be class-sensitive for DS and DNSKEY */
+ if ((flags & crecp->flags & (F_DNSKEY | F_DS)) && crecp->uid == addr->addr.dnssec.class)
{
if (crecp->flags & F_CONFIG)
return crecp;
@@ -532,13 +522,9 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
struct all_addr free_addr = new->addr.addr;;
#ifdef HAVE_DNSSEC
- /* For DNSSEC records, addr holds class and type_covered for RRSIG */
+ /* For DNSSEC records, addr holds class. */
if (new->flags & (F_DS | F_DNSKEY))
- {
- free_addr.addr.dnssec.class = new->uid;
- if ((new->flags & (F_DS | F_DNSKEY)) == (F_DS | F_DNSKEY))
- free_addr.addr.dnssec.type = new->addr.sig.type_covered;
- }
+ free_addr.addr.dnssec.class = new->uid;
#endif
free_avail = 1; /* Must be free space now. */
@@ -653,9 +639,6 @@ struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsi
if (!is_expired(now, crecp) && !is_outdated_cname_pointer(crecp))
{
if ((crecp->flags & F_FORWARD) &&
-#ifdef HAVE_DNSSEC
- (((crecp->flags & (F_DNSKEY | F_DS)) == (prot & (F_DNSKEY | F_DS))) || (prot & F_NSIGMATCH)) &&
-#endif
(crecp->flags & prot) &&
hostname_isequal(cache_get_name(crecp), name))
{
@@ -713,9 +696,6 @@ struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsi
if (ans &&
(ans->flags & F_FORWARD) &&
-#ifdef HAVE_DNSSEC
- (((ans->flags & (F_DNSKEY | F_DS)) == (prot & (F_DNSKEY | F_DS))) || (prot & F_NSIGMATCH)) &&
-#endif
(ans->flags & prot) &&
hostname_isequal(cache_get_name(ans), name))
return ans;
@@ -1472,11 +1452,7 @@ void dump_cache(time_t now)
#ifdef HAVE_DNSSEC
else if (cache->flags & F_DS)
{
- if (cache->flags & F_DNSKEY)
- /* 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))
+ if (!(cache->flags & F_NEG))
sprintf(a, "%5u %3u %3u", cache->addr.ds.keytag,
cache->addr.ds.algo, cache->addr.ds.digest);
}
@@ -1502,8 +1478,6 @@ void dump_cache(time_t now)
else if (cache->flags & F_CNAME)
t = "C";
#ifdef HAVE_DNSSEC
- else if ((cache->flags & (F_DS | F_DNSKEY)) == (F_DS | F_DNSKEY))
- t = "G"; /* DNSKEY and DS set -> RRISG */
else if (cache->flags & F_DS)
t = "S";
else if (cache->flags & F_DNSKEY)
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index 023a1cf..4344cae 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -398,14 +398,9 @@ struct crec {
unsigned char algo;
unsigned char digest;
} ds;
- struct {
- struct blockdata *keydata;
- unsigned short keylen, type_covered, keytag;
- char algo;
- } sig;
} addr;
time_t ttd; /* time to die */
- /* used as class if DNSKEY/DS/RRSIG, index to source for F_HOSTS */
+ /* used as class if DNSKEY/DS, index to source for F_HOSTS */
unsigned int uid;
unsigned short flags;
union {
@@ -445,8 +440,7 @@ struct crec {
#define F_SECSTAT (1u<<24)
#define F_NO_RR (1u<<25)
#define F_IPSET (1u<<26)
-#define F_NSIGMATCH (1u<<27)
-#define F_NOEXTRA (1u<<28)
+#define F_NOEXTRA (1u<<27)
/* Values of uid in crecs with F_CONFIG bit set. */
#define SRC_INTERFACE 0
diff --git a/src/dnssec.c b/src/dnssec.c
index de7b335..1ae03a6 100644
--- a/src/dnssec.c
+++ b/src/dnssec.c
@@ -1004,7 +1004,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
{
unsigned char *psave, *p = (unsigned char *)(header+1);
struct crec *crecp, *recp1;
- int rc, j, qtype, qclass, ttl, rdlen, flags, algo, valid, keytag, type_covered;
+ int rc, j, qtype, qclass, ttl, rdlen, flags, algo, valid, keytag;
struct blockdata *key;
struct all_addr a;
@@ -1115,7 +1115,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
if (valid)
{
- /* DNSKEY RRset determined to be OK, now cache it and the RRsigs that sign it. */
+ /* DNSKEY RRset determined to be OK, now cache it. */
cache_start_insert();
p = skip_questions(header, plen);
@@ -1155,7 +1155,10 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
if ((key = blockdata_alloc((char*)p, rdlen - 4)))
{
if (!(recp1 = cache_insert(name, &a, now, ttl, F_FORWARD | F_DNSKEY | F_DNSSECOK)))
- blockdata_free(key);
+ {
+ blockdata_free(key);
+ return STAT_BOGUS;
+ }
else
{
a.addr.keytag = keytag;
@@ -1169,38 +1172,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
}
}
}
- else if (qtype == T_RRSIG)
- {
- /* RRSIG, cache if covers DNSKEY RRset */
- if (rdlen < 18)
- return STAT_BOGUS; /* bad packet */
-
- GETSHORT(type_covered, p);
-
- if (type_covered == T_DNSKEY)
- {
- a.addr.dnssec.class = class;
- a.addr.dnssec.type = type_covered;
-
- algo = *p++;
- p += 13; /* labels, orig_ttl, expiration, inception */
- GETSHORT(keytag, p);
- if ((key = blockdata_alloc((char*)psave, rdlen)))
- {
- if (!(crecp = cache_insert(name, &a, now, ttl, F_FORWARD | F_DNSKEY | F_DS)))
- blockdata_free(key);
- else
- {
- crecp->addr.sig.keydata = key;
- crecp->addr.sig.keylen = rdlen;
- crecp->addr.sig.keytag = keytag;
- crecp->addr.sig.type_covered = type_covered;
- crecp->addr.sig.algo = algo;
- }
- }
- }
- }
-
+
p = psave;
}
@@ -1326,7 +1298,8 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
cache_start_insert();
a.addr.dnssec.class = class;
- cache_insert(name, &a, now, ttl, flags);
+ if (!cache_insert(name, &a, now, ttl, flags))
+ return STAT_BOGUS;
cache_end_insert();
@@ -2028,14 +2001,13 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
/* Not done, validate now */
if (j == i)
{
- int ttl, keytag, algo, digest, type_covered, sigcnt, rrcnt;
+ int ttl, keytag, algo, digest, sigcnt, rrcnt;
unsigned char *psave;
struct all_addr a;
struct blockdata *key;
struct crec *crecp;
char *wildname;
- int have_wildcard = 0;
-
+
if (!explore_rrset(header, plen, class1, type1, name, keyname, &sigcnt, &rrcnt))
return STAT_BOGUS;
@@ -2096,8 +2068,6 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
if (rc == STAT_SECURE_WILDCARD)
{
- have_wildcard = 1;
-
/* An attacker replay a wildcard answer with a different
answer and overlay a genuine RR. To prove this
hasn't happened, the answer must prove that
@@ -2119,7 +2089,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
return rc;
}
- /* Cache RRsigs in answer section, and if we just validated a DS RRset, cache it */
+ /* If we just validated a DS RRset, cache it */
/* Also note if the RRset is the answer to the question, or the target of a CNAME */
cache_start_insert();
@@ -2168,45 +2138,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
}
}
}
- else if (type2 == T_RRSIG)
- {
- if (rdlen2 < 18)
- return STAT_BOGUS; /* bad packet */
-
- GETSHORT(type_covered, p2);
-
- if (type_covered == type1 &&
- (type_covered == T_A || type_covered == T_AAAA ||
- type_covered == T_CNAME || type_covered == T_DS ||
- type_covered == T_DNSKEY || type_covered == T_PTR))
- {
- a.addr.dnssec.type = type_covered;
- a.addr.dnssec.class = class1;
-
- algo = *p2++;
- p2 += 13; /* labels, orig_ttl, expiration, inception */
- GETSHORT(keytag, p2);
-
- /* We don't cache sigs for wildcard answers, because to reproduce the
- answer from the cache will require one or more NSEC/NSEC3 records
- which we don't cache. The lack of the RRSIG ensures that a query for
- this RRset asking for a secure answer will always be forwarded. */
- if (!have_wildcard && (key = blockdata_alloc((char*)psave, rdlen2)))
- {
- if (!(crecp = cache_insert(name, &a, now, ttl, F_FORWARD | F_DNSKEY | F_DS)))
- blockdata_free(key);
- else
- {
- crecp->addr.sig.keydata = key;
- crecp->addr.sig.keylen = rdlen2;
- crecp->addr.sig.keytag = keytag;
- crecp->addr.sig.type_covered = type_covered;
- crecp->addr.sig.algo = algo;
- }
- }
- }
- }
-
+
p2 = psave;
}
diff --git a/src/rfc1035.c b/src/rfc1035.c
index 4eb1772..def8fa0 100644
--- a/src/rfc1035.c
+++ b/src/rfc1035.c
@@ -1275,11 +1275,9 @@ int check_for_local_domain(char *name, time_t now)
struct naptr *naptr;
/* Note: the call to cache_find_by_name is intended to find any record which matches
- ie A, AAAA, CNAME, DS. Because RRSIG records are marked by setting both F_DS and F_DNSKEY,
- cache_find_by name ordinarily only returns records with an exact match on those bits (ie
- for the call below, only DS records). The F_NSIGMATCH bit changes this behaviour */
+ ie A, AAAA, CNAME. */
- if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_CNAME | F_DS | F_NO_RR | F_NSIGMATCH)) &&
+ 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;
@@ -1566,9 +1564,11 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
GETSHORT(flags, pheader);
if ((sec_reqd = flags & 0x8000))
- *do_bit = 1;/* do bit */
+ {
+ *do_bit = 1;/* do bit */
+ *ad_reqd = 1;
+ }
- *ad_reqd = 1;
dryrun = 1;
}
@@ -1636,98 +1636,6 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
}
}
-#ifdef HAVE_DNSSEC
- if (option_bool(OPT_DNSSEC_VALID) && (qtype == T_DNSKEY || qtype == T_DS))
- {
- int gotone = 0;
- struct blockdata *keydata;
-
- /* Do we have RRSIG? Can't do DS or DNSKEY otherwise. */
- if (sec_reqd)
- {
- crecp = NULL;
- while ((crecp = cache_find_by_name(crecp, name, now, F_DNSKEY | F_DS)))
- if (crecp->uid == qclass && crecp->addr.sig.type_covered == qtype)
- break;
- }
-
- if (!sec_reqd || crecp)
- {
- if (qtype == T_DS)
- {
- crecp = NULL;
- while ((crecp = cache_find_by_name(crecp, name, now, F_DS)))
- if (crecp->uid == qclass)
- {
- gotone = 1;
- if (!dryrun)
- {
- if (crecp->flags & F_NEG)
- {
- if (crecp->flags & F_NXDOMAIN)
- nxdomain = 1;
- log_query(F_UPSTREAM, name, NULL, "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;
- a.addr.keytag = crecp->addr.key.keytag;
- log_query(F_KEYTAG | (crecp->flags & F_CONFIG), name, &a, "DNSKEY keytag %u");
- if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
- crec_ttl(crecp, now), &nameoffset,
- T_DNSKEY, qclass, "sbbt",
- crecp->addr.key.flags, 3, crecp->addr.key.algo, crecp->addr.key.keylen, keydata))
- anscount++;
- }
- }
- }
- }
-
- /* Now do RRSIGs */
- if (gotone)
- {
- ans = 1;
- auth = 0;
- if (!dryrun && sec_reqd)
- {
- crecp = NULL;
- while ((crecp = cache_find_by_name(crecp, name, now, F_DNSKEY | F_DS)))
- if (crecp->uid == qclass && crecp->addr.sig.type_covered == qtype &&
- (keydata = blockdata_retrieve(crecp->addr.sig.keydata, crecp->addr.sig.keylen, NULL)))
- {
- add_resource_record(header, limit, &trunc, nameoffset, &ansp,
- crec_ttl(crecp, now), &nameoffset,
- T_RRSIG, qclass, "t", crecp->addr.sig.keylen, keydata);
- anscount++;
- }
- }
- }
- }
-#endif
-
if (qclass == C_IN)
{
struct txt_record *t;
@@ -1736,6 +1644,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if ((t->class == qtype || qtype == T_ANY) && hostname_isequal(name, t->name))
{
ans = 1;
+ sec_data = 0;
if (!dryrun)
{
log_query(F_CONFIG | F_RRNAME, name, NULL, "<RR>");
@@ -1792,6 +1701,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (intr)
{
+ sec_data = 0;
ans = 1;
if (!dryrun)
{
@@ -1805,6 +1715,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
else if (ptr)
{
ans = 1;
+ sec_data = 0;
if (!dryrun)
{
log_query(F_CONFIG | F_RRNAME, name, NULL, "<PTR>");
@@ -1819,38 +1730,8 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
}
else if ((crecp = cache_find_by_addr(NULL, &addr, now, is_arpa)))
{
- if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) && sec_reqd)
- {
- if (!option_bool(OPT_DNSSEC_VALID) || ((crecp->flags & F_NEG) && (crecp->flags & F_DNSSECOK)))
- crecp = NULL;
-#ifdef HAVE_DNSSEC
- else if (crecp->flags & F_DNSSECOK)
- {
- int gotsig = 0;
- struct crec *rr_crec = NULL;
-
- while ((rr_crec = cache_find_by_name(rr_crec, name, now, F_DS | F_DNSKEY)))
- {
- if (rr_crec->addr.sig.type_covered == T_PTR && rr_crec->uid == C_IN)
- {
- char *sigdata = blockdata_retrieve(rr_crec->addr.sig.keydata, rr_crec->addr.sig.keylen, NULL);
- gotsig = 1;
-
- if (!dryrun &&
- add_resource_record(header, limit, &trunc, nameoffset, &ansp,
- rr_crec->ttd - now, &nameoffset,
- T_RRSIG, C_IN, "t", crecp->addr.sig.keylen, sigdata))
- anscount++;
- }
- }
-
- if (!gotsig)
- crecp = NULL;
- }
-#endif
- }
-
- if (crecp)
+ /* Don't use cache when DNSSEC data required. */
+ if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || !sec_reqd || !(crecp->flags & F_DNSSECOK))
{
do
{
@@ -1860,19 +1741,19 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (!(crecp->flags & F_DNSSECOK))
sec_data = 0;
-
+
+ ans = 1;
+
if (crecp->flags & F_NEG)
{
- ans = 1;
auth = 0;
if (crecp->flags & F_NXDOMAIN)
nxdomain = 1;
if (!dryrun)
log_query(crecp->flags & ~F_FORWARD, name, &addr, NULL);
}
- else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd || option_bool(OPT_DNSSEC_VALID))
+ else
{
- ans = 1;
if (!(crecp->flags & (F_HOSTS | F_DHCP)))
auth = 0;
if (!dryrun)
@@ -1892,6 +1773,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
else if (is_rev_synth(is_arpa, &addr, name))
{
ans = 1;
+ sec_data = 0;
if (!dryrun)
{
log_query(F_CONFIG | F_REVERSE | is_arpa, name, &addr, NULL);
@@ -1908,6 +1790,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
{
/* if not in cache, enabled and private IPV4 address, return NXDOMAIN */
ans = 1;
+ sec_data = 0;
nxdomain = 1;
if (!dryrun)
log_query(F_CONFIG | F_REVERSE | F_IPV4 | F_NEG | F_NXDOMAIN,
@@ -1955,6 +1838,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (i == 4)
{
ans = 1;
+ sec_data = 0;
if (!dryrun)
{
addr.addr.addr4.s_addr = htonl(a);
@@ -1993,6 +1877,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
continue;
#endif
ans = 1;
+ sec_data = 0;
if (!dryrun)
{
gotit = 1;
@@ -2032,48 +1917,8 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
crecp = save;
}
- /* If the client asked for DNSSEC and we can't provide RRSIGs, either
- because we've not doing DNSSEC or the cached answer is signed by negative,
- don't answer from the cache, forward instead. */
- if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) && sec_reqd)
- {
- if (!option_bool(OPT_DNSSEC_VALID) || ((crecp->flags & F_NEG) && (crecp->flags & F_DNSSECOK)))
- crecp = NULL;
-#ifdef HAVE_DNSSEC
- else if (crecp->flags & F_DNSSECOK)
- {
- /* We're returning validated data, need to return the RRSIG too. */
- struct crec *rr_crec = NULL;
- int sigtype = type;
- /* The signature may have expired even though the data is still in cache,
- forward instead of answering from cache if so. */
- int gotsig = 0;
-
- if (crecp->flags & F_CNAME)
- sigtype = T_CNAME;
-
- while ((rr_crec = cache_find_by_name(rr_crec, name, now, F_DS | F_DNSKEY)))
- {
- if (rr_crec->addr.sig.type_covered == sigtype && rr_crec->uid == C_IN)
- {
- char *sigdata = blockdata_retrieve(rr_crec->addr.sig.keydata, rr_crec->addr.sig.keylen, NULL);
- gotsig = 1;
-
- if (!dryrun &&
- add_resource_record(header, limit, &trunc, nameoffset, &ansp,
- rr_crec->ttd - now, &nameoffset,
- T_RRSIG, C_IN, "t", rr_crec->addr.sig.keylen, sigdata))
- anscount++;
- }
- }
-
- if (!gotsig)
- crecp = NULL;
- }
-#endif
- }
-
- if (crecp)
+ /* If the client asked for DNSSEC don't use cached data. */
+ if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || !sec_reqd || !(crecp->flags & F_DNSSECOK))
do
{
/* don't answer wildcard queries with data not from /etc/hosts
--
1.7.10.4

View File

@@ -1,269 +0,0 @@
From d64c81fff7faf4392b688223ef3a617c5c07e7dc Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Tue, 15 Dec 2015 16:11:06 +0000
Subject: [PATCH] Move code which caches DS records to a more logical place.
---
src/dnssec.c | 179 +++++++++++++++++++++++++++++-----------------------------
1 file changed, 90 insertions(+), 89 deletions(-)
diff --git a/src/dnssec.c b/src/dnssec.c
index 1ae03a6..359231f 100644
--- a/src/dnssec.c
+++ b/src/dnssec.c
@@ -1204,7 +1204,10 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class)
{
unsigned char *p = (unsigned char *)(header+1);
- int qtype, qclass, val, i, neganswer, nons;
+ int qtype, qclass, rc, i, neganswer, nons;
+ int aclass, atype, rdlen;
+ unsigned long ttl;
+ struct all_addr a;
if (ntohs(header->qdcount) != 1 ||
!(p = skip_name(p, header, plen, 4)))
@@ -1214,40 +1217,100 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
GETSHORT(qclass, p);
if (qtype != T_DS || qclass != class)
- val = STAT_BOGUS;
+ rc = STAT_BOGUS;
else
- val = dnssec_validate_reply(now, header, plen, name, keyname, NULL, 0, &neganswer, &nons);
+ rc = dnssec_validate_reply(now, header, plen, name, keyname, NULL, 0, &neganswer, &nons);
/* Note dnssec_validate_reply() will have cached positive answers */
- if (val == STAT_INSECURE)
- val = STAT_BOGUS;
-
+ if (rc == STAT_INSECURE)
+ rc = STAT_BOGUS;
+
p = (unsigned char *)(header+1);
extract_name(header, plen, &p, name, 1, 4);
p += 4; /* qtype, qclass */
- if (!(p = skip_section(p, ntohs(header->ancount), header, plen)))
- val = STAT_BOGUS;
-
/* If the key needed to validate the DS is on the same domain as the DS, we'll
loop getting nowhere. Stop that now. This can happen of the DS answer comes
from the DS's zone, and not the parent zone. */
- if (val == STAT_BOGUS || (val == STAT_NEED_KEY && hostname_isequal(name, keyname)))
+ if (rc == STAT_BOGUS || (rc == STAT_NEED_KEY && hostname_isequal(name, keyname)))
{
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS");
return STAT_BOGUS;
}
- if (val != STAT_SECURE)
- return val;
-
- /* By here, the answer is proved secure, and a positive answer has been cached. */
- if (neganswer)
+ if (rc != STAT_SECURE)
+ return rc;
+
+ if (!neganswer)
{
- int rdlen, flags = F_FORWARD | F_DS | F_NEG | F_DNSSECOK;
- unsigned long ttl, minttl = ULONG_MAX;
- struct all_addr a;
+ cache_start_insert();
+
+ for (i = 0; i < ntohs(header->ancount); i++)
+ {
+ if (!(rc = extract_name(header, plen, &p, name, 0, 10)))
+ return STAT_BOGUS; /* bad packet */
+
+ GETSHORT(atype, p);
+ GETSHORT(aclass, p);
+ GETLONG(ttl, p);
+ GETSHORT(rdlen, p);
+
+ if (!CHECK_LEN(header, p, plen, rdlen))
+ return STAT_BOGUS; /* bad packet */
+
+ if (aclass == class && atype == T_DS && rc == 1)
+ {
+ int algo, digest, keytag;
+ unsigned char *psave = p;
+ struct blockdata *key;
+ struct crec *crecp;
+ if (rdlen < 4)
+ return STAT_BOGUS; /* bad packet */
+
+ GETSHORT(keytag, p);
+ algo = *p++;
+ digest = *p++;
+
+ /* Cache needs to known class for DNSSEC stuff */
+ a.addr.dnssec.class = class;
+
+ if ((key = blockdata_alloc((char*)p, rdlen - 4)))
+ {
+ if (!(crecp = cache_insert(name, &a, now, ttl, F_FORWARD | F_DS | F_DNSSECOK)))
+ {
+ blockdata_free(key);
+ return STAT_BOGUS;
+ }
+ else
+ {
+ a.addr.keytag = keytag;
+ log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %u");
+ crecp->addr.ds.digest = digest;
+ crecp->addr.ds.keydata = key;
+ crecp->addr.ds.algo = algo;
+ crecp->addr.ds.keytag = keytag;
+ crecp->addr.ds.keylen = rdlen - 4;
+ }
+ }
+
+ p = psave;
+
+ if (!ADD_RDLEN(header, p, plen, rdlen))
+ return STAT_BOGUS; /* bad packet */
+ }
+
+ cache_end_insert();
+ }
+ }
+ else
+ {
+ int flags = F_FORWARD | F_DS | F_NEG | F_DNSSECOK;
+ unsigned long minttl = ULONG_MAX;
+
+ if (!(p = skip_section(p, ntohs(header->ancount), header, plen)))
+ return STAT_BOGUS;
+
if (RCODE(header) == NXDOMAIN)
flags |= F_NXDOMAIN;
@@ -1261,20 +1324,20 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
if (!(p = skip_name(p, header, plen, 0)))
return STAT_BOGUS;
- GETSHORT(qtype, p);
- GETSHORT(qclass, p);
+ GETSHORT(atype, p);
+ GETSHORT(aclass, p);
GETLONG(ttl, p);
GETSHORT(rdlen, p);
-
+
if (!CHECK_LEN(header, p, plen, rdlen))
return STAT_BOGUS; /* bad packet */
-
- if (qclass != class || qtype != T_SOA)
+
+ if (aclass != class || atype != T_SOA)
{
p += rdlen;
continue;
}
-
+
if (ttl < minttl)
minttl = ttl;
@@ -1306,7 +1369,7 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "no DS");
}
}
-
+
return STAT_OK;
}
@@ -2001,11 +2064,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
/* Not done, validate now */
if (j == i)
{
- int ttl, keytag, algo, digest, sigcnt, rrcnt;
- unsigned char *psave;
- struct all_addr a;
- struct blockdata *key;
- struct crec *crecp;
+ int sigcnt, rrcnt;
char *wildname;
if (!explore_rrset(header, plen, class1, type1, name, keyname, &sigcnt, &rrcnt))
@@ -2032,6 +2091,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
Can't overwrite name here. */
strcpy(daemon->workspacename, keyname);
rc = zone_status(daemon->workspacename, class1, keyname, now);
+
if (rc != STAT_SECURE)
{
/* Zone is insecure, don't need to validate RRset */
@@ -2088,65 +2148,6 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
if (rc == STAT_BOGUS)
return rc;
}
-
- /* If we just validated a DS RRset, cache it */
- /* Also note if the RRset is the answer to the question, or the target of a CNAME */
- cache_start_insert();
-
- for (p2 = ans_start, j = 0; j < ntohs(header->ancount); j++)
- {
- if (!(rc = extract_name(header, plen, &p2, name, 0, 10)))
- return STAT_BOGUS; /* bad packet */
-
- GETSHORT(type2, p2);
- GETSHORT(class2, p2);
- GETLONG(ttl, p2);
- GETSHORT(rdlen2, p2);
-
- if (!CHECK_LEN(header, p2, plen, rdlen2))
- return STAT_BOGUS; /* bad packet */
-
- if (class2 == class1 && rc == 1)
- {
- psave = p2;
-
- if (type1 == T_DS && type2 == T_DS)
- {
- if (rdlen2 < 4)
- return STAT_BOGUS; /* bad packet */
-
- GETSHORT(keytag, p2);
- algo = *p2++;
- digest = *p2++;
-
- /* Cache needs to known class for DNSSEC stuff */
- a.addr.dnssec.class = class2;
-
- if ((key = blockdata_alloc((char*)p2, rdlen2 - 4)))
- {
- if (!(crecp = cache_insert(name, &a, now, ttl, F_FORWARD | F_DS | F_DNSSECOK)))
- blockdata_free(key);
- else
- {
- a.addr.keytag = keytag;
- log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %u");
- crecp->addr.ds.digest = digest;
- crecp->addr.ds.keydata = key;
- crecp->addr.ds.algo = algo;
- crecp->addr.ds.keytag = keytag;
- crecp->addr.ds.keylen = rdlen2 - 4;
- }
- }
- }
-
- p2 = psave;
- }
-
- if (!ADD_RDLEN(header, p2, plen, rdlen2))
- return STAT_BOGUS; /* bad packet */
- }
-
- cache_end_insert();
}
}
}
--
1.7.10.4

View File

@@ -1,755 +0,0 @@
From c2bcd1e183bcc5fdd63811c045355fc57e36ecfd Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Tue, 15 Dec 2015 17:25:21 +0000
Subject: [PATCH] Generalise RR-filtering code, for use with EDNS0.
---
Makefile | 3 +-
bld/Android.mk | 2 +-
src/dnsmasq.h | 5 +
src/dnssec.c | 307 +-------------------------------------------------
src/forward.c | 2 +-
src/rrfilter.c | 339 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 349 insertions(+), 309 deletions(-)
create mode 100644 src/rrfilter.c
diff --git a/Makefile b/Makefile
index 4c87ea9..b664160 100644
--- a/Makefile
+++ b/Makefile
@@ -73,7 +73,8 @@ objs = cache.o rfc1035.o util.o option.o forward.o network.o \
dnsmasq.o dhcp.o lease.o rfc2131.o netlink.o dbus.o bpf.o \
helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o \
dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o \
- domain.o dnssec.o blockdata.o tables.o loop.o inotify.o poll.o
+ domain.o dnssec.o blockdata.o tables.o loop.o inotify.o \
+ poll.o rrfilter.o
hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \
dns-protocol.h radv-protocol.h ip6addr.h
diff --git a/bld/Android.mk b/bld/Android.mk
index 5364ee7..67b9c4b 100644
--- a/bld/Android.mk
+++ b/bld/Android.mk
@@ -10,7 +10,7 @@ LOCAL_SRC_FILES := bpf.c cache.c dbus.c dhcp.c dnsmasq.c \
dhcp6.c rfc3315.c dhcp-common.c outpacket.c \
radv.c slaac.c auth.c ipset.c domain.c \
dnssec.c dnssec-openssl.c blockdata.c tables.c \
- loop.c inotify.c poll.c
+ loop.c inotify.c poll.c rrfilter.c
LOCAL_MODULE := dnsmasq
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index 4344cae..39a930c 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -1513,3 +1513,8 @@ int poll_check(int fd, short event);
void poll_listen(int fd, short event);
int do_poll(int timeout);
+/* rrfilter.c */
+size_t rrfilter(struct dns_header *header, size_t plen, int mode);
+u16 *rrfilter_desc(int type);
+int expand_workspace(unsigned char ***wkspc, int *szp, int new);
+
diff --git a/src/dnssec.c b/src/dnssec.c
index 359231f..fa3eb81 100644
--- a/src/dnssec.c
+++ b/src/dnssec.c
@@ -507,50 +507,6 @@ static int check_date_range(unsigned long date_start, unsigned long date_end)
&& serial_compare_32(curtime, date_end) == SERIAL_LT;
}
-static u16 *get_desc(int type)
-{
- /* List of RRtypes which include domains in the data.
- 0 -> domain
- integer -> no of plain bytes
- -1 -> end
-
- zero is not a valid RRtype, so the final entry is returned for
- anything which needs no mangling.
- */
-
- static u16 rr_desc[] =
- {
- T_NS, 0, -1,
- T_MD, 0, -1,
- T_MF, 0, -1,
- T_CNAME, 0, -1,
- T_SOA, 0, 0, -1,
- T_MB, 0, -1,
- T_MG, 0, -1,
- T_MR, 0, -1,
- T_PTR, 0, -1,
- T_MINFO, 0, 0, -1,
- T_MX, 2, 0, -1,
- T_RP, 0, 0, -1,
- T_AFSDB, 2, 0, -1,
- T_RT, 2, 0, -1,
- T_SIG, 18, 0, -1,
- T_PX, 2, 0, 0, -1,
- T_NXT, 0, -1,
- T_KX, 2, 0, -1,
- T_SRV, 6, 0, -1,
- T_DNAME, 0, -1,
- 0, -1 /* wildcard/catchall */
- };
-
- u16 *p = rr_desc;
-
- while (*p != type && *p != 0)
- while (*p++ != (u16)-1);
-
- return p+1;
-}
-
/* Return bytes of canonicalised rdata, when the return value is zero, the remaining
data, pointed to by *p, should be used raw. */
static int get_rdata(struct dns_header *header, size_t plen, unsigned char *end, char *buff, int bufflen,
@@ -594,34 +550,6 @@ static int get_rdata(struct dns_header *header, size_t plen, unsigned char *end,
}
}
-static int expand_workspace(unsigned char ***wkspc, int *szp, int new)
-{
- unsigned char **p;
- int old = *szp;
-
- if (old >= new+1)
- return 1;
-
- if (new >= 100)
- return 0;
-
- new += 5;
-
- if (!(p = whine_malloc(new * sizeof(unsigned char **))))
- return 0;
-
- if (old != 0 && *wkspc)
- {
- memcpy(p, *wkspc, old * sizeof(unsigned char **));
- free(*wkspc);
- }
-
- *wkspc = p;
- *szp = new;
-
- return 1;
-}
-
/* Bubble sort the RRset into the canonical order.
Note that the byte-streams from two RRs may get unsynced: consider
RRs which have two domain-names at the start and then other data.
@@ -849,7 +777,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
int rdlen, j, name_labels;
struct crec *crecp = NULL;
int algo, labels, orig_ttl, key_tag;
- u16 *rr_desc = get_desc(type);
+ u16 *rr_desc = rrfilter_desc(type);
if (wildcard_out)
*wildcard_out = NULL;
@@ -2266,239 +2194,6 @@ size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, i
return ret;
}
-/* Go through a domain name, find "pointers" and fix them up based on how many bytes
- we've chopped out of the packet, or check they don't point into an elided part. */
-static int check_name(unsigned char **namep, struct dns_header *header, size_t plen, int fixup, unsigned char **rrs, int rr_count)
-{
- unsigned char *ansp = *namep;
-
- while(1)
- {
- unsigned int label_type;
-
- if (!CHECK_LEN(header, ansp, plen, 1))
- return 0;
-
- label_type = (*ansp) & 0xc0;
-
- if (label_type == 0xc0)
- {
- /* pointer for compression. */
- unsigned int offset;
- int i;
- unsigned char *p;
-
- if (!CHECK_LEN(header, ansp, plen, 2))
- return 0;
-
- offset = ((*ansp++) & 0x3f) << 8;
- offset |= *ansp++;
-
- p = offset + (unsigned char *)header;
-
- for (i = 0; i < rr_count; i++)
- if (p < rrs[i])
- break;
- else
- if (i & 1)
- offset -= rrs[i] - rrs[i-1];
-
- /* does the pointer end up in an elided RR? */
- if (i & 1)
- return 0;
-
- /* No, scale the pointer */
- if (fixup)
- {
- ansp -= 2;
- *ansp++ = (offset >> 8) | 0xc0;
- *ansp++ = offset & 0xff;
- }
- break;
- }
- else if (label_type == 0x80)
- return 0; /* reserved */
- else if (label_type == 0x40)
- {
- /* Extended label type */
- unsigned int count;
-
- if (!CHECK_LEN(header, ansp, plen, 2))
- return 0;
-
- if (((*ansp++) & 0x3f) != 1)
- return 0; /* we only understand bitstrings */
-
- count = *(ansp++); /* Bits in bitstring */
-
- if (count == 0) /* count == 0 means 256 bits */
- ansp += 32;
- else
- ansp += ((count-1)>>3)+1;
- }
- else
- { /* label type == 0 Bottom six bits is length */
- unsigned int len = (*ansp++) & 0x3f;
-
- if (!ADD_RDLEN(header, ansp, plen, len))
- return 0;
-
- if (len == 0)
- break; /* zero length label marks the end. */
- }
- }
-
- *namep = ansp;
-
- return 1;
-}
-
-/* Go through RRs and check or fixup the domain names contained within */
-static int check_rrs(unsigned char *p, struct dns_header *header, size_t plen, int fixup, unsigned char **rrs, int rr_count)
-{
- int i, type, class, rdlen;
- unsigned char *pp;
-
- for (i = 0; i < ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount); i++)
- {
- pp = p;
-
- if (!(p = skip_name(p, header, plen, 10)))
- return 0;
-
- GETSHORT(type, p);
- GETSHORT(class, p);
- p += 4; /* TTL */
- GETSHORT(rdlen, p);
-
- if (type != T_NSEC && type != T_NSEC3 && type != T_RRSIG)
- {
- /* fixup name of RR */
- if (!check_name(&pp, header, plen, fixup, rrs, rr_count))
- return 0;
-
- if (class == C_IN)
- {
- u16 *d;
-
- for (pp = p, d = get_desc(type); *d != (u16)-1; d++)
- {
- if (*d != 0)
- pp += *d;
- else if (!check_name(&pp, header, plen, fixup, rrs, rr_count))
- return 0;
- }
- }
- }
-
- if (!ADD_RDLEN(header, p, plen, rdlen))
- return 0;
- }
-
- return 1;
-}
-
-
-size_t filter_rrsigs(struct dns_header *header, size_t plen)
-{
- static unsigned char **rrs;
- static int rr_sz = 0;
-
- unsigned char *p = (unsigned char *)(header+1);
- int i, rdlen, qtype, qclass, rr_found, chop_an, chop_ns, chop_ar;
-
- if (ntohs(header->qdcount) != 1 ||
- !(p = skip_name(p, header, plen, 4)))
- return plen;
-
- GETSHORT(qtype, p);
- GETSHORT(qclass, p);
-
- /* First pass, find pointers to start and end of all the records we wish to elide:
- records added for DNSSEC, unless explicity queried for */
- for (rr_found = 0, chop_ns = 0, chop_an = 0, chop_ar = 0, i = 0;
- i < ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount);
- i++)
- {
- unsigned char *pstart = p;
- int type, class;
-
- if (!(p = skip_name(p, header, plen, 10)))
- return plen;
-
- GETSHORT(type, p);
- GETSHORT(class, p);
- p += 4; /* TTL */
- GETSHORT(rdlen, p);
-
- if ((type == T_NSEC || type == T_NSEC3 || type == T_RRSIG) &&
- (type != qtype || class != qclass))
- {
- if (!expand_workspace(&rrs, &rr_sz, rr_found + 1))
- return plen;
-
- rrs[rr_found++] = pstart;
-
- if (!ADD_RDLEN(header, p, plen, rdlen))
- return plen;
-
- rrs[rr_found++] = p;
-
- if (i < ntohs(header->ancount))
- chop_an++;
- else if (i < (ntohs(header->nscount) + ntohs(header->ancount)))
- chop_ns++;
- else
- chop_ar++;
- }
- else if (!ADD_RDLEN(header, p, plen, rdlen))
- return plen;
- }
-
- /* Nothing to do. */
- if (rr_found == 0)
- return plen;
-
- /* Second pass, look for pointers in names in the records we're keeping and make sure they don't
- point to records we're going to elide. This is theoretically possible, but unlikely. If
- it happens, we give up and leave the answer unchanged. */
- p = (unsigned char *)(header+1);
-
- /* question first */
- if (!check_name(&p, header, plen, 0, rrs, rr_found))
- return plen;
- p += 4; /* qclass, qtype */
-
- /* Now answers and NS */
- if (!check_rrs(p, header, plen, 0, rrs, rr_found))
- return plen;
-
- /* Third pass, elide records */
- for (p = rrs[0], i = 1; i < rr_found; i += 2)
- {
- unsigned char *start = rrs[i];
- unsigned char *end = (i != rr_found - 1) ? rrs[i+1] : ((unsigned char *)(header+1)) + plen;
-
- memmove(p, start, end-start);
- p += end-start;
- }
-
- plen = p - (unsigned char *)header;
- header->ancount = htons(ntohs(header->ancount) - chop_an);
- header->nscount = htons(ntohs(header->nscount) - chop_ns);
- header->arcount = htons(ntohs(header->arcount) - chop_ar);
-
- /* Fourth pass, fix up pointers in the remaining records */
- p = (unsigned char *)(header+1);
-
- check_name(&p, header, plen, 1, rrs, rr_found);
- p += 4; /* qclass, qtype */
-
- check_rrs(p, header, plen, 1, rrs, rr_found);
-
- return plen;
-}
-
unsigned char* hash_questions(struct dns_header *header, size_t plen, char *name)
{
int q;
diff --git a/src/forward.c b/src/forward.c
index dd22a62..3e801c8 100644
--- a/src/forward.c
+++ b/src/forward.c
@@ -662,7 +662,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
/* If the requestor didn't set the DO bit, don't return DNSSEC info. */
if (!do_bit)
- n = filter_rrsigs(header, n);
+ n = rrfilter(header, n, 1);
#endif
/* do this after extract_addresses. Ensure NODATA reply and remove
diff --git a/src/rrfilter.c b/src/rrfilter.c
new file mode 100644
index 0000000..ae12261
--- /dev/null
+++ b/src/rrfilter.c
@@ -0,0 +1,339 @@
+/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 dated June, 1991, or
+ (at your option) version 3 dated 29 June, 2007.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Code to safely remove RRs from an DNS answer */
+
+#include "dnsmasq.h"
+
+/* Go through a domain name, find "pointers" and fix them up based on how many bytes
+ we've chopped out of the packet, or check they don't point into an elided part. */
+static int check_name(unsigned char **namep, struct dns_header *header, size_t plen, int fixup, unsigned char **rrs, int rr_count)
+{
+ unsigned char *ansp = *namep;
+
+ while(1)
+ {
+ unsigned int label_type;
+
+ if (!CHECK_LEN(header, ansp, plen, 1))
+ return 0;
+
+ label_type = (*ansp) & 0xc0;
+
+ if (label_type == 0xc0)
+ {
+ /* pointer for compression. */
+ unsigned int offset;
+ int i;
+ unsigned char *p;
+
+ if (!CHECK_LEN(header, ansp, plen, 2))
+ return 0;
+
+ offset = ((*ansp++) & 0x3f) << 8;
+ offset |= *ansp++;
+
+ p = offset + (unsigned char *)header;
+
+ for (i = 0; i < rr_count; i++)
+ if (p < rrs[i])
+ break;
+ else
+ if (i & 1)
+ offset -= rrs[i] - rrs[i-1];
+
+ /* does the pointer end up in an elided RR? */
+ if (i & 1)
+ return 0;
+
+ /* No, scale the pointer */
+ if (fixup)
+ {
+ ansp -= 2;
+ *ansp++ = (offset >> 8) | 0xc0;
+ *ansp++ = offset & 0xff;
+ }
+ break;
+ }
+ else if (label_type == 0x80)
+ return 0; /* reserved */
+ else if (label_type == 0x40)
+ {
+ /* Extended label type */
+ unsigned int count;
+
+ if (!CHECK_LEN(header, ansp, plen, 2))
+ return 0;
+
+ if (((*ansp++) & 0x3f) != 1)
+ return 0; /* we only understand bitstrings */
+
+ count = *(ansp++); /* Bits in bitstring */
+
+ if (count == 0) /* count == 0 means 256 bits */
+ ansp += 32;
+ else
+ ansp += ((count-1)>>3)+1;
+ }
+ else
+ { /* label type == 0 Bottom six bits is length */
+ unsigned int len = (*ansp++) & 0x3f;
+
+ if (!ADD_RDLEN(header, ansp, plen, len))
+ return 0;
+
+ if (len == 0)
+ break; /* zero length label marks the end. */
+ }
+ }
+
+ *namep = ansp;
+
+ return 1;
+}
+
+/* Go through RRs and check or fixup the domain names contained within */
+static int check_rrs(unsigned char *p, struct dns_header *header, size_t plen, int fixup, unsigned char **rrs, int rr_count)
+{
+ int i, j, type, class, rdlen;
+ unsigned char *pp;
+
+ for (i = 0; i < ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount); i++)
+ {
+ pp = p;
+
+ if (!(p = skip_name(p, header, plen, 10)))
+ return 0;
+
+ GETSHORT(type, p);
+ GETSHORT(class, p);
+ p += 4; /* TTL */
+ GETSHORT(rdlen, p);
+
+ /* If this RR is to be elided, don't fix up its contents */
+ for (j = 0; j < rr_count; j += 2)
+ if (rrs[j] == pp)
+ break;
+
+ if (j >= rr_count)
+ {
+ /* fixup name of RR */
+ if (!check_name(&pp, header, plen, fixup, rrs, rr_count))
+ return 0;
+
+ if (class == C_IN)
+ {
+ u16 *d;
+
+ for (pp = p, d = rrfilter_desc(type); *d != (u16)-1; d++)
+ {
+ if (*d != 0)
+ pp += *d;
+ else if (!check_name(&pp, header, plen, fixup, rrs, rr_count))
+ return 0;
+ }
+ }
+ }
+
+ if (!ADD_RDLEN(header, p, plen, rdlen))
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/* mode is 0 to remove EDNS0, 1 to filter DNSSEC RRs */
+size_t rrfilter(struct dns_header *header, size_t plen, int mode)
+{
+ static unsigned char **rrs;
+ static int rr_sz = 0;
+
+ unsigned char *p = (unsigned char *)(header+1);
+ int i, rdlen, qtype, qclass, rr_found, chop_an, chop_ns, chop_ar;
+
+ if (ntohs(header->qdcount) != 1 ||
+ !(p = skip_name(p, header, plen, 4)))
+ return plen;
+
+ GETSHORT(qtype, p);
+ GETSHORT(qclass, p);
+
+ /* First pass, find pointers to start and end of all the records we wish to elide:
+ records added for DNSSEC, unless explicity queried for */
+ for (rr_found = 0, chop_ns = 0, chop_an = 0, chop_ar = 0, i = 0;
+ i < ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount);
+ i++)
+ {
+ unsigned char *pstart = p;
+ int type, class;
+
+ if (!(p = skip_name(p, header, plen, 10)))
+ return plen;
+
+ GETSHORT(type, p);
+ GETSHORT(class, p);
+ p += 4; /* TTL */
+ GETSHORT(rdlen, p);
+
+ if (!ADD_RDLEN(header, p, plen, rdlen))
+ return plen;
+
+ /* Don't remove the answer. */
+ if (i < ntohs(header->ancount) && type == qtype && class == qclass)
+ continue;
+
+ if (mode == 0) /* EDNS */
+ {
+ /* EDNS mode, remove T_OPT from additional section only */
+ if (i < (ntohs(header->nscount) + ntohs(header->ancount)) || type != T_OPT)
+ continue;
+ }
+ else if (type != T_NSEC && type != T_NSEC3 && type != T_RRSIG)
+ /* DNSSEC mode, remove SIGs and NSECs from all three sections. */
+ continue;
+
+
+ if (!expand_workspace(&rrs, &rr_sz, rr_found + 1))
+ return plen;
+
+ rrs[rr_found++] = pstart;
+ rrs[rr_found++] = p;
+
+ if (i < ntohs(header->ancount))
+ chop_an++;
+ else if (i < (ntohs(header->nscount) + ntohs(header->ancount)))
+ chop_ns++;
+ else
+ chop_ar++;
+ }
+
+ /* Nothing to do. */
+ if (rr_found == 0)
+ return plen;
+
+ /* Second pass, look for pointers in names in the records we're keeping and make sure they don't
+ point to records we're going to elide. This is theoretically possible, but unlikely. If
+ it happens, we give up and leave the answer unchanged. */
+ p = (unsigned char *)(header+1);
+
+ /* question first */
+ if (!check_name(&p, header, plen, 0, rrs, rr_found))
+ return plen;
+ p += 4; /* qclass, qtype */
+
+ /* Now answers and NS */
+ if (!check_rrs(p, header, plen, 0, rrs, rr_found))
+ return plen;
+
+ /* Third pass, elide records */
+ for (p = rrs[0], i = 1; i < rr_found; i += 2)
+ {
+ unsigned char *start = rrs[i];
+ unsigned char *end = (i != rr_found - 1) ? rrs[i+1] : ((unsigned char *)(header+1)) + plen;
+
+ memmove(p, start, end-start);
+ p += end-start;
+ }
+
+ plen = p - (unsigned char *)header;
+ header->ancount = htons(ntohs(header->ancount) - chop_an);
+ header->nscount = htons(ntohs(header->nscount) - chop_ns);
+ header->arcount = htons(ntohs(header->arcount) - chop_ar);
+
+ /* Fourth pass, fix up pointers in the remaining records */
+ p = (unsigned char *)(header+1);
+
+ check_name(&p, header, plen, 1, rrs, rr_found);
+ p += 4; /* qclass, qtype */
+
+ check_rrs(p, header, plen, 1, rrs, rr_found);
+
+ return plen;
+}
+
+/* This is used in the DNSSEC code too, hence it's exported */
+u16 *rrfilter_desc(int type)
+{
+ /* List of RRtypes which include domains in the data.
+ 0 -> domain
+ integer -> no of plain bytes
+ -1 -> end
+
+ zero is not a valid RRtype, so the final entry is returned for
+ anything which needs no mangling.
+ */
+
+ static u16 rr_desc[] =
+ {
+ T_NS, 0, -1,
+ T_MD, 0, -1,
+ T_MF, 0, -1,
+ T_CNAME, 0, -1,
+ T_SOA, 0, 0, -1,
+ T_MB, 0, -1,
+ T_MG, 0, -1,
+ T_MR, 0, -1,
+ T_PTR, 0, -1,
+ T_MINFO, 0, 0, -1,
+ T_MX, 2, 0, -1,
+ T_RP, 0, 0, -1,
+ T_AFSDB, 2, 0, -1,
+ T_RT, 2, 0, -1,
+ T_SIG, 18, 0, -1,
+ T_PX, 2, 0, 0, -1,
+ T_NXT, 0, -1,
+ T_KX, 2, 0, -1,
+ T_SRV, 6, 0, -1,
+ T_DNAME, 0, -1,
+ 0, -1 /* wildcard/catchall */
+ };
+
+ u16 *p = rr_desc;
+
+ while (*p != type && *p != 0)
+ while (*p++ != (u16)-1);
+
+ return p+1;
+}
+
+int expand_workspace(unsigned char ***wkspc, int *szp, int new)
+{
+ unsigned char **p;
+ int old = *szp;
+
+ if (old >= new+1)
+ return 1;
+
+ if (new >= 100)
+ return 0;
+
+ new += 5;
+
+ if (!(p = whine_malloc(new * sizeof(unsigned char **))))
+ return 0;
+
+ if (old != 0 && *wkspc)
+ {
+ memcpy(p, *wkspc, old * sizeof(unsigned char **));
+ free(*wkspc);
+ }
+
+ *wkspc = p;
+ *szp = new;
+
+ return 1;
+}
--
1.7.10.4

View File

@@ -1,134 +0,0 @@
From 2dbba34b2c1289a108f876c78b84889f2a93115d Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Wed, 16 Dec 2015 13:41:58 +0000
Subject: [PATCH] DNSSEC validation tweak.
A zone which has at least one key with an algorithm we don't
support should be considered as insecure.
---
src/dnssec.c | 82 ++++++++++++++++++++++++++++++++++++++--------------------
1 file changed, 54 insertions(+), 28 deletions(-)
diff --git a/src/dnssec.c b/src/dnssec.c
index fa3eb81..dc563e0 100644
--- a/src/dnssec.c
+++ b/src/dnssec.c
@@ -763,10 +763,10 @@ static int explore_rrset(struct dns_header *header, size_t plen, int class, int
STAT_NEED_KEY need DNSKEY to complete validation (name is returned in keyname)
STAT_NEED_DS need DS to complete validation (name is returned in keyname)
- if key is non-NULL, use that key, which has the algo and tag given in the params of those names,
+ If key is non-NULL, use that key, which has the algo and tag given in the params of those names,
otherwise find the key in the cache.
- name is unchanged on exit. keyname is used as workspace and trashed.
+ Name is unchanged on exit. keyname is used as workspace and trashed.
Call explore_rrset first to find and count RRs and sigs.
*/
@@ -919,6 +919,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
return STAT_BOGUS;
}
+
/* The DNS packet is expected to contain the answer to a DNSKEY query.
Put all DNSKEYs in the answer which are valid into the cache.
return codes:
@@ -1831,15 +1832,15 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
/* Check signing status of name.
returns:
- STAT_SECURE zone is signed.
- STAT_INSECURE zone proved unsigned.
- STAT_NEED_DS require DS record of name returned in keyname.
-
+ STAT_SECURE zone is signed.
+ STAT_INSECURE zone proved unsigned.
+ STAT_NEED_DS require DS record of name returned in keyname.
+ STAT_NEED_DNSKEY require DNSKEY record of name returned in keyname.
name returned unaltered.
*/
static int zone_status(char *name, int class, char *keyname, time_t now)
{
- int name_start = strlen(name);
+ int secure_ds, name_start = strlen(name);
struct crec *crecp;
char *p;
@@ -1850,27 +1851,52 @@ static int zone_status(char *name, int class, char *keyname, time_t now)
if (!(crecp = cache_find_by_name(NULL, keyname, now, F_DS)))
return STAT_NEED_DS;
else
- do
- {
- if (crecp->uid == (unsigned int)class)
- {
- /* F_DNSSECOK misused in DS cache records to non-existance of NS record.
- F_NEG && !F_DNSSECOK implies that we've proved there's no DS record here,
- but that's because there's no NS record either, ie this isn't the start
- of a zone. We only prove that the DNS tree below a node is unsigned when
- we prove that we're at a zone cut AND there's no DS record.
- */
- if (crecp->flags & F_NEG)
- {
- if (crecp->flags & F_DNSSECOK)
- return STAT_INSECURE; /* proved no DS here */
- }
- else if (!ds_digest_name(crecp->addr.ds.digest) || !algo_digest_name(crecp->addr.ds.algo))
- return STAT_INSECURE; /* algo we can't use - insecure */
- }
- }
- while ((crecp = cache_find_by_name(crecp, keyname, now, F_DS)));
-
+ {
+ secure_ds = 0;
+
+ do
+ {
+ if (crecp->uid == (unsigned int)class)
+ {
+ /* F_DNSSECOK misused in DS cache records to non-existance of NS record.
+ F_NEG && !F_DNSSECOK implies that we've proved there's no DS record here,
+ but that's because there's no NS record either, ie this isn't the start
+ of a zone. We only prove that the DNS tree below a node is unsigned when
+ we prove that we're at a zone cut AND there's no DS record.
+ */
+ if (crecp->flags & F_NEG)
+ {
+ if (crecp->flags & F_DNSSECOK)
+ return STAT_INSECURE; /* proved no DS here */
+ }
+ else if (!ds_digest_name(crecp->addr.ds.digest) || !algo_digest_name(crecp->addr.ds.algo))
+ return STAT_INSECURE; /* algo we can't use - insecure */
+ else
+ secure_ds = 1;
+ }
+ }
+ while ((crecp = cache_find_by_name(crecp, keyname, now, F_DS)));
+ }
+
+ if (secure_ds)
+ {
+ /* We've found only DS records that attest to the DNSKEY RRset in the zone, so we believe
+ that RRset is good. Furthermore the DNSKEY whose hash is proved by the DS record is
+ one we can use. However the DNSKEY RRset may contain more than one key and
+ one of the other keys may use an algorithm we don't support. If that's
+ the case the zone is insecure for us. */
+
+ if (!(crecp = cache_find_by_name(NULL, keyname, now, F_DNSKEY)))
+ return STAT_NEED_KEY;
+
+ do
+ {
+ if (crecp->uid == (unsigned int)class && !algo_digest_name(crecp->addr.key.algo))
+ return STAT_INSECURE;
+ }
+ while ((crecp = cache_find_by_name(crecp, keyname, now, F_DNSKEY)));
+ }
+
if (name_start == 0)
break;
--
1.7.10.4

View File

@@ -1,133 +0,0 @@
From dd4ad9ac7ea6d51dcc34a1f2cd2da14efbb87714 Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Thu, 17 Dec 2015 10:44:58 +0000
Subject: [PATCH] Tweaks to EDNS0 handling in DNS replies.
---
src/dnssec.c | 20 +++++++++-----------
src/rfc1035.c | 57 +++++++++++++++++++++++++++++++++------------------------
2 files changed, 42 insertions(+), 35 deletions(-)
diff --git a/src/dnssec.c b/src/dnssec.c
index dc563e0..012b2a6 100644
--- a/src/dnssec.c
+++ b/src/dnssec.c
@@ -2129,18 +2129,16 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
/* Empty DS without NSECS */
if (qtype == T_DS)
return STAT_BOGUS;
- else
+
+ rc = zone_status(name, qclass, keyname, now);
+ if (rc != STAT_SECURE)
{
- rc = zone_status(name, qclass, keyname, now);
- if (rc != STAT_SECURE)
- {
- if (class)
- *class = qclass; /* Class for NEED_DS or NEED_DNSKEY */
- return rc;
- }
-
- return STAT_BOGUS; /* signed zone, no NSECs */
- }
+ if (class)
+ *class = qclass; /* Class for NEED_DS or NEED_DNSKEY */
+ return rc;
+ }
+
+ return STAT_BOGUS; /* signed zone, no NSECs */
}
if (nsec_type == T_NSEC)
diff --git a/src/rfc1035.c b/src/rfc1035.c
index def8fa0..188d05f 100644
--- a/src/rfc1035.c
+++ b/src/rfc1035.c
@@ -1539,7 +1539,13 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
int nxdomain = 0, auth = 1, trunc = 0, sec_data = 1;
struct mx_srv_record *rec;
size_t len;
-
+
+ if (ntohs(header->ancount) != 0 ||
+ ntohs(header->nscount) != 0 ||
+ ntohs(header->qdcount) == 0 ||
+ OPCODE(header) != QUERY )
+ return 0;
+
/* Don't return AD set if checking disabled. */
if (header->hb4 & HB4_CD)
sec_data = 0;
@@ -1548,33 +1554,32 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
*ad_reqd = header->hb4 & HB4_AD;
*do_bit = 0;
- /* If there is an RFC2671 pseudoheader then it will be overwritten by
+ /* If there is an additional data section then it will be overwritten by
partial replies, so we have to do a dry run to see if we can answer
- the query. We check to see if the do bit is set, if so we always
- forward rather than answering from the cache, which doesn't include
- security information, unless we're in DNSSEC validation mode. */
+ the query. */
- if (find_pseudoheader(header, qlen, NULL, &pheader, NULL))
- {
- unsigned short flags;
-
- have_pseudoheader = 1;
+ if (ntohs(header->arcount) != 0)
+ {
+ dryrun = 1;
- pheader += 4; /* udp size, ext_rcode */
- GETSHORT(flags, pheader);
-
- if ((sec_reqd = flags & 0x8000))
- {
- *do_bit = 1;/* do bit */
- *ad_reqd = 1;
+ /* If there's an additional section, there might be an EDNS(0) pseudoheader */
+ if (find_pseudoheader(header, qlen, NULL, &pheader, NULL))
+ {
+ unsigned short flags;
+
+ have_pseudoheader = 1;
+
+ pheader += 4; /* udp size, ext_rcode */
+ GETSHORT(flags, pheader);
+
+ if ((sec_reqd = flags & 0x8000))
+ {
+ *do_bit = 1;/* do bit */
+ *ad_reqd = 1;
+ }
}
-
- dryrun = 1;
}
- if (ntohs(header->qdcount) == 0 || OPCODE(header) != QUERY )
- return 0;
-
for (rec = daemon->mxnames; rec; rec = rec->next)
rec->offset = 0;
@@ -1730,8 +1735,12 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
}
else if ((crecp = cache_find_by_addr(NULL, &addr, now, is_arpa)))
{
- /* Don't use cache when DNSSEC data required. */
- if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || !sec_reqd || !(crecp->flags & F_DNSSECOK))
+ /* Don't use cache when DNSSEC data required, unless we know that
+ the zone is unsigned, which implies that we're doing
+ validation. */
+ if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) ||
+ !sec_reqd ||
+ (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK)))
{
do
{
--
1.7.10.4

View File

@@ -1,409 +0,0 @@
From b40f26c0199235073abc37e1e1d6ed93bed372f5 Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Thu, 17 Dec 2015 11:57:26 +0000
Subject: [PATCH] Tidy up DNSSEC non-existence code. Check zone status is NSEC
proof bad.
---
src/dnssec.c | 207 +++++++++++++++++++++++++---------------------------------
1 file changed, 90 insertions(+), 117 deletions(-)
diff --git a/src/dnssec.c b/src/dnssec.c
index 012b2a6..ddae497 100644
--- a/src/dnssec.c
+++ b/src/dnssec.c
@@ -1367,59 +1367,6 @@ static int hostname_cmp(const char *a, const char *b)
}
}
-/* Find all the NSEC or NSEC3 records in a reply.
- return an array of pointers to them. */
-static int find_nsec_records(struct dns_header *header, size_t plen, unsigned char ***nsecsetp, int *nsecsetl, int class_reqd)
-{
- static unsigned char **nsecset = NULL;
- static int nsecset_sz = 0;
-
- int type_found = 0;
- unsigned char *p = skip_questions(header, plen);
- int type, class, rdlen, i, nsecs_found;
-
- /* Move to NS section */
- if (!p || !(p = skip_section(p, ntohs(header->ancount), header, plen)))
- return 0;
-
- for (nsecs_found = 0, i = ntohs(header->nscount); i != 0; i--)
- {
- unsigned char *pstart = p;
-
- if (!(p = skip_name(p, header, plen, 10)))
- return 0;
-
- GETSHORT(type, p);
- GETSHORT(class, p);
- p += 4; /* TTL */
- GETSHORT(rdlen, p);
-
- if (class == class_reqd && (type == T_NSEC || type == T_NSEC3))
- {
- /* No mixed NSECing 'round here, thankyouverymuch */
- if (type_found == T_NSEC && type == T_NSEC3)
- return 0;
- if (type_found == T_NSEC3 && type == T_NSEC)
- return 0;
-
- type_found = type;
-
- if (!expand_workspace(&nsecset, &nsecset_sz, nsecs_found))
- return 0;
-
- nsecset[nsecs_found++] = pstart;
- }
-
- if (!ADD_RDLEN(header, p, plen, rdlen))
- return 0;
- }
-
- *nsecsetp = nsecset;
- *nsecsetl = nsecs_found;
-
- return type_found;
-}
-
static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsigned char **nsecs, int nsec_count,
char *workspace1, char *workspace2, char *name, int type, int *nons)
{
@@ -1436,12 +1383,12 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
{
p = nsecs[i];
if (!extract_name(header, plen, &p, workspace1, 1, 10))
- return STAT_BOGUS;
+ return 0;
p += 8; /* class, type, TTL */
GETSHORT(rdlen, p);
psave = p;
if (!extract_name(header, plen, &p, workspace2, 1, 10))
- return STAT_BOGUS;
+ return 0;
rc = hostname_cmp(workspace1, name);
@@ -1449,7 +1396,7 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
{
/* 4035 para 5.4. Last sentence */
if (type == T_NSEC || type == T_RRSIG)
- return STAT_SECURE;
+ return 1;
/* NSEC with the same name as the RR we're testing, check
that the type in question doesn't appear in the type map */
@@ -1465,24 +1412,24 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
/* A CNAME answer would also be valid, so if there's a CNAME is should
have been returned. */
if ((p[2] & (0x80 >> T_CNAME)) != 0)
- return STAT_BOGUS;
+ return 0;
/* If the SOA bit is set for a DS record, then we have the
DS from the wrong side of the delegation. */
if (type == T_DS && (p[2] & (0x80 >> T_SOA)) != 0)
- return STAT_BOGUS;
+ return 0;
}
while (rdlen >= 2)
{
if (!CHECK_LEN(header, p, plen, rdlen))
- return STAT_BOGUS;
+ return 0;
if (p[0] == type >> 8)
{
/* Does the NSEC say our type exists? */
if (offset < p[1] && (p[offset+2] & mask) != 0)
- return STAT_BOGUS;
+ return 0;
break; /* finshed checking */
}
@@ -1491,24 +1438,24 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
p += p[1];
}
- return STAT_SECURE;
+ return 1;
}
else if (rc == -1)
{
/* Normal case, name falls between NSEC name and next domain name,
wrap around case, name falls between NSEC name (rc == -1) and end */
if (hostname_cmp(workspace2, name) >= 0 || hostname_cmp(workspace1, workspace2) >= 0)
- return STAT_SECURE;
+ return 1;
}
else
{
/* wrap around case, name falls between start and next domain name */
if (hostname_cmp(workspace1, workspace2) >= 0 && hostname_cmp(workspace2, name) >=0 )
- return STAT_SECURE;
+ return 1;
}
}
- return STAT_BOGUS;
+ return 0;
}
/* return digest length, or zero on error */
@@ -1701,7 +1648,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
for (i = 0; i < nsec_count; i++)
{
if (!(p = skip_name(nsecs[i], header, plen, 15)))
- return STAT_BOGUS; /* bad packet */
+ return 0; /* bad packet */
p += 10; /* type, class, TTL, rdlen */
algo = *p++;
@@ -1712,14 +1659,14 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
/* No usable NSEC3s */
if (i == nsec_count)
- return STAT_BOGUS;
+ return 0;
p++; /* flags */
GETSHORT (iterations, p);
salt_len = *p++;
salt = p;
if (!CHECK_LEN(header, salt, plen, salt_len))
- return STAT_BOGUS; /* bad packet */
+ return 0; /* bad packet */
/* Now prune so we only have NSEC3 records with same iterations, salt and algo */
for (i = 0; i < nsec_count; i++)
@@ -1730,7 +1677,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
nsecs[i] = NULL; /* Speculative, will be restored if OK. */
if (!(p = skip_name(nsec3p, header, plen, 15)))
- return STAT_BOGUS; /* bad packet */
+ return 0; /* bad packet */
p += 10; /* type, class, TTL, rdlen */
@@ -1747,7 +1694,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
continue;
if (!CHECK_LEN(header, p, plen, salt_len))
- return STAT_BOGUS; /* bad packet */
+ return 0; /* bad packet */
if (memcmp(p, salt, salt_len) != 0)
continue;
@@ -1758,13 +1705,13 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
/* Algo is checked as 1 above */
if (!(hash = hash_find("sha1")))
- return STAT_BOGUS;
+ return 0;
if ((digest_len = hash_name(name, &digest, hash, salt, salt_len, iterations)) == 0)
- return STAT_BOGUS;
+ return 0;
if (check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count, nons))
- return STAT_SECURE;
+ return 1;
/* Can't find an NSEC3 which covers the name directly, we need the "closest encloser NSEC3"
or an answer inferred from a wildcard record. */
@@ -1780,14 +1727,14 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
break;
if ((digest_len = hash_name(closest_encloser, &digest, hash, salt, salt_len, iterations)) == 0)
- return STAT_BOGUS;
+ return 0;
for (i = 0; i < nsec_count; i++)
if ((p = nsecs[i]))
{
if (!extract_name(header, plen, &p, workspace1, 1, 0) ||
!(base32_len = base32_decode(workspace1, (unsigned char *)workspace2)))
- return STAT_BOGUS;
+ return 0;
if (digest_len == base32_len &&
memcmp(digest, workspace2, digest_len) == 0)
@@ -1802,32 +1749,81 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
while ((closest_encloser = strchr(closest_encloser, '.')));
if (!closest_encloser)
- return STAT_BOGUS;
+ return 0;
/* Look for NSEC3 that proves the non-existence of the next-closest encloser */
if ((digest_len = hash_name(next_closest, &digest, hash, salt, salt_len, iterations)) == 0)
- return STAT_BOGUS;
+ return 0;
if (!check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count, NULL))
- return STAT_BOGUS;
+ return 0;
/* Finally, check that there's no seat of wildcard synthesis */
if (!wildname)
{
if (!(wildcard = strchr(next_closest, '.')) || wildcard == next_closest)
- return STAT_BOGUS;
+ return 0;
wildcard--;
*wildcard = '*';
if ((digest_len = hash_name(wildcard, &digest, hash, salt, salt_len, iterations)) == 0)
- return STAT_BOGUS;
+ return 0;
if (!check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count, NULL))
- return STAT_BOGUS;
+ return 0;
}
- return STAT_SECURE;
+ return 1;
+}
+
+static int prove_non_existence(struct dns_header *header, size_t plen, char *keyname, char *name, int qtype, int qclass, char *wildname, int *nons)
+{
+ static unsigned char **nsecset = NULL;
+ static int nsecset_sz = 0;
+
+ int type_found = 0;
+ unsigned char *p = skip_questions(header, plen);
+ int type, class, rdlen, i, nsecs_found;
+
+ /* Move to NS section */
+ if (!p || !(p = skip_section(p, ntohs(header->ancount), header, plen)))
+ return 0;
+
+ for (nsecs_found = 0, i = ntohs(header->nscount); i != 0; i--)
+ {
+ unsigned char *pstart = p;
+
+ if (!(p = skip_name(p, header, plen, 10)))
+ return 0;
+
+ GETSHORT(type, p);
+ GETSHORT(class, p);
+ p += 4; /* TTL */
+ GETSHORT(rdlen, p);
+
+ if (class == qclass && (type == T_NSEC || type == T_NSEC3))
+ {
+ /* No mixed NSECing 'round here, thankyouverymuch */
+ if (type_found != 0 && type_found != type)
+ return 0;
+
+ type_found = type;
+
+ if (!expand_workspace(&nsecset, &nsecset_sz, nsecs_found))
+ return 0;
+
+ nsecset[nsecs_found++] = pstart;
+ }
+
+ if (!ADD_RDLEN(header, p, plen, rdlen))
+ return 0;
+ }
+
+ if (type_found == T_NSEC)
+ return prove_non_existence_nsec(header, plen, nsecset, nsecs_found, daemon->workspacename, keyname, name, qtype, nons);
+ else
+ return prove_non_existence_nsec3(header, plen, nsecset, nsecs_found, daemon->workspacename, keyname, name, qtype, wildname, nons);
}
/* Check signing status of name.
@@ -1925,10 +1921,9 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
static unsigned char **targets = NULL;
static int target_sz = 0;
- unsigned char *ans_start, *p1, *p2, **nsecs;
+ unsigned char *ans_start, *p1, *p2;
int type1, class1, rdlen1, type2, class2, rdlen2, qclass, qtype, targetidx;
- int i, j, rc, nsec_count;
- int nsec_type;
+ int i, j, rc;
if (neganswer)
*neganswer = 0;
@@ -2080,28 +2075,15 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
targets[j] = NULL;
}
- if (rc == STAT_SECURE_WILDCARD)
- {
- /* An attacker replay a wildcard answer with a different
- answer and overlay a genuine RR. To prove this
- hasn't happened, the answer must prove that
- the gennuine record doesn't exist. Check that here. */
- if (!(nsec_type = find_nsec_records(header, plen, &nsecs, &nsec_count, class1)))
- return STAT_BOGUS; /* No NSECs or bad packet */
-
- /* Note that we may not yet have validated the NSEC/NSEC3 RRsets. Since the check
- below returns either SECURE or BOGUS, that's not a problem. If the RRsets later fail
- we'll return BOGUS then. */
-
- if (nsec_type == T_NSEC)
- rc = prove_non_existence_nsec(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, type1, NULL);
- else
- rc = prove_non_existence_nsec3(header, plen, nsecs, nsec_count, daemon->workspacename,
- keyname, name, type1, wildname, NULL);
-
- if (rc == STAT_BOGUS)
- return rc;
- }
+ /* An attacker replay a wildcard answer with a different
+ answer and overlay a genuine RR. To prove this
+ hasn't happened, the answer must prove that
+ the gennuine record doesn't exist. Check that here.
+ Note that we may not yet have validated the NSEC/NSEC3 RRsets.
+ That's not a problem since if the RRsets later fail
+ we'll return BOGUS then. */
+ if (rc == STAT_SECURE_WILDCARD && !prove_non_existence(header, plen, keyname, name, type1, class1, wildname, NULL))
+ return STAT_BOGUS;
}
}
}
@@ -2124,14 +2106,13 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
/* For anything other than a DS record, this situation is OK if either
the answer is in an unsigned zone, or there's a NSEC records. */
- if (!(nsec_type = find_nsec_records(header, plen, &nsecs, &nsec_count, qclass)))
+ if (!prove_non_existence(header, plen, keyname, name, qtype, qclass, NULL, nons))
{
/* Empty DS without NSECS */
if (qtype == T_DS)
return STAT_BOGUS;
- rc = zone_status(name, qclass, keyname, now);
- if (rc != STAT_SECURE)
+ if ((rc = zone_status(name, qclass, keyname, now)) != STAT_SECURE)
{
if (class)
*class = qclass; /* Class for NEED_DS or NEED_DNSKEY */
@@ -2140,14 +2121,6 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
return STAT_BOGUS; /* signed zone, no NSECs */
}
-
- if (nsec_type == T_NSEC)
- rc = prove_non_existence_nsec(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, qtype, nons);
- else
- rc = prove_non_existence_nsec3(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, qtype, NULL, nons);
-
- if (rc != STAT_SECURE)
- return rc;
}
return STAT_SECURE;
--
1.7.10.4

View File

@@ -1,98 +0,0 @@
From 3b799c826db05fc2da1c6d15cbe372e394209d27 Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Thu, 17 Dec 2015 16:58:04 +0000
Subject: [PATCH] Fix brace botch in dnssec_validate_ds()
MIME-Version: 1.0
Content-Type: text/plain; charset=utf8
Content-Transfer-Encoding: 8bit
Thanks to Michał™pień for spotting this.
---
src/dnssec.c | 34 +++++++++++++++++-----------------
1 file changed, 17 insertions(+), 17 deletions(-)
diff --git a/src/dnssec.c b/src/dnssec.c
index ddae497..1f8c954 100644
--- a/src/dnssec.c
+++ b/src/dnssec.c
@@ -923,11 +923,11 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
/* The DNS packet is expected to contain the answer to a DNSKEY query.
Put all DNSKEYs in the answer which are valid into the cache.
return codes:
- STAT_OK Done, key(s) in cache.
- STAT_BOGUS No DNSKEYs found, which can be validated with DS,
- or self-sign for DNSKEY RRset is not valid, bad packet.
- STAT_NEED_DS DS records to validate a key not found, name in keyname
- STAT_NEED_DNSKEY DNSKEY records to validate a key not found, name in keyname
+ STAT_OK Done, key(s) in cache.
+ STAT_BOGUS No DNSKEYs found, which can be validated with DS,
+ or self-sign for DNSKEY RRset is not valid, bad packet.
+ STAT_NEED_DS DS records to validate a key not found, name in keyname
+ STAT_NEED_KEY DNSKEY records to validate a key not found, name in keyname
*/
int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class)
{
@@ -1224,13 +1224,13 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
}
p = psave;
-
- if (!ADD_RDLEN(header, p, plen, rdlen))
- return STAT_BOGUS; /* bad packet */
}
-
- cache_end_insert();
+ if (!ADD_RDLEN(header, p, plen, rdlen))
+ return STAT_BOGUS; /* bad packet */
}
+
+ cache_end_insert();
+
}
else
{
@@ -1828,10 +1828,10 @@ static int prove_non_existence(struct dns_header *header, size_t plen, char *key
/* Check signing status of name.
returns:
- STAT_SECURE zone is signed.
- STAT_INSECURE zone proved unsigned.
- STAT_NEED_DS require DS record of name returned in keyname.
- STAT_NEED_DNSKEY require DNSKEY record of name returned in keyname.
+ STAT_SECURE zone is signed.
+ STAT_INSECURE zone proved unsigned.
+ STAT_NEED_DS require DS record of name returned in keyname.
+ STAT_NEED_KEY require DNSKEY record of name returned in keyname.
name returned unaltered.
*/
static int zone_status(char *name, int class, char *keyname, time_t now)
@@ -2028,7 +2028,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
if (rc == STAT_SECURE)
rc = STAT_BOGUS;
if (class)
- *class = class1; /* Class for NEED_DS or NEED_DNSKEY */
+ *class = class1; /* Class for NEED_DS or NEED_KEY */
}
else
rc = STAT_INSECURE;
@@ -2045,7 +2045,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
{
/* Zone is insecure, don't need to validate RRset */
if (class)
- *class = class1; /* Class for NEED_DS or NEED_DNSKEY */
+ *class = class1; /* Class for NEED_DS or NEED_KEY */
return rc;
}
@@ -2115,7 +2115,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
if ((rc = zone_status(name, qclass, keyname, now)) != STAT_SECURE)
{
if (class)
- *class = qclass; /* Class for NEED_DS or NEED_DNSKEY */
+ *class = qclass; /* Class for NEED_DS or NEED_KEY */
return rc;
}
--
1.7.10.4

View File

@@ -1,145 +0,0 @@
From 14a4ae883d51130d33da7133287e8867c64bab65 Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Thu, 17 Dec 2015 17:23:03 +0000
Subject: [PATCH] Do a better job of determining which DNSSEC sig algos are
supported.
---
src/dnssec.c | 52 +++++++++++++++++++++++++++++++++++++---------------
1 file changed, 37 insertions(+), 15 deletions(-)
diff --git a/src/dnssec.c b/src/dnssec.c
index 1f8c954..82394ee 100644
--- a/src/dnssec.c
+++ b/src/dnssec.c
@@ -65,10 +65,9 @@ static char *algo_digest_name(int algo)
case 8: return "sha256";
case 10: return "sha512";
case 12: return "gosthash94";
-#ifndef NO_NETTLE_ECC
case 13: return "sha256";
case 14: return "sha384";
-#endif
+
default: return NULL;
}
}
@@ -129,13 +128,15 @@ static int hash_init(const struct nettle_hash *hash, void **ctxp, unsigned char
}
static int dnsmasq_rsa_verify(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len,
- unsigned char *digest, int algo)
+ unsigned char *digest, size_t digest_len, int algo)
{
unsigned char *p;
size_t exp_len;
static struct rsa_public_key *key = NULL;
static mpz_t sig_mpz;
+
+ (void)digest_len;
if (key == NULL)
{
@@ -181,7 +182,7 @@ static int dnsmasq_rsa_verify(struct blockdata *key_data, unsigned int key_len,
}
static int dnsmasq_dsa_verify(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len,
- unsigned char *digest, int algo)
+ unsigned char *digest, size_t digest_len, int algo)
{
unsigned char *p;
unsigned int t;
@@ -189,6 +190,8 @@ static int dnsmasq_dsa_verify(struct blockdata *key_data, unsigned int key_len,
static struct dsa_public_key *key = NULL;
static struct dsa_signature *sig_struct;
+ (void)digest_len;
+
if (key == NULL)
{
if (!(sig_struct = whine_malloc(sizeof(struct dsa_signature))) ||
@@ -292,26 +295,45 @@ static int dnsmasq_ecdsa_verify(struct blockdata *key_data, unsigned int key_len
}
#endif
-static int verify(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len,
- unsigned char *digest, size_t digest_len, int algo)
+static int (*verify_func(int algo))(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len,
+ unsigned char *digest, size_t digest_len, int algo)
{
- (void)digest_len;
-
+
+ /* Enure at runtime that we have support for this digest */
+ if (!hash_find(algo_digest_name(algo)))
+ return NULL;
+
+ /* This switch defines which sig algorithms we support, can't introspect Nettle for that. */
switch (algo)
{
case 1: case 5: case 7: case 8: case 10:
- return dnsmasq_rsa_verify(key_data, key_len, sig, sig_len, digest, algo);
+ return dnsmasq_rsa_verify;
case 3: case 6:
- return dnsmasq_dsa_verify(key_data, key_len, sig, sig_len, digest, algo);
+ return dnsmasq_dsa_verify;
#ifndef NO_NETTLE_ECC
case 13: case 14:
- return dnsmasq_ecdsa_verify(key_data, key_len, sig, sig_len, digest, digest_len, algo);
+ return dnsmasq_ecdsa_verify;
#endif
}
- return 0;
+ return NULL;
+}
+
+static int verify(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len,
+ unsigned char *digest, size_t digest_len, int algo)
+{
+
+ int (*func)(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len,
+ unsigned char *digest, size_t digest_len, int algo);
+
+ func = verify_func(algo);
+
+ if (!func)
+ return 0;
+
+ return (*func)(key_data, key_len, sig, sig_len, digest, digest_len, algo);
}
/* Convert from presentation format to wire format, in place.
@@ -732,7 +754,7 @@ static int explore_rrset(struct dns_header *header, size_t plen, int class, int
if (check_date_range(sig_inception, sig_expiration) &&
labels <= name_labels &&
type_covered == type &&
- algo_digest_name(algo))
+ verify_func(algo))
{
if (!expand_workspace(&sigs, &sig_sz, sigidx))
return 0;
@@ -1865,7 +1887,7 @@ static int zone_status(char *name, int class, char *keyname, time_t now)
if (crecp->flags & F_DNSSECOK)
return STAT_INSECURE; /* proved no DS here */
}
- else if (!ds_digest_name(crecp->addr.ds.digest) || !algo_digest_name(crecp->addr.ds.algo))
+ else if (!hash_find(ds_digest_name(crecp->addr.ds.digest)) || !verify_func(crecp->addr.ds.algo))
return STAT_INSECURE; /* algo we can't use - insecure */
else
secure_ds = 1;
@@ -1887,7 +1909,7 @@ static int zone_status(char *name, int class, char *keyname, time_t now)
do
{
- if (crecp->uid == (unsigned int)class && !algo_digest_name(crecp->addr.key.algo))
+ if (crecp->uid == (unsigned int)class && !verify_func(crecp->addr.key.algo))
return STAT_INSECURE;
}
while ((crecp = cache_find_by_name(crecp, keyname, now, F_DNSKEY)));
--
1.7.10.4

View File

@@ -1,643 +0,0 @@
From fa14bec83b2db010fd076910fddab56957b9375d Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Sun, 20 Dec 2015 17:12:16 +0000
Subject: [PATCH] Major tidy up of EDNS0 handling and computation/use of udp
packet size.
---
src/auth.c | 8 ++-
src/dnsmasq.h | 7 ++-
src/dnssec.c | 1 -
src/forward.c | 184 ++++++++++++++++++++++++++++++++++++++++----------------
src/netlink.c | 3 +-
src/rfc1035.c | 81 +++++++------------------
src/rrfilter.c | 2 +-
7 files changed, 168 insertions(+), 118 deletions(-)
diff --git a/src/auth.c b/src/auth.c
index 2b0b7d6..85bd5e7 100644
--- a/src/auth.c
+++ b/src/auth.c
@@ -81,7 +81,8 @@ int in_zone(struct auth_zone *zone, char *name, char **cut)
}
-size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t now, union mysockaddr *peer_addr, int local_query)
+size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t now, union mysockaddr *peer_addr,
+ int local_query, int do_bit, int have_pseudoheader)
{
char *name = daemon->namebuff;
unsigned char *p, *ansp;
@@ -820,6 +821,11 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
header->ancount = htons(anscount);
header->nscount = htons(authcount);
header->arcount = htons(0);
+
+ /* Advertise our packet size limit in our reply */
+ if (have_pseudoheader)
+ return add_pseudoheader(header, ansp - (unsigned char *)header, (unsigned char *)limit, daemon->edns_pktsz, 0, NULL, 0, do_bit);
+
return ansp - (unsigned char *)header;
}
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index 39a930c..abb34c5 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -1113,7 +1113,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *namebuff,
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, int *ad_reqd, int *do_bit);
+ time_t now, int ad_reqd, int do_bit, int have_pseudoheader);
int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
struct bogus_addr *addr, time_t now);
int check_for_ignored_address(struct dns_header *header, size_t qlen, struct bogus_addr *baddr);
@@ -1123,6 +1123,8 @@ int check_for_local_domain(char *name, time_t now);
unsigned int questions_crc(struct dns_header *header, size_t plen, char *buff);
size_t resize_packet(struct dns_header *header, size_t plen,
unsigned char *pheader, size_t hlen);
+size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit,
+ unsigned short udp_sz, int optno, unsigned char *opt, size_t optlen, int set_do);
size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysockaddr *l3);
size_t add_source_addr(struct dns_header *header, size_t plen, char *limit, union mysockaddr *source);
#ifdef HAVE_DNSSEC
@@ -1141,7 +1143,8 @@ int private_net(struct in_addr addr, int ban_localhost);
/* auth.c */
#ifdef HAVE_AUTH
size_t answer_auth(struct dns_header *header, char *limit, size_t qlen,
- time_t now, union mysockaddr *peer_addr, int local_query);
+ time_t now, union mysockaddr *peer_addr, int local_query,
+ int do_bit, int have_pseudoheader);
int in_zone(struct auth_zone *zone, char *name, char **cut);
#endif
diff --git a/src/dnssec.c b/src/dnssec.c
index 82394ee..299ca64 100644
--- a/src/dnssec.c
+++ b/src/dnssec.c
@@ -67,7 +67,6 @@ static char *algo_digest_name(int algo)
case 12: return "gosthash94";
case 13: return "sha256";
case 14: return "sha384";
-
default: return NULL;
}
}
diff --git a/src/forward.c b/src/forward.c
index 3e801c8..041353c 100644
--- a/src/forward.c
+++ b/src/forward.c
@@ -244,7 +244,6 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
void *hash = &crc;
#endif
unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL);
- unsigned char *pheader;
(void)do_bit;
@@ -264,7 +263,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
there's no point retrying the query, retry the key query instead...... */
if (forward->blocking_query)
{
- int fd;
+ int fd, is_sign;
+ unsigned char *pheader;
forward->flags &= ~FREC_TEST_PKTSZ;
@@ -276,8 +276,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
plen = forward->stash_len;
- if (find_pseudoheader(header, plen, NULL, &pheader, NULL))
- PUTSHORT((forward->flags & FREC_TEST_PKTSZ) ? SAFE_PKTSZ : forward->sentto->edns_pktsz, pheader);
+ if (find_pseudoheader(header, plen, NULL, &pheader, &is_sign) && !is_sign)
+ PUTSHORT(SAFE_PKTSZ, pheader);
if (forward->sentto->addr.sa.sa_family == AF_INET)
log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, "retry", (struct all_addr *)&forward->sentto->addr.in.sin_addr, "dnssec");
@@ -394,32 +394,40 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
forward->log_id = daemon->log_id;
if (option_bool(OPT_ADD_MAC))
- plen = add_mac(header, plen, ((char *) header) + daemon->packet_buff_sz, &forward->source);
-
+ {
+ size_t new = add_mac(header, plen, ((char *) header) + daemon->packet_buff_sz, &forward->source);
+ if (new != plen)
+ {
+ plen = new;
+ forward->flags |= FREC_ADDED_PHEADER;
+ }
+ }
+
if (option_bool(OPT_CLIENT_SUBNET))
{
size_t new = add_source_addr(header, plen, ((char *) header) + daemon->packet_buff_sz, &forward->source);
if (new != plen)
{
plen = new;
- forward->flags |= FREC_HAS_SUBNET;
+ forward->flags |= FREC_HAS_SUBNET | FREC_ADDED_PHEADER;
}
}
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID))
{
- size_t new_plen = add_do_bit(header, plen, ((char *) header) + daemon->packet_buff_sz);
+ size_t new = add_do_bit(header, plen, ((char *) header) + daemon->packet_buff_sz);
+ if (new != plen)
+ forward->flags |= FREC_ADDED_PHEADER;
+
+ plen = new;
+
/* 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
@@ -469,10 +477,23 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
}
#endif
}
-
- if (find_pseudoheader(header, plen, NULL, &pheader, NULL))
- PUTSHORT((forward->flags & FREC_TEST_PKTSZ) ? SAFE_PKTSZ : start->edns_pktsz, pheader);
+#ifdef HAVE_DNSSEC
+ if (option_bool(OPT_DNSSEC_VALID) && !do_bit)
+ {
+ /* Difficult one here. If our client didn't send EDNS0, we will have set the UDP
+ packet size to 512. But that won't provide space for the RRSIGS in many cases.
+ The RRSIGS will be stripped out before the answer goes back, so the packet should
+ shrink again. So, if we added a do-bit, bump the udp packet size to the value
+ known to be OK for this server. Maybe check returned size after stripping and set
+ the truncated bit? */
+ unsigned char *pheader;
+ int is_sign;
+ if (find_pseudoheader(header, plen, NULL, &pheader, &is_sign))
+ PUTSHORT(start->edns_pktsz, pheader);
+ }
+#endif
+
if (retry_send(sendto(fd, (char *)header, plen, 0,
&start->addr.sa,
sa_len(&start->addr))))
@@ -563,30 +584,34 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
}
#endif
- /* If upstream is advertising a larger UDP packet size
- than we allow, trim it so that we don't get overlarge
- requests for the client. We can't do this for signed packets. */
-
if ((pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign)))
{
- 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)
+ if (!is_sign)
{
- pheader = 0;
- header->arcount = htons(0);
+ if (added_pheader)
+ {
+ /* client didn't send EDNS0, we added one, strip it off before returning answer. */
+ n = rrfilter(header, n, 0);
+ pheader = NULL;
+ }
+ else
+ {
+ /* If upstream is advertising a larger UDP packet size
+ than we allow, trim it so that we don't get overlarge
+ requests for the client. We can't do this for signed packets. */
+ unsigned short udpsz;
+ unsigned char *psave = sizep;
+
+ GETSHORT(udpsz, sizep);
+ if (udpsz > daemon->edns_pktsz)
+ PUTSHORT(daemon->edns_pktsz, psave);
+ }
}
}
@@ -655,14 +680,16 @@ 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) && 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 = rrfilter(header, n, 1);
+ {
+ header->hb4 &= ~HB4_AD;
+
+ 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 = rrfilter(header, n, 1);
+ }
#endif
/* do this after extract_addresses. Ensure NODATA reply and remove
@@ -761,8 +788,14 @@ 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_AA | HB3_TC);
- header->hb4 &= ~(HB4_RA | HB4_RCODE);
- forward_query(-1, NULL, NULL, 0, header, nn, now, forward, 0, 0);
+ header->hb4 &= ~(HB4_RA | HB4_RCODE | HB4_CD | HB4_AD);
+ if (forward->flags |= FREC_CHECKING_DISABLED)
+ header->hb4 |= HB4_CD;
+ if (forward->flags |= FREC_AD_QUESTION)
+ header->hb4 |= HB4_AD;
+ if (forward->flags & FREC_DO_QUESTION)
+ add_do_bit(header, nn, (char *)pheader + plen);
+ forward_query(-1, NULL, NULL, 0, header, nn, now, forward, forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION);
return;
}
}
@@ -1007,12 +1040,13 @@ void receive_query(struct listener *listen, time_t now)
{
struct dns_header *header = (struct dns_header *)daemon->packet;
union mysockaddr source_addr;
- unsigned short type;
+ unsigned char *pheader;
+ unsigned short type, udp_size = PACKETSZ; /* default if no EDNS0 */
struct all_addr dst_addr;
struct in_addr netmask, dst_addr_4;
size_t m;
ssize_t n;
- int if_index = 0, auth_dns = 0;
+ int if_index = 0, auth_dns = 0, do_bit = 0, have_pseudoheader = 0;
#ifdef HAVE_AUTH
int local_auth = 0;
#endif
@@ -1279,10 +1313,30 @@ void receive_query(struct listener *listen, time_t now)
#endif
}
+ if (find_pseudoheader(header, (size_t)n, NULL, &pheader, NULL))
+ {
+ unsigned short flags;
+
+ have_pseudoheader = 1;
+ GETSHORT(udp_size, pheader);
+ pheader += 2; /* ext_rcode */
+ GETSHORT(flags, pheader);
+
+ if (flags & 0x8000)
+ do_bit = 1;/* do bit */
+
+ /* If the client provides an EDNS0 UDP size, use that to limit our reply.
+ (bounded by the maximum configured). If no EDNS0, then it
+ defaults to 512 */
+ if (udp_size > daemon->edns_pktsz)
+ udp_size = daemon->edns_pktsz;
+ }
+
#ifdef HAVE_AUTH
if (auth_dns)
{
- m = answer_auth(header, ((char *) header) + daemon->packet_buff_sz, (size_t)n, now, &source_addr, local_auth);
+ m = answer_auth(header, ((char *) header) + udp_size, (size_t)n, now, &source_addr,
+ local_auth, do_bit, have_pseudoheader);
if (m >= 1)
{
send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND),
@@ -1293,9 +1347,13 @@ 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, &ad_reqd, &do_bit);
+ int ad_reqd = do_bit;
+ /* RFC 6840 5.7 */
+ if (header->hb4 & HB4_AD)
+ ad_reqd = 1;
+
+ m = answer_request(header, ((char *) header) + udp_size, (size_t)n,
+ dst_addr_4, netmask, now, ad_reqd, do_bit, have_pseudoheader);
if (m >= 1)
{
@@ -1397,7 +1455,7 @@ unsigned char *tcp_request(int confd, time_t now,
#ifdef HAVE_AUTH
int local_auth = 0;
#endif
- int checking_disabled, ad_question, do_bit, added_pheader = 0;
+ int checking_disabled, do_bit, added_pheader = 0, have_pseudoheader = 0;
int check_subnet, no_cache_dnssec = 0, cache_secure = 0, bogusanswer = 0;
size_t m;
unsigned short qtype;
@@ -1414,6 +1472,7 @@ unsigned char *tcp_request(int confd, time_t now,
union mysockaddr peer_addr;
socklen_t peer_len = sizeof(union mysockaddr);
int query_count = 0;
+ unsigned char *pheader;
if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) == -1)
return packet;
@@ -1508,15 +1567,35 @@ unsigned char *tcp_request(int confd, time_t now,
else
dst_addr_4.s_addr = 0;
+ do_bit = 0;
+
+ if (find_pseudoheader(header, (size_t)size, NULL, &pheader, NULL))
+ {
+ unsigned short flags;
+
+ have_pseudoheader = 1;
+ pheader += 4; /* udp_size, ext_rcode */
+ GETSHORT(flags, pheader);
+
+ if (flags & 0x8000)
+ do_bit = 1;/* do bit */
+ }
+
#ifdef HAVE_AUTH
if (auth_dns)
- m = answer_auth(header, ((char *) header) + 65536, (size_t)size, now, &peer_addr, local_auth);
+ m = answer_auth(header, ((char *) header) + 65536, (size_t)size, now, &peer_addr,
+ local_auth, do_bit, have_pseudoheader);
else
#endif
{
- /* m > 0 if answered from cache */
- m = answer_request(header, ((char *) header) + 65536, (size_t)size,
- dst_addr_4, netmask, now, &ad_question, &do_bit);
+ int ad_reqd = do_bit;
+ /* RFC 6840 5.7 */
+ if (header->hb4 & HB4_AD)
+ ad_reqd = 1;
+
+ /* m > 0 if answered from cache */
+ m = answer_request(header, ((char *) header) + 65536, (size_t)size,
+ dst_addr_4, netmask, now, ad_reqd, do_bit, have_pseudoheader);
/* Do this by steam now we're not in the select() loop */
check_log_writer(1);
@@ -1615,6 +1694,7 @@ unsigned char *tcp_request(int confd, time_t now,
}
#ifdef HAVE_DNSSEC
+ added_pheader = 0;
if (option_bool(OPT_DNSSEC_VALID))
{
size_t new_size = add_do_bit(header, size, ((char *) header) + 65536);
@@ -1719,7 +1799,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, bogusanswer,
- ad_question, do_bit, added_pheader, check_subnet, &peer_addr);
+ ad_reqd, do_bit, added_pheader, check_subnet, &peer_addr);
break;
}
diff --git a/src/netlink.c b/src/netlink.c
index 753784d..3376d68 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -288,7 +288,8 @@ int iface_enumerate(int family, void *parm, int (*callback)())
rta = RTA_NEXT(rta, len1);
}
- if (inaddr && mac && callback_ok)
+ if (!(neigh->ndm_state & (NUD_NOARP | NUD_INCOMPLETE | NUD_FAILED)) &&
+ inaddr && mac && callback_ok)
if (!((*callback)(neigh->ndm_family, inaddr, mac, maclen, parm)))
callback_ok = 0;
}
diff --git a/src/rfc1035.c b/src/rfc1035.c
index 188d05f..18858a8 100644
--- a/src/rfc1035.c
+++ b/src/rfc1035.c
@@ -489,8 +489,8 @@ struct macparm {
union mysockaddr *l3;
};
-static size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit,
- int optno, unsigned char *opt, size_t optlen, int set_do)
+size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit,
+ unsigned short udp_sz, int optno, unsigned char *opt, size_t optlen, int set_do)
{
unsigned char *lenp, *datap, *p;
int rdlen, is_sign;
@@ -508,7 +508,7 @@ static size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned
return plen;
*p++ = 0; /* empty name */
PUTSHORT(T_OPT, p);
- PUTSHORT(SAFE_PKTSZ, p); /* max packet length, this will be overwritten */
+ PUTSHORT(udp_sz, p); /* max packet length, 512 if not given in EDNS0 header */
PUTSHORT(0, p); /* extended RCODE and version */
PUTSHORT(set_do ? 0x8000 : 0, p); /* DO flag */
lenp = p;
@@ -594,7 +594,7 @@ static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *p
if (!match)
return 1; /* continue */
- parm->plen = add_pseudoheader(parm->header, parm->plen, parm->limit, EDNS0_OPTION_MAC, (unsigned char *)mac, maclen, 0);
+ parm->plen = add_pseudoheader(parm->header, parm->plen, parm->limit, PACKETSZ, EDNS0_OPTION_MAC, (unsigned char *)mac, maclen, 0);
return 0; /* done */
}
@@ -603,12 +603,6 @@ size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysock
{
struct macparm parm;
-/* Must have an existing pseudoheader as the only ar-record,
- or have no ar-records. Must also not be signed */
-
- if (ntohs(header->arcount) > 1)
- return plen;
-
parm.header = header;
parm.limit = (unsigned char *)limit;
parm.plen = plen;
@@ -699,13 +693,13 @@ size_t add_source_addr(struct dns_header *header, size_t plen, char *limit, unio
struct subnet_opt opt;
len = calc_subnet_opt(&opt, source);
- return add_pseudoheader(header, plen, (unsigned char *)limit, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0);
+ return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0);
}
#ifdef HAVE_DNSSEC
size_t add_do_bit(struct dns_header *header, size_t plen, char *limit)
{
- return add_pseudoheader(header, plen, (unsigned char *)limit, 0, NULL, 0, 1);
+ return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, 0, NULL, 0, 1);
}
#endif
@@ -1525,16 +1519,16 @@ 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, int *ad_reqd, int *do_bit)
+ time_t now, int ad_reqd, int do_bit, int have_pseudoheader)
{
char *name = daemon->namebuff;
- unsigned char *p, *ansp, *pheader;
+ unsigned char *p, *ansp;
unsigned int qtype, qclass;
struct all_addr addr;
int nameoffset;
unsigned short flag;
int q, ans, anscount = 0, addncount = 0;
- int dryrun = 0, sec_reqd = 0, have_pseudoheader = 0;
+ int dryrun = 0;
struct crec *crecp;
int nxdomain = 0, auth = 1, trunc = 0, sec_data = 1;
struct mx_srv_record *rec;
@@ -1550,35 +1544,11 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
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 additional data section then it will be overwritten by
partial replies, so we have to do a dry run to see if we can answer
the query. */
-
if (ntohs(header->arcount) != 0)
- {
- dryrun = 1;
-
- /* If there's an additional section, there might be an EDNS(0) pseudoheader */
- if (find_pseudoheader(header, qlen, NULL, &pheader, NULL))
- {
- unsigned short flags;
-
- have_pseudoheader = 1;
-
- pheader += 4; /* udp size, ext_rcode */
- GETSHORT(flags, pheader);
-
- if ((sec_reqd = flags & 0x8000))
- {
- *do_bit = 1;/* do bit */
- *ad_reqd = 1;
- }
- }
- }
+ dryrun = 1;
for (rec = daemon->mxnames; rec; rec = rec->next)
rec->offset = 0;
@@ -1603,11 +1573,6 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
GETSHORT(qtype, p);
GETSHORT(qclass, p);
- /* Don't filter RRSIGS from answers to ANY queries, even if do-bit
- not set. */
- if (qtype == T_ANY)
- *do_bit = 1;
-
ans = 0; /* have we answered this question */
if (qtype == T_TXT || qtype == T_ANY)
@@ -1739,7 +1704,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
the zone is unsigned, which implies that we're doing
validation. */
if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) ||
- !sec_reqd ||
+ !do_bit ||
(option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK)))
{
do
@@ -1927,7 +1892,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
}
/* If the client asked for DNSSEC don't use cached data. */
- if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || !sec_reqd || !(crecp->flags & F_DNSSECOK))
+ if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || !do_bit || !(crecp->flags & F_DNSSECOK))
do
{
/* don't answer wildcard queries with data not from /etc/hosts
@@ -1961,17 +1926,12 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (crecp->flags & F_NEG)
{
- /* We don't cache NSEC records, so if a DNSSEC-validated negative answer
- is cached and the client wants DNSSEC, forward rather than answering from the cache */
- if (!sec_reqd || !(crecp->flags & F_DNSSECOK))
- {
- ans = 1;
- auth = 0;
- if (crecp->flags & F_NXDOMAIN)
- nxdomain = 1;
- if (!dryrun)
- log_query(crecp->flags, name, NULL, NULL);
- }
+ ans = 1;
+ auth = 0;
+ if (crecp->flags & F_NXDOMAIN)
+ nxdomain = 1;
+ if (!dryrun)
+ log_query(crecp->flags, name, NULL, NULL);
}
else
{
@@ -2209,10 +2169,11 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
len = ansp - (unsigned char *)header;
+ /* Advertise our packet size limit in our reply */
if (have_pseudoheader)
- len = add_pseudoheader(header, len, (unsigned char *)limit, 0, NULL, 0, sec_reqd);
+ len = add_pseudoheader(header, len, (unsigned char *)limit, daemon->edns_pktsz, 0, NULL, 0, do_bit);
- if (*ad_reqd && sec_data)
+ if (ad_reqd && sec_data)
header->hb4 |= HB4_AD;
else
header->hb4 &= ~HB4_AD;
diff --git a/src/rrfilter.c b/src/rrfilter.c
index ae12261..b26b39f 100644
--- a/src/rrfilter.c
+++ b/src/rrfilter.c
@@ -243,7 +243,7 @@ size_t rrfilter(struct dns_header *header, size_t plen, int mode)
for (p = rrs[0], i = 1; i < rr_found; i += 2)
{
unsigned char *start = rrs[i];
- unsigned char *end = (i != rr_found - 1) ? rrs[i+1] : ((unsigned char *)(header+1)) + plen;
+ unsigned char *end = (i != rr_found - 1) ? rrs[i+1] : ((unsigned char *)header) + plen;
memmove(p, start, end-start);
p += end-start;
--
1.7.10.4

View File

@@ -1,262 +0,0 @@
From d67ecac59d58f249707d26e38d49c29b552af4d8 Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Sun, 20 Dec 2015 20:44:23 +0000
Subject: [PATCH] More tweaks in handling unknown DNSSEC algorithms.
---
src/dnssec.c | 128 +++++++++++++++++++++++++++++-----------------------------
1 file changed, 63 insertions(+), 65 deletions(-)
diff --git a/src/dnssec.c b/src/dnssec.c
index 299ca64..e09f304 100644
--- a/src/dnssec.c
+++ b/src/dnssec.c
@@ -70,7 +70,17 @@ static char *algo_digest_name(int algo)
default: return NULL;
}
}
-
+
+/* http://www.iana.org/assignments/dnssec-nsec3-parameters/dnssec-nsec3-parameters.xhtml */
+static char *nsec3_digest_name(int digest)
+{
+ switch (digest)
+ {
+ case 1: return "sha1";
+ default: return NULL;
+ }
+}
+
/* Find pointer to correct hash function in nettle library */
static const struct nettle_hash *hash_find(char *name)
{
@@ -667,7 +677,6 @@ static int explore_rrset(struct dns_header *header, size_t plen, int class, int
static int rrset_sz = 0, sig_sz = 0;
unsigned char *p;
int rrsetidx, sigidx, j, rdlen, res;
- int name_labels = count_labels(name); /* For 4035 5.3.2 check */
int gotkey = 0;
if (!(p = skip_questions(header, plen)))
@@ -678,7 +687,7 @@ static int explore_rrset(struct dns_header *header, size_t plen, int class, int
j != 0; j--)
{
unsigned char *pstart, *pdata;
- int stype, sclass, algo, type_covered, labels, sig_expiration, sig_inception;
+ int stype, sclass, type_covered;
pstart = p;
@@ -712,12 +721,7 @@ static int explore_rrset(struct dns_header *header, size_t plen, int class, int
return 0; /* bad packet */
GETSHORT(type_covered, p);
- algo = *p++;
- labels = *p++;
- p += 4; /* orig_ttl */
- GETLONG(sig_expiration, p);
- GETLONG(sig_inception, p);
- p += 2; /* key_tag */
+ p += 16; /* algo, labels, orig_ttl, sig_expiration, sig_inception, key_tag */
if (gotkey)
{
@@ -749,11 +753,8 @@ static int explore_rrset(struct dns_header *header, size_t plen, int class, int
}
}
- /* Don't count signatures for algos we don't support */
- if (check_date_range(sig_inception, sig_expiration) &&
- labels <= name_labels &&
- type_covered == type &&
- verify_func(algo))
+
+ if (type_covered == type)
{
if (!expand_workspace(&sigs, &sig_sz, sigidx))
return 0;
@@ -795,7 +796,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
char *name, char *keyname, char **wildcard_out, struct blockdata *key, int keylen, int algo_in, int keytag_in)
{
unsigned char *p;
- int rdlen, j, name_labels;
+ int rdlen, j, name_labels, sig_expiration, sig_inception;
struct crec *crecp = NULL;
int algo, labels, orig_ttl, key_tag;
u16 *rr_desc = rrfilter_desc(type);
@@ -828,13 +829,16 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
algo = *p++;
labels = *p++;
GETLONG(orig_ttl, p);
- p += 8; /* sig_expiration, sig_inception already checked */
+ GETLONG(sig_expiration, p);
+ GETLONG(sig_inception, p);
GETSHORT(key_tag, p);
if (!extract_name(header, plen, &p, keyname, 1, 0))
return STAT_BOGUS;
- if (!(hash = hash_find(algo_digest_name(algo))) ||
+ if (!check_date_range(sig_inception, sig_expiration) ||
+ labels > name_labels ||
+ !(hash = hash_find(algo_digest_name(algo))) ||
!hash_init(hash, &ctx, &digest))
continue;
@@ -1112,7 +1116,10 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
else
{
a.addr.keytag = keytag;
- log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %u");
+ if (verify_func(algo))
+ log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %u");
+ else
+ log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %u (not supported)");
recp1->addr.key.keylen = rdlen - 4;
recp1->addr.key.keydata = key;
@@ -1235,7 +1242,11 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
else
{
a.addr.keytag = keytag;
- log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %u");
+ if (hash_find(ds_digest_name(digest)) && verify_func(algo))
+ log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %u");
+ else
+ log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %u (not supported)");
+
crecp->addr.ds.digest = digest;
crecp->addr.ds.keydata = key;
crecp->addr.ds.algo = algo;
@@ -1660,7 +1671,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
*nons = 1;
/* Look though the NSEC3 records to find the first one with
- an algorithm we support (currently only algo == 1).
+ an algorithm we support.
Take the algo, iterations, and salt of that record
as the ones we're going to use, and prune any
@@ -1674,7 +1685,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
p += 10; /* type, class, TTL, rdlen */
algo = *p++;
- if (algo == 1)
+ if ((hash = hash_find(nsec3_digest_name(algo))))
break; /* known algo */
}
@@ -1724,10 +1735,6 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
nsecs[i] = nsec3p;
}
- /* Algo is checked as 1 above */
- if (!(hash = hash_find("sha1")))
- return 0;
-
if ((digest_len = hash_name(name, &digest, hash, salt, salt_len, iterations)) == 0)
return 0;
@@ -1843,8 +1850,10 @@ static int prove_non_existence(struct dns_header *header, size_t plen, char *key
if (type_found == T_NSEC)
return prove_non_existence_nsec(header, plen, nsecset, nsecs_found, daemon->workspacename, keyname, name, qtype, nons);
- else
+ else if (type_found == T_NSEC3)
return prove_non_existence_nsec3(header, plen, nsecset, nsecs_found, daemon->workspacename, keyname, name, qtype, wildname, nons);
+ else
+ return 0;
}
/* Check signing status of name.
@@ -1857,7 +1866,7 @@ static int prove_non_existence(struct dns_header *header, size_t plen, char *key
*/
static int zone_status(char *name, int class, char *keyname, time_t now)
{
- int secure_ds, name_start = strlen(name);
+ int name_start = strlen(name);
struct crec *crecp;
char *p;
@@ -1867,51 +1876,40 @@ static int zone_status(char *name, int class, char *keyname, time_t now)
if (!(crecp = cache_find_by_name(NULL, keyname, now, F_DS)))
return STAT_NEED_DS;
+
+ /* F_DNSSECOK misused in DS cache records to non-existance of NS record.
+ F_NEG && !F_DNSSECOK implies that we've proved there's no DS record here,
+ but that's because there's no NS record either, ie this isn't the start
+ of a zone. We only prove that the DNS tree below a node is unsigned when
+ we prove that we're at a zone cut AND there's no DS record. */
+ if (crecp->flags & F_NEG)
+ {
+ if (crecp->flags & F_DNSSECOK)
+ return STAT_INSECURE; /* proved no DS here */
+ }
else
{
- secure_ds = 0;
-
+ int gotone = 0;
+
+ /* If all the DS records have digest and/or sig algos we don't support,
+ then the zone is insecure. Note that if an algo
+ appears in the DS, then RRSIGs for that algo MUST
+ exist for each RRset: 4035 para 2.2 So if we find
+ a DS here with digest and sig we can do, we're entitled
+ to assume we can validate the zone and if we can't later,
+ because an RRSIG is missing we return BOGUS.
+ */
do
{
- if (crecp->uid == (unsigned int)class)
- {
- /* F_DNSSECOK misused in DS cache records to non-existance of NS record.
- F_NEG && !F_DNSSECOK implies that we've proved there's no DS record here,
- but that's because there's no NS record either, ie this isn't the start
- of a zone. We only prove that the DNS tree below a node is unsigned when
- we prove that we're at a zone cut AND there's no DS record.
- */
- if (crecp->flags & F_NEG)
- {
- if (crecp->flags & F_DNSSECOK)
- return STAT_INSECURE; /* proved no DS here */
- }
- else if (!hash_find(ds_digest_name(crecp->addr.ds.digest)) || !verify_func(crecp->addr.ds.algo))
- return STAT_INSECURE; /* algo we can't use - insecure */
- else
- secure_ds = 1;
- }
+ if (crecp->uid == (unsigned int)class &&
+ hash_find(ds_digest_name(crecp->addr.ds.digest)) &&
+ verify_func(crecp->addr.ds.algo))
+ gotone = 1;
}
while ((crecp = cache_find_by_name(crecp, keyname, now, F_DS)));
- }
-
- if (secure_ds)
- {
- /* We've found only DS records that attest to the DNSKEY RRset in the zone, so we believe
- that RRset is good. Furthermore the DNSKEY whose hash is proved by the DS record is
- one we can use. However the DNSKEY RRset may contain more than one key and
- one of the other keys may use an algorithm we don't support. If that's
- the case the zone is insecure for us. */
-
- if (!(crecp = cache_find_by_name(NULL, keyname, now, F_DNSKEY)))
- return STAT_NEED_KEY;
- do
- {
- if (crecp->uid == (unsigned int)class && !verify_func(crecp->addr.key.algo))
- return STAT_INSECURE;
- }
- while ((crecp = cache_find_by_name(crecp, keyname, now, F_DNSKEY)));
+ if (!gotone)
+ return STAT_INSECURE;
}
if (name_start == 0)
--
1.7.10.4

View File

@@ -1,27 +0,0 @@
From 3e86d316c4bb406ed813aa5256615c8a95cac6d8 Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Sun, 20 Dec 2015 20:50:05 +0000
Subject: [PATCH] Nasty, rare and obscure off-by-one in DNSSEC hostname_cmp().
---
src/dnssec.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/dnssec.c b/src/dnssec.c
index e09f304..29848e1 100644
--- a/src/dnssec.c
+++ b/src/dnssec.c
@@ -1394,8 +1394,8 @@ static int hostname_cmp(const char *a, const char *b)
if (sb == b)
return 1;
- ea = sa--;
- eb = sb--;
+ ea = --sa;
+ eb = --sb;
}
}
--
1.7.10.4

View File

@@ -1,39 +0,0 @@
From a86fdf437ecc29398f9715ceb5240442a17ac014 Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Sun, 20 Dec 2015 21:19:20 +0000
Subject: [PATCH] Minor tweak to previous commit.
---
src/dnssec.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/src/dnssec.c b/src/dnssec.c
index 29848e1..9fa64b6 100644
--- a/src/dnssec.c
+++ b/src/dnssec.c
@@ -1889,8 +1889,6 @@ static int zone_status(char *name, int class, char *keyname, time_t now)
}
else
{
- int gotone = 0;
-
/* If all the DS records have digest and/or sig algos we don't support,
then the zone is insecure. Note that if an algo
appears in the DS, then RRSIGs for that algo MUST
@@ -1904,11 +1902,11 @@ static int zone_status(char *name, int class, char *keyname, time_t now)
if (crecp->uid == (unsigned int)class &&
hash_find(ds_digest_name(crecp->addr.ds.digest)) &&
verify_func(crecp->addr.ds.algo))
- gotone = 1;
+ break;
}
while ((crecp = cache_find_by_name(crecp, keyname, now, F_DS)));
- if (!gotone)
+ if (!crecp)
return STAT_INSECURE;
}
--
1.7.10.4

View File

@@ -1,39 +0,0 @@
From ce5732e84fc46d7f99c152f736cfb4ef5ec98a01 Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Sun, 20 Dec 2015 21:39:19 +0000
Subject: [PATCH] NSEC3 check: RFC5155 para 8.2
---
src/dnssec.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/src/dnssec.c b/src/dnssec.c
index 9fa64b6..486e422 100644
--- a/src/dnssec.c
+++ b/src/dnssec.c
@@ -1704,7 +1704,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
for (i = 0; i < nsec_count; i++)
{
unsigned char *nsec3p = nsecs[i];
- int this_iter;
+ int this_iter, flags;
nsecs[i] = NULL; /* Speculative, will be restored if OK. */
@@ -1716,8 +1716,12 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
if (*p++ != algo)
continue;
- p++; /* flags */
+ flags = *p++; /* flags */
+ /* 5155 8.2 */
+ if (flags != 0 && flags != 1)
+ continue;
+
GETSHORT(this_iter, p);
if (this_iter != iterations)
continue;
--
1.7.10.4