Rationalise query-reply logging.

Try and log exactly what was returned, rather than just what
got cached. Also give validation status of RRsets if extra logging specified.

This commit also fixes a long-standing bug in caching of CNAME chains
leading to a PTR record.

Based on and inspired by a patch from Dominik DL6ER <dl6er@dl6er.de>
This commit is contained in:
Simon Kelley
2021-08-31 18:23:03 +01:00
parent 79337f99ae
commit 7b80c75d9d
2 changed files with 331 additions and 280 deletions

View File

@@ -488,8 +488,6 @@ struct crec *cache_insert(char *name, union all_addr *addr, unsigned short class
else else
#endif #endif
{ {
/* Don't log DNSSEC records here, done elsewhere */
log_query(flags | F_UPSTREAM, name, addr, NULL);
if (daemon->max_cache_ttl != 0 && daemon->max_cache_ttl < ttl) if (daemon->max_cache_ttl != 0 && daemon->max_cache_ttl < ttl)
ttl = daemon->max_cache_ttl; ttl = daemon->max_cache_ttl;
if (daemon->min_cache_ttl != 0 && daemon->min_cache_ttl > ttl) if (daemon->min_cache_ttl != 0 && daemon->min_cache_ttl > ttl)
@@ -1897,17 +1895,24 @@ static char *edestr(int ede)
void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg) void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg)
{ {
char *source, *dest = daemon->addrbuff; char *source, *dest = arg;
char *verb = "is"; char *verb = "is";
char *extra = ""; char *extra = "";
if (!option_bool(OPT_LOG)) if (!option_bool(OPT_LOG))
return; return;
#ifdef HAVE_DNSSEC
if ((flags & F_DNSSECOK) && option_bool(OPT_EXTRALOG))
extra = " (DNSSEC signed)";
#endif
name = sanitise(name); name = sanitise(name);
if (addr) if (addr)
{ {
dest = daemon->addrbuff;
if (flags & F_KEYTAG) if (flags & F_KEYTAG)
sprintf(daemon->addrbuff, arg, addr->log.keytag, addr->log.algo, addr->log.digest); sprintf(daemon->addrbuff, arg, addr->log.keytag, addr->log.algo, addr->log.digest);
else if (flags & F_RCODE) else if (flags & F_RCODE)
@@ -1929,13 +1934,12 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg)
sprintf(extra, " (EDE: %s)", edestr(addr->log.ede)); sprintf(extra, " (EDE: %s)", edestr(addr->log.ede));
} }
} }
else else if (flags & (F_IPV4 | F_IPV6))
inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6, inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
addr, daemon->addrbuff, ADDRSTRLEN); addr, daemon->addrbuff, ADDRSTRLEN);
else
dest = arg;
} }
else
dest = arg;
if (flags & F_REVERSE) if (flags & F_REVERSE)
{ {

View File

@@ -394,18 +394,13 @@ static int private_net6(struct in6_addr *a, int ban_localhost)
((u32 *)a)[0] == htonl(0x20010db8); /* RFC 6303 4.6 */ ((u32 *)a)[0] == htonl(0x20010db8); /* RFC 6303 4.6 */
} }
static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *header, size_t qlen, char *name, int *doctored) static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *header, size_t qlen, int *doctored)
{ {
int i, qtype, qclass, rdlen; int i, qtype, qclass, rdlen;
for (i = count; i != 0; i--) for (i = count; i != 0; i--)
{ {
if (name && option_bool(OPT_LOG)) if (!(p = skip_name(p, header, qlen, 10)))
{
if (!extract_name(header, qlen, &p, name, 1, 10))
return 0;
}
else if (!(p = skip_name(p, header, qlen, 10)))
return 0; /* bad packet */ return 0; /* bad packet */
GETSHORT(qtype, p); GETSHORT(qtype, p);
@@ -444,34 +439,6 @@ static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *
break; break;
} }
} }
else if (qtype == T_TXT && name && option_bool(OPT_LOG))
{
unsigned char *p1 = p;
if (!CHECK_LEN(header, p1, qlen, rdlen))
return 0;
while ((p1 - p) < rdlen)
{
unsigned int i, len = *p1;
unsigned char *p2 = p1;
if ((p1 + len - p) >= rdlen)
return 0; /* bad packet */
/* make counted string zero-term and sanitise */
for (i = 0; i < len; i++)
{
if (!isprint((int)*(p2+1)))
break;
*p2 = *(p2+1);
p2++;
}
*p2 = 0;
my_syslog(LOG_INFO, "reply %s is %s", name, p1);
/* restore */
memmove(p1 + 1, p1, i);
*p1 = len;
p1 += len+1;
}
}
if (!ADD_RDLEN(header, p, qlen, rdlen)) if (!ADD_RDLEN(header, p, qlen, rdlen))
return 0; /* bad packet */ return 0; /* bad packet */
@@ -480,7 +447,7 @@ static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *
return p; return p;
} }
static int find_soa(struct dns_header *header, size_t qlen, char *name, int *doctored) static int find_soa(struct dns_header *header, size_t qlen, int *doctored)
{ {
unsigned char *p; unsigned char *p;
int qtype, qclass, rdlen; int qtype, qclass, rdlen;
@@ -489,7 +456,7 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *doc
/* first move to NS section and find TTL from any SOA section */ /* first move to NS section and find TTL from any SOA section */
if (!(p = skip_questions(header, qlen)) || if (!(p = skip_questions(header, qlen)) ||
!(p = do_doctor(p, ntohs(header->ancount), header, qlen, name, doctored))) !(p = do_doctor(p, ntohs(header->ancount), header, qlen, doctored)))
return 0; /* bad packet */ return 0; /* bad packet */
for (i = ntohs(header->nscount); i != 0; i--) for (i = ntohs(header->nscount); i != 0; i--)
@@ -525,7 +492,7 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *doc
} }
/* rewrite addresses in additional section too */ /* rewrite addresses in additional section too */
if (!do_doctor(p, ntohs(header->arcount), header, qlen, NULL, doctored)) if (!do_doctor(p, ntohs(header->arcount), header, qlen, doctored))
return 0; return 0;
if (!found_soa) if (!found_soa)
@@ -534,6 +501,40 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *doc
return minttl; return minttl;
} }
/* Print TXT reply to log */
static int print_txt(struct dns_header *header, const size_t qlen, char *name,
unsigned char *p, const int ardlen)
{
unsigned char *p1 = p;
if (!CHECK_LEN(header, p1, qlen, ardlen))
return 0;
/* Loop over TXT payload */
while ((p1 - p) < ardlen)
{
unsigned int i, len = *p1;
unsigned char *p3 = p1;
if ((p1 + len - p) >= ardlen)
return 0; /* bad packet */
/* make counted string zero-term and sanitise */
for (i = 0; i < len; i++)
{
if (!isprint((int)*(p3+1)))
break;
*p3 = *(p3+1);
p3++;
}
*p3 = 0;
log_query(F_FORWARD | F_UPSTREAM, name, NULL, (char*)p1);
/* restore */
memmove(p1 + 1, p1, i);
*p1 = len;
p1 += len+1;
}
return 1;
}
/* Note that the following code can create CNAME chains that don't point to a real record, /* Note that the following code can create CNAME chains that don't point to a real record,
either because of lack of memory, or lack of SOA records. These are treated by the cache code as either because of lack of memory, or lack of SOA records. These are treated by the cache code as
expired and cleaned out that way. expired and cleaned out that way.
@@ -551,15 +552,21 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
#else #else
(void)ipsets; /* unused */ (void)ipsets; /* unused */
#endif #endif
int found = 0, cname_count = CNAME_CHAIN;
struct crec *cpp = NULL;
int flags = RCODE(header) == NXDOMAIN ? F_NXDOMAIN : 0;
#ifdef HAVE_DNSSEC
int cname_short = 0;
#endif
unsigned long cttl = ULONG_MAX, attl;
cache_start_insert(); cache_start_insert();
/* find_soa is needed for dns_doctor and logging side-effects, so don't call it lazily if there are any. */ /* find_soa is needed for dns_doctor side effects, so don't call it lazily if there are any. */
if (daemon->doctors || option_bool(OPT_LOG) || option_bool(OPT_DNSSEC_VALID)) if (daemon->doctors || option_bool(OPT_DNSSEC_VALID))
{ {
searched_soa = 1; searched_soa = 1;
ttl = find_soa(header, qlen, name, doctored); ttl = find_soa(header, qlen, doctored);
if (*doctored) if (*doctored)
{ {
@@ -574,152 +581,39 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
} }
} }
/* go through the questions. */ namep = p = (unsigned char *)(header+1);
p = (unsigned char *)(header+1);
for (i = ntohs(header->qdcount); i != 0; i--) if (ntohs(header->qdcount) != 1 || !extract_name(header, qlen, &p, name, 1, 4))
{ return 0; /* bad packet */
int found = 0, cname_count = CNAME_CHAIN;
struct crec *cpp = NULL; GETSHORT(qtype, p);
int flags = RCODE(header) == NXDOMAIN ? F_NXDOMAIN : 0; GETSHORT(qclass, p);
#ifdef HAVE_DNSSEC
int cname_short = 0; if (qclass != C_IN)
#endif return 0;
unsigned long cttl = ULONG_MAX, attl;
/* PTRs: we chase CNAMEs here, since we have no way to
namep = p; represent them in the cache. */
if (!extract_name(header, qlen, &p, name, 1, 4)) if (qtype == T_PTR)
return 0; /* bad packet */ {
int insert = 1, name_encoding = in_arpa_name_2_addr(name, &addr);
GETSHORT(qtype, p);
GETSHORT(qclass, p);
if (qclass != C_IN) if (!(flags & F_NXDOMAIN))
continue;
/* PTRs: we chase CNAMEs here, since we have no way to
represent them in the cache. */
if (qtype == T_PTR)
{
int name_encoding = in_arpa_name_2_addr(name, &addr);
if (!name_encoding)
continue;
if (!(flags & F_NXDOMAIN))
{
cname_loop:
if (!(p1 = skip_questions(header, qlen)))
return 0;
for (j = 0; j < ntohs(header->ancount); j++)
{
int secflag = 0;
unsigned char *tmp = namep;
/* the loop body overwrites the original name, so get it back here. */
if (!extract_name(header, qlen, &tmp, name, 1, 0) ||
!(res = extract_name(header, qlen, &p1, name, 0, 10)))
return 0; /* bad packet */
GETSHORT(aqtype, p1);
GETSHORT(aqclass, p1);
GETLONG(attl, p1);
if ((daemon->max_ttl != 0) && (attl > daemon->max_ttl) && !is_sign)
{
(p1) -= 4;
PUTLONG(daemon->max_ttl, p1);
}
GETSHORT(ardlen, p1);
endrr = p1+ardlen;
/* TTL of record is minimum of CNAMES and PTR */
if (attl < cttl)
cttl = attl;
if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == T_PTR))
{
if (!extract_name(header, qlen, &p1, name, 1, 0))
return 0;
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && daemon->rr_status[j] != 0)
{
/* validated RR anywhere in CNAME chain, don't cache. */
if (cname_short || aqtype == T_CNAME)
return 0;
secflag = F_DNSSECOK;
/* limit TTL based on signature. */
if (daemon->rr_status[j] < cttl)
cttl = daemon->rr_status[j];
}
#endif
if (aqtype == T_CNAME)
{
if (!cname_count--)
return 0; /* looped CNAMES, we can't cache. */
#ifdef HAVE_DNSSEC
cname_short = 1;
#endif
goto cname_loop;
}
cache_insert(name, &addr, C_IN, now, cttl, name_encoding | secflag | F_REVERSE);
found = 1;
}
p1 = endrr;
if (!CHECK_LEN(header, p1, qlen, 0))
return 0; /* bad packet */
}
}
if (!found && !option_bool(OPT_NO_NEG))
{
if (!searched_soa)
{
searched_soa = 1;
ttl = find_soa(header, qlen, NULL, doctored);
}
if (ttl)
cache_insert(NULL, &addr, C_IN, now, ttl, name_encoding | F_REVERSE | F_NEG | flags | (secure ? F_DNSSECOK : 0));
}
}
else
{ {
/* everything other than PTR */ cname_loop:
struct crec *newc;
int addrlen = 0;
if (qtype == T_A)
{
addrlen = INADDRSZ;
flags |= F_IPV4;
}
else if (qtype == T_AAAA)
{
addrlen = IN6ADDRSZ;
flags |= F_IPV6;
}
else if (qtype == T_SRV)
flags |= F_SRV;
else
continue;
cname_loop1:
if (!(p1 = skip_questions(header, qlen))) if (!(p1 = skip_questions(header, qlen)))
return 0; return 0;
for (j = 0; j < ntohs(header->ancount); j++) for (j = 0; j < ntohs(header->ancount); j++)
{ {
int secflag = 0; int secflag = 0;
if (!(res = extract_name(header, qlen, &p1, name, 0, 10))) if (!(res = extract_name(header, qlen, &p1, name, 0, 10)))
return 0; /* bad packet */ return 0; /* bad packet */
GETSHORT(aqtype, p1); GETSHORT(aqtype, p1);
GETSHORT(aqclass, p1); GETSHORT(aqclass, p1);
GETLONG(attl, p1); GETLONG(attl, p1);
if ((daemon->max_ttl != 0) && (attl > daemon->max_ttl) && !is_sign) if ((daemon->max_ttl != 0) && (attl > daemon->max_ttl) && !is_sign)
{ {
(p1) -= 4; (p1) -= 4;
@@ -728,128 +622,281 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
GETSHORT(ardlen, p1); GETSHORT(ardlen, p1);
endrr = p1+ardlen; endrr = p1+ardlen;
if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == qtype)) /* TTL of record is minimum of CNAMES and PTR */
if (attl < cttl)
cttl = attl;
if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == T_PTR))
{ {
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && daemon->rr_status[j] != 0) if (option_bool(OPT_DNSSEC_VALID) && daemon->rr_status[j] != 0)
{ {
/* validated RR anywhere in CNAME chain, don't cache. */
if (cname_short || aqtype == T_CNAME)
insert = 0;
secflag = F_DNSSECOK; secflag = F_DNSSECOK;
/* limit TTL based on signature. */
/* limit TTl based on sig. */ if (daemon->rr_status[j] < cttl)
if (daemon->rr_status[j] < attl) cttl = daemon->rr_status[j];
attl = daemon->rr_status[j];
} }
#endif #endif
if (aqtype == T_CNAME)
log_query(secflag | F_CNAME | F_FORWARD | F_UPSTREAM, name, NULL, NULL);
if (!extract_name(header, qlen, &p1, name, 1, 0))
return 0;
if (aqtype == T_CNAME) if (aqtype == T_CNAME)
{ {
if (!cname_count--) if (!cname_count--)
return 0; /* looped CNAMES */ return 0; /* looped CNAMES, we can't cache. */
#ifdef HAVE_DNSSEC
if ((newc = cache_insert(name, NULL, C_IN, now, attl, F_CNAME | F_FORWARD | secflag))) cname_short = 1;
{
newc->addr.cname.target.cache = NULL;
newc->addr.cname.is_name_ptr = 0;
if (cpp)
{
next_uid(newc);
cpp->addr.cname.target.cache = newc;
cpp->addr.cname.uid = newc->uid;
}
}
cpp = newc;
if (attl < cttl)
cttl = attl;
namep = p1;
if (!extract_name(header, qlen, &p1, name, 1, 0))
return 0;
goto cname_loop1;
}
else if (!(flags & F_NXDOMAIN))
{
found = 1;
if (flags & F_SRV)
{
unsigned char *tmp = namep;
if (!CHECK_LEN(header, p1, qlen, 6))
return 0; /* bad packet */
GETSHORT(addr.srv.priority, p1);
GETSHORT(addr.srv.weight, p1);
GETSHORT(addr.srv.srvport, p1);
if (!extract_name(header, qlen, &p1, name, 1, 0))
return 0;
addr.srv.targetlen = strlen(name) + 1; /* include terminating zero */
if (!(addr.srv.target = blockdata_alloc(name, addr.srv.targetlen)))
return 0;
/* we overwrote the original name, so get it back here. */
if (!extract_name(header, qlen, &tmp, name, 1, 0))
return 0;
}
else
{
/* copy address into aligned storage */
if (!CHECK_LEN(header, p1, qlen, addrlen))
return 0; /* bad packet */
memcpy(&addr, p1, addrlen);
/* check for returned address in private space */
if (check_rebind)
{
if ((flags & F_IPV4) &&
private_net(addr.addr4, !option_bool(OPT_LOCAL_REBIND)))
return 1;
if ((flags & F_IPV6) &&
private_net6(&addr.addr6, !option_bool(OPT_LOCAL_REBIND)))
return 1;
}
#ifdef HAVE_IPSET
if (ipsets && (flags & (F_IPV4 | F_IPV6)))
{
ipsets_cur = ipsets;
while (*ipsets_cur)
{
log_query((flags & (F_IPV4 | F_IPV6)) | F_IPSET, name, &addr, *ipsets_cur);
add_to_ipset(*ipsets_cur++, &addr, flags, 0);
}
}
#endif #endif
} goto cname_loop;
}
newc = cache_insert(name, &addr, C_IN, now, attl, flags | F_FORWARD | secflag);
if (newc && cpp) found = 1;
if (!name_encoding)
log_query(secflag | F_FORWARD | F_UPSTREAM, name, NULL, querystr(NULL, aqtype));
else
{
log_query(name_encoding | secflag | F_REVERSE | F_UPSTREAM, name, &addr, NULL);
if (insert)
cache_insert(name, &addr, C_IN, now, cttl, name_encoding | secflag | F_REVERSE);
}
}
p1 = endrr;
if (!CHECK_LEN(header, p1, qlen, 0))
return 0; /* bad packet */
}
}
if (!found && !option_bool(OPT_NO_NEG))
{
if (!searched_soa)
{
searched_soa = 1;
ttl = find_soa(header, qlen, doctored);
}
flags |= F_NEG | (secure ? F_DNSSECOK : 0);
if (name_encoding && ttl)
{
flags |= F_REVERSE | name_encoding;
cache_insert(NULL, &addr, C_IN, now, ttl, flags);
}
log_query(flags | F_UPSTREAM, name, &addr, NULL);
}
}
else
{
/* everything other than PTR */
struct crec *newc;
int addrlen = 0, insert = 1;
if (qtype == T_A)
{
addrlen = INADDRSZ;
flags |= F_IPV4;
}
else if (qtype == T_AAAA)
{
addrlen = IN6ADDRSZ;
flags |= F_IPV6;
}
else if (qtype == T_SRV)
flags |= F_SRV;
else
insert = 0; /* NOTE: do not cache data from CNAME queries. */
cname_loop1:
if (!(p1 = skip_questions(header, qlen)))
return 0;
for (j = 0; j < ntohs(header->ancount); j++)
{
int secflag = 0;
if (!(res = extract_name(header, qlen, &p1, name, 0, 10)))
return 0; /* bad packet */
GETSHORT(aqtype, p1);
GETSHORT(aqclass, p1);
GETLONG(attl, p1);
if ((daemon->max_ttl != 0) && (attl > daemon->max_ttl) && !is_sign)
{
(p1) -= 4;
PUTLONG(daemon->max_ttl, p1);
}
GETSHORT(ardlen, p1);
endrr = p1+ardlen;
/* Not what we're looking for? */
if (aqclass != C_IN || res == 2)
{
p1 = endrr;
if (!CHECK_LEN(header, p1, qlen, 0))
return 0; /* bad packet */
continue;
}
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && daemon->rr_status[j] != 0)
{
secflag = F_DNSSECOK;
/* limit TTl based on sig. */
if (daemon->rr_status[j] < attl)
attl = daemon->rr_status[j];
}
#endif
if (aqtype == T_CNAME)
{
if (!cname_count--)
return 0; /* looped CNAMES */
log_query(secflag | F_CNAME | F_FORWARD | F_UPSTREAM, name, NULL, NULL);
if (insert)
{
if ((newc = cache_insert(name, NULL, C_IN, now, attl, F_CNAME | F_FORWARD | secflag)))
{
newc->addr.cname.target.cache = NULL;
newc->addr.cname.is_name_ptr = 0;
if (cpp)
{ {
next_uid(newc); next_uid(newc);
cpp->addr.cname.target.cache = newc; cpp->addr.cname.target.cache = newc;
cpp->addr.cname.uid = newc->uid; cpp->addr.cname.uid = newc->uid;
} }
cpp = NULL;
} }
cpp = newc;
if (attl < cttl)
cttl = attl;
} }
p1 = endrr; namep = p1;
if (!CHECK_LEN(header, p1, qlen, 0)) if (!extract_name(header, qlen, &p1, name, 1, 0))
return 0; /* bad packet */ return 0;
goto cname_loop1;
}
else if (aqtype != qtype)
{
#ifdef HAVE_DNSSEC
if (!option_bool(OPT_DNSSEC_VALID) || aqtype != T_RRSIG)
#endif
log_query(secflag | F_FORWARD | F_UPSTREAM, name, NULL, querystr(NULL, aqtype));
}
else if (!(flags & F_NXDOMAIN))
{
found = 1;
if (flags & F_SRV)
{
unsigned char *tmp = namep;
if (!CHECK_LEN(header, p1, qlen, 6))
return 0; /* bad packet */
GETSHORT(addr.srv.priority, p1);
GETSHORT(addr.srv.weight, p1);
GETSHORT(addr.srv.srvport, p1);
if (!extract_name(header, qlen, &p1, name, 1, 0))
return 0;
addr.srv.targetlen = strlen(name) + 1; /* include terminating zero */
if (!(addr.srv.target = blockdata_alloc(name, addr.srv.targetlen)))
return 0;
/* we overwrote the original name, so get it back here. */
if (!extract_name(header, qlen, &tmp, name, 1, 0))
return 0;
}
else if (flags & (F_IPV4 | F_IPV6))
{
/* copy address into aligned storage */
if (!CHECK_LEN(header, p1, qlen, addrlen))
return 0; /* bad packet */
memcpy(&addr, p1, addrlen);
/* check for returned address in private space */
if (check_rebind)
{
if ((flags & F_IPV4) &&
private_net(addr.addr4, !option_bool(OPT_LOCAL_REBIND)))
return 1;
if ((flags & F_IPV6) &&
private_net6(&addr.addr6, !option_bool(OPT_LOCAL_REBIND)))
return 1;
}
#ifdef HAVE_IPSET
if (ipsets && (flags & (F_IPV4 | F_IPV6)))
{
ipsets_cur = ipsets;
while (*ipsets_cur)
{
log_query((flags & (F_IPV4 | F_IPV6)) | F_IPSET, name, &addr, *ipsets_cur);
add_to_ipset(*ipsets_cur++, &addr, flags, 0);
}
}
#endif
}
if (insert)
{
newc = cache_insert(name, &addr, C_IN, now, attl, flags | F_FORWARD | secflag);
if (newc && cpp)
{
next_uid(newc);
cpp->addr.cname.target.cache = newc;
cpp->addr.cname.uid = newc->uid;
}
cpp = NULL;
}
if (aqtype == T_TXT)
{
if (!print_txt(header, qlen, name, p1, ardlen))
return 0;
}
else
log_query(flags | F_FORWARD | secflag | F_UPSTREAM, name, &addr, querystr(NULL, aqtype));
} }
if (!found && !option_bool(OPT_NO_NEG)) p1 = endrr;
if (!CHECK_LEN(header, p1, qlen, 0))
return 0; /* bad packet */
}
if (!found && !option_bool(OPT_NO_NEG))
{
if (!searched_soa)
{ {
if (!searched_soa) searched_soa = 1;
ttl = find_soa(header, qlen, doctored);
}
/* 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 == 0)
ttl = cttl;
log_query(F_UPSTREAM | F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0), name, NULL, NULL);
if (insert)
{ {
searched_soa = 1; newc = cache_insert(name, NULL, C_IN, now, ttl, F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0));
ttl = find_soa(header, qlen, NULL, doctored);
}
/* If there's no SOA to get the TTL from, but there is a CNAME
pointing at this, inherit its TTL */
if (ttl || cpp)
{
newc = cache_insert(name, NULL, C_IN, now, ttl ? ttl : cttl, F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0));
if (newc && cpp) if (newc && cpp)
{ {
next_uid(newc); next_uid(newc);