Always save forwarded query locally.

We do this in many cases anyway (DNSSEC, fast-retry) and doing
it unconditionally allows much simplification of knarly code, to follow.
This commit is contained in:
Simon Kelley
2024-11-01 16:06:58 +00:00
parent a9d46d42cb
commit 3b6df06fb8

View File

@@ -21,14 +21,13 @@ static struct frec *lookup_frec(unsigned short id, int fd, void *hash, int *firs
static struct frec *lookup_frec_by_query(void *hash, unsigned int flags, unsigned int flagmask);
#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,
int class, char *name, char *keyname, struct server *server,
int have_mark, unsigned int mark, int *keycount, int *validatecount);
#endif
static unsigned short get_id(void);
static void free_frec(struct frec *f);
static void query_full(time_t now, char *domain);
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 have_mark, unsigned int mark, int *keycount, int *validatecount);
/* Send a UDP packet with its source address set as "source"
unless nowild is true, when we just send it with the kernel default */
@@ -272,7 +271,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
return 0;
}
}
/* new query */
if (!forward)
{
@@ -293,7 +292,9 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
ede = EDE_NOT_READY;
flags = 0;
}
master = daemon->serverarray[first];
/* don't forward A or AAAA queries for simple names, except the empty name */
if (!flags &&
option_bool(OPT_NODOTS_LOCAL) &&
@@ -306,18 +307,41 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
if (flags || ede == EDE_NOT_READY)
goto reply;
master = daemon->serverarray[first];
if (!(forward = get_new_frec(now, master, 0)))
goto reply;
/* table full - flags == 0, return REFUSED */
/* Keep copy of query if we're doing fast retry or DNSSEC. */
if (daemon->fast_retry_time != 0 || option_bool(OPT_DNSSEC_VALID))
plen = add_edns0_config(header, plen, ((unsigned char *)header) + PACKETSZ, &forward->frec_src.source, now, &cacheable);
if (!cacheable)
forward->flags |= FREC_NO_CACHE;
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && (master->flags & SERV_DO_DNSSEC))
{
forward->stash = blockdata_alloc((char *)header, plen);
forward->stash_len = plen;
plen = add_do_bit(header, plen, ((unsigned char *) header) + PACKETSZ);
/* For debugging, set Checking Disabled, otherwise, have the upstream check too,
this allows it to select auth servers when one is returning bad data. */
if (option_bool(OPT_DNSSEC_DEBUG))
header->hb4 |= HB4_CD;
}
#endif
if (find_pseudoheader(header, plen, &edns0_len, &pheader, NULL, NULL))
{
/* If there wasn't a PH before, and there is now, we added it. */
if (!oph)
forward->flags |= FREC_ADDED_PHEADER;
/* Reduce udp size on retransmits. */
if (forward->flags & FREC_TEST_PKTSZ)
PUTSHORT(SAFE_PKTSZ, pheader);
}
/* Keep copy of query. */
forward->stash = blockdata_alloc((char *)header, plen);
forward->stash_len = plen;
forward->frec_src.log_id = daemon->log_id;
forward->frec_src.source = *udpaddr;
@@ -327,6 +351,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
forward->frec_src.next = NULL;
forward->frec_src.fd = udpfd;
forward->new_id = get_id();
header->id = htons(forward->new_id);
memcpy(forward->hash, hash, HASH_SIZE);
forward->forwardall = 0;
forward->flags = fwd_flags;
@@ -383,6 +408,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
/* get query for logging. */
extract_request(header, plen, daemon->namebuff, NULL);
/* Use go-anywhere size limit, as it's a retry. */
if (find_pseudoheader(header, plen, NULL, &pheader, &is_sign, NULL) && !is_sign)
PUTSHORT(SAFE_PKTSZ, pheader);
@@ -442,45 +468,6 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
forward->flags |= FREC_TEST_PKTSZ;
}
/* We may be resending a DNSSEC query here, for which the below processing is not necessary. */
if (!is_dnssec)
{
header->id = htons(forward->new_id);
plen = add_edns0_config(header, plen, ((unsigned char *)header) + PACKETSZ, &forward->frec_src.source, now, &cacheable);
if (!cacheable)
forward->flags |= FREC_NO_CACHE;
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && (master->flags & SERV_DO_DNSSEC))
{
plen = add_do_bit(header, plen, ((unsigned char *) header) + PACKETSZ);
/* For debugging, set Checking Disabled, otherwise, have the upstream check too,
this allows it to select auth servers when one is returning bad data. */
if (option_bool(OPT_DNSSEC_DEBUG))
header->hb4 |= HB4_CD;
}
#endif
if (find_pseudoheader(header, plen, &edns0_len, &pheader, NULL, NULL))
{
/* If there wasn't a PH before, and there is now, we added it. */
if (!oph)
forward->flags |= FREC_ADDED_PHEADER;
/* If we're sending an EDNS0 with any options, we can't recreate the query from a reply. */
if (edns0_len > 11)
forward->flags |= FREC_HAS_EXTRADATA;
/* Reduce udp size on retransmits. */
if (forward->flags & FREC_TEST_PKTSZ)
PUTSHORT(SAFE_PKTSZ, pheader);
}
}
if (forward->forwardall)
start = first;
@@ -601,7 +588,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
send_from(udpfd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND), (char *)header, plen, udpaddr, dst_addr, dst_iface);
}
return 0;
}
@@ -656,7 +643,6 @@ int fast_retry(time_t now)
if (ret == -1 || ret > to_run)
ret = to_run;
}
}
return ret;
}
@@ -1227,10 +1213,8 @@ void reply_query(int fd, time_t now)
!(forward->flags & FREC_HAS_EXTRADATA))
/* for broken servers, attempt to send to another one. */
{
unsigned char *pheader, *udpsz;
unsigned char *udpsz;
unsigned short udp_size = PACKETSZ; /* default if no EDNS0 */
size_t plen;
int is_sign;
size_t nn = 0;
#ifdef HAVE_DNSSEC
@@ -1239,57 +1223,16 @@ void reply_query(int fd, time_t now)
The Stash contains something else and we don't need to retry anyway. */
if (forward->blocking_query || (forward->flags & FREC_GONE_TO_TCP))
return;
if (forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY))
{
/* DNSSEC queries have a copy of the original query stashed. */
blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
nn = forward->stash_len;
udp_size = daemon->edns_pktsz;
}
else
#endif
{
/* in fast retry mode, we have a copy of the query. */
if ((daemon->fast_retry_time != 0 || option_bool(OPT_DNSSEC_VALID)) && forward->stash)
{
blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
nn = forward->stash_len;
/* UDP size already set in saved query. */
if (find_pseudoheader(header, (size_t)n, NULL, &udpsz, NULL, NULL))
GETSHORT(udp_size, udpsz);
}
else
{
/* recreate query from reply */
if ((pheader = find_pseudoheader(header, (size_t)n, &plen, &udpsz, &is_sign, NULL)))
GETSHORT(udp_size, udpsz);
/* If the client provides an EDNS0 UDP size, use that to limit our reply.
(bounded by the maximum configured). If no EDNS0, then it
defaults to 512 */
if (udp_size > daemon->edns_pktsz)
udp_size = daemon->edns_pktsz;
else if (udp_size < PACKETSZ)
udp_size = PACKETSZ; /* Sanity check - can't reduce below default. RFC 6891 6.2.3 */
header->ancount = htons(0);
header->nscount = htons(0);
header->arcount = htons(0);
header->hb3 &= ~(HB3_QR | HB3_AA | HB3_TC);
header->hb4 &= ~(HB4_RA | HB4_RCODE | HB4_CD | HB4_AD);
if (forward->flags & FREC_CHECKING_DISABLED)
header->hb4 |= HB4_CD;
if (forward->flags & FREC_AD_QUESTION)
header->hb4 |= HB4_AD;
if (!is_sign &&
(nn = resize_packet(header, (size_t)n, pheader, plen)) &&
(forward->flags & FREC_DO_QUESTION))
add_do_bit(header, nn, (unsigned char *)pheader + plen);
}
}
/* Get the saved query back. */
blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
nn = forward->stash_len;
/* UDP size already set in saved query. */
if (find_pseudoheader(header, (size_t)n, NULL, &udpsz, NULL, NULL))
GETSHORT(udp_size, udpsz);
if (nn)
{
forward_query(-1, NULL, NULL, 0, header, nn, ((char *) header) + udp_size, now, forward,
@@ -2169,9 +2112,9 @@ int tcp_from_udp(time_t now, int status, struct dns_header *header, ssize_t *ple
}
/* Recurse down the key hierarchy */
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 have_mark, unsigned int mark, int *keycount, int *validatecount)
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 have_mark, unsigned int mark, int *keycount, int *validatecount)
{
int first, last, start, new_status;
unsigned char *packet = NULL;