if (option_bool(OPT_DNSSEC_VALID))
header->hb4 &= ~HB4_AD;
- if (cache_secure)
+ if (!(header->hb4 & HB4_CD) && cache_secure)
header->hb4 |= HB4_AD;
#endif
}
}
+#ifdef HAVE_DNSSEC
+ if (no_cache && !(header->hb4 & HB4_CD))
+ {
+ if (option_bool(OPT_DNSSEC_PERMISS))
+ {
+ unsigned short type;
+ char types[20];
+
+ if (extract_request(header, (size_t)n, daemon->namebuff, &type))
+ {
+ querystr("", types, type);
+ my_syslog(LOG_WARNING, _("DNSSEC validation failed: query %s%s"), daemon->namebuff, types);
+ }
+ else
+ my_syslog(LOG_WARNING, _("DNSSEC validation failed for unknown query"));
+ }
+ else
+ {
+ /* Bogus reply, turn into SERVFAIL */
+ SET_RCODE(header, SERVFAIL);
+ munged = 1;
+ }
+ }
+#endif
+
/* do this after extract_addresses. Ensure NODATA reply and remove
nameserver info. */
if (status == STAT_SECURE)
cache_secure = 1;
- /* TODO return SERVFAIL here */
else if (status == STAT_BOGUS)
no_cache_dnssec = 1;
#define LOPT_QUIET_RA 328
#define LOPT_SEC_VALID 329
#define LOPT_DNSKEY 330
-
+#define LOPT_DNSSEC_PERM 331
#ifdef HAVE_GETOPT_LONG
static const struct option opts[] =
{ "synth-domain", 1, 0, LOPT_SYNTH },
{ "dnssec", 0, 0, LOPT_SEC_VALID },
{ "dnskey", 1, 0, LOPT_DNSKEY },
+ { "dnssec-permissive", 0, 0, LOPT_DNSSEC_PERM },
#ifdef OPTION6_PREFIX_CLASS
{ "dhcp-prefix-class", 1, 0, LOPT_PREF_CLSS },
#endif
{ LOPT_AUTHPEER, ARG_DUP, "<ipaddr>[,<ipaddr>...]", gettext_noop("Peers which are allowed to do zone transfer"), NULL },
{ LOPT_IPSET, ARG_DUP, "/<domain>/<ipset>[,<ipset>...]", gettext_noop("Specify ipsets to which matching domains should be added"), NULL },
{ LOPT_SYNTH, ARG_DUP, "<domain>,<range>,[<prefix>]", gettext_noop("Specify a domain and address range for synthesised names"), NULL },
-#ifdef HAVE_DNSSEC
{ LOPT_SEC_VALID, OPT_DNSSEC_VALID, NULL, gettext_noop("Activate DNSSEC validation"), NULL },
{ LOPT_DNSKEY, ARG_DUP, "<domain>,<algo>,<key>", gettext_noop("Specify trust anchor DNSKEY"), NULL },
-#endif
+ { LOPT_DNSSEC_PERM, OPT_DNSSEC_PERMISS, NULL, gettext_noop("Do NOT return SERVFAIL whne DNSSEC validation fails."), NULL },
#ifdef OPTION6_PREFIX_CLASS
{ LOPT_PREF_CLSS, ARG_DUP, "set:tag,<class>", gettext_noop("Specify DHCPv6 prefix class"), NULL },
#endif
int optno, unsigned char *opt, size_t optlen, int set_do)
{
unsigned char *lenp, *datap, *p;
- int rdlen;
+ int rdlen, is_sign;
- if (ntohs(header->arcount) == 0)
+ if (!(p = find_pseudoheader(header, plen, NULL, NULL, &is_sign)))
{
+ if (is_sign)
+ return plen;
+
/* We are adding the pseudoheader */
if (!(p = skip_questions(header, plen)) ||
!(p = skip_section(p,
- ntohs(header->ancount) + ntohs(header->nscount),
+ ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount),
header, plen)))
return plen;
*p++ = 0; /* empty name */
rdlen = 0;
if (((ssize_t)optlen) > (limit - (p + 4)))
return plen; /* Too big */
- header->arcount = htons(1);
+ header->arcount = htons(ntohs(header->arcount) + 1);
datap = p;
}
else
{
- int i, is_sign;
+ int i;
unsigned short code, len, flags;
+ /* Must be at the end, if exists */
if (ntohs(header->arcount) != 1 ||
- !(p = find_pseudoheader(header, plen, NULL, NULL, &is_sign)) ||
is_sign ||
(!(p = skip_name(p, header, plen, 10))))
return plen;
/* If the packet holds exactly one query
return F_IPV4 or F_IPV6 and leave the name from the query in name */
-
unsigned int extract_request(struct dns_header *header, size_t qlen, char *name, unsigned short *typep)
{
unsigned char *p = (unsigned char *)(header+1);
int nameoffset;
unsigned short flag;
int q, ans, anscount = 0, addncount = 0;
- int dryrun = 0, sec_reqd = 0;
+ int dryrun = 0, sec_reqd = 0, have_pseudoheader = 0;
int is_sign;
struct crec *crecp;
int nxdomain = 0, auth = 1, trunc = 0, sec_data = 1;
struct mx_srv_record *rec;
+ size_t len;
+ /* Don't return AD set even for local data if checking disabled. */
+ if (header->hb4 & HB4_CD)
+ sec_data = 0;
+
/* If there is an RFC2671 pseudoheader then it will be overwritten by
partial replies, so we have to do a dry run to see if we can answer
the query. We check to see if the do bit is set, if so we always
forward rather than answering from the cache, which doesn't include
- security information. */
+ security information, unless we're in DNSSEC validation mode. */
if (find_pseudoheader(header, qlen, NULL, &pheader, &is_sign))
{
unsigned short udpsz, flags;
unsigned char *psave = pheader;
+ have_pseudoheader = 1;
+
GETSHORT(udpsz, pheader);
pheader += 2; /* ext_rcode */
GETSHORT(flags, pheader);
if (!dryrun)
log_query(crecp->flags & ~F_FORWARD, name, &addr, NULL);
}
- else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd)
+ else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd || option_bool(OPT_DNSSEC_VALID))
{
ans = 1;
if (!(crecp->flags & (F_HOSTS | F_DHCP)))
if (!dryrun)
log_query(crecp->flags, name, NULL, NULL);
}
- else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd)
+ else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd || option_bool(OPT_DNSSEC_VALID))
{
/* If we are returning local answers depending on network,
filter here. */
if (trunc)
header->hb3 |= HB3_TC;
- header->hb4 &= ~HB4_AD;
-
- if (option_bool(OPT_DNSSEC_VALID) || option_bool(OPT_DNSSEC_PROXY))
- if (sec_data)
- header->hb4 |= HB4_AD;
-
if (nxdomain)
SET_RCODE(header, NXDOMAIN);
else
header->ancount = htons(anscount);
header->nscount = htons(0);
header->arcount = htons(addncount);
- return ansp - (unsigned char *)header;
+
+ header->hb4 &= ~HB4_AD;
+ len = ansp - (unsigned char *)header;
+
+ if (have_pseudoheader)
+ {
+ len = add_pseudoheader(header, len, (unsigned char *)limit, 0, NULL, 0, sec_reqd);
+ if (sec_reqd && sec_data)
+ header->hb4 |= HB4_AD;
+
+ }
+
+ return len ;
}