Remove query hashing in UDP DNS code path.

Now we're always saving the query, this is no longer necessary,
and allows the removal of a lot of quite hairy code.

Much more code removal to come, once the TCP code path is also purged.
This commit is contained in:
Simon Kelley
2024-11-02 15:33:37 +00:00
parent 5d6399b71c
commit ed6d29a784
2 changed files with 91 additions and 119 deletions

View File

@@ -774,9 +774,8 @@ struct dyndir {
#define FREC_DO_QUESTION 64 #define FREC_DO_QUESTION 64
#define FREC_ADDED_PHEADER 128 #define FREC_ADDED_PHEADER 128
#define FREC_TEST_PKTSZ 256 #define FREC_TEST_PKTSZ 256
#define FREC_HAS_EXTRADATA 512 #define FREC_HAS_PHEADER 512
#define FREC_HAS_PHEADER 1024 #define FREC_GONE_TO_TCP 1024
#define FREC_GONE_TO_TCP 2048
#define HASH_SIZE 32 /* SHA-256 digest size */ #define HASH_SIZE 32 /* SHA-256 digest size */
@@ -796,8 +795,7 @@ struct frec {
time_t time; time_t time;
u32 forward_timestamp; u32 forward_timestamp;
int forward_delay; int forward_delay;
unsigned char *hash[HASH_SIZE]; struct blockdata *stash; /* saved query or saved reply, whilst we validate */
struct blockdata *stash; /* Saved reply, whilst we validate */
size_t stash_len; size_t stash_len;
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
int uid, class, work_counter, validate_counter; int uid, class, work_counter, validate_counter;

View File

@@ -17,10 +17,8 @@
#include "dnsmasq.h" #include "dnsmasq.h"
static struct frec *get_new_frec(time_t now, struct server *serv, int force); static struct frec *get_new_frec(time_t now, struct server *serv, int force);
static struct frec *lookup_frec(unsigned short id, int fd, void *hash, int *firstp, int *lastp); static struct frec *lookup_frec(char *target, int class, int rrtype, int id, int flags, int flagmask);
static struct frec *lookup_frec_by_query(void *hash, unsigned int flags, unsigned int flagmask);
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
static struct frec *lookup_frec_dnssec(char *target, int class, int flags, struct dns_header *header);
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 class, char *name, char *keyname, struct server *server,
int have_mark, unsigned int mark, int *keycount, int *validatecount); int have_mark, unsigned int mark, int *keycount, int *validatecount);
@@ -175,20 +173,22 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
unsigned int fwd_flags = 0; unsigned int fwd_flags = 0;
int is_dnssec = forward && (forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY)); int is_dnssec = forward && (forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY));
struct server *master; struct server *master;
void *hash = hash_questions(header, plen, daemon->namebuff); unsigned int gotname;
unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL);
unsigned char *oph = find_pseudoheader(header, plen, NULL, NULL, NULL, NULL);
int old_src = 0, old_reply = 0; int old_src = 0, old_reply = 0;
int first, last, start = 0; int first, last, start = 0;
int cacheable, forwarded = 0; int cacheable, forwarded = 0;
size_t edns0_len; size_t edns0_len;
unsigned char *pheader; unsigned char *pheader, *oph;
int ede = EDE_UNSET; int ede = EDE_UNSET;
(void)do_bit; (void)do_bit;
unsigned short rrtype;
if (saved_question) if (saved_question)
blockdata_retrieve(saved_question, plen, header); blockdata_retrieve(saved_question, plen, header);
gotname = extract_request(header, plen, daemon->namebuff, &rrtype);
oph = find_pseudoheader(header, plen, NULL, NULL, NULL, NULL);
if (header->hb4 & HB4_CD) if (header->hb4 & HB4_CD)
fwd_flags |= FREC_CHECKING_DISABLED; fwd_flags |= FREC_CHECKING_DISABLED;
if (ad_reqd) if (ad_reqd)
@@ -211,7 +211,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
old_src = 1; old_src = 1;
old_reply = 1; old_reply = 1;
} }
else if ((forward = lookup_frec_by_query(hash, fwd_flags, else if (gotname && (forward = lookup_frec(daemon->namebuff, C_IN, (int)rrtype, fwd_flags, -1,
FREC_CHECKING_DISABLED | FREC_AD_QUESTION | FREC_DO_QUESTION | FREC_CHECKING_DISABLED | FREC_AD_QUESTION | FREC_DO_QUESTION |
FREC_HAS_PHEADER | FREC_DNSKEY_QUERY | FREC_DS_QUERY | FREC_NO_CACHE))) FREC_HAS_PHEADER | FREC_DNSKEY_QUERY | FREC_DS_QUERY | FREC_NO_CACHE)))
{ {
@@ -272,16 +272,19 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
it's safe to wait for the reply from the first without it's safe to wait for the reply from the first without
forwarding the second. */ forwarding the second. */
if (difftime(now, forward->time) < 2) if (difftime(now, forward->time) < 2)
{
blockdata_free(saved_question);
return 0; return 0;
} }
} }
}
/* new query */ /* new query */
if (!forward) if (!forward)
{ {
/* If the query is malformed, we can't forward it because /* If the query is malformed, we can't forward it because
we can't get a reliable hash to recognise the answer. */ we can't recognise the answer. */
if (!hash) if (!gotname)
{ {
flags = 0; flags = 0;
ede = EDE_INVALID_DATA; ede = EDE_INVALID_DATA;
@@ -351,8 +354,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
} }
else else
forward->stash = blockdata_alloc((char *)header, plen); forward->stash = blockdata_alloc((char *)header, plen);
forward->stash_len = plen;
forward->stash_len = plen;
forward->frec_src.log_id = daemon->log_id; forward->frec_src.log_id = daemon->log_id;
forward->frec_src.source = *udpaddr; forward->frec_src.source = *udpaddr;
forward->frec_src.orig_id = ntohs(header->id); forward->frec_src.orig_id = ntohs(header->id);
@@ -362,7 +365,6 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
forward->frec_src.fd = udpfd; forward->frec_src.fd = udpfd;
forward->new_id = get_id(); forward->new_id = get_id();
header->id = htons(forward->new_id); header->id = htons(forward->new_id);
memcpy(forward->hash, hash, HASH_SIZE);
forward->forwardall = 0; forward->forwardall = 0;
forward->flags = fwd_flags; forward->flags = fwd_flags;
if (domain_no_rebind(daemon->namebuff)) if (domain_no_rebind(daemon->namebuff))
@@ -623,7 +625,7 @@ int fast_retry(time_t now)
u32 millis = dnsmasq_milliseconds(); u32 millis = dnsmasq_milliseconds();
for (f = daemon->frec_list; f; f = f->next) for (f = daemon->frec_list; f; f = f->next)
if (f->sentto && f->stash && difftime(now, f->time) < daemon->fast_retry_timeout) if (f->sentto && difftime(now, f->time) < daemon->fast_retry_timeout)
{ {
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
if (f->blocking_query || (f->flags & FREC_GONE_TO_TCP)) if (f->blocking_query || (f->flags & FREC_GONE_TO_TCP))
@@ -1002,7 +1004,7 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
/* validate routines leave name of required record in daemon->keyname */ /* validate routines leave name of required record in daemon->keyname */
unsigned int flags = STAT_ISEQUAL(status, STAT_NEED_KEY) ? FREC_DNSKEY_QUERY : FREC_DS_QUERY; unsigned int flags = STAT_ISEQUAL(status, STAT_NEED_KEY) ? FREC_DNSKEY_QUERY : FREC_DS_QUERY;
if ((new = lookup_frec_dnssec(daemon->keyname, forward->class, flags, header))) if ((new = lookup_frec(daemon->keyname, forward->class, -1, -1, flags, flags)))
{ {
/* This is tricky; it detects loops in the dependency /* This is tricky; it detects loops in the dependency
graph for DNSSEC validation, say validating A requires DS B graph for DNSSEC validation, say validating A requires DS B
@@ -1039,7 +1041,6 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
else else
{ {
struct server *server; struct server *server;
void *hash;
size_t nn; size_t nn;
int serverind, fd; int serverind, fd;
struct randfd_list *rfds = NULL; struct randfd_list *rfds = NULL;
@@ -1051,7 +1052,6 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
(nn = dnssec_generate_query(header, ((unsigned char *) header) + server->edns_pktsz, (nn = dnssec_generate_query(header, ((unsigned char *) header) + server->edns_pktsz,
daemon->keyname, forward->class, daemon->keyname, forward->class,
STAT_ISEQUAL(status, STAT_NEED_KEY) ? T_DNSKEY : T_DS, server->edns_pktsz)) && STAT_ISEQUAL(status, STAT_NEED_KEY) ? T_DNSKEY : T_DS, server->edns_pktsz)) &&
(hash = hash_questions(header, nn, daemon->namebuff)) &&
(fd = allocate_rfd(&rfds, server)) != -1 && (fd = allocate_rfd(&rfds, server)) != -1 &&
(new = get_new_frec(now, server, 1))) (new = get_new_frec(now, server, 1)))
{ {
@@ -1065,7 +1065,7 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
new->sentto = server; new->sentto = server;
new->rfds = rfds; new->rfds = rfds;
new->frec_src.next = NULL; new->frec_src.next = NULL;
new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY | FREC_HAS_EXTRADATA); new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY);
new->flags |= flags; new->flags |= flags;
new->forwardall = 0; new->forwardall = 0;
@@ -1080,7 +1080,6 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
forward->stash_len = plen; forward->stash_len = plen;
forward->stash = stash; forward->stash = stash;
memcpy(new->hash, hash, HASH_SIZE);
new->new_id = get_id(); new->new_id = get_id();
header->id = htons(new->new_id); header->id = htons(new->new_id);
/* Save query for retransmission and de-dup */ /* Save query for retransmission and de-dup */
@@ -1169,8 +1168,9 @@ void reply_query(int fd, time_t now)
socklen_t addrlen = sizeof(serveraddr); socklen_t addrlen = sizeof(serveraddr);
ssize_t n = recvfrom(fd, daemon->packet, daemon->packet_buff_sz, 0, &serveraddr.sa, &addrlen); ssize_t n = recvfrom(fd, daemon->packet, daemon->packet_buff_sz, 0, &serveraddr.sa, &addrlen);
struct server *server; struct server *server;
void *hash; int first, last, serv, c, class, rrtype;
int first, last, c; unsigned char *p;
struct randfd_list *fdl;
/* packet buffer overwritten */ /* packet buffer overwritten */
daemon->srv_save = NULL; daemon->srv_save = NULL;
@@ -1181,14 +1181,43 @@ void reply_query(int fd, time_t now)
header = (struct dns_header *)daemon->packet; header = (struct dns_header *)daemon->packet;
if (n < (int)sizeof(struct dns_header) || !(header->hb3 & HB3_QR)) if (n < (int)sizeof(struct dns_header) || !(header->hb3 & HB3_QR) || ntohs(header->qdcount) != 1)
return; return;
hash = hash_questions(header, n, daemon->namebuff); p = (unsigned char *)(header+1);
if (!extract_name(header, n, &p, daemon->namebuff, 1, 4))
return; /* bad packet */
GETSHORT(rrtype, p);
GETSHORT(class, p);
if (!(forward = lookup_frec(ntohs(header->id), fd, hash, &first, &last))) if (!(forward = lookup_frec(daemon->namebuff, class, rrtype, ntohs(header->id), 0, 0)))
return; return;
filter_servers(forward->sentto->arrayposn, F_SERVER, &first, &last);
/* Check that this arrived on the file descriptor we expected. */
/* sent from random port */
for (fdl = forward->rfds; fdl; fdl = fdl->next)
if (fdl->rfd->fd == fd)
break;
if (!fdl)
{
/* Sent to upstream from socket associated with a server.
Note we have to iterate over all the possible servers, since they may
have different bound sockets. */
for (serv = first; serv != last; serv++)
{
server = daemon->serverarray[serv];
if (server->sfd && server->sfd->fd == fd)
break;
if (serv == last)
return;
}
}
/* spoof check: answer must come from known server, also /* spoof check: answer must come from known server, also
we may have sent the same query to multiple servers from we may have sent the same query to multiple servers from
the same local socket, and would like to know which one has answered. */ the same local socket, and would like to know which one has answered. */
@@ -1224,16 +1253,11 @@ void reply_query(int fd, time_t now)
check_for_ignored_address(header, n)) check_for_ignored_address(header, n))
return; return;
/* Note: if we send extra options in the EDNS0 header, we can't recreate if ((RCODE(header) == REFUSED || RCODE(header) == SERVFAIL) && forward->forwardall == 0)
the query from the reply. */
if ((RCODE(header) == REFUSED || RCODE(header) == SERVFAIL) &&
forward->forwardall == 0 &&
!(forward->flags & FREC_HAS_EXTRADATA))
/* for broken servers, attempt to send to another one. */ /* for broken servers, attempt to send to another one. */
{ {
unsigned char *udpsz; unsigned char *udpsz;
unsigned short udp_size = PACKETSZ; /* default if no EDNS0 */ unsigned short udp_size = PACKETSZ; /* default if no EDNS0 */
size_t nn = 0;
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
/* The query MAY have got a good answer, and be awaiting /* The query MAY have got a good answer, and be awaiting
@@ -1245,19 +1269,15 @@ void reply_query(int fd, time_t now)
/* Get the saved query back. */ /* Get the saved query back. */
blockdata_retrieve(forward->stash, forward->stash_len, (void *)header); blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
nn = forward->stash_len;
/* UDP size already set in saved query. */ /* UDP size already set in saved query. */
if (find_pseudoheader(header, (size_t)n, NULL, &udpsz, NULL, NULL)) if (find_pseudoheader(header, (size_t)forward->stash_len, NULL, &udpsz, NULL, NULL))
GETSHORT(udp_size, udpsz); GETSHORT(udp_size, udpsz);
if (nn) forward_query(-1, NULL, NULL, 0, header, forward->stash_len, ((char *) header) + udp_size, now, forward,
{
forward_query(-1, NULL, NULL, 0, header, nn, ((char *) header) + udp_size, now, forward,
forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION, 0, NULL); forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION, 0, NULL);
return; return;
} }
}
/* If the answer is an error, keep the forward record in place in case /* If the answer is an error, keep the forward record in place in case
we get a good reply from another server. Kill it when we've we get a good reply from another server. Kill it when we've
@@ -3059,75 +3079,30 @@ static void query_full(time_t now, char *domain)
} }
} }
static struct frec *lookup_frec(char *target, int class, int rrtype, int id, int flags, int flagmask)
static struct frec *lookup_frec(unsigned short id, int fd, void *hash, int *firstp, int *lastp)
{ {
struct frec *f; struct frec *f;
struct server *s; struct dns_header *header;
int first, last;
struct randfd_list *fdl;
if (hash)
for (f = daemon->frec_list; f; f = f->next)
if (f->sentto && f->new_id == id &&
(memcmp(hash, f->hash, HASH_SIZE) == 0))
{
filter_servers(f->sentto->arrayposn, F_SERVER, firstp, lastp);
/* sent from random port */
for (fdl = f->rfds; fdl; fdl = fdl->next)
if (fdl->rfd->fd == fd)
return f;
/* Sent to upstream from socket associated with a server.
Note we have to iterate over all the possible servers, since they may
have different bound sockets. */
for (first = *firstp, last = *lastp; first != last; first++)
{
s = daemon->serverarray[first];
if (s->sfd && s->sfd->fd == fd)
return f;
}
}
return NULL;
}
static struct frec *lookup_frec_by_query(void *hash, unsigned int flags, unsigned int flagmask)
{
struct frec *f;
if (hash)
for (f = daemon->frec_list; f; f = f->next) for (f = daemon->frec_list; f; f = f->next)
if (f->sentto && if (f->sentto &&
(f->flags & flagmask) == flags && (f->flags & flagmask) == flags &&
memcmp(hash, f->hash, HASH_SIZE) == 0) (f->new_id == id || id == -1) &&
return f; (header = blockdata_retrieve(f->stash, f->stash_len, NULL)))
return NULL;
}
#ifdef HAVE_DNSSEC
/* DNSSEC frecs have the complete query in the block stash.
Search for an existing query using that. */
static struct frec *lookup_frec_dnssec(char *target, int class, int flags, struct dns_header *header)
{
struct frec *f;
for (f = daemon->frec_list; f; f = f->next)
if (f->sentto &&
(f->flags & flags) &&
blockdata_retrieve(f->stash, f->stash_len, (void *)header))
{ {
unsigned char *p = (unsigned char *)(header+1); unsigned char *p = (unsigned char *)(header+1);
int hclass; int hclass, hrrtype;
if (extract_name(header, f->stash_len, &p, target, 0, 4) != 1) if (extract_name(header, f->stash_len, &p, target, 0, 4) != 1)
continue; continue;
p += 2; /* type, known from flags */ GETSHORT(hrrtype, p);
GETSHORT(hclass, p); GETSHORT(hclass, p);
/* type checked by flags for DNSSEC queries. */
if (rrtype != -1 && rrtype != hrrtype)
continue;
if (class != hclass) if (class != hclass)
continue; continue;
@@ -3136,7 +3111,6 @@ static struct frec *lookup_frec_dnssec(char *target, int class, int flags, struc
return NULL; return NULL;
} }
#endif
/* Send query packet again, if we can. */ /* Send query packet again, if we can. */
void resend_query() void resend_query()