void *hash = &crc;
#endif
unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL);
- unsigned char *pheader;
(void)do_bit;
there's no point retrying the query, retry the key query instead...... */
if (forward->blocking_query)
{
- int fd;
+ int fd, is_sign;
+ unsigned char *pheader;
forward->flags &= ~FREC_TEST_PKTSZ;
blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
plen = forward->stash_len;
- if (find_pseudoheader(header, plen, NULL, &pheader, NULL))
- PUTSHORT((forward->flags & FREC_TEST_PKTSZ) ? SAFE_PKTSZ : forward->sentto->edns_pktsz, pheader);
+ if (find_pseudoheader(header, plen, NULL, &pheader, &is_sign) && !is_sign)
+ PUTSHORT(SAFE_PKTSZ, pheader);
if (forward->sentto->addr.sa.sa_family == AF_INET)
log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, "retry", (struct all_addr *)&forward->sentto->addr.in.sin_addr, "dnssec");
forward->log_id = daemon->log_id;
if (option_bool(OPT_ADD_MAC))
- plen = add_mac(header, plen, ((char *) header) + daemon->packet_buff_sz, &forward->source);
-
+ {
+ size_t new = add_mac(header, plen, ((char *) header) + daemon->packet_buff_sz, &forward->source);
+ if (new != plen)
+ {
+ plen = new;
+ forward->flags |= FREC_ADDED_PHEADER;
+ }
+ }
+
if (option_bool(OPT_CLIENT_SUBNET))
{
size_t new = add_source_addr(header, plen, ((char *) header) + daemon->packet_buff_sz, &forward->source);
if (new != plen)
{
plen = new;
- forward->flags |= FREC_HAS_SUBNET;
+ forward->flags |= FREC_HAS_SUBNET | FREC_ADDED_PHEADER;
}
}
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID))
{
- size_t new_plen = add_do_bit(header, plen, ((char *) header) + daemon->packet_buff_sz);
+ size_t new = add_do_bit(header, plen, ((char *) header) + daemon->packet_buff_sz);
+ if (new != plen)
+ forward->flags |= FREC_ADDED_PHEADER;
+
+ plen = new;
+
/* 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;
- if (new_plen != plen)
- forward->flags |= FREC_ADDED_PHEADER;
-
- plen = new_plen;
}
#endif
}
#endif
}
-
- if (find_pseudoheader(header, plen, NULL, &pheader, NULL))
- PUTSHORT((forward->flags & FREC_TEST_PKTSZ) ? SAFE_PKTSZ : start->edns_pktsz, pheader);
+#ifdef HAVE_DNSSEC
+ if (option_bool(OPT_DNSSEC_VALID) && !do_bit)
+ {
+ /* Difficult one here. If our client didn't send EDNS0, we will have set the UDP
+ packet size to 512. But that won't provide space for the RRSIGS in many cases.
+ The RRSIGS will be stripped out before the answer goes back, so the packet should
+ shrink again. So, if we added a do-bit, bump the udp packet size to the value
+ known to be OK for this server. Maybe check returned size after stripping and set
+ the truncated bit? */
+ unsigned char *pheader;
+ int is_sign;
+ if (find_pseudoheader(header, plen, NULL, &pheader, &is_sign))
+ PUTSHORT(start->edns_pktsz, pheader);
+ }
+#endif
+
if (retry_send(sendto(fd, (char *)header, plen, 0,
&start->addr.sa,
sa_len(&start->addr))))
}
#endif
- /* If upstream is advertising a larger UDP packet size
- than we allow, trim it so that we don't get overlarge
- requests for the client. We can't do this for signed packets. */
-
if ((pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign)))
{
- unsigned short udpsz;
- unsigned char *psave = sizep;
-
- GETSHORT(udpsz, sizep);
-
- if (!is_sign && udpsz > daemon->edns_pktsz)
- PUTSHORT(daemon->edns_pktsz, psave);
-
if (check_subnet && !check_source(header, plen, pheader, query_source))
{
my_syslog(LOG_WARNING, _("discarding DNS reply: subnet option mismatch"));
return 0;
}
- if (added_pheader)
+ if (!is_sign)
{
- pheader = 0;
- header->arcount = htons(0);
+ if (added_pheader)
+ {
+ /* client didn't send EDNS0, we added one, strip it off before returning answer. */
+ n = rrfilter(header, n, 0);
+ pheader = NULL;
+ }
+ else
+ {
+ /* If upstream is advertising a larger UDP packet size
+ than we allow, trim it so that we don't get overlarge
+ requests for the client. We can't do this for signed packets. */
+ unsigned short udpsz;
+ unsigned char *psave = sizep;
+
+ GETSHORT(udpsz, sizep);
+ if (udpsz > daemon->edns_pktsz)
+ PUTSHORT(daemon->edns_pktsz, psave);
+ }
}
}
}
if (option_bool(OPT_DNSSEC_VALID))
- header->hb4 &= ~HB4_AD;
-
- 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. */
- if (!do_bit)
- n = rrfilter(header, n, 1);
+ {
+ header->hb4 &= ~HB4_AD;
+
+ 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. */
+ if (!do_bit)
+ n = rrfilter(header, n, 1);
+ }
#endif
/* do this after extract_addresses. Ensure NODATA reply and remove
if ((nn = resize_packet(header, (size_t)n, pheader, plen)))
{
header->hb3 &= ~(HB3_QR | HB3_AA | HB3_TC);
- header->hb4 &= ~(HB4_RA | HB4_RCODE);
- forward_query(-1, NULL, NULL, 0, header, nn, now, forward, 0, 0);
+ 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 (forward->flags & FREC_DO_QUESTION)
+ add_do_bit(header, nn, (char *)pheader + plen);
+ forward_query(-1, NULL, NULL, 0, header, nn, now, forward, forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION);
return;
}
}
{
struct dns_header *header = (struct dns_header *)daemon->packet;
union mysockaddr source_addr;
- unsigned short type;
+ unsigned char *pheader;
+ unsigned short type, udp_size = PACKETSZ; /* default if no EDNS0 */
struct all_addr dst_addr;
struct in_addr netmask, dst_addr_4;
size_t m;
ssize_t n;
- int if_index = 0, auth_dns = 0;
+ int if_index = 0, auth_dns = 0, do_bit = 0, have_pseudoheader = 0;
#ifdef HAVE_AUTH
int local_auth = 0;
#endif
#endif
}
+ if (find_pseudoheader(header, (size_t)n, NULL, &pheader, NULL))
+ {
+ unsigned short flags;
+
+ have_pseudoheader = 1;
+ GETSHORT(udp_size, pheader);
+ pheader += 2; /* ext_rcode */
+ GETSHORT(flags, pheader);
+
+ if (flags & 0x8000)
+ do_bit = 1;/* do bit */
+
+ /* 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;
+ }
+
#ifdef HAVE_AUTH
if (auth_dns)
{
- m = answer_auth(header, ((char *) header) + daemon->packet_buff_sz, (size_t)n, now, &source_addr, local_auth);
+ m = answer_auth(header, ((char *) header) + udp_size, (size_t)n, now, &source_addr,
+ local_auth, do_bit, have_pseudoheader);
if (m >= 1)
{
send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND),
else
#endif
{
- int ad_reqd, do_bit;
- m = answer_request(header, ((char *) header) + daemon->packet_buff_sz, (size_t)n,
- dst_addr_4, netmask, now, &ad_reqd, &do_bit);
+ int ad_reqd = do_bit;
+ /* RFC 6840 5.7 */
+ if (header->hb4 & HB4_AD)
+ ad_reqd = 1;
+
+ m = answer_request(header, ((char *) header) + udp_size, (size_t)n,
+ dst_addr_4, netmask, now, ad_reqd, do_bit, have_pseudoheader);
if (m >= 1)
{
#ifdef HAVE_AUTH
int local_auth = 0;
#endif
- int checking_disabled, ad_question, do_bit, added_pheader = 0;
+ int checking_disabled, do_bit, added_pheader = 0, have_pseudoheader = 0;
int check_subnet, no_cache_dnssec = 0, cache_secure = 0, bogusanswer = 0;
size_t m;
unsigned short qtype;
union mysockaddr peer_addr;
socklen_t peer_len = sizeof(union mysockaddr);
int query_count = 0;
+ unsigned char *pheader;
if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) == -1)
return packet;
else
dst_addr_4.s_addr = 0;
+ do_bit = 0;
+
+ if (find_pseudoheader(header, (size_t)size, NULL, &pheader, NULL))
+ {
+ unsigned short flags;
+
+ have_pseudoheader = 1;
+ pheader += 4; /* udp_size, ext_rcode */
+ GETSHORT(flags, pheader);
+
+ if (flags & 0x8000)
+ do_bit = 1;/* do bit */
+ }
+
#ifdef HAVE_AUTH
if (auth_dns)
- m = answer_auth(header, ((char *) header) + 65536, (size_t)size, now, &peer_addr, local_auth);
+ m = answer_auth(header, ((char *) header) + 65536, (size_t)size, now, &peer_addr,
+ local_auth, do_bit, have_pseudoheader);
else
#endif
{
- /* m > 0 if answered from cache */
- m = answer_request(header, ((char *) header) + 65536, (size_t)size,
- dst_addr_4, netmask, now, &ad_question, &do_bit);
+ int ad_reqd = do_bit;
+ /* RFC 6840 5.7 */
+ if (header->hb4 & HB4_AD)
+ ad_reqd = 1;
+
+ /* m > 0 if answered from cache */
+ m = answer_request(header, ((char *) header) + 65536, (size_t)size,
+ dst_addr_4, netmask, now, ad_reqd, do_bit, have_pseudoheader);
/* Do this by steam now we're not in the select() loop */
check_log_writer(1);
}
#ifdef HAVE_DNSSEC
+ added_pheader = 0;
if (option_bool(OPT_DNSSEC_VALID))
{
size_t new_size = add_do_bit(header, size, ((char *) header) + 65536);
m = process_reply(header, now, last_server, (unsigned int)m,
option_bool(OPT_NO_REBIND) && !norebind, no_cache_dnssec, cache_secure, bogusanswer,
- ad_question, do_bit, added_pheader, check_subnet, &peer_addr);
+ ad_reqd, do_bit, added_pheader, check_subnet, &peer_addr);
break;
}
union mysockaddr *l3;
};
-static size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit,
- int optno, unsigned char *opt, size_t optlen, int set_do)
+size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit,
+ unsigned short udp_sz, int optno, unsigned char *opt, size_t optlen, int set_do)
{
unsigned char *lenp, *datap, *p;
int rdlen, is_sign;
return plen;
*p++ = 0; /* empty name */
PUTSHORT(T_OPT, p);
- PUTSHORT(SAFE_PKTSZ, p); /* max packet length, this will be overwritten */
+ PUTSHORT(udp_sz, p); /* max packet length, 512 if not given in EDNS0 header */
PUTSHORT(0, p); /* extended RCODE and version */
PUTSHORT(set_do ? 0x8000 : 0, p); /* DO flag */
lenp = p;
if (!match)
return 1; /* continue */
- parm->plen = add_pseudoheader(parm->header, parm->plen, parm->limit, EDNS0_OPTION_MAC, (unsigned char *)mac, maclen, 0);
+ parm->plen = add_pseudoheader(parm->header, parm->plen, parm->limit, PACKETSZ, EDNS0_OPTION_MAC, (unsigned char *)mac, maclen, 0);
return 0; /* done */
}
{
struct macparm parm;
-/* Must have an existing pseudoheader as the only ar-record,
- or have no ar-records. Must also not be signed */
-
- if (ntohs(header->arcount) > 1)
- return plen;
-
parm.header = header;
parm.limit = (unsigned char *)limit;
parm.plen = plen;
struct subnet_opt opt;
len = calc_subnet_opt(&opt, source);
- return add_pseudoheader(header, plen, (unsigned char *)limit, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0);
+ return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0);
}
#ifdef HAVE_DNSSEC
size_t add_do_bit(struct dns_header *header, size_t plen, char *limit)
{
- return add_pseudoheader(header, plen, (unsigned char *)limit, 0, NULL, 0, 1);
+ return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, 0, NULL, 0, 1);
}
#endif
/* return zero if we can't answer from cache, or packet size if we can */
size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
struct in_addr local_addr, struct in_addr local_netmask,
- time_t now, int *ad_reqd, int *do_bit)
+ time_t now, int ad_reqd, int do_bit, int have_pseudoheader)
{
char *name = daemon->namebuff;
- unsigned char *p, *ansp, *pheader;
+ unsigned char *p, *ansp;
unsigned int qtype, qclass;
struct all_addr addr;
int nameoffset;
unsigned short flag;
int q, ans, anscount = 0, addncount = 0;
- int dryrun = 0, sec_reqd = 0, have_pseudoheader = 0;
+ int dryrun = 0;
struct crec *crecp;
int nxdomain = 0, auth = 1, trunc = 0, sec_data = 1;
struct mx_srv_record *rec;
if (header->hb4 & HB4_CD)
sec_data = 0;
- /* RFC 6840 5.7 */
- *ad_reqd = header->hb4 & HB4_AD;
- *do_bit = 0;
-
/* If there is an additional data section then it will be overwritten by
partial replies, so we have to do a dry run to see if we can answer
the query. */
-
if (ntohs(header->arcount) != 0)
- {
- dryrun = 1;
-
- /* If there's an additional section, there might be an EDNS(0) pseudoheader */
- if (find_pseudoheader(header, qlen, NULL, &pheader, NULL))
- {
- unsigned short flags;
-
- have_pseudoheader = 1;
-
- pheader += 4; /* udp size, ext_rcode */
- GETSHORT(flags, pheader);
-
- if ((sec_reqd = flags & 0x8000))
- {
- *do_bit = 1;/* do bit */
- *ad_reqd = 1;
- }
- }
- }
+ dryrun = 1;
for (rec = daemon->mxnames; rec; rec = rec->next)
rec->offset = 0;
GETSHORT(qtype, p);
GETSHORT(qclass, p);
- /* Don't filter RRSIGS from answers to ANY queries, even if do-bit
- not set. */
- if (qtype == T_ANY)
- *do_bit = 1;
-
ans = 0; /* have we answered this question */
if (qtype == T_TXT || qtype == T_ANY)
the zone is unsigned, which implies that we're doing
validation. */
if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) ||
- !sec_reqd ||
+ !do_bit ||
(option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK)))
{
do
}
/* If the client asked for DNSSEC don't use cached data. */
- if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || !sec_reqd || !(crecp->flags & F_DNSSECOK))
+ if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || !do_bit || !(crecp->flags & F_DNSSECOK))
do
{
/* don't answer wildcard queries with data not from /etc/hosts
if (crecp->flags & F_NEG)
{
- /* We don't cache NSEC records, so if a DNSSEC-validated negative answer
- is cached and the client wants DNSSEC, forward rather than answering from the cache */
- if (!sec_reqd || !(crecp->flags & F_DNSSECOK))
- {
- ans = 1;
- auth = 0;
- if (crecp->flags & F_NXDOMAIN)
- nxdomain = 1;
- if (!dryrun)
- log_query(crecp->flags, name, NULL, NULL);
- }
+ ans = 1;
+ auth = 0;
+ if (crecp->flags & F_NXDOMAIN)
+ nxdomain = 1;
+ if (!dryrun)
+ log_query(crecp->flags, name, NULL, NULL);
}
else
{
len = ansp - (unsigned char *)header;
+ /* Advertise our packet size limit in our reply */
if (have_pseudoheader)
- len = add_pseudoheader(header, len, (unsigned char *)limit, 0, NULL, 0, sec_reqd);
+ len = add_pseudoheader(header, len, (unsigned char *)limit, daemon->edns_pktsz, 0, NULL, 0, do_bit);
- if (*ad_reqd && sec_data)
+ if (ad_reqd && sec_data)
header->hb4 |= HB4_AD;
else
header->hb4 &= ~HB4_AD;