Refactor the accumulated crud of years in process_reply().

This commit is contained in:
Simon Kelley
2024-02-03 22:44:54 +00:00
parent a827127c77
commit 6d35601da4
3 changed files with 82 additions and 90 deletions

View File

@@ -1373,7 +1373,7 @@ int is_name_synthetic(int flags, char *name, union all_addr *addr);
int is_rev_synth(int flag, union all_addr *addr, char *name);
/* rfc1035.c */
int do_doctor(struct dns_header *header, size_t qlen);
int do_doctor(struct dns_header *header, size_t qlen, char *namebuff);
int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
char *name, int isExtract, int extrabytes);
unsigned char *skip_name(unsigned char *ansp, struct dns_header *header, size_t plen, int extrabytes);

View File

@@ -687,14 +687,13 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
{
unsigned char *pheader, *sizep;
struct ipsets *ipsets = NULL, *nftsets = NULL;
int munged = 0, is_sign;
int is_sign;
unsigned int rcode = RCODE(header);
size_t plen;
(void)ad_reqd;
(void)do_bit;
(void)bogusanswer;
#ifdef HAVE_IPSET
if (daemon->ipsets && extract_request(header, n, daemon->namebuff, NULL))
ipsets = domain_find_sets(daemon->ipsets, daemon->namebuff);
@@ -785,64 +784,64 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
if (header->hb3 & HB3_TC)
{
log_query(F_UPSTREAM, NULL, NULL, "truncated", 0);
munged = 1;
header->ancount = htons(0);
header->nscount = htons(0);
header->arcount = htons(0);
}
else if (daemon->bogus_addr && rcode != NXDOMAIN &&
check_for_bogus_wildcard(header, n, daemon->namebuff, now))
if (!(header->hb3 & HB3_TC) && (!bogusanswer || (header->hb4 & HB4_CD)))
{
munged = 1;
SET_RCODE(header, NXDOMAIN);
header->hb3 &= ~HB3_AA;
cache_secure = 0;
ede = EDE_BLOCKED;
}
else
{
if (rcode == NXDOMAIN &&
extract_request(header, n, daemon->namebuff, NULL))
if (rcode == NXDOMAIN && extract_request(header, n, daemon->namebuff, NULL) &&
(check_for_local_domain(daemon->namebuff, now) || lookup_domain(daemon->namebuff, F_CONFIG, NULL, NULL)))
{
if (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
an unknown type) and the answer is NXDOMAIN, convert that to NODATA,
since we know that the domain exists, even if upstream doesn't */
munged = 1;
header->hb3 |= HB3_AA;
SET_RCODE(header, NOERROR);
cache_secure = 0;
}
/* if we forwarded a query for a locally known name (because it was for
an unknown type) and the answer is NXDOMAIN, convert that to NODATA,
since we know that the domain exists, even if upstream doesn't */
header->hb3 |= HB3_AA;
SET_RCODE(header, NOERROR);
cache_secure = 0;
}
if (!bogusanswer)
if (daemon->doctors && do_doctor(header, n, daemon->namebuff))
cache_secure = 0;
/* check_for_bogus_wildcard() does it's 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))
{
if (daemon->doctors && !do_doctor(header, n))
header->ancount = htons(0);
header->nscount = htons(0);
header->arcount = htons(0);
SET_RCODE(header, NXDOMAIN);
header->hb3 &= ~HB3_AA;
cache_secure = 0;
ede = EDE_BLOCKED;
}
else
{
int rc = extract_addresses(header, n, daemon->namebuff, now, ipsets, nftsets, is_sign, check_rebind, no_cache, cache_secure);
if (rc != 0)
{
/* do_doctors found malformed answer. */
munged = 1;
SET_RCODE(header, SERVFAIL);
header->ancount = htons(0);
header->nscount = htons(0);
header->arcount = htons(0);
cache_secure = 0;
ede = EDE_OTHER;
}
if (RCODE(header) != SERVFAIL)
switch (extract_addresses(header, n, daemon->namebuff, now, ipsets, nftsets, is_sign, check_rebind, no_cache, cache_secure))
{
case 1:
my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff);
munged = 1;
cache_secure = 0;
ede = EDE_BLOCKED;
break;
/* extract_addresses() found a malformed answer. */
case 2:
munged = 1;
SET_RCODE(header, SERVFAIL);
cache_secure = 0;
ede = EDE_OTHER;
break;
}
if (rc == 1)
{
my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff);
ede = EDE_BLOCKED;
}
if (rc == 2)
{
/* extract_addresses() found a malformed answer. */
SET_RCODE(header, SERVFAIL);
ede = EDE_OTHER;
}
}
if (RCODE(header) == NOERROR && rrfilter(header, &n, RRFILTER_CONF) > 0)
@@ -850,18 +849,21 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
}
#ifdef HAVE_DNSSEC
if (bogusanswer && !(header->hb4 & HB4_CD) && !option_bool(OPT_DNSSEC_DEBUG))
{
/* Bogus reply, turn into SERVFAIL */
SET_RCODE(header, SERVFAIL);
munged = 1;
}
if (option_bool(OPT_DNSSEC_VALID))
{
header->hb4 &= ~HB4_AD;
if (!(header->hb4 & HB4_CD) && ad_reqd && cache_secure)
if (bogusanswer)
{
if (!(header->hb4 & HB4_CD) && !option_bool(OPT_DNSSEC_DEBUG))
{
/* Bogus reply, turn into SERVFAIL */
SET_RCODE(header, SERVFAIL);
header->ancount = htons(0);
header->nscount = htons(0);
header->arcount = htons(0);
ede = EDE_DNSSEC_BOGUS;
}
}
else if (!(header->hb4 & HB4_CD) && ad_reqd && cache_secure)
header->hb4 |= HB4_AD;
/* If the requestor didn't set the DO bit, don't return DNSSEC info. */
@@ -869,19 +871,9 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
rrfilter(header, &n, RRFILTER_DNSSEC);
}
#endif
/* do this after extract_addresses. Ensure NODATA reply and remove
nameserver info. */
if (munged)
{
header->ancount = htons(0);
header->nscount = htons(0);
header->arcount = htons(0);
}
/* the bogus-nxdomain stuff, doctor and NXDOMAIN->NODATA munging can all elide
sections of the packet. Find the new length here and put back pseudoheader
if it was removed. */
/* the code above can elide sections of the packet. Find the new length here
and put back pseudoheader if it was removed. */
n = resize_packet(header, n, pheader, plen);
if (pheader && ede != EDE_UNSET)

View File

@@ -384,22 +384,23 @@ static int private_net6(struct in6_addr *a, int ban_localhost)
((u32 *)a)[0] == htonl(0x20010db8); /* RFC 6303 4.6 */
}
int do_doctor(struct dns_header *header, size_t qlen)
int do_doctor(struct dns_header *header, size_t qlen, char *namebuff)
{
unsigned char *p;
int i, qtype, qclass, rdlen;
int done = 0;
if (!(p = skip_questions(header, qlen)))
return 0;
return done;
for (i = 0; i < ntohs(header->ancount) + ntohs(header->arcount); i++)
{
/* Skip over auth section */
if (i == ntohs(header->ancount) && !(p = skip_section(p, ntohs(header->nscount), header, qlen)))
return 0;
return done;
if (!extract_name(header, qlen, &p, daemon->workspacename, 1, 10))
return 0; /* bad packet */
if (!extract_name(header, qlen, &p, namebuff, 1, 10))
return done; /* bad packet */
GETSHORT(qtype, p);
GETSHORT(qclass, p);
@@ -412,7 +413,7 @@ int do_doctor(struct dns_header *header, size_t qlen)
union all_addr addr;
if (!CHECK_LEN(header, p, qlen, INADDRSZ))
return 0;
return done;
/* alignment */
memcpy(&addr.addr4, p, INADDRSZ);
@@ -437,17 +438,18 @@ int do_doctor(struct dns_header *header, size_t qlen)
if (option_bool(OPT_DNSSEC_VALID) && i < ntohs(header->ancount))
daemon->rr_status[i] = 0;
#endif
done = 1;
memcpy(p, &addr.addr4, INADDRSZ);
log_query(F_FORWARD | F_CONFIG | F_IPV4, daemon->workspacename, &addr, NULL, 0);
log_query(F_FORWARD | F_CONFIG | F_IPV4, namebuff, &addr, NULL, 0);
break;
}
}
if (!ADD_RDLEN(header, p, qlen, rdlen))
return 0; /* bad packet */
return done; /* bad packet */
}
return 1;
return done;
}
/* Find SOA RR in auth section to get TTL for negative caching of name.
@@ -1046,8 +1048,6 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
if (!found && (qtype != T_ANY || (flags & F_NXDOMAIN)))
{
int substring, have_soa;
if (flags & F_NXDOMAIN)
{
flags &= ~(F_IPV4 | F_IPV6 | F_RR);
@@ -1060,7 +1060,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
if (insert && !option_bool(OPT_NO_NEG))
{
int have_soa = find_soa(header, qlen, name, &substring, &ttl, no_cache_dnssec, now);
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 */
@@ -1323,8 +1323,7 @@ static int check_bad_address(struct dns_header *header, size_t qlen, struct bogu
GETSHORT(qtype, p);
GETSHORT(qclass, p);
GETLONG(ttl, p);
GETSHORT(rdlen, p);
GETSHORT(rdlen, p)
if (ttlp)
*ttlp = ttl;
@@ -1377,8 +1376,9 @@ int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
/* Found a bogus address. Insert that info here, since there no SOA record
to get the ttl from in the normal processing */
cache_start_insert();
cache_insert(name, NULL, C_IN, now, ttl, F_IPV4 | F_FORWARD | F_NEG | F_NXDOMAIN);
cache_insert(name, NULL, C_IN, now, ttl, F_FORWARD | F_NEG | F_NXDOMAIN);
cache_end_insert();
log_query(F_CONFIG | F_FORWARD | F_NEG | F_NXDOMAIN, name, NULL, NULL, 0);
return 1;
}