Compare commits

...

19 Commits

Author SHA1 Message Date
Simon Kelley
ae57f84061 Do a better job of 942a35f517 2025-04-18 14:01:14 +01:00
Simon Kelley
0620309b73 Revise negative DNS caching to better comply with RFC2308. 2025-04-16 21:29:42 +01:00
Opty
942a35f517 Silence compiler warnings. 2025-04-16 16:00:47 +01:00
Simon Kelley
83658efbf4 Fix occasional crashes with DNSSEC and large nunbers of --address configs.
Commit 3e659bd4ec removed the concept of
an usptream DNS server which is capable of DNSSEC: they are all
(at least in theory) now usable. As a very unfortunate side-effect,
this removed the filter that ensured that dnssec_server() ONLY
returns servers, and not domains with literal addresses.

If we try and do DNSSEC queries for a domain, and there's
a --address line which matches the domain, then dnssec_server()
will return that. This would break DNSSEC validation, but that's
turns out not to matter, because under these circumstances
dnssec_server() will probably return an out-of-bounds index into
the servers[] array, and the process dies with SIGSEGV.

Many thanks to the hard workers at the Tomato project who
found this bug and provided enough information to diagnose it.
2025-04-04 22:01:51 +01:00
Paul Donald
b0b4d90b6a Multiple typo and spelling fixes. 2025-03-29 21:41:40 +00:00
Simon Kelley
bdce03f928 DNAME documentation update. 2025-03-15 17:02:02 +00:00
Simon Kelley
d390dc0338 Implement RFC6672 para 5.3.2. check for DNAME.
Also fix overflow checking of NSEC type maps.
2025-03-15 16:47:55 +00:00
Simon Kelley
105c25e561 Fix DNSSEC and DNAME.
Do the correct things to validate replies which
include a DNAME record.

Thanks to Graham Clinch for pointing this out.
2025-03-15 09:05:47 +00:00
Simon Kelley
67e07b7fe8 Make extract_name() easier to call operating on first name in message. 2025-03-14 15:12:46 +00:00
Simon Kelley
f5659b406b Move find_pseudoheader() before add_edns0_config() in TCP codepath.
There's no point in checking if the query has edns0 headers _after_
adding our own.

This has the affect that if --add-cpe-id or --add-subnet or their friends
are configured,  a query via TCP without EDNS0 will get an answer with EDNS0.

It's highly unlikely that this breaks anything, but it is incorrect.

Thanks to  Tijs Van Buggenhout  for spotting this.
2025-03-14 15:12:46 +00:00
Simon Kelley
484fea238a Silence compiler warning. 2025-03-14 15:12:46 +00:00
Simon Kelley
1e587bec57 Silence compiler warning. 2025-03-14 15:12:45 +00:00
Simon Kelley
581c201aa8 Avoid division by zero with unlucky choices of max-port and min-port. 2025-03-14 15:12:45 +00:00
Simon Kelley
5487f6979e Fix (benign) use of uninitialised data. 2025-03-14 15:12:45 +00:00
Simon Kelley
99f12e3541 Default --dump-mask to all-on, rather than all-off. 2025-03-14 15:12:45 +00:00
Simon Kelley
7c1212e3d1 Fix query-combining for queries with class other than IN.
Along the way, use of extract_request() and extract_name() got further
refined.
2025-03-14 15:12:45 +00:00
Simon Kelley
0ccbdf8087 Make extract_name() easier to call operating on first name in message. 2025-03-14 15:12:45 +00:00
Simon Kelley
57f0489f38 Redesign the interaction between DNSSEC vaildation and per-domain servers.
This should just work in all cases now. If the normal chain-of-trust exists into
the delegated domain then whether the domain is signed or not, DNSSEC
validation will function normally. In the case the delgated domain
is an "overlay" on top of the global DNS and no NS and/or DS records
exist connecting it to the global dns, then if the domain is
unsigned the situation will be handled by synthesising a
proof-of-non-existance-of-DS for the domain and queries will be
answered unvalidated; this action will be logged. A signed domain
without chain-of-trust can be validated if a suitable trust-anchor
is provided using --trust-anchor.

Thanks to Uwe Kleine-König for prompting this change, and contributing
valuable insights into what could be improved.
2025-03-14 15:12:45 +00:00
Simon Kelley
3e659bd4ec Remove the concept of "DNSSEC incapable servers".
We're going to replace this with configured or extrapolated DS records.
2025-03-14 15:12:45 +00:00
20 changed files with 515 additions and 357 deletions

View File

@@ -1,3 +1,25 @@
version 2.92
Redesign the interaction between DNSSEC validation and per-domain
servers, specified as --server=/<domain>/<ip-address>. This should
just work in all cases now. If the normal chain-of-trust exists into
the delegated domain then whether the domain is signed or not, DNSSEC
validation will function normally. In the case the delegated domain
is an "overlay" on top of the global DNS and no NS and/or DS records
exist connecting it to the global dns, then if the domain is
unsigned the situation will be handled by synthesising a
proof-of-non-existence-of-DS for the domain and queries will be
answered unvalidated; this action will be logged. A signed domain
without chain-of-trust can be validated if a suitable trust-anchor
is provided using --trust-anchor. This change should be backwards
compatible for all existing working configurations; it extends the
space of possible configurations which are functional.
Fix a couple of problems with DNSSEC validation and DNAME. One
could cause validation failure on correct domains, and the other
would fail to spot an invalid domain. Thanks to Graham Clinch
for spotting the problem.
version 2.91
Fix spurious "resource limit exceeded messages". Thanks to
Dominik Derigs for the bug report.
@@ -119,7 +141,7 @@ version 2.91
changing the behaviour of an installation with --no-x20-encode.
Fix a long-standing problem when two queries which are identical
in every repect _except_ case, get combined by dnsmasq. If
in every respect _except_ case, get combined by dnsmasq. If
dnsmasq gets eg, two queries for example.com and Example.com
in quick succession it will get the answer for example.com from
upstream and send that answer to both requestors. This means that
@@ -137,7 +159,7 @@ version 2.90
for a particular domain. Thanks to Daniel Danzberger for
spotting this bug.
Set the default maximum DNS UDP packet sice to 1232. This
Set the default maximum DNS UDP packet size to 1232. This
has been the recommended value since 2020 because it's the
largest value that avoid fragmentation, and fragmentation
is just not reliable on the modern internet, especially
@@ -145,14 +167,14 @@ version 2.90
--edns-packet-max for special circumstances.
Add --no-dhcpv4-interface and --no-dhcpv6-interface for
better control over which inetrfaces are providing DHCP service.
better control over which interfaces are providing DHCP service.
Fix issue with stale caching: After replying with stale data,
dnsmasq sends the query upstream to refresh the cache asynchronously
and sometimes sends the wrong packet: packet length can be wrong,
and if an EDE marking stale data is added to the answer that can
end up in the query also. This bug only seems to cause problems
when the usptream server is a DOH/DOT proxy. Thanks to Justin He
when the upstream server is a DOH/DOT proxy. Thanks to Justin He
for the bug report.
Add configurable caching for arbitrary RR-types.
@@ -190,7 +212,7 @@ version 2.90
Applied Cybersecurity ATHENE for finding this vulnerability.
CVE 2023-50387 and CVE 2023-50868 apply.
Note that the is a security vulnerablity only when DNSSEC validation
Note that this a security vulnerability only when DNSSEC validation
is enabled.
Fix memory-leak when attempting to cache SRV records with zero TTL.
@@ -276,7 +298,7 @@ version 2.88
upstream servers from /etc/resolv.conf or other sources that
can change dnsmasq tries to avoid memory fragmentation by re-using
existing records that are being re-read unchanged. This involves
seaching all the server records for each new one installed.
searching all the server records for each new one installed.
During startup this search is pointless, and can cause long
start times with thousands of --server options because the work
needed is O(n^2). Handle this case more intelligently.
@@ -339,7 +361,7 @@ version 2.87
Enhance --domain to accept, for instance,
--domain=net2.thekelleys.org.uk,eth2 so that hosts get a domain
which relects the interface they are attached to in a way which
which reflects the interface they are attached to in a way which
doesn't require hard-coding addresses. Thanks to Sten Spans for
the idea.
@@ -713,22 +735,22 @@ version 2.80
but those which used the default of no checking will need to be
altered to explicitly select no checking. The new default is
because switching off checking for unsigned replies is
inherently dangerous. Not only does it open the possiblity of forged
inherently dangerous. Not only does it open the possibility of forged
replies, but it allows everything to appear to be working even
when the upstream namesevers do not support DNSSEC, and in this
case no DNSSEC validation at all is occuring.
case no DNSSEC validation at all is occurring.
Fix DHCP broken-ness when --no-ping AND --dhcp-sequential-ip
are set. Thanks to Daniel Miess for help with this.
Add a facilty to store DNS packets sent/recieved in a
Add a facility to store DNS packets sent/received in a
pcap-format file for later debugging. The file location
is given by the --dumpfile option, and a bitmap controlling
which packets should be dumped is given by the --dumpmask
option.
Handle the case of both standard and constructed dhcp-ranges on the
same interface better. We don't now contruct a dhcp-range if there's
same interface better. We don't now construct a dhcp-range if there's
already one specified. This allows the specified interface to
have different parameters and avoids advertising the same
prefix twice. Thanks to Luis Marsano for spotting this case.
@@ -1198,7 +1220,7 @@ version 2.73
Use inotify for checking on updates to /etc/resolv.conf and
friends under Linux. This fixes race conditions when the files are
updated rapidly and saves CPU by noy polling. To build
updated rapidly and saves CPU by not polling. To build
a binary that runs on old Linux kernels without inotify,
use make COPTS=-DNO_INOTIFY
@@ -1538,7 +1560,7 @@ version 2.68
are dynamic and works much better than the previous
work-around which exempted constructed DHCP ranges from the
IP address filtering. As a consequence, that work-around
is removed. Under certain circumstances, this change wil
is removed. Under certain circumstances, this change will
break existing configuration: if you're relying on the
constructed-range exception, you need to change --auth-zone
to specify the same interface as is used to construct your

View File

@@ -498,10 +498,7 @@ xxx.internal.thekelleys.org.uk at 192.168.1.1 then giving the flag
.B --server=/internal.thekelleys.org.uk/192.168.1.1
will send all queries for
internal machines to that nameserver, everything else will go to the
servers in /etc/resolv.conf. DNSSEC validation is turned off for such
private nameservers, UNLESS a
.B --trust-anchor
is specified for the domain in question. An empty domain specification,
servers in /etc/resolv.conf. An empty domain specification,
.B //
has the special meaning of "unqualified names only" ie names without any
dots in them. A non-standard port may be specified as
@@ -876,7 +873,7 @@ Set the maximum number of concurrent DNS queries. The default value is
150, which should be fine for most setups. The only known situation
where this needs to be increased is when using web-server log file
resolvers, which can generate large numbers of concurrent queries. This
parameter actually controls the number of concurrent queries per server group, where a server group is the set of server(s) associated with a single domain. So if a domain has it's own server via --server=/example.com/1.2.3.4 and 1.2.3.4 is not responding, but queries for *.example.com cannot go elsewhere, then other queries will not be affected. On configurations with many such server groups and tight resources, this value may need to be reduced.
parameter actually controls the number of concurrent queries per server group, where a server group is the set of server(s) associated with a single domain. So if a domain has its own server via --server=/example.com/1.2.3.4 and 1.2.3.4 is not responding, but queries for *.example.com cannot go elsewhere, then other queries will not be affected. On configurations with many such server groups and tight resources, this value may need to be reduced.
.TP
.B --dnssec
Validate DNS replies and cache DNSSEC data. When forwarding DNS queries, dnsmasq requests the
@@ -894,12 +891,15 @@ ie capable of returning DNSSEC records with data. If they are not,
then dnsmasq will not be able to determine the trusted status of
answers and this means that DNS service will be entirely broken.
.TP
.B --trust-anchor=<domain>,[<class>,]<key-tag>,<algorithm>,<digest-type>,<digest>
.B --trust-anchor=<domain>,[<class>,][<key-tag>,<algorithm>,<digest-type>,<digest>]
Provide DS records to act a trust anchors for DNSSEC
validation. Typically these will be the DS record(s) for Key Signing
validation. The class defaults to IN. Typically these will be the DS record(s) for Key Signing
key(s) (KSK) of the root zone,
but trust anchors for limited domains are also possible. The current
root-zone trust anchors may be downloaded from https://data.iana.org/root-anchors/root-anchors.xml
but trust anchors for limited domains are also possible.
A negative trust anchor (ie. proof that a DS record doesn't exist) may be configured be specifying
only the name or only the name and class. This can be useful for forcing dnsmasq to treat zones delegated
using \fB--server=/<domain>/<ip-address>\fP as unsigned. The current
root-zone trust anchors may be downloaded from https://data.iana.org/root-anchors/root-anchors.xml
.TP
.B --dnssec-check-unsigned[=no]
As a default, dnsmasq checks that unsigned DNS replies are

View File

@@ -844,12 +844,15 @@ void cache_end_insert(void)
#ifdef HAVE_DNSSEC
/* Sneak out possibly updated crypto HWM values. */
m = daemon->metrics[METRIC_CRYPTO_HWM];
read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), RW_WRITE);
m = daemon->metrics[METRIC_SIG_FAIL_HWM];
read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), RW_WRITE);
m = daemon->metrics[METRIC_WORK_HWM];
read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), RW_WRITE);
read_write(daemon->pipe_to_parent,
(unsigned char *)&daemon->metrics[METRIC_CRYPTO_HWM],
sizeof(daemon->metrics[METRIC_CRYPTO_HWM]), RW_WRITE);
read_write(daemon->pipe_to_parent,
(unsigned char *)&daemon->metrics[METRIC_SIG_FAIL_HWM],
sizeof(daemon->metrics[METRIC_SIG_FAIL_HWM]), RW_WRITE);
read_write(daemon->pipe_to_parent,
(unsigned char *)&daemon->metrics[METRIC_WORK_HWM],
sizeof(daemon->metrics[METRIC_WORK_HWM]), RW_WRITE);
#endif
}
@@ -879,18 +882,20 @@ int cache_recv_insert(time_t now, int fd)
{
#ifdef HAVE_DNSSEC
/* Sneak in possibly updated crypto HWM. */
if (!read_write(fd, (unsigned char *)&m, sizeof(m), RW_READ))
unsigned int val;
if (!read_write(fd, (unsigned char *)&val, sizeof(val), RW_READ))
return 0;
if (m > daemon->metrics[METRIC_CRYPTO_HWM])
daemon->metrics[METRIC_CRYPTO_HWM] = m;
if (!read_write(fd, (unsigned char *)&m, sizeof(m), RW_READ))
if (val > daemon->metrics[METRIC_CRYPTO_HWM])
daemon->metrics[METRIC_CRYPTO_HWM] = val;
if (!read_write(fd, (unsigned char *)&val, sizeof(val), RW_READ))
return 0;
if (m > daemon->metrics[METRIC_SIG_FAIL_HWM])
daemon->metrics[METRIC_SIG_FAIL_HWM] = m;
if (!read_write(fd, (unsigned char *)&m, sizeof(m), RW_READ))
if (val > daemon->metrics[METRIC_SIG_FAIL_HWM])
daemon->metrics[METRIC_SIG_FAIL_HWM] = val;
if (!read_write(fd, (unsigned char *)&val, sizeof(val), RW_READ))
return 0;
if (m > daemon->metrics[METRIC_WORK_HWM])
daemon->metrics[METRIC_WORK_HWM] = m;
if (val > daemon->metrics[METRIC_WORK_HWM])
daemon->metrics[METRIC_WORK_HWM] = val;
#endif
cache_end_insert();
return 1;
@@ -1448,11 +1453,17 @@ void cache_reload(void)
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;
cache->addr.ds.keytag = ds->keytag;
cache->addr.ds.digest = ds->digest_type;
cache->uid = ds->class;
if (ds->digestlen != 0)
{
cache->addr.ds.keylen = ds->digestlen;
cache->addr.ds.algo = ds->algo;
cache->addr.ds.keytag = ds->keytag;
cache->addr.ds.digest = ds->digest_type;
}
else
cache->flags |= F_NEG | F_DNSSECOK | F_NO_RR;
cache_hash(cache);
make_non_terminals(cache);
}

View File

@@ -26,6 +26,7 @@
#define DNSSEC_LIMIT_SIG_FAIL 20 /* Number of signature that can fail to validate in one answer */
#define DNSSEC_LIMIT_CRYPTO 200 /* max no. of crypto operations to validate one query. */
#define DNSSEC_LIMIT_NSEC3_ITERS 150 /* Max. number if iterations allowed in NSEC3 record. */
#define DNSSEC_ASSUMED_DS_TTL 3600 /* TTL for negative DS records implied by server=/domain/ */
#define TIMEOUT 10 /* drop UDP queries after TIMEOUT seconds */
#define SMALL_PORT_RANGE 30 /* If DNS port range is smaller than this, use different allocation. */
#define FORWARD_TEST 50 /* try all servers every 50 queries */

View File

@@ -812,7 +812,7 @@ void dhcp_construct_contexts(time_t now)
{
if ((context->flags & CONTEXT_RA) || option_bool(OPT_RA))
{
/* previously constructed context has gone. advertise it's demise */
/* previously constructed context has gone; advertise its demise */
context->flags |= CONTEXT_OLD;
context->address_lost_time = now;
/* Apply same ceiling of configured lease time as in radv.c */

View File

@@ -133,6 +133,7 @@ int main (int argc, char **argv)
'.' or NAME_ESCAPE then all would have to be escaped, so the
presentation format would be twice as long as the spec. */
daemon->keyname = safe_malloc((MAXDNAME * 2) + 1);
daemon->cname = safe_malloc((MAXDNAME * 2) + 1);
/* one char flag per possible RR in answer section (may get extended). */
daemon->rr_status_sz = 64;
daemon->rr_status = safe_malloc(sizeof(*daemon->rr_status) * daemon->rr_status_sz);
@@ -930,7 +931,8 @@ int main (int argc, char **argv)
my_syslog(LOG_INFO, _("DNSSEC signature timestamps not checked until system time valid"));
for (ds = daemon->ds; ds; ds = ds->next)
my_syslog(LOG_INFO, _("configured with trust anchor for %s keytag %u"),
my_syslog(LOG_INFO,
ds->digestlen == 0 ? _("configured with negative trust anchor for %s") : _("configured with trust anchor for %s keytag %u"),
ds->name[0] == 0 ? "<root>" : ds->name, ds->keytag);
}
#endif
@@ -2140,7 +2142,7 @@ static void do_tcp_connection(struct listener *listener, time_t now, int slot)
cache_recv_insert() calls pop_and_retry_query() after the result
arrives via the pipe to the parent. */
int swap_to_tcp(struct frec *forward, time_t now, int status, struct dns_header *header,
ssize_t *plen, int class, struct server *server, int *keycount, int *validatecount)
ssize_t *plen, char *name, int class, struct server *server, int *keycount, int *validatecount)
{
struct server *s;
@@ -2214,8 +2216,7 @@ int swap_to_tcp(struct frec *forward, time_t now, int status, struct dns_header
}
}
status = tcp_from_udp(now, status, header, plen, class, daemon->namebuff, daemon->keyname,
server, keycount, validatecount);
status = tcp_from_udp(now, status, header, plen, class, name, server, keycount, validatecount);
/* close upstream connections. */
for (s = daemon->servers; s; s = s->next)

View File

@@ -1248,7 +1248,7 @@ extern struct daemon {
char *namebuff; /* MAXDNAME size buffer */
char *workspacename;
#ifdef HAVE_DNSSEC
char *keyname; /* MAXDNAME size buffer */
char *keyname, *cname; /* MAXDNAME size buffer */
unsigned long *rr_status; /* ceiling in TTL from DNSSEC or zero for insecure */
int rr_status_sz;
int dnssec_no_time_check;
@@ -1393,8 +1393,8 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
unsigned char *skip_name(unsigned char *ansp, struct dns_header *header, size_t plen, int extrabytes);
unsigned char *skip_questions(struct dns_header *header, size_t plen);
unsigned char *skip_section(unsigned char *ansp, int count, struct dns_header *header, size_t plen);
unsigned int extract_request(struct dns_header *header, size_t qlen,
char *name, unsigned short *typep);
unsigned int extract_request(struct dns_header *header, size_t qlen, char *name,
unsigned short *typep, unsigned short *classp);
void setup_reply(struct dns_header *header, unsigned int flags, int ede);
int extract_addresses(struct dns_header *header, size_t qlen, char *name,
time_t now, struct ipsets *ipsets, struct ipsets *nftsets, int is_sign,
@@ -1530,7 +1530,7 @@ void return_reply(time_t now, struct frec *forward, struct dns_header *header, s
#ifdef HAVE_DNSSEC
void pop_and_retry_query(struct frec *forward, int status, time_t now);
int tcp_from_udp(time_t now, int status, struct dns_header *header, ssize_t *n,
int class, char *name, char *keyname, struct server *server,
int class, char *name, struct server *server,
int *keycount, int *validatecount);
#endif
unsigned char *tcp_request(int confd, time_t now,
@@ -1654,7 +1654,7 @@ void send_event(int fd, int event, int data, char *msg);
void clear_cache_and_reload(time_t now);
#ifdef HAVE_DNSSEC
int swap_to_tcp(struct frec *forward, time_t now, int status, struct dns_header *header,
ssize_t *plen, int class, struct server *server, int *keycount, int *validatecount);
ssize_t *plen, char *name, int class, struct server *server, int *keycount, int *validatecount);
#endif
/* netlink.c */

View File

@@ -997,49 +997,57 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
unsigned long ttl;
union all_addr a;
if (ntohs(header->qdcount) != 1 ||
!(p = skip_name(p, header, plen, 4)))
return STAT_BOGUS;
GETSHORT(qtype, p);
GETSHORT(qclass, p);
if (qtype != T_DS || qclass != class)
return STAT_BOGUS;
/* A SERVFAIL answer has been seen to a DS query not at start of authority,
/* A SERVFAIL answer has been seen to a DS query not at start of authority,
so treat it as such and continue to search for a DS or proof of no existence
further down the tree. */
if (RCODE(header) == SERVFAIL)
servfail = neganswer = nons = 1;
else
{
rc = dnssec_validate_reply(now, header, plen, name, keyname, NULL, 0, &neganswer, &nons, &neg_ttl, validate_counter);
rc = dnssec_validate_reply(now, header, plen, name, keyname, NULL, 0, &neganswer, &nons, &neg_ttl, validate_counter);
p = (unsigned char *)(header+1);
if (ntohs(header->qdcount) != 1 ||
!extract_name(header, plen, &p, name, EXTR_NAME_EXTRACT, 4))
return STAT_BOGUS;
GETSHORT(qtype, p);
GETSHORT(qclass, p);
if (qtype != T_DS || qclass != class)
return STAT_BOGUS;
if (!servfail)
{
if (STAT_ISEQUAL(rc, STAT_INSECURE))
{
my_syslog(LOG_WARNING, _("Insecure DS reply received for %s, check domain configuration and upstream DNS server DNSSEC support"), name);
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS - not secure", 0);
return STAT_BOGUS | DNSSEC_FAIL_INDET;
if (lookup_domain(name, F_DOMAINSRV, NULL, NULL))
{
my_syslog(LOG_INFO, _("Insecure reply received for DS %s, assuming non-DNSSEC domain-specific server."), name);
neganswer = 1;
nons = 0; /* If we're faking a DS, fake one with an NS. */
neg_ttl = DNSSEC_ASSUMED_DS_TTL;
}
else
{
my_syslog(LOG_WARNING, _("Insecure DS reply received for %s, check domain configuration and upstream DNS server DNSSEC support"), name);
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS - not secure", 0);
return STAT_BOGUS | DNSSEC_FAIL_INDET;
}
}
p = (unsigned char *)(header+1);
if (!extract_name(header, plen, &p, name, EXTR_NAME_EXTRACT, 4))
return STAT_BOGUS;
p += 4; /* qtype, qclass */
/* 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 (STAT_ISEQUAL(rc, STAT_NEED_KEY) && hostname_isequal(name, keyname))
else
{
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS", 0);
return STAT_BOGUS;
if (STAT_ISEQUAL(rc, STAT_NEED_KEY) && hostname_isequal(name, keyname))
{
/* 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. */
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS", 0);
return STAT_BOGUS;
}
if (!STAT_ISEQUAL(rc, STAT_SECURE))
return rc;
}
if (!STAT_ISEQUAL(rc, STAT_SECURE))
return rc;
}
if (!neganswer)
@@ -1129,9 +1137,19 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
/* We only cache validated DS records, DNSSECOK flag hijacked
to store presence/absence of NS. */
if (nons)
flags &= ~F_DNSSECOK;
{
if (lookup_domain(name, F_DOMAINSRV, NULL, NULL))
{
my_syslog(LOG_WARNING, _("Negative DS reply without NS record received for %s, assuming non-DNSSEC domain-specific server."), name);
nons = 0;
}
else
/* We only cache validated DS records, DNSSECOK flag hijacked
to store presence/absence of NS. */
flags &= ~F_DNSSECOK;
}
}
cache_start_insert();
/* Use TTL from NSEC for negative cache entries */
@@ -1236,6 +1254,7 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
p += 8; /* class, type, TTL */
GETSHORT(rdlen, p);
psave = p;
if (!extract_name(header, plen, &p, workspace2, EXTR_NAME_EXTRACT, 0))
return DNSSEC_FAIL_BADPACKET;
@@ -1258,7 +1277,22 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
workspace1--;
*workspace1 = '*';
}
rdlen -= p - psave;
/* rdlen is now length of type map, and p points to it
packet checked to be as long as rdlen implies in prove_non_existence() */
/* check that the first typemap is complete. */
if (rdlen < 2 || rdlen < p[1] + 2)
return DNSSEC_FAIL_BADPACKET;
/* RFC 6672 5.3.4.1. */
#define DNAME_OFFSET (T_DNAME >> 3)
#define DNAME_MASK (0x80 >> (T_DNAME & 0x07))
if (p[0] == 0 && (p[1] >= DNAME_OFFSET + 1) && (p[2 + DNAME_OFFSET] & DNAME_MASK) != 0 &&
hostname_issubdomain(name, workspace1) == 1)
return DNSSEC_FAIL_NONSEC;
rc = hostname_cmp(workspace1, name);
if (rc == 0)
@@ -1269,16 +1303,12 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
/* NSEC with the same name as the RR we're testing, check
that the type in question doesn't appear in the type map */
rdlen -= p - psave;
/* rdlen is now length of type map, and p points to it
packet checked to be as long as rdlen implies in prove_non_existence() */
/* If we can prove that there's no NS record, return that information. */
if (nons && rdlen >= 2 && p[0] == 0 && (p[2] & (0x80 >> T_NS)) != 0)
*nons = 0;
if (rdlen >= 2 && p[0] == 0)
if (p[0] == 0 && p[1] >= 1)
{
/* If we can prove that there's no NS record, return that information. */
if (nons && (p[2] & (0x80 >> T_NS)) != 0)
*nons = 0;
/* 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)
@@ -1290,10 +1320,10 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
if (name_labels != 0 && type == T_DS && (p[2] & (0x80 >> T_SOA)) != 0)
return DNSSEC_FAIL_NONSEC;
}
while (rdlen >= 2)
while (rdlen > 0)
{
if (!CHECK_LEN(header, p, plen, rdlen))
if (rdlen < 2 || rdlen < p[1] + 2)
return DNSSEC_FAIL_BADPACKET;
if (p[0] == type >> 8)
@@ -1433,7 +1463,11 @@ static int check_nsec3_coverage(struct dns_header *header, size_t plen, int dige
p += hash_len; /* skip next-domain hash */
rdlen -= p - psave;
if (rdlen >= 2 && p[0] == 0)
/* check that the first typemap is complete. */
if (rdlen < 2 || rdlen < p[1] + 2)
return DNSSEC_FAIL_BADPACKET;
if (p[0] == 0 && p[1] >= 1)
{
/* If we can prove that there's no NS record, return that information. */
if (nons && (p[2] & (0x80 >> T_NS)) != 0)
@@ -1451,8 +1485,11 @@ static int check_nsec3_coverage(struct dns_header *header, size_t plen, int dige
return 0;
}
while (rdlen >= 2)
while (rdlen > 0)
{
if (rdlen < 2 || rdlen < p[1] + 2)
return DNSSEC_FAIL_BADPACKET;
if (p[0] == type >> 8)
{
/* Does the NSEC3 say our type exists? */
@@ -1924,11 +1961,13 @@ 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;
int type1, class1, rdlen1 = 0, type2, class2, rdlen2, qclass, qtype, targetidx;
int i, j, rc = STAT_INSECURE;
unsigned char *ans_start, *p1, *p2, *p3;
int type1, class1, rdlen1 = 0, type2, class2, rdlen2, qclass, qtype, targetidx, gotdname;
int i, j, k, rc = STAT_INSECURE;
int secure = STAT_SECURE;
int rc_nsec;
unsigned long ttl;
/* extend rr_status if necessary */
if (daemon->rr_status_sz < ntohs(header->ancount) + ntohs(header->nscount))
{
@@ -1975,27 +2014,119 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
/* Can't validate an RRSIG query */
if (qtype == T_RRSIG)
return STAT_INSECURE;
/* Find CNAME targets. */
for (gotdname = i = 0; i < ntohs(header->ancount); i++)
{
if (!(p1 = skip_name(p1, header, plen, 10)))
return STAT_BOGUS; /* bad packet */
GETSHORT(type1, p1);
GETSHORT(class1, p1);
p1 += 4; /* TTL */
GETSHORT(rdlen1, p1);
if (type1 == T_DNAME)
gotdname = 1;
if (qtype != T_CNAME && qtype != T_ANY && type1 == T_CNAME && class1 == qclass)
{
if (!expand_workspace(&targets, &target_sz, targetidx))
return STAT_BOGUS;
targets[targetidx++] = p1; /* pointer to target name */
}
if (!ADD_RDLEN(header, p1, plen, rdlen1))
return STAT_BOGUS;
}
if (qtype != T_CNAME && qtype != T_ANY)
for (j = ntohs(header->ancount); j != 0; j--)
/* A DNAME capable of sythesising a CNAME means we don't need to validate the CNAME,
we can just assume that it's valid. RFC 4035 3.2.3 */
if (gotdname)
for (p1 = ans_start, i = 0; i < ntohs(header->ancount); i++)
{
if (!(p1 = skip_name(p1, header, plen, 10)))
if (!extract_name(header, plen, &p1, name, EXTR_NAME_EXTRACT, 10))
return STAT_BOGUS; /* bad packet */
GETSHORT(type2, p1);
p1 += 6; /* class, TTL */
GETSHORT(rdlen2, p1);
GETSHORT(type1, p1);
GETSHORT(class1, p1);
p1 += 4; /* TTL */
GETSHORT(rdlen1, p1);
if (type2 == T_CNAME)
if (type1 != T_DNAME)
{
if (!expand_workspace(&targets, &target_sz, targetidx))
if (!ADD_RDLEN(header, p1, plen, rdlen1))
return STAT_BOGUS;
targets[targetidx++] = p1; /* pointer to target name */
}
if (!ADD_RDLEN(header, p1, plen, rdlen2))
return STAT_BOGUS;
else
{
if (!extract_name(header, plen, &p1, keyname, EXTR_NAME_EXTRACT, 0))
return STAT_BOGUS; /* bad packet */
/* We now have the name of the DNAME in name, and the target in keyname.
Look for any CNAMEs which could have been synthesised from this DNAME
and pre-qualify them. */
for (p2 = ans_start, j = 0; j < ntohs(header->ancount); j++)
{
if (!extract_name(header, plen, &p2, daemon->cname, EXTR_NAME_EXTRACT, 10))
return STAT_BOGUS; /* bad packet */
GETSHORT(type2, p2);
GETSHORT(class2, p2);
GETLONG(ttl, p2);
GETSHORT(rdlen2, p2);
if (type2 != T_CNAME || class2 != class1)
{
if (!ADD_RDLEN(header, p2, plen, rdlen2))
return STAT_BOGUS;
}
else
{
size_t name_prefix_len = strlen(daemon->cname) - strlen(name);
if (!extract_name(header, plen, &p2, daemon->workspacename, EXTR_NAME_EXTRACT, 0))
return STAT_BOGUS; /* bad packet */
/* We have the name of the CNAME in daemon->cname, and the target in daemon->workspacename.
See if the CNAME was sythesised from the DNAME.
CNAME must be <subdomain>.<dname>
CNAME target must be <subdomain>.<dname_target>
<subdomain>s must match for name and target. */
if (hostname_issubdomain(name, daemon->cname) == 1 &&
hostname_issubdomain(keyname, daemon->workspacename) == 1 &&
name_prefix_len == strlen(daemon->workspacename) - strlen(keyname))
{
char save = daemon->cname[name_prefix_len];
daemon->cname[name_prefix_len] = 0;
daemon->workspacename[name_prefix_len] = 0;
if (hostname_isequal(daemon->cname, daemon->workspacename))
{
/* pre-qualify this as validated */
daemon->rr_status[j] = ttl > 0 ? ttl : 1;
/* and remove it from the targets we need to have validated answers to. */
if (class2 == qclass)
{
daemon->cname[name_prefix_len] = save;
for (k = 0; k <targetidx; k++)
if ((p3 = targets[k]))
{
int rc1;
if (!(rc1 = extract_name(header, plen, &p3, daemon->cname, EXTR_NAME_COMPARE, 0)))
return STAT_BOGUS; /* bad packet */
if (rc1 == 1)
targets[k] = NULL;
}
}
}
}
}
}
}
}
for (p1 = ans_start, i = 0; i < ntohs(header->ancount) + ntohs(header->nscount); i++)
@@ -2014,6 +2145,10 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
/* Don't try and validate RRSIGs! */
if (type1 == T_RRSIG)
continue;
/* Pre-validated by DNAME above don't validate. */
if (daemon->rr_status[i] != 0)
continue;
/* Check if we've done this RRset already */
for (p2 = ans_start, j = 0; j < i; j++)
@@ -2155,7 +2290,9 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
/* NXDOMAIN or NODATA reply, unanswered question is (name, qclass, qtype) */
/* 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. */
the answer is in an unsigned zone, or there's NSEC records.
For a DS record, we return INSECURE, which almost always turns
into BOGUS in the caller. */
if ((rc_nsec = prove_non_existence(header, plen, keyname, name, qtype, qclass, NULL, nons, nsec_ttl, validate_counter)) != 0)
{
if (rc_nsec & DNSSEC_FAIL_WORK)
@@ -2163,7 +2300,7 @@ 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 | rc_nsec;
return STAT_INSECURE;
if ((rc_nsec & (DNSSEC_FAIL_NONSEC | DNSSEC_FAIL_NSEC3_ITERS)) &&
!STAT_ISEQUAL((rc = zone_status(name, qclass, keyname, now)), STAT_SECURE))

View File

@@ -94,8 +94,7 @@ void build_server_array(void)
server=/.example.com/ works.
A flag of F_SERVER returns an upstream server only.
A flag of F_DNSSECOK returns a DNSSEC capable server only and
also disables NODOTS servers from consideration.
A flag of F_DNSSECOK disables NODOTS servers from consideration.
A flag of F_DOMAINSRV returns a domain-specific server only.
A flag of F_CONFIG returns anything that generates a local
reply of IPv4 or IPV6.
@@ -338,12 +337,8 @@ int filter_servers(int seed, int flags, int *lowout, int *highout)
if (i != nlow)
{
/* If we want a server that can do DNSSEC, and this one can't,
return nothing, similarly if were looking only for a server
for a particular domain. */
if ((flags & F_DNSSECOK) && !(daemon->serverarray[nlow]->flags & SERV_DO_DNSSEC))
nlow = nhigh;
else if ((flags & F_DOMAINSRV) && daemon->serverarray[nlow]->domain_len == 0)
/* If we want a server for a particular domain, and this one isn't, return nothing. */
if ((flags & F_DOMAINSRV) && daemon->serverarray[nlow]->domain_len == 0)
nlow = nhigh;
else
nhigh = i;
@@ -351,7 +346,7 @@ int filter_servers(int seed, int flags, int *lowout, int *highout)
else
{
/* --local=/domain/, only return if we don't need a server. */
if (flags & (F_DNSSECOK | F_DOMAINSRV | F_SERVER))
if (flags & (F_DOMAINSRV | F_SERVER))
nhigh = i;
}
}
@@ -472,7 +467,7 @@ int dnssec_server(struct server *server, char *keyname, int *firstp, int *lastp)
/* Find server to send DNSSEC query to. This will normally be the
same as for the original query, but may be another if
servers for domains are involved. */
if (!lookup_domain(keyname, F_DNSSECOK, &first, &last))
if (!lookup_domain(keyname, F_SERVER | F_DNSSECOK, &first, &last))
return -1;
for (index = first; index != last; index++)

View File

@@ -176,9 +176,9 @@ static void forward_query(int udpfd, union mysockaddr *udpaddr,
int first, last, start = 0;
int forwarded = 0;
int ede = EDE_UNSET;
unsigned short rrtype;
unsigned short rrtype, rrclass;
gotname = extract_request(header, plen, daemon->namebuff, &rrtype);
gotname = extract_request(header, plen, daemon->namebuff, &rrtype, &rrclass);
/* Check for retry on existing query.
FREC_DNSKEY and FREC_DS_QUERY are never set in flags, so the test below
@@ -192,7 +192,7 @@ static void forward_query(int udpfd, union mysockaddr *udpaddr,
old_reply = 1;
fwd_flags = forward->flags;
}
else if (gotname && (forward = lookup_frec(daemon->namebuff, C_IN, (int)rrtype, -1, fwd_flags,
else if (gotname && (forward = lookup_frec(daemon->namebuff, (int)rrclass, (int)rrtype, -1, fwd_flags,
FREC_CHECKING_DISABLED | FREC_AD_QUESTION | FREC_DO_QUESTION |
FREC_HAS_PHEADER | FREC_DNSKEY_QUERY | FREC_DS_QUERY | FREC_NO_CACHE)))
{
@@ -264,7 +264,7 @@ static void forward_query(int udpfd, union mysockaddr *udpaddr,
The original query we sent is now in packet buffer and the query name in the
new instance is on daemon->namebuff. */
if (extract_request(header, forward->stash_len, daemon->workspacename, NULL))
if (extract_name(header, forward->stash_len, NULL, daemon->workspacename, EXTR_NAME_EXTRACT, 0))
{
unsigned int i, gobig = 0;
char *s1, *s2;
@@ -329,8 +329,6 @@ static void forward_query(int udpfd, union mysockaddr *udpaddr,
/* new query */
if (!forward)
{
unsigned char *p;
if (OPCODE(header) != QUERY)
{
flags = F_RCODE;
@@ -375,7 +373,7 @@ static void forward_query(int udpfd, union mysockaddr *udpaddr,
forward->flags = fwd_flags;
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && (master->flags & SERV_DO_DNSSEC))
if (option_bool(OPT_DNSSEC_VALID))
{
plen = add_do_bit(header, plen, ((unsigned char *) header) + daemon->edns_pktsz);
@@ -390,11 +388,10 @@ static void forward_query(int udpfd, union mysockaddr *udpaddr,
forward->frec_src.orig_id = ntohs(header->id);
forward->new_id = get_id();
header->id = ntohs(forward->new_id);
forward->frec_src.encode_bitmap = (!option_bool(OPT_NO_0x20) && option_bool(OPT_DO_0x20)) ? rand32() : 0;
forward->frec_src.encode_bigmap = NULL;
p = (unsigned char *)(header+1);
if (!extract_name(header, plen, &p, (char *)&forward->frec_src.encode_bitmap, EXTR_NAME_FLIP, 1))
if (!extract_name(header, plen, NULL, (char *)&forward->frec_src.encode_bitmap, EXTR_NAME_FLIP, 1))
goto reply;
/* Keep copy of query for retries and move to TCP */
@@ -459,7 +456,7 @@ static void forward_query(int udpfd, union mysockaddr *udpaddr,
blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
plen = forward->stash_len;
/* get query for logging. */
gotname = extract_request(header, plen, daemon->namebuff, NULL);
gotname = extract_request(header, plen, daemon->namebuff, NULL, NULL);
/* Find suitable servers: should never fail. */
if (!filter_servers(forward->sentto->arrayposn, F_DNSSECOK, &first, &last))
@@ -708,12 +705,12 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
(void)do_bit;
#ifdef HAVE_IPSET
if (daemon->ipsets && extract_request(header, n, daemon->namebuff, NULL))
if (daemon->ipsets && extract_name(header, n, NULL, daemon->namebuff, EXTR_NAME_EXTRACT, 0))
ipsets = domain_find_sets(daemon->ipsets, daemon->namebuff);
#endif
#ifdef HAVE_NFTSET
if (daemon->nftsets && extract_request(header, n, daemon->namebuff, NULL))
if (daemon->nftsets && extract_name(header, n, NULL, daemon->namebuff, EXTR_NAME_EXTRACT, 0))
nftsets = domain_find_sets(daemon->nftsets, daemon->namebuff);
#endif
@@ -790,7 +787,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
log_query(F_UPSTREAM, NULL, NULL, "truncated", 0);
else if (!bogusanswer || (header->hb4 & HB4_CD))
{
if (rcode == NXDOMAIN && extract_request(header, n, daemon->namebuff, NULL) &&
if (rcode == NXDOMAIN && extract_name(header, n, NULL, daemon->namebuff, EXTR_NAME_EXTRACT, 0) &&
(check_for_local_domain(daemon->namebuff, now) || lookup_domain(daemon->namebuff, F_CONFIG, NULL, NULL)))
{
/* if we forwarded a query for a locally known name (because it was for
@@ -804,7 +801,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
if (daemon->doctors && do_doctor(header, n, daemon->namebuff))
cache_secure = 0;
/* check_for_bogus_wildcard() does it's own caching, so
/* check_for_bogus_wildcard() does its own caching, so
don't call extract_addresses() if it triggers. */
if (daemon->bogus_addr && rcode != NXDOMAIN &&
check_for_bogus_wildcard(header, n, daemon->namebuff, now))
@@ -922,22 +919,26 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
/* Get the query we sent by UDP */
blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
if (extract_request(header, forward->stash_len, daemon->namebuff, NULL))
log_query(F_UPSTREAM | F_NOEXTRA, daemon->namebuff, NULL, "truncated", 0);
/* Don't count failed UDP attempt AND TCP */
if (status != STAT_OK)
orig->work_counter++;
/* NOTE: Can't move connection marks from UDP to TCP */
plen = forward->stash_len;
status = swap_to_tcp(forward, now, status, header, &plen, forward->class, forward->sentto, &orig->work_counter, &orig->validate_counter);
/* We forked a new process. pop_and_retry_query() will be called when is completes. */
if (STAT_ISEQUAL(status, STAT_ASYNC))
if (!extract_name(header, forward->stash_len, NULL, daemon->namebuff, EXTR_NAME_EXTRACT, 0))
status = STAT_ABANDONED;
else
{
forward->flags |= FREC_GONE_TO_TCP;
return;
log_query(F_UPSTREAM | F_NOEXTRA, daemon->namebuff, NULL, "truncated", 0);
/* Don't count failed UDP attempt AND TCP */
if (status != STAT_OK)
orig->work_counter++;
/* NOTE: Can't move connection marks from UDP to TCP */
plen = forward->stash_len;
status = swap_to_tcp(forward, now, status, header, &plen, daemon->namebuff, forward->class, forward->sentto, &orig->work_counter, &orig->validate_counter);
/* We forked a new process. pop_and_retry_query() will be called when is completes. */
if (STAT_ISEQUAL(status, STAT_ASYNC))
{
forward->flags |= FREC_GONE_TO_TCP;
return;
}
}
}
else
@@ -954,8 +955,7 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
status = dnssec_validate_ds(now, header, plen, daemon->namebuff, daemon->keyname, forward->class, &orig->validate_counter);
else
status = dnssec_validate_reply(now, header, plen, daemon->namebuff, daemon->keyname, &forward->class,
!option_bool(OPT_DNSSEC_IGN_NS) && (forward->sentto->flags & SERV_DO_DNSSEC),
NULL, NULL, NULL, &orig->validate_counter);
!option_bool(OPT_DNSSEC_IGN_NS), NULL, NULL, NULL, &orig->validate_counter);
if (STAT_ISEQUAL(status, STAT_ABANDONED))
log_resource = 1;
@@ -1094,7 +1094,7 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
if (log_resource)
{
/* Log the actual validation that made us barf. */
if (extract_request(header, plen, daemon->namebuff, NULL))
if (extract_name(header, plen, NULL, daemon->namebuff, EXTR_NAME_EXTRACT, 0))
my_syslog(LOG_WARNING, _("validation of %s failed: resource limit exceeded."),
daemon->namebuff[0] ? daemon->namebuff : ".");
}
@@ -1271,14 +1271,13 @@ void reply_query(int fd, time_t now)
server->query_latency = server->mma_latency/128;
/* Flip the bits back in the query name. */
p = (unsigned char *)(header+1);
if (!extract_name(header, n, &p, (char *)&forward->frec_src.encode_bitmap, EXTR_NAME_FLIP, 1))
if (!extract_name(header, n, NULL, (char *)&forward->frec_src.encode_bitmap, EXTR_NAME_FLIP, 1))
return;
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID))
{
if ((forward->sentto->flags & SERV_DO_DNSSEC) && !(forward->flags & FREC_CHECKING_DISABLED))
if (!(forward->flags & FREC_CHECKING_DISABLED))
{
dnssec_validate(forward, header, n, STAT_OK, now);
return;
@@ -1305,8 +1304,7 @@ static void xor_array(unsigned int *arg1, unsigned int *arg2, unsigned int len)
/* Call extract_name() to flip case of query in packet according to the XOR of the bit maps help in arg1 and arg2 */
static void flip_queryname(struct dns_header *header, ssize_t len, struct frec_src *arg1, struct frec_src *arg2)
{
unsigned char *p = (unsigned char *)(header+1);
unsigned int *arg1p, *arg2p, arg1len, arg2len, *swapp, swap;
unsigned int *arg1p, *arg2p, arg1len, arg2len;
/* Two cases: bitmap is single 32 bit int, or it's arbitrary-length array of 32bit ints.
The two args may be different and of different lengths.
@@ -1325,17 +1323,14 @@ static void flip_queryname(struct dns_header *header, ssize_t len, struct frec_s
/* make arg1 the longer, if they differ. */
if (arg2len > arg1len)
{
swap = arg1len;
swapp = arg1p;
arg1len = arg2len;
arg1p = arg2p;
arg2len = swap;
arg2p = swapp;
unsigned int swapl = arg1len, *swapp = arg1p;
arg1len = arg2len, arg1p = arg2p;
arg2len = swapl, arg2p = swapp;
}
/* XOR on shorter length, flip on longer, operate on longer */
xor_array(arg1p, arg2p, arg2len);
extract_name(header, len, &p, (char *)arg1p, EXTR_NAME_FLIP, arg1len);
extract_name(header, len, NULL, (char *)arg1p, EXTR_NAME_FLIP, arg1len);
xor_array(arg1p, arg2p, arg2len); /* restore */
}
@@ -1393,7 +1388,7 @@ void return_reply(time_t now, struct frec *forward, struct dns_header *header, s
no_cache_dnssec = 1;
bogusanswer = 1;
if (extract_request(header, n, daemon->namebuff, NULL))
if (extract_name(header, n, NULL, daemon->namebuff, EXTR_NAME_EXTRACT, 0))
domain = daemon->namebuff;
}
@@ -1822,7 +1817,7 @@ void receive_query(struct listener *listen, time_t now)
if (OPCODE(header) != QUERY)
log_query_mysockaddr(F_QUERY | F_FORWARD, "opcode", &source_addr, "non-query", 0);
else if (extract_request(header, (size_t)n, daemon->namebuff, &type))
else if (extract_request(header, (size_t)n, daemon->namebuff, &type, NULL))
{
#ifdef HAVE_AUTH
struct auth_zone *zone;
@@ -2130,7 +2125,7 @@ static ssize_t tcp_talk(int first, int last, int start, unsigned char *packet,
/* We us the _ONCE veriant of read_write() here because we've set a timeout on the tcp socket
and wish to abort if the whole data is not read/written within the timeout. */
if ((!data_sent && !read_write(serv->tcpfd, (unsigned char *)packet, qsize + sizeof(u16), RW_WRITE_ONCE)) ||
if ((!data_sent && !read_write(serv->tcpfd, (unsigned char *)packet, qsize + sizeof(u16), RW_WRITE_ONCE)) ||
!read_write(serv->tcpfd, (unsigned char *)length, sizeof (*length), RW_READ_ONCE) ||
!read_write(serv->tcpfd, payload, (rsize = ntohs(*length)), RW_READ_ONCE))
{
@@ -2146,7 +2141,7 @@ static ssize_t tcp_talk(int first, int last, int start, unsigned char *packet,
else
goto failed;
}
/* If the question section of the reply doesn't match the question we sent, then
someone might be attempting to insert bogus values into the cache by
sending replies containing questions and bogus answers.
@@ -2176,14 +2171,13 @@ static ssize_t tcp_talk(int first, int last, int start, unsigned char *packet,
returned truncated. (Which type held in status).
Resend the query (in header) via TCP */
int tcp_from_udp(time_t now, int status, struct dns_header *header, ssize_t *plenp,
int class, char *name, char *keyname, struct server *server,
int class, char *name, struct server *server,
int *keycount, int *validatecount)
{
unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ + sizeof(u16));
struct dns_header *new_header = (struct dns_header *)&packet[2];
int start, first, last, new_status;
ssize_t n = *plenp;
int have_req = extract_request(header, n, keyname, NULL);
int log_save = daemon->log_display_id;
*plenp = 0;
@@ -2200,48 +2194,48 @@ int tcp_from_udp(time_t now, int status, struct dns_header *header, ssize_t *ple
first = start = server->arrayposn;
last = first + 1;
if (!STAT_ISEQUAL(status, STAT_OK) && (!have_req || (start = dnssec_server(server, keyname, &first, &last)) == -1))
new_status = STAT_ABANDONED;
else if ((n = tcp_talk(first, last, start, packet, n, 0, 0, &server)) == 0)
if (!STAT_ISEQUAL(status, STAT_OK) && (start = dnssec_server(server, name, &first, &last)) == -1)
new_status = STAT_ABANDONED;
else
{
if (have_req)
{
if (STAT_ISEQUAL(status, STAT_OK))
log_query_mysockaddr(F_SERVER | F_FORWARD, keyname, &server->addr, NULL, 0);
else
log_query_mysockaddr(F_NOEXTRA | F_DNSSEC | F_SERVER, keyname, &server->addr,
STAT_ISEQUAL(status, STAT_NEED_KEY) ? "dnssec-query[DNSKEY]" : "dnssec-query[DS]", 0);
}
new_status = tcp_key_recurse(now, status, new_header, n, class, name, keyname, server, 0, 0, keycount, validatecount);
if (STAT_ISEQUAL(status, STAT_OK))
{
/* downstream query: strip DNSSSEC RRs and see if it will
fit in a UDP reply. */
rrfilter(new_header, (size_t *)&n, RRFILTER_DNSSEC);
log_query_mysockaddr(F_SERVER | F_FORWARD, name, &server->addr, NULL, 0);
else
log_query_mysockaddr(F_NOEXTRA | F_DNSSEC | F_SERVER, name, &server->addr,
STAT_ISEQUAL(status, STAT_NEED_KEY) ? "dnssec-query[DNSKEY]" : "dnssec-query[DS]", 0);
if (n >= daemon->edns_pktsz)
if ((n = tcp_talk(first, last, start, packet, n, 0, 0, &server)) == 0)
new_status = STAT_ABANDONED;
else
{
new_status = tcp_key_recurse(now, status, new_header, n, class, daemon->namebuff, daemon->keyname, server, 0, 0, keycount, validatecount);
if (STAT_ISEQUAL(status, STAT_OK))
{
/* still too bIg, strip optional sections and try again. */
new_header->nscount = htons(0);
new_header->arcount = htons(0);
n = resize_packet(new_header, n, NULL, 0);
/* downstream query: strip DNSSSEC RRs and see if it will
fit in a UDP reply. */
rrfilter(new_header, (size_t *)&n, RRFILTER_DNSSEC);
if (n >= daemon->edns_pktsz)
{
/* truncating the packet will break the answers, so remove them too
and mark the reply as truncated. */
new_header->ancount = htons(0);
/* still too bIg, strip optional sections and try again. */
new_header->nscount = htons(0);
new_header->arcount = htons(0);
n = resize_packet(new_header, n, NULL, 0);
new_status = STAT_TRUNCATED;
if (n >= daemon->edns_pktsz)
{
/* truncating the packet will break the answers, so remove them too
and mark the reply as truncated. */
new_header->ancount = htons(0);
n = resize_packet(new_header, n, NULL, 0);
new_status = STAT_TRUNCATED;
}
}
/* return the stripped or truncated reply. */
memcpy(header, new_header, n);
*plenp = n;
}
/* return the stripped or truncated reply. */
memcpy(header, new_header, n);
*plenp = n;
}
}
@@ -2271,8 +2265,7 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
new_status = dnssec_validate_ds(now, header, n, name, keyname, class, validatecount);
else
new_status = dnssec_validate_reply(now, header, n, name, keyname, &class,
!option_bool(OPT_DNSSEC_IGN_NS) && (server->flags & SERV_DO_DNSSEC),
NULL, NULL, NULL, validatecount);
!option_bool(OPT_DNSSEC_IGN_NS), NULL, NULL, NULL, validatecount);
if (!STAT_ISEQUAL(new_status, STAT_NEED_DS) && !STAT_ISEQUAL(new_status, STAT_NEED_KEY) && !STAT_ISEQUAL(new_status, STAT_ABANDONED))
break;
@@ -2286,7 +2279,7 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
if (STAT_ISEQUAL(new_status, STAT_ABANDONED))
{
/* Log the actual validation that made us barf. */
if (extract_request(header, n, daemon->namebuff, NULL))
if (extract_name(header, n, NULL, daemon->namebuff, EXTR_NAME_EXTRACT, 0))
my_syslog(LOG_WARNING, _("validation of %s failed: resource limit exceeded."),
daemon->namebuff[0] ? daemon->namebuff : ".");
break;
@@ -2470,13 +2463,27 @@ unsigned char *tcp_request(int confd, time_t now,
gotname = 0;
flags = F_RCODE;
}
else if (!(gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype)))
else if (!(gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype, NULL)))
ede = EDE_INVALID_DATA;
else
{
if (saved_question)
blockdata_free(saved_question);
do_bit = 0;
if (find_pseudoheader(header, (size_t)size, NULL, &pheader, NULL, NULL))
{
unsigned short ede_flags;
have_pseudoheader = 1;
pheader += 4; /* udp_size, ext_rcode */
GETSHORT(ede_flags, pheader);
if (ede_flags & 0x8000)
do_bit = 1; /* do bit */
}
size = add_edns0_config(header, size, ((unsigned char *) header) + 65536, &peer_addr, now, &cacheable);
saved_question = blockdata_alloc((char *)header, (size_t)size);
saved_size = size;
@@ -2515,20 +2522,6 @@ 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, NULL))
{
unsigned short ede_flags;
have_pseudoheader = 1;
pheader += 4; /* udp_size, ext_rcode */
GETSHORT(ede_flags, pheader);
if (ede_flags & 0x8000)
do_bit = 1; /* do bit */
}
ad_reqd = do_bit;
/* RFC 6840 5.7 */
if (header->hb4 & HB4_AD)
@@ -2598,7 +2591,7 @@ unsigned char *tcp_request(int confd, time_t now,
start = master->last_server;
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && (master->flags & SERV_DO_DNSSEC))
if (option_bool(OPT_DNSSEC_VALID))
{
size = add_do_bit(header, size, ((unsigned char *) header) + 65536);
@@ -2615,7 +2608,7 @@ unsigned char *tcp_request(int confd, time_t now,
else
{
/* get query name again for logging - may have been overwritten */
if (!(gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype)))
if (!extract_name(header, (unsigned int)size, NULL, daemon->namebuff, EXTR_NAME_EXTRACT, 0))
strcpy(daemon->namebuff, "query");
log_query_mysockaddr(F_SERVER | F_FORWARD, daemon->namebuff, &serv->addr, NULL, 0);
@@ -2627,7 +2620,7 @@ unsigned char *tcp_request(int confd, time_t now,
if (checking_disabled || (header->hb4 & HB4_CD))
no_cache_dnssec = 1;
else if (master->flags & SERV_DO_DNSSEC)
else
{
int keycount = daemon->limit[LIMIT_WORK]; /* Limit to number of DNSSEC questions, to catch loops and avoid filling cache. */
int validatecount = daemon->limit[LIMIT_CRYPTO];
@@ -2657,7 +2650,7 @@ unsigned char *tcp_request(int confd, time_t now,
no_cache_dnssec = 1;
bogusanswer = 1;
if (extract_request(header, m, daemon->namebuff, NULL))
if (extract_name(header, m, NULL, daemon->namebuff, EXTR_NAME_EXTRACT, 0))
domain = daemon->namebuff;
}

View File

@@ -1379,7 +1379,7 @@ int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifind
/* cannot set source _port_ for TCP connections. */
if (is_tcp)
port = 0;
else if (port == 0 && daemon->max_port != 0)
else if (port == 0 && daemon->max_port != 0 && daemon->max_port >= daemon->min_port)
{
/* Bind a random port within the range given by min-port and max-port if either
or both are set. Otherwise use the OS's random ephemeral port allocation by
@@ -1587,33 +1587,6 @@ void check_servers(int no_loop_check)
for (count = 0, serv = daemon->servers; serv; serv = serv->next)
{
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID))
{
if (!(serv->flags & SERV_FOR_NODOTS))
serv->flags |= SERV_DO_DNSSEC;
/* Disable DNSSEC validation when using server=/domain/.... servers
unless there's a configured trust anchor. */
if (strlen(serv->domain) != 0)
{
struct ds_config *ds;
char *domain = serv->domain;
/* .example.com is valid */
while (*domain == '.')
domain++;
for (ds = daemon->ds; ds; ds = ds->next)
if (ds->name[0] != 0 && hostname_isequal(domain, ds->name))
break;
if (!ds)
serv->flags &= ~SERV_DO_DNSSEC;
}
}
#endif
port = prettyprint_addr(&serv->addr, daemon->namebuff);
/* 0.0.0.0 is nothing, the stack treats it like 127.0.0.1 */
@@ -1659,10 +1632,6 @@ void check_servers(int no_loop_check)
{
char *s1, *s2, *s3 = "", *s4 = "";
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && !(serv->flags & SERV_DO_DNSSEC))
s3 = _("(no DNSSEC)");
#endif
if (serv->flags & SERV_FOR_NODOTS)
s1 = _("unqualified"), s2 = _("names");
else if (strlen(serv->domain) == 0)

View File

@@ -961,7 +961,7 @@ char *parse_server(char *arg, struct server_details *sdetails)
hints.ai_family = AF_UNSPEC;
/* Get addresses suitable for sending datagrams. We assume that we can use the
same addresses for TCP connections. Settting this to zero gets each address
same addresses for TCP connections. Setting this to zero gets each address
threes times, for SOCK_STREAM, SOCK_RAW and SOCK_DGRAM, which is not useful. */
hints.ai_socktype = SOCK_DGRAM;
@@ -2675,15 +2675,15 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
if (msize > 128)
ret_err_free(_("bad prefix length"), new);
mask = (1LLU << (128 - msize)) - 1LLU;
/* prefix==64 overflows the mask calculation */
if (msize <= 64)
mask = (u64)-1LL;
else
mask = (1LLU << (128 - msize)) - 1LLU;
new->is6 = 1;
new->prefixlen = msize;
/* prefix==64 overflows the mask calculation above */
if (msize <= 64)
mask = (u64)-1LL;
new->end6 = new->start6;
setaddr6part(&new->start6, addrpart & ~mask);
setaddr6part(&new->end6, addrpart | mask);
@@ -3987,7 +3987,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
while (arg)
{
comma = split(arg);
if (strchr(arg, ':')) /* ethernet address, netid or binary CLID */
if (strchr(arg, ':')) /* Ethernet address, netid or binary CLID */
{
if ((arg[0] == 'i' || arg[0] == 'I') &&
(arg[1] == 'd' || arg[1] == 'D') &&
@@ -5337,7 +5337,8 @@ err:
new->class = C_IN;
new->name = NULL;
new->digestlen = 0;
if ((comma = split(arg)) && (algo = split(comma)))
{
int class = 0;
@@ -5355,29 +5356,37 @@ err:
algo = split(comma);
}
}
if (!comma || !algo || !(digest = split(algo)) || !(keyhex = split(digest)) ||
!atoi_check16(comma, &new->keytag) ||
!atoi_check8(algo, &new->algo) ||
!atoi_check8(digest, &new->digest_type) ||
!(new->name = canonicalise_opt(arg)))
if (!(new->name = canonicalise_opt(arg)))
ret_err_free(_("bad trust anchor"), new);
/* Upper bound on length */
len = (2*strlen(keyhex))+1;
new->digest = opt_malloc(len);
unhide_metas(keyhex);
/* 4034: "Whitespace is allowed within digits" */
for (cp = keyhex; *cp; )
if (isspace((unsigned char)*cp))
for (cp1 = cp; *cp1; cp1++)
*cp1 = *(cp1+1);
else
cp++;
if ((new->digestlen = parse_hex(keyhex, (unsigned char *)new->digest, len, NULL, NULL)) == -1)
if (comma)
{
free(new->name);
ret_err_free(_("bad HEX in trust anchor"), new);
if (!algo || !(digest = split(algo)) || !(keyhex = split(digest)) ||
!atoi_check16(comma, &new->keytag) ||
!atoi_check8(algo, &new->algo) ||
!atoi_check8(digest, &new->digest_type))
{
free(new->name);
ret_err_free(_("bad trust anchor"), new);
}
/* Upper bound on length */
len = (2*strlen(keyhex))+1;
new->digest = opt_malloc(len);
unhide_metas(keyhex);
/* 4034: "Whitespace is allowed within digits" */
for (cp = keyhex; *cp; )
if (isspace((unsigned char)*cp))
for (cp1 = cp; *cp1; cp1++)
*cp1 = *(cp1+1);
else
cp++;
if ((new->digestlen = parse_hex(keyhex, (unsigned char *)new->digest, len, NULL, NULL)) == -1)
{
free(new->name);
ret_err_free(_("bad HEX in trust anchor"), new);
}
}
new->next = daemon->ds;
@@ -5926,6 +5935,9 @@ void read_opts(int argc, char **argv, char *compile_opts)
daemon->randport_limit = 1;
daemon->host_index = SRC_AH;
daemon->max_procs = MAX_PROCS;
#ifdef HAVE_DUMPFILE
daemon->dump_mask = 0xffffffff;
#endif
#ifdef HAVE_DNSSEC
daemon->limit[LIMIT_SIG_FAIL] = DNSSEC_LIMIT_SIG_FAIL;
daemon->limit[LIMIT_CRYPTO] = DNSSEC_LIMIT_CRYPTO;

View File

@@ -98,7 +98,7 @@ void poll_listen(int fd, short event)
{
if (arrsize == nfds)
{
/* Array too small, extend. */
/* Array too small. Extend. */
struct pollfd *new;
arrsize = (arrsize == 0) ? 64 : arrsize * 2;

View File

@@ -411,7 +411,7 @@ static void send_ra_alias(time_t now, int iface, char *iface_name, struct in6_ad
if (!old_prefix && !parm.found_context)
return;
/* If we're sending router address instead of prefix in at least on prefix,
/* If we're sending router address instead of prefix in at least one prefix,
include the advertisement interval option. */
if (parm.adv_router)
{
@@ -825,10 +825,10 @@ time_t periodic_ra(time_t now)
}
else if (iface_enumerate(AF_INET6, &param, (callback_t){.af_inet6=iface_search}))
/* There's a context overdue, but we can't find an interface
associated with it, because it's for a subnet we dont
associated with it, because it's for a subnet we don't
have an interface on. Probably we're doing DHCP on
a remote subnet via a relay. Zero the timer, since we won't
ever be able to send ra's and satisfy it. */
ever be able to send RAs to satisfy it. */
context->ra_time = 0;
if (param.iface != 0 &&

View File

@@ -29,16 +29,19 @@
return = 1 -> extract OK, compare OK, flip OK
return = 2 -> extract OK, compare failed.
return = 3 -> extract OK, compare failed but only on case.
If pp == NULL, operate on the query name in the packet.
*/
int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
char *name, int func, unsigned int parm)
{
unsigned char *cp = (unsigned char *)name, *p = *pp, *p1 = NULL;
unsigned char *cp = (unsigned char *)name, *p1 = NULL;
unsigned int j, l, namelen = 0, hops = 0;
unsigned int bigmap_counter = 0, bigmap_posn = 0, bigmap_size = parm, bitmap = 0;
int retvalue = 1, case_insens = 1, isExtract = 0, flip = 0, extrabytes = (int)parm;
unsigned int *bigmap = (unsigned int *)name;
unsigned char *p = pp ? *pp : (unsigned char *)(header+1);
if (func == EXTR_NAME_EXTRACT)
isExtract = 1, *cp = 0;
else if (func == EXTR_NAME_NOCASE)
@@ -71,11 +74,14 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
}
else if (!flip && *cp != 0)
retvalue = 2;
if (p1) /* we jumped via compression */
*pp = p1;
else
*pp = p;
if (pp)
{
if (p1) /* we jumped via compression */
*pp = p1;
else
*pp = p;
}
return retvalue;
}
@@ -528,9 +534,6 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *sub
if (substring)
*substring = name_len;
if (ttlp)
*ttlp = daemon->neg_ttl;
for (i = 0; i < ntohs(header->nscount); i++)
{
if (!extract_name(header, qlen, &p, daemon->workspacename, EXTR_NAME_EXTRACT, 0))
@@ -590,18 +593,18 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *sub
}
/* rest of RR */
if (!no_cache && !blockdata_expand(addr.rrblock.rrdata, addr.rrblock.datalen, (char *)p, 20))
{
blockdata_free(addr.rrblock.rrdata);
return 0;
}
addr.rrblock.datalen += 20;
if (!no_cache)
{
int secflag = 0;
if (!blockdata_expand(addr.rrblock.rrdata, addr.rrblock.datalen, (char *)p, 20))
{
blockdata_free(addr.rrblock.rrdata);
return 0;
}
addr.rrblock.datalen += 20;
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && daemon->rr_status[i + ntohs(header->ancount)] != 0)
{
@@ -806,21 +809,32 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
}
}
if (!found && !option_bool(OPT_NO_NEG))
if (!found)
{
/* For reverse records, we use the name field to store the SOA name. */
int substring, have_soa = find_soa(header, qlen, name, &substring, &ttl, no_cache_dnssec, now);
flags |= F_NEG | (secure ? F_DNSSECOK : 0);
if (name_encoding && ttl)
{
flags |= F_REVERSE | name_encoding;
if (!have_soa)
flags |= F_NO_RR; /* Marks no SOA found. */
cache_insert(name + substring, &addr, C_IN, now, ttl, flags);
}
if (name_encoding)
flags |= F_REVERSE | name_encoding;
log_query(flags | F_UPSTREAM, name, &addr, NULL, 0);
if (name_encoding && !option_bool(OPT_NO_NEG))
{
/* For reverse records, we use the name field to store the SOA name. */
int substring, have_soa = find_soa(header, qlen, name, &substring, &ttl, no_cache_dnssec, now);
if (have_soa || daemon->neg_ttl)
{
/* If daemon->neg_ttl is set, we can cache even without an SOA. */
if (!have_soa)
{
flags |= F_NO_RR; /* Marks no SOA found. */
ttl = daemon->neg_ttl;
}
cache_insert(name + substring, &addr, C_IN, now, ttl, flags);
}
}
}
}
else
@@ -1108,26 +1122,27 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
{
int substring, have_soa = find_soa(header, qlen, name, &substring, &ttl, no_cache_dnssec, now);
/* If there's no SOA to get the TTL from, but there is a CNAME
pointing at this, inherit its TTL */
if (ttl || cpp)
{
if (!ttl)
ttl = cttl;
addr.rrdata.datalen = substring;
addr.rrdata.rrtype = qtype;
if (!have_soa)
flags |= F_NO_RR; /* Marks no SOA found. */
}
newc = cache_insert(name, &addr, C_IN, now, ttl, F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0));
if (newc && cpp)
{
next_uid(newc);
cpp->addr.cname.target.cache = newc;
cpp->addr.cname.uid = newc->uid;
if (have_soa || daemon->neg_ttl)
{
if (have_soa)
{
addr.rrdata.datalen = substring;
addr.rrdata.rrtype = qtype;
}
else
{
/* If daemon->neg_ttl is set, we can cache even without an SOA. */
ttl = daemon->neg_ttl;
flags |= F_NO_RR; /* Marks no SOA found. */
}
newc = cache_insert(name, &addr, C_IN, now, ttl, F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0));
if (newc && cpp)
{
next_uid(newc);
cpp->addr.cname.target.cache = newc;
cpp->addr.cname.uid = newc->uid;
}
}
}
}
@@ -1232,7 +1247,8 @@ void report_addresses(struct dns_header *header, size_t len, u32 mark)
/* If the packet holds exactly one query
return F_IPV4 or F_IPV6 and leave the name from the query in name */
unsigned int extract_request(struct dns_header *header, size_t qlen, char *name, unsigned short *typep)
unsigned int extract_request(struct dns_header *header, size_t qlen, char *name,
unsigned short *typep, unsigned short *classp)
{
unsigned char *p = (unsigned char *)(header+1);
int qtype, qclass;
@@ -1257,6 +1273,9 @@ unsigned int extract_request(struct dns_header *header, size_t qlen, char *name,
if (typep)
*typep = qtype;
if (classp)
*classp = qclass;
if (qclass == C_IN)
{
if (qtype == T_A)
@@ -1268,9 +1287,7 @@ unsigned int extract_request(struct dns_header *header, size_t qlen, char *name,
}
#ifdef HAVE_DNSSEC
/* F_DNSSECOK as agument to search_servers() inhibits forwarding
to servers for domains without a trust anchor. This make the
behaviour for DS and DNSKEY queries we forward the same
/* Make the behaviour for DS and DNSKEY queries we forward the same
as for DS and DNSKEY queries we originate. */
if (option_bool(OPT_DNSSEC_VALID) && (qtype == T_DS || qtype == T_DNSKEY))
return F_DNSSECOK;

View File

@@ -1345,7 +1345,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
else if (!lease && (ltmp = lease_find_by_addr(mess->yiaddr)))
{
/* If a host is configured with more than one MAC address, it's OK to 'nix
a lease from one of it's MACs to give the address to another. */
a lease from one of its MACs to give the address to another. */
if (config && config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type))
{
inet_ntop(AF_INET, &ltmp->addr, daemon->addrbuff, ADDRSTRLEN);

View File

@@ -377,7 +377,7 @@ int expand_workspace(unsigned char ***wkspc, int *szp, int new)
int to_wire(char *name)
{
unsigned char *l, *p, *q, term;
int len;
unsigned int len;
for (l = (unsigned char*)name; *l != 0; l = p)
{
@@ -409,7 +409,7 @@ int to_wire(char *name)
void from_wire(char *name)
{
unsigned char *l, *p, *last;
int len;
unsigned int len;
for (last = (unsigned char *)name; *last != 0; last += *last+1);

View File

@@ -61,7 +61,7 @@ void slaac_add_addrs(struct dhcp_lease *lease, time_t now, int force)
else if (lease->clid_len == 9 &&
lease->clid[0] == ARPHRD_EUI64 &&
lease->hwaddr_type == ARPHRD_IEEE1394)
/* firewire has EUI-64 identifier as clid */
/* FireWire has EUI-64 identifier as clid */
memcpy(&addr.s6_addr[8], &lease->clid[1], 8);
#endif
else

View File

@@ -274,7 +274,7 @@ void tftp_request(struct listener *listen, time_t now)
}
/* Enforce simultaneous transfer limit. In non-single-port mode
this is doene by not listening on the server socket when
this is done by not listening on the server socket when
too many transfers are in progress. */
if (!transfer && tftp_cnt >= daemon->tftp_max)
return;

View File

@@ -426,7 +426,7 @@ int hostname_order(const char *a, const char *b)
int hostname_isequal(const char *a, const char *b)
{
return hostname_order(a, b) == 0;
return strlen(a) == strlen(b) && hostname_order(a, b) == 0;
}
/* is b equal to or a subdomain of a return 2 for equal, 1 for subdomain */