mirror of
https://github.com/pi-hole/dnsmasq.git
synced 2025-12-19 10:18:25 +00:00
Major rationalisation of DNSSEC validation.
Much gnarly special-case code removed and replaced with correct general implementaion. Checking of zone-status moved to DNSSEC code, where it should be, vastly simplifying query-forwarding code.
This commit is contained in:
@@ -586,12 +586,8 @@ struct hostsfile {
|
|||||||
#define STAT_NEED_KEY 5
|
#define STAT_NEED_KEY 5
|
||||||
#define STAT_TRUNCATED 6
|
#define STAT_TRUNCATED 6
|
||||||
#define STAT_SECURE_WILDCARD 7
|
#define STAT_SECURE_WILDCARD 7
|
||||||
#define STAT_NO_SIG 8
|
#define STAT_OK 8
|
||||||
#define STAT_NO_DS 9
|
#define STAT_ABANDONED 9
|
||||||
#define STAT_NO_NS 10
|
|
||||||
#define STAT_NEED_DS_NEG 11
|
|
||||||
#define STAT_CHASE_CNAME 12
|
|
||||||
#define STAT_INSECURE_DS 13
|
|
||||||
|
|
||||||
#define FREC_NOREBIND 1
|
#define FREC_NOREBIND 1
|
||||||
#define FREC_CHECKING_DISABLED 2
|
#define FREC_CHECKING_DISABLED 2
|
||||||
@@ -601,8 +597,7 @@ struct hostsfile {
|
|||||||
#define FREC_AD_QUESTION 32
|
#define FREC_AD_QUESTION 32
|
||||||
#define FREC_DO_QUESTION 64
|
#define FREC_DO_QUESTION 64
|
||||||
#define FREC_ADDED_PHEADER 128
|
#define FREC_ADDED_PHEADER 128
|
||||||
#define FREC_CHECK_NOSIGN 256
|
#define FREC_TEST_PKTSZ 256
|
||||||
#define FREC_TEST_PKTSZ 512
|
|
||||||
|
|
||||||
#ifdef HAVE_DNSSEC
|
#ifdef HAVE_DNSSEC
|
||||||
#define HASH_SIZE 20 /* SHA-1 digest size */
|
#define HASH_SIZE 20 /* SHA-1 digest size */
|
||||||
@@ -626,9 +621,7 @@ struct frec {
|
|||||||
#ifdef HAVE_DNSSEC
|
#ifdef HAVE_DNSSEC
|
||||||
int class, work_counter;
|
int class, work_counter;
|
||||||
struct blockdata *stash; /* Saved reply, whilst we validate */
|
struct blockdata *stash; /* Saved reply, whilst we validate */
|
||||||
struct blockdata *orig_domain; /* domain of original query, whilst
|
size_t stash_len;
|
||||||
we're seeing is if in unsigned domain */
|
|
||||||
size_t stash_len, name_start, name_len;
|
|
||||||
struct frec *dependent; /* Query awaiting internally-generated DNSKEY or DS query */
|
struct frec *dependent; /* Query awaiting internally-generated DNSKEY or DS query */
|
||||||
struct frec *blocking_query; /* Query which is blocking us. */
|
struct frec *blocking_query; /* Query which is blocking us. */
|
||||||
#endif
|
#endif
|
||||||
@@ -1162,8 +1155,8 @@ int in_zone(struct auth_zone *zone, char *name, char **cut);
|
|||||||
size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, int class, int type, union mysockaddr *addr, int edns_pktsz);
|
size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, int class, int type, union mysockaddr *addr, int edns_pktsz);
|
||||||
int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t n, char *name, char *keyname, int class);
|
int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t n, char *name, char *keyname, int class);
|
||||||
int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class);
|
int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class);
|
||||||
int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class, int *neganswer, int *nons);
|
int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class,
|
||||||
int dnssec_chase_cname(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname);
|
int check_unsigned, int *neganswer, int *nons);
|
||||||
int dnskey_keytag(int alg, int flags, unsigned char *rdata, int rdlen);
|
int dnskey_keytag(int alg, int flags, unsigned char *rdata, int rdlen);
|
||||||
size_t filter_rrsigs(struct dns_header *header, size_t plen);
|
size_t filter_rrsigs(struct dns_header *header, size_t plen);
|
||||||
unsigned char* hash_questions(struct dns_header *header, size_t plen, char *name);
|
unsigned char* hash_questions(struct dns_header *header, size_t plen, char *name);
|
||||||
|
|||||||
928
src/dnssec.c
928
src/dnssec.c
File diff suppressed because it is too large
Load Diff
751
src/forward.c
751
src/forward.c
@@ -23,15 +23,6 @@ static struct frec *lookup_frec_by_sender(unsigned short id,
|
|||||||
static unsigned short get_id(void);
|
static unsigned short get_id(void);
|
||||||
static void free_frec(struct frec *f);
|
static void free_frec(struct frec *f);
|
||||||
|
|
||||||
#ifdef HAVE_DNSSEC
|
|
||||||
static int tcp_key_recurse(time_t now, int status, struct dns_header *header, size_t n,
|
|
||||||
int class, char *name, char *keyname, struct server *server, int *keycount);
|
|
||||||
static int do_check_sign(struct frec *forward, int status, time_t now, char *name, char *keyname);
|
|
||||||
static int send_check_sign(struct frec *forward, time_t now, struct dns_header *header, size_t plen,
|
|
||||||
char *name, char *keyname);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/* Send a UDP packet with its source address set as "source"
|
/* Send a UDP packet with its source address set as "source"
|
||||||
unless nowild is true, when we just send it with the kernel default */
|
unless nowild is true, when we just send it with the kernel default */
|
||||||
int send_from(int fd, int nowild, char *packet, size_t len,
|
int send_from(int fd, int nowild, char *packet, size_t len,
|
||||||
@@ -825,236 +816,142 @@ void reply_query(int fd, int family, time_t now)
|
|||||||
#ifdef HAVE_DNSSEC
|
#ifdef HAVE_DNSSEC
|
||||||
if (server && option_bool(OPT_DNSSEC_VALID) && !(forward->flags & FREC_CHECKING_DISABLED))
|
if (server && option_bool(OPT_DNSSEC_VALID) && !(forward->flags & FREC_CHECKING_DISABLED))
|
||||||
{
|
{
|
||||||
int status;
|
int status = 0;
|
||||||
|
|
||||||
/* We've had a reply already, which we're validating. Ignore this duplicate */
|
/* We've had a reply already, which we're validating. Ignore this duplicate */
|
||||||
if (forward->blocking_query)
|
if (forward->blocking_query)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (header->hb3 & HB3_TC)
|
/* Truncated answer can't be validated.
|
||||||
{
|
|
||||||
/* Truncated answer can't be validated.
|
|
||||||
If this is an answer to a DNSSEC-generated query, we still
|
If this is an answer to a DNSSEC-generated query, we still
|
||||||
need to get the client to retry over TCP, so return
|
need to get the client to retry over TCP, so return
|
||||||
an answer with the TC bit set, even if the actual answer fits.
|
an answer with the TC bit set, even if the actual answer fits.
|
||||||
*/
|
*/
|
||||||
status = STAT_TRUNCATED;
|
if (header->hb3 & HB3_TC)
|
||||||
}
|
status = STAT_TRUNCATED;
|
||||||
else if (forward->flags & FREC_DNSKEY_QUERY)
|
|
||||||
status = dnssec_validate_by_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
|
while (1)
|
||||||
else if (forward->flags & FREC_DS_QUERY)
|
|
||||||
{
|
{
|
||||||
status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
|
/* As soon as anything returns BOGUS, we stop and unwind, to do otherwise
|
||||||
/* Provably no DS, everything below is insecure, even if signatures are offered */
|
would invite infinite loops, since the answers to DNSKEY and DS queries
|
||||||
if (status == STAT_NO_DS)
|
will not be cached, so they'll be repeated. */
|
||||||
/* We only cache sigs when we've validated a reply.
|
if (status != STAT_BOGUS && status != STAT_TRUNCATED && status != STAT_ABANDONED)
|
||||||
Avoid caching a reply with sigs if there's a vaildated break in the
|
|
||||||
DS chain, so we don't return replies from cache missing sigs. */
|
|
||||||
status = STAT_INSECURE_DS;
|
|
||||||
else if (status == STAT_NO_SIG)
|
|
||||||
{
|
|
||||||
if (option_bool(OPT_DNSSEC_NO_SIGN))
|
|
||||||
{
|
|
||||||
status = send_check_sign(forward, now, header, n, daemon->namebuff, daemon->keyname);
|
|
||||||
if (status == STAT_INSECURE)
|
|
||||||
status = STAT_INSECURE_DS;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
status = STAT_INSECURE_DS;
|
|
||||||
}
|
|
||||||
else if (status == STAT_NO_NS)
|
|
||||||
status = STAT_BOGUS;
|
|
||||||
}
|
|
||||||
else if (forward->flags & FREC_CHECK_NOSIGN)
|
|
||||||
{
|
|
||||||
status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
|
|
||||||
if (status != STAT_NEED_KEY)
|
|
||||||
status = do_check_sign(forward, status, now, daemon->namebuff, daemon->keyname);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class, NULL, NULL);
|
|
||||||
if (status == STAT_NO_SIG)
|
|
||||||
{
|
{
|
||||||
if (option_bool(OPT_DNSSEC_NO_SIGN))
|
if (forward->flags & FREC_DNSKEY_QUERY)
|
||||||
status = send_check_sign(forward, now, header, n, daemon->namebuff, daemon->keyname);
|
status = dnssec_validate_by_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
|
||||||
|
else if (forward->flags & FREC_DS_QUERY)
|
||||||
|
status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
|
||||||
else
|
else
|
||||||
status = STAT_INSECURE;
|
status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class,
|
||||||
|
option_bool(OPT_DNSSEC_NO_SIGN), NULL, NULL);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
/* Can't validate, as we're missing key data. Put this
|
|
||||||
answer aside, whilst we get that. */
|
|
||||||
if (status == STAT_NEED_DS || status == STAT_NEED_DS_NEG || status == STAT_NEED_KEY)
|
|
||||||
{
|
|
||||||
struct frec *new, *orig;
|
|
||||||
|
|
||||||
/* Free any saved query */
|
/* Can't validate, as we're missing key data. Put this
|
||||||
if (forward->stash)
|
answer aside, whilst we get that. */
|
||||||
blockdata_free(forward->stash);
|
if (status == STAT_NEED_DS || status == STAT_NEED_KEY)
|
||||||
|
|
||||||
/* Now save reply pending receipt of key data */
|
|
||||||
if (!(forward->stash = blockdata_alloc((char *)header, n)))
|
|
||||||
return;
|
|
||||||
forward->stash_len = n;
|
|
||||||
|
|
||||||
anotherkey:
|
|
||||||
/* Find the original query that started it all.... */
|
|
||||||
for (orig = forward; orig->dependent; orig = orig->dependent);
|
|
||||||
|
|
||||||
if (--orig->work_counter == 0 || !(new = get_new_frec(now, NULL, 1)))
|
|
||||||
status = STAT_INSECURE;
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
int fd;
|
struct frec *new, *orig;
|
||||||
struct frec *next = new->next;
|
|
||||||
*new = *forward; /* copy everything, then overwrite */
|
|
||||||
new->next = next;
|
|
||||||
new->blocking_query = NULL;
|
|
||||||
new->sentto = server;
|
|
||||||
new->rfd4 = NULL;
|
|
||||||
new->orig_domain = NULL;
|
|
||||||
#ifdef HAVE_IPV6
|
|
||||||
new->rfd6 = NULL;
|
|
||||||
#endif
|
|
||||||
new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY | FREC_CHECK_NOSIGN);
|
|
||||||
|
|
||||||
new->dependent = forward; /* to find query awaiting new one. */
|
/* Free any saved query */
|
||||||
forward->blocking_query = new; /* for garbage cleaning */
|
if (forward->stash)
|
||||||
/* validate routines leave name of required record in daemon->keyname */
|
blockdata_free(forward->stash);
|
||||||
if (status == STAT_NEED_KEY)
|
|
||||||
{
|
/* Now save reply pending receipt of key data */
|
||||||
new->flags |= FREC_DNSKEY_QUERY;
|
if (!(forward->stash = blockdata_alloc((char *)header, n)))
|
||||||
nn = dnssec_generate_query(header, ((char *) header) + daemon->packet_buff_sz,
|
|
||||||
daemon->keyname, forward->class, T_DNSKEY, &server->addr, server->edns_pktsz);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (status == STAT_NEED_DS_NEG)
|
|
||||||
new->flags |= FREC_CHECK_NOSIGN;
|
|
||||||
else
|
|
||||||
new->flags |= FREC_DS_QUERY;
|
|
||||||
nn = dnssec_generate_query(header,((char *) header) + daemon->packet_buff_sz,
|
|
||||||
daemon->keyname, forward->class, T_DS, &server->addr, server->edns_pktsz);
|
|
||||||
}
|
|
||||||
if ((hash = hash_questions(header, nn, daemon->namebuff)))
|
|
||||||
memcpy(new->hash, hash, HASH_SIZE);
|
|
||||||
new->new_id = get_id();
|
|
||||||
header->id = htons(new->new_id);
|
|
||||||
/* Save query for retransmission */
|
|
||||||
if (!(new->stash = blockdata_alloc((char *)header, nn)))
|
|
||||||
return;
|
return;
|
||||||
|
forward->stash_len = n;
|
||||||
new->stash_len = nn;
|
|
||||||
|
|
||||||
/* Don't resend this. */
|
/* Find the original query that started it all.... */
|
||||||
daemon->srv_save = NULL;
|
for (orig = forward; orig->dependent; orig = orig->dependent);
|
||||||
|
|
||||||
if (server->sfd)
|
if (--orig->work_counter == 0 || !(new = get_new_frec(now, NULL, 1)))
|
||||||
fd = server->sfd->fd;
|
status = STAT_ABANDONED;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fd = -1;
|
int fd;
|
||||||
|
struct frec *next = new->next;
|
||||||
|
*new = *forward; /* copy everything, then overwrite */
|
||||||
|
new->next = next;
|
||||||
|
new->blocking_query = NULL;
|
||||||
|
new->sentto = server;
|
||||||
|
new->rfd4 = NULL;
|
||||||
#ifdef HAVE_IPV6
|
#ifdef HAVE_IPV6
|
||||||
if (server->addr.sa.sa_family == AF_INET6)
|
new->rfd6 = NULL;
|
||||||
{
|
|
||||||
if (new->rfd6 || (new->rfd6 = allocate_rfd(AF_INET6)))
|
|
||||||
fd = new->rfd6->fd;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
#endif
|
#endif
|
||||||
|
new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY);
|
||||||
|
|
||||||
|
new->dependent = forward; /* to find query awaiting new one. */
|
||||||
|
forward->blocking_query = new; /* for garbage cleaning */
|
||||||
|
/* validate routines leave name of required record in daemon->keyname */
|
||||||
|
if (status == STAT_NEED_KEY)
|
||||||
{
|
{
|
||||||
if (new->rfd4 || (new->rfd4 = allocate_rfd(AF_INET)))
|
new->flags |= FREC_DNSKEY_QUERY;
|
||||||
fd = new->rfd4->fd;
|
nn = dnssec_generate_query(header, ((char *) header) + daemon->packet_buff_sz,
|
||||||
|
daemon->keyname, forward->class, T_DNSKEY, &server->addr, server->edns_pktsz);
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
|
{
|
||||||
if (fd != -1)
|
new->flags |= FREC_DS_QUERY;
|
||||||
{
|
nn = dnssec_generate_query(header,((char *) header) + daemon->packet_buff_sz,
|
||||||
while (retry_send(sendto(fd, (char *)header, nn, 0,
|
daemon->keyname, forward->class, T_DS, &server->addr, server->edns_pktsz);
|
||||||
&server->addr.sa,
|
}
|
||||||
sa_len(&server->addr))));
|
if ((hash = hash_questions(header, nn, daemon->namebuff)))
|
||||||
server->queries++;
|
memcpy(new->hash, hash, HASH_SIZE);
|
||||||
}
|
new->new_id = get_id();
|
||||||
|
header->id = htons(new->new_id);
|
||||||
|
/* Save query for retransmission */
|
||||||
|
new->stash = blockdata_alloc((char *)header, nn);
|
||||||
|
new->stash_len = nn;
|
||||||
|
|
||||||
|
/* Don't resend this. */
|
||||||
|
daemon->srv_save = NULL;
|
||||||
|
|
||||||
|
if (server->sfd)
|
||||||
|
fd = server->sfd->fd;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fd = -1;
|
||||||
|
#ifdef HAVE_IPV6
|
||||||
|
if (server->addr.sa.sa_family == AF_INET6)
|
||||||
|
{
|
||||||
|
if (new->rfd6 || (new->rfd6 = allocate_rfd(AF_INET6)))
|
||||||
|
fd = new->rfd6->fd;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
if (new->rfd4 || (new->rfd4 = allocate_rfd(AF_INET)))
|
||||||
|
fd = new->rfd4->fd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fd != -1)
|
||||||
|
{
|
||||||
|
while (retry_send(sendto(fd, (char *)header, nn, 0,
|
||||||
|
&server->addr.sa,
|
||||||
|
sa_len(&server->addr))));
|
||||||
|
server->queries++;
|
||||||
|
}
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* Ok, we reached far enough up the chain-of-trust that we can validate something.
|
/* Validated original answer, all done. */
|
||||||
Now wind back down, pulling back answers which wouldn't previously validate
|
if (!forward->dependent)
|
||||||
and validate them with the new data. Note that if an answer needs multiple
|
break;
|
||||||
keys to validate, we may find another key is needed, in which case we set off
|
|
||||||
down another branch of the tree. Once we get to the original answer
|
/* validated subsdiary query, (and cached result)
|
||||||
(FREC_DNSSEC_QUERY not set) and it validates, return it to the original requestor. */
|
pop that and return to the previous query we were working on. */
|
||||||
while (forward->dependent)
|
|
||||||
{
|
|
||||||
struct frec *prev = forward->dependent;
|
struct frec *prev = forward->dependent;
|
||||||
free_frec(forward);
|
free_frec(forward);
|
||||||
forward = prev;
|
forward = prev;
|
||||||
forward->blocking_query = NULL; /* already gone */
|
forward->blocking_query = NULL; /* already gone */
|
||||||
blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
|
blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
|
||||||
n = forward->stash_len;
|
n = forward->stash_len;
|
||||||
|
|
||||||
if (status == STAT_SECURE)
|
|
||||||
{
|
|
||||||
if (forward->flags & FREC_DNSKEY_QUERY)
|
|
||||||
status = dnssec_validate_by_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
|
|
||||||
else if (forward->flags & FREC_DS_QUERY)
|
|
||||||
{
|
|
||||||
status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
|
|
||||||
/* Provably no DS, everything below is insecure, even if signatures are offered */
|
|
||||||
if (status == STAT_NO_DS)
|
|
||||||
/* We only cache sigs when we've validated a reply.
|
|
||||||
Avoid caching a reply with sigs if there's a vaildated break in the
|
|
||||||
DS chain, so we don't return replies from cache missing sigs. */
|
|
||||||
status = STAT_INSECURE_DS;
|
|
||||||
else if (status == STAT_NO_SIG)
|
|
||||||
{
|
|
||||||
if (option_bool(OPT_DNSSEC_NO_SIGN))
|
|
||||||
{
|
|
||||||
status = send_check_sign(forward, now, header, n, daemon->namebuff, daemon->keyname);
|
|
||||||
if (status == STAT_INSECURE)
|
|
||||||
status = STAT_INSECURE_DS;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
status = STAT_INSECURE_DS;
|
|
||||||
}
|
|
||||||
else if (status == STAT_NO_NS)
|
|
||||||
status = STAT_BOGUS;
|
|
||||||
}
|
|
||||||
else if (forward->flags & FREC_CHECK_NOSIGN)
|
|
||||||
{
|
|
||||||
status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
|
|
||||||
if (status != STAT_NEED_KEY)
|
|
||||||
status = do_check_sign(forward, status, now, daemon->namebuff, daemon->keyname);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class, NULL, NULL);
|
|
||||||
if (status == STAT_NO_SIG)
|
|
||||||
{
|
|
||||||
if (option_bool(OPT_DNSSEC_NO_SIGN))
|
|
||||||
status = send_check_sign(forward, now, header, n, daemon->namebuff, daemon->keyname);
|
|
||||||
else
|
|
||||||
status = STAT_INSECURE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status == STAT_NEED_DS || status == STAT_NEED_DS_NEG || status == STAT_NEED_KEY)
|
|
||||||
goto anotherkey;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
no_cache_dnssec = 0;
|
no_cache_dnssec = 0;
|
||||||
|
|
||||||
if (status == STAT_INSECURE_DS)
|
|
||||||
{
|
|
||||||
/* We only cache sigs when we've validated a reply.
|
|
||||||
Avoid caching a reply with sigs if there's a vaildated break in the
|
|
||||||
DS chain, so we don't return replies from cache missing sigs. */
|
|
||||||
status = STAT_INSECURE;
|
|
||||||
no_cache_dnssec = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status == STAT_TRUNCATED)
|
if (status == STAT_TRUNCATED)
|
||||||
header->hb3 |= HB3_TC;
|
header->hb3 |= HB3_TC;
|
||||||
@@ -1062,7 +959,7 @@ void reply_query(int fd, int family, time_t now)
|
|||||||
{
|
{
|
||||||
char *result, *domain = "result";
|
char *result, *domain = "result";
|
||||||
|
|
||||||
if (forward->work_counter == 0)
|
if (status == STAT_ABANDONED)
|
||||||
{
|
{
|
||||||
result = "ABANDONED";
|
result = "ABANDONED";
|
||||||
status = STAT_BOGUS;
|
status = STAT_BOGUS;
|
||||||
@@ -1072,7 +969,7 @@ void reply_query(int fd, int family, time_t now)
|
|||||||
|
|
||||||
if (status == STAT_BOGUS && extract_request(header, n, daemon->namebuff, NULL))
|
if (status == STAT_BOGUS && extract_request(header, n, daemon->namebuff, NULL))
|
||||||
domain = daemon->namebuff;
|
domain = daemon->namebuff;
|
||||||
|
|
||||||
log_query(F_KEYTAG | F_SECSTAT, domain, NULL, result);
|
log_query(F_KEYTAG | F_SECSTAT, domain, NULL, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1415,315 +1312,49 @@ void receive_query(struct listener *listen, time_t now)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_DNSSEC
|
#ifdef HAVE_DNSSEC
|
||||||
|
|
||||||
/* UDP: we've got an unsigned answer, return STAT_INSECURE if we can prove there's no DS
|
|
||||||
and therefore the answer shouldn't be signed, or STAT_BOGUS if it should be, or
|
|
||||||
STAT_NEED_DS_NEG and keyname if we need to do the query. */
|
|
||||||
static int send_check_sign(struct frec *forward, time_t now, struct dns_header *header, size_t plen,
|
|
||||||
char *name, char *keyname)
|
|
||||||
{
|
|
||||||
int status = dnssec_chase_cname(now, header, plen, name, keyname);
|
|
||||||
|
|
||||||
if (status != STAT_INSECURE)
|
|
||||||
return status;
|
|
||||||
|
|
||||||
/* Store the domain we're trying to check. */
|
|
||||||
forward->name_start = strlen(name);
|
|
||||||
forward->name_len = forward->name_start + 1;
|
|
||||||
if (!(forward->orig_domain = blockdata_alloc(name, forward->name_len)))
|
|
||||||
return STAT_BOGUS;
|
|
||||||
|
|
||||||
return do_check_sign(forward, 0, now, name, keyname);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We either have a a reply (header non-NULL, or we need to start by looking in the cache */
|
|
||||||
static int do_check_sign(struct frec *forward, int status, time_t now, char *name, char *keyname)
|
|
||||||
{
|
|
||||||
/* get domain we're checking back from blockdata store, it's stored on the original query. */
|
|
||||||
while (forward->dependent && !forward->orig_domain)
|
|
||||||
forward = forward->dependent;
|
|
||||||
|
|
||||||
blockdata_retrieve(forward->orig_domain, forward->name_len, name);
|
|
||||||
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
char *p;
|
|
||||||
|
|
||||||
if (status == 0)
|
|
||||||
{
|
|
||||||
struct crec *crecp;
|
|
||||||
|
|
||||||
/* Haven't received answer, see if in cache */
|
|
||||||
if (!(crecp = cache_find_by_name(NULL, &name[forward->name_start], now, F_DS)))
|
|
||||||
{
|
|
||||||
/* put name of DS record we're missing into keyname */
|
|
||||||
strcpy(keyname, &name[forward->name_start]);
|
|
||||||
/* and wait for reply to arrive */
|
|
||||||
return STAT_NEED_DS_NEG;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* F_DNSSECOK misused in DS cache records to non-existance of NS record */
|
|
||||||
if (!(crecp->flags & F_NEG))
|
|
||||||
status = STAT_SECURE;
|
|
||||||
else if (crecp->flags & F_DNSSECOK)
|
|
||||||
status = STAT_NO_DS;
|
|
||||||
else
|
|
||||||
status = STAT_NO_NS;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Have entered non-signed part of DNS tree. */
|
|
||||||
if (status == STAT_NO_DS)
|
|
||||||
return forward->dependent ? STAT_INSECURE_DS : STAT_INSECURE;
|
|
||||||
|
|
||||||
if (status == STAT_BOGUS)
|
|
||||||
return STAT_BOGUS;
|
|
||||||
|
|
||||||
if (status == STAT_NO_SIG && *keyname != 0)
|
|
||||||
{
|
|
||||||
/* There is a validated CNAME chain that doesn't end in a DS record. Start
|
|
||||||
the search again in that domain. */
|
|
||||||
blockdata_free(forward->orig_domain);
|
|
||||||
forward->name_start = strlen(keyname);
|
|
||||||
forward->name_len = forward->name_start + 1;
|
|
||||||
if (!(forward->orig_domain = blockdata_alloc(keyname, forward->name_len)))
|
|
||||||
return STAT_BOGUS;
|
|
||||||
|
|
||||||
strcpy(name, keyname);
|
|
||||||
status = 0; /* force to cache when we iterate. */
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* There's a proven DS record, or we're within a zone, where there doesn't need
|
|
||||||
to be a DS record. Add a name and try again.
|
|
||||||
If we've already tried the whole name, then fail */
|
|
||||||
|
|
||||||
if (forward->name_start == 0)
|
|
||||||
return STAT_BOGUS;
|
|
||||||
|
|
||||||
for (p = &name[forward->name_start-2]; (*p != '.') && (p != name); p--);
|
|
||||||
|
|
||||||
if (p != name)
|
|
||||||
p++;
|
|
||||||
|
|
||||||
forward->name_start = p - name;
|
|
||||||
status = 0; /* force to cache when we iterate. */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Move down from the root, until we find a signed non-existance of a DS, in which case
|
|
||||||
an unsigned answer is OK, or we find a signed DS, in which case there should be
|
|
||||||
a signature, and the answer is BOGUS */
|
|
||||||
static int tcp_check_for_unsigned_zone(time_t now, struct dns_header *header, size_t plen, int class, char *name,
|
|
||||||
char *keyname, struct server *server, int *keycount)
|
|
||||||
{
|
|
||||||
size_t m;
|
|
||||||
unsigned char *packet, *payload;
|
|
||||||
u16 *length;
|
|
||||||
int status, name_len;
|
|
||||||
struct blockdata *block;
|
|
||||||
|
|
||||||
char *name_start;
|
|
||||||
|
|
||||||
/* Get first insecure entry in CNAME chain */
|
|
||||||
status = tcp_key_recurse(now, STAT_CHASE_CNAME, header, plen, class, name, keyname, server, keycount);
|
|
||||||
if (status == STAT_BOGUS)
|
|
||||||
return STAT_BOGUS;
|
|
||||||
|
|
||||||
if (!(packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ + sizeof(u16))))
|
|
||||||
return STAT_BOGUS;
|
|
||||||
|
|
||||||
payload = &packet[2];
|
|
||||||
header = (struct dns_header *)payload;
|
|
||||||
length = (u16 *)packet;
|
|
||||||
|
|
||||||
/* Stash the name away, since the buffer will be trashed when we recurse */
|
|
||||||
name_len = strlen(name) + 1;
|
|
||||||
name_start = name + name_len - 1;
|
|
||||||
|
|
||||||
if (!(block = blockdata_alloc(name, name_len)))
|
|
||||||
{
|
|
||||||
free(packet);
|
|
||||||
return STAT_BOGUS;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
unsigned char c1, c2;
|
|
||||||
struct crec *crecp;
|
|
||||||
|
|
||||||
if (--(*keycount) == 0)
|
|
||||||
{
|
|
||||||
free(packet);
|
|
||||||
blockdata_free(block);
|
|
||||||
return STAT_BOGUS;
|
|
||||||
}
|
|
||||||
|
|
||||||
while ((crecp = cache_find_by_name(NULL, name_start, now, F_DS)))
|
|
||||||
{
|
|
||||||
if ((crecp->flags & F_NEG) && (crecp->flags & F_DNSSECOK))
|
|
||||||
{
|
|
||||||
/* Found a secure denial of DS - delegation is indeed insecure */
|
|
||||||
free(packet);
|
|
||||||
blockdata_free(block);
|
|
||||||
return STAT_INSECURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Here, either there's a secure DS, or no NS and no DS, and therefore no delegation.
|
|
||||||
Add another label and continue. */
|
|
||||||
|
|
||||||
if (name_start == name)
|
|
||||||
{
|
|
||||||
free(packet);
|
|
||||||
blockdata_free(block);
|
|
||||||
return STAT_BOGUS; /* run out of labels */
|
|
||||||
}
|
|
||||||
|
|
||||||
name_start -= 2;
|
|
||||||
while (*name_start != '.' && name_start != name)
|
|
||||||
name_start--;
|
|
||||||
if (name_start != name)
|
|
||||||
name_start++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Can't find it in the cache, have to send a query */
|
|
||||||
|
|
||||||
m = dnssec_generate_query(header, ((char *) header) + 65536, name_start, class, T_DS, &server->addr, server->edns_pktsz);
|
|
||||||
|
|
||||||
*length = htons(m);
|
|
||||||
|
|
||||||
if (read_write(server->tcpfd, packet, m + sizeof(u16), 0) &&
|
|
||||||
read_write(server->tcpfd, &c1, 1, 1) &&
|
|
||||||
read_write(server->tcpfd, &c2, 1, 1) &&
|
|
||||||
read_write(server->tcpfd, payload, (c1 << 8) | c2, 1))
|
|
||||||
{
|
|
||||||
m = (c1 << 8) | c2;
|
|
||||||
|
|
||||||
/* Note this trashes all three name workspaces */
|
|
||||||
status = tcp_key_recurse(now, STAT_NEED_DS_NEG, header, m, class, name, keyname, server, keycount);
|
|
||||||
|
|
||||||
if (status == STAT_NO_DS)
|
|
||||||
{
|
|
||||||
/* Found a secure denial of DS - delegation is indeed insecure */
|
|
||||||
free(packet);
|
|
||||||
blockdata_free(block);
|
|
||||||
return STAT_INSECURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status == STAT_NO_SIG && *keyname != 0)
|
|
||||||
{
|
|
||||||
/* There is a validated CNAME chain that doesn't end in a DS record. Start
|
|
||||||
the search again in that domain. */
|
|
||||||
blockdata_free(block);
|
|
||||||
name_len = strlen(keyname) + 1;
|
|
||||||
name_start = name + name_len - 1;
|
|
||||||
|
|
||||||
if (!(block = blockdata_alloc(keyname, name_len)))
|
|
||||||
return STAT_BOGUS;
|
|
||||||
|
|
||||||
strcpy(name, keyname);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status == STAT_BOGUS)
|
|
||||||
{
|
|
||||||
free(packet);
|
|
||||||
blockdata_free(block);
|
|
||||||
return STAT_BOGUS;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Here, either there's a secure DS, or no NS and no DS, and therefore no delegation.
|
|
||||||
Add another label and continue. */
|
|
||||||
|
|
||||||
/* Get name we're checking back. */
|
|
||||||
blockdata_retrieve(block, name_len, name);
|
|
||||||
|
|
||||||
if (name_start == name)
|
|
||||||
{
|
|
||||||
free(packet);
|
|
||||||
blockdata_free(block);
|
|
||||||
return STAT_BOGUS; /* run out of labels */
|
|
||||||
}
|
|
||||||
|
|
||||||
name_start -= 2;
|
|
||||||
while (*name_start != '.' && name_start != name)
|
|
||||||
name_start--;
|
|
||||||
if (name_start != name)
|
|
||||||
name_start++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* IO failure */
|
|
||||||
free(packet);
|
|
||||||
blockdata_free(block);
|
|
||||||
return STAT_BOGUS; /* run out of labels */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int tcp_key_recurse(time_t now, int status, struct dns_header *header, size_t n,
|
static int tcp_key_recurse(time_t now, int status, struct dns_header *header, size_t n,
|
||||||
int class, char *name, char *keyname, struct server *server, int *keycount)
|
int class, char *name, char *keyname, struct server *server, int *keycount)
|
||||||
{
|
{
|
||||||
/* Recurse up the key heirarchy */
|
/* Recurse up the key heirarchy */
|
||||||
int new_status;
|
int new_status;
|
||||||
|
unsigned char *packet = NULL;
|
||||||
|
size_t m;
|
||||||
|
unsigned char *payload = NULL;
|
||||||
|
struct dns_header *new_header = NULL;
|
||||||
|
u16 *length = NULL;
|
||||||
|
unsigned char c1, c2;
|
||||||
|
|
||||||
/* limit the amount of work we do, to avoid cycling forever on loops in the DNS */
|
while (1)
|
||||||
if (--(*keycount) == 0)
|
|
||||||
return STAT_INSECURE;
|
|
||||||
|
|
||||||
if (status == STAT_NEED_KEY)
|
|
||||||
new_status = dnssec_validate_by_ds(now, header, n, name, keyname, class);
|
|
||||||
else if (status == STAT_NEED_DS || status == STAT_NEED_DS_NEG)
|
|
||||||
{
|
{
|
||||||
new_status = dnssec_validate_ds(now, header, n, name, keyname, class);
|
/* limit the amount of work we do, to avoid cycling forever on loops in the DNS */
|
||||||
if (status == STAT_NEED_DS)
|
if (--(*keycount) == 0)
|
||||||
{
|
new_status = STAT_ABANDONED;
|
||||||
if (new_status == STAT_NO_DS)
|
else if (status == STAT_NEED_KEY)
|
||||||
new_status = STAT_INSECURE_DS;
|
new_status = dnssec_validate_by_ds(now, header, n, name, keyname, class);
|
||||||
if (new_status == STAT_NO_SIG)
|
else if (status == STAT_NEED_DS)
|
||||||
{
|
new_status = dnssec_validate_ds(now, header, n, name, keyname, class);
|
||||||
if (option_bool(OPT_DNSSEC_NO_SIGN))
|
else
|
||||||
{
|
new_status = dnssec_validate_reply(now, header, n, name, keyname, &class, option_bool(OPT_DNSSEC_NO_SIGN), NULL, NULL);
|
||||||
new_status = tcp_check_for_unsigned_zone(now, header, n, class, name, keyname, server, keycount);
|
|
||||||
if (new_status == STAT_INSECURE)
|
|
||||||
new_status = STAT_INSECURE_DS;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
new_status = STAT_INSECURE_DS;
|
|
||||||
}
|
|
||||||
else if (new_status == STAT_NO_NS)
|
|
||||||
new_status = STAT_BOGUS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (status == STAT_CHASE_CNAME)
|
|
||||||
new_status = dnssec_chase_cname(now, header, n, name, keyname);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
new_status = dnssec_validate_reply(now, header, n, name, keyname, &class, NULL, NULL);
|
|
||||||
|
|
||||||
if (new_status == STAT_NO_SIG)
|
if (new_status != STAT_NEED_DS && new_status != STAT_NEED_KEY)
|
||||||
{
|
break;
|
||||||
if (option_bool(OPT_DNSSEC_NO_SIGN))
|
|
||||||
new_status = tcp_check_for_unsigned_zone(now, header, n, class, name, keyname, server, keycount);
|
|
||||||
else
|
|
||||||
new_status = STAT_INSECURE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Can't validate because we need a key/DS whose name now in keyname.
|
/* Can't validate because we need a key/DS whose name now in keyname.
|
||||||
Make query for same, and recurse to validate */
|
Make query for same, and recurse to validate */
|
||||||
if (new_status == STAT_NEED_DS || new_status == STAT_NEED_KEY)
|
|
||||||
{
|
|
||||||
size_t m;
|
|
||||||
unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ + sizeof(u16));
|
|
||||||
unsigned char *payload = &packet[2];
|
|
||||||
struct dns_header *new_header = (struct dns_header *)payload;
|
|
||||||
u16 *length = (u16 *)packet;
|
|
||||||
unsigned char c1, c2;
|
|
||||||
|
|
||||||
if (!packet)
|
if (!packet)
|
||||||
return STAT_INSECURE;
|
{
|
||||||
|
packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ + sizeof(u16));
|
||||||
another_tcp_key:
|
payload = &packet[2];
|
||||||
|
new_header = (struct dns_header *)payload;
|
||||||
|
length = (u16 *)packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!packet)
|
||||||
|
{
|
||||||
|
new_status = STAT_ABANDONED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
m = dnssec_generate_query(new_header, ((char *) new_header) + 65536, keyname, class,
|
m = dnssec_generate_query(new_header, ((char *) new_header) + 65536, keyname, class,
|
||||||
new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS, &server->addr, server->edns_pktsz);
|
new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS, &server->addr, server->edns_pktsz);
|
||||||
|
|
||||||
@@ -1733,65 +1364,22 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
|
|||||||
!read_write(server->tcpfd, &c1, 1, 1) ||
|
!read_write(server->tcpfd, &c1, 1, 1) ||
|
||||||
!read_write(server->tcpfd, &c2, 1, 1) ||
|
!read_write(server->tcpfd, &c2, 1, 1) ||
|
||||||
!read_write(server->tcpfd, payload, (c1 << 8) | c2, 1))
|
!read_write(server->tcpfd, payload, (c1 << 8) | c2, 1))
|
||||||
new_status = STAT_INSECURE;
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
m = (c1 << 8) | c2;
|
new_status = STAT_ABANDONED;
|
||||||
|
break;
|
||||||
new_status = tcp_key_recurse(now, new_status, new_header, m, class, name, keyname, server, keycount);
|
|
||||||
|
|
||||||
if (new_status == STAT_SECURE)
|
|
||||||
{
|
|
||||||
/* Reached a validated record, now try again at this level.
|
|
||||||
Note that we may get ANOTHER NEED_* if an answer needs more than one key.
|
|
||||||
If so, go round again. */
|
|
||||||
|
|
||||||
if (status == STAT_NEED_KEY)
|
|
||||||
new_status = dnssec_validate_by_ds(now, header, n, name, keyname, class);
|
|
||||||
else if (status == STAT_NEED_DS || status == STAT_NEED_DS_NEG)
|
|
||||||
{
|
|
||||||
new_status = dnssec_validate_ds(now, header, n, name, keyname, class);
|
|
||||||
if (status == STAT_NEED_DS)
|
|
||||||
{
|
|
||||||
if (new_status == STAT_NO_DS)
|
|
||||||
new_status = STAT_INSECURE_DS;
|
|
||||||
else if (new_status == STAT_NO_SIG)
|
|
||||||
{
|
|
||||||
if (option_bool(OPT_DNSSEC_NO_SIGN))
|
|
||||||
{
|
|
||||||
new_status = tcp_check_for_unsigned_zone(now, header, n, class, name, keyname, server, keycount);
|
|
||||||
if (new_status == STAT_INSECURE)
|
|
||||||
new_status = STAT_INSECURE_DS;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
new_status = STAT_INSECURE_DS;
|
|
||||||
}
|
|
||||||
else if (new_status == STAT_NO_NS)
|
|
||||||
new_status = STAT_BOGUS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (status == STAT_CHASE_CNAME)
|
|
||||||
new_status = dnssec_chase_cname(now, header, n, name, keyname);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
new_status = dnssec_validate_reply(now, header, n, name, keyname, &class, NULL, NULL);
|
|
||||||
|
|
||||||
if (new_status == STAT_NO_SIG)
|
|
||||||
{
|
|
||||||
if (option_bool(OPT_DNSSEC_NO_SIGN))
|
|
||||||
new_status = tcp_check_for_unsigned_zone(now, header, n, class, name, keyname, server, keycount);
|
|
||||||
else
|
|
||||||
new_status = STAT_INSECURE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (new_status == STAT_NEED_DS || new_status == STAT_NEED_KEY)
|
|
||||||
goto another_tcp_key;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m = (c1 << 8) | c2;
|
||||||
|
|
||||||
free(packet);
|
new_status = tcp_key_recurse(now, new_status, new_header, m, class, name, keyname, server, keycount);
|
||||||
|
|
||||||
|
if (new_status != STAT_OK)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (packet)
|
||||||
|
free(packet);
|
||||||
|
|
||||||
return new_status;
|
return new_status;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -2075,19 +1663,10 @@ unsigned char *tcp_request(int confd, time_t now,
|
|||||||
if (option_bool(OPT_DNSSEC_VALID) && !checking_disabled)
|
if (option_bool(OPT_DNSSEC_VALID) && !checking_disabled)
|
||||||
{
|
{
|
||||||
int keycount = DNSSEC_WORK; /* Limit to number of DNSSEC questions, to catch loops and avoid filling cache. */
|
int keycount = DNSSEC_WORK; /* Limit to number of DNSSEC questions, to catch loops and avoid filling cache. */
|
||||||
int status = tcp_key_recurse(now, STAT_TRUNCATED, header, m, 0, daemon->namebuff, daemon->keyname, last_server, &keycount);
|
int status = tcp_key_recurse(now, STAT_OK, header, m, 0, daemon->namebuff, daemon->keyname, last_server, &keycount);
|
||||||
char *result, *domain = "result";
|
char *result, *domain = "result";
|
||||||
|
|
||||||
if (status == STAT_INSECURE_DS)
|
|
||||||
{
|
|
||||||
/* We only cache sigs when we've validated a reply.
|
|
||||||
Avoid caching a reply with sigs if there's a vaildated break in the
|
|
||||||
DS chain, so we don't return replies from cache missing sigs. */
|
|
||||||
status = STAT_INSECURE;
|
|
||||||
no_cache_dnssec = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (keycount == 0)
|
if (status == STAT_ABANDONED)
|
||||||
{
|
{
|
||||||
result = "ABANDONED";
|
result = "ABANDONED";
|
||||||
status = STAT_BOGUS;
|
status = STAT_BOGUS;
|
||||||
@@ -2179,7 +1758,6 @@ static struct frec *allocate_frec(time_t now)
|
|||||||
f->dependent = NULL;
|
f->dependent = NULL;
|
||||||
f->blocking_query = NULL;
|
f->blocking_query = NULL;
|
||||||
f->stash = NULL;
|
f->stash = NULL;
|
||||||
f->orig_domain = NULL;
|
|
||||||
#endif
|
#endif
|
||||||
daemon->frec_list = f;
|
daemon->frec_list = f;
|
||||||
}
|
}
|
||||||
@@ -2248,12 +1826,6 @@ static void free_frec(struct frec *f)
|
|||||||
f->stash = NULL;
|
f->stash = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (f->orig_domain)
|
|
||||||
{
|
|
||||||
blockdata_free(f->orig_domain);
|
|
||||||
f->orig_domain = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Anything we're waiting on is pointless now, too */
|
/* Anything we're waiting on is pointless now, too */
|
||||||
if (f->blocking_query)
|
if (f->blocking_query)
|
||||||
free_frec(f->blocking_query);
|
free_frec(f->blocking_query);
|
||||||
@@ -2281,14 +1853,23 @@ struct frec *get_new_frec(time_t now, int *wait, int force)
|
|||||||
target = f;
|
target = f;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (difftime(now, f->time) >= 4*TIMEOUT)
|
#ifdef HAVE_DNSSEC
|
||||||
{
|
/* Don't free DNSSEC sub-queries here, as we may end up with
|
||||||
free_frec(f);
|
dangling references to them. They'll go when their "real" query
|
||||||
target = f;
|
is freed. */
|
||||||
}
|
if (!f->dependent)
|
||||||
|
#endif
|
||||||
if (!oldest || difftime(f->time, oldest->time) <= 0)
|
{
|
||||||
oldest = f;
|
if (difftime(now, f->time) >= 4*TIMEOUT)
|
||||||
|
{
|
||||||
|
free_frec(f);
|
||||||
|
target = f;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!oldest || difftime(f->time, oldest->time) <= 0)
|
||||||
|
oldest = f;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target)
|
if (target)
|
||||||
|
|||||||
Reference in New Issue
Block a user