Commit fa14bec8 authored by Simon Kelley's avatar Simon Kelley

Major tidy up of EDNS0 handling and computation/use of udp packet size.

parent 14a4ae88
...@@ -81,7 +81,8 @@ int in_zone(struct auth_zone *zone, char *name, char **cut) ...@@ -81,7 +81,8 @@ int in_zone(struct auth_zone *zone, char *name, char **cut)
} }
size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t now, union mysockaddr *peer_addr, int local_query) size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t now, union mysockaddr *peer_addr,
int local_query, int do_bit, int have_pseudoheader)
{ {
char *name = daemon->namebuff; char *name = daemon->namebuff;
unsigned char *p, *ansp; unsigned char *p, *ansp;
...@@ -820,6 +821,11 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n ...@@ -820,6 +821,11 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
header->ancount = htons(anscount); header->ancount = htons(anscount);
header->nscount = htons(authcount); header->nscount = htons(authcount);
header->arcount = htons(0); header->arcount = htons(0);
/* Advertise our packet size limit in our reply */
if (have_pseudoheader)
return add_pseudoheader(header, ansp - (unsigned char *)header, (unsigned char *)limit, daemon->edns_pktsz, 0, NULL, 0, do_bit);
return ansp - (unsigned char *)header; return ansp - (unsigned char *)header;
} }
......
...@@ -1113,7 +1113,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *namebuff, ...@@ -1113,7 +1113,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *namebuff,
int no_cache, int secure, int *doctored); int no_cache, int secure, int *doctored);
size_t answer_request(struct dns_header *header, char *limit, size_t qlen, size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
struct in_addr local_addr, struct in_addr local_netmask, 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);
int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name, int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
struct bogus_addr *addr, time_t now); struct bogus_addr *addr, time_t now);
int check_for_ignored_address(struct dns_header *header, size_t qlen, struct bogus_addr *baddr); int check_for_ignored_address(struct dns_header *header, size_t qlen, struct bogus_addr *baddr);
...@@ -1123,6 +1123,8 @@ int check_for_local_domain(char *name, time_t now); ...@@ -1123,6 +1123,8 @@ int check_for_local_domain(char *name, time_t now);
unsigned int questions_crc(struct dns_header *header, size_t plen, char *buff); unsigned int questions_crc(struct dns_header *header, size_t plen, char *buff);
size_t resize_packet(struct dns_header *header, size_t plen, size_t resize_packet(struct dns_header *header, size_t plen,
unsigned char *pheader, size_t hlen); unsigned char *pheader, size_t hlen);
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);
size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysockaddr *l3); size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysockaddr *l3);
size_t add_source_addr(struct dns_header *header, size_t plen, char *limit, union mysockaddr *source); size_t add_source_addr(struct dns_header *header, size_t plen, char *limit, union mysockaddr *source);
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
...@@ -1141,7 +1143,8 @@ int private_net(struct in_addr addr, int ban_localhost); ...@@ -1141,7 +1143,8 @@ int private_net(struct in_addr addr, int ban_localhost);
/* auth.c */ /* auth.c */
#ifdef HAVE_AUTH #ifdef HAVE_AUTH
size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, size_t answer_auth(struct dns_header *header, char *limit, size_t qlen,
time_t now, union mysockaddr *peer_addr, int local_query); time_t now, union mysockaddr *peer_addr, int local_query,
int do_bit, int have_pseudoheader);
int in_zone(struct auth_zone *zone, char *name, char **cut); int in_zone(struct auth_zone *zone, char *name, char **cut);
#endif #endif
......
...@@ -67,7 +67,6 @@ static char *algo_digest_name(int algo) ...@@ -67,7 +67,6 @@ static char *algo_digest_name(int algo)
case 12: return "gosthash94"; case 12: return "gosthash94";
case 13: return "sha256"; case 13: return "sha256";
case 14: return "sha384"; case 14: return "sha384";
default: return NULL; default: return NULL;
} }
} }
......
...@@ -244,7 +244,6 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, ...@@ -244,7 +244,6 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
void *hash = &crc; void *hash = &crc;
#endif #endif
unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL); unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL);
unsigned char *pheader;
(void)do_bit; (void)do_bit;
...@@ -264,7 +263,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, ...@@ -264,7 +263,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
there's no point retrying the query, retry the key query instead...... */ there's no point retrying the query, retry the key query instead...... */
if (forward->blocking_query) if (forward->blocking_query)
{ {
int fd; int fd, is_sign;
unsigned char *pheader;
forward->flags &= ~FREC_TEST_PKTSZ; forward->flags &= ~FREC_TEST_PKTSZ;
...@@ -276,8 +276,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, ...@@ -276,8 +276,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
blockdata_retrieve(forward->stash, forward->stash_len, (void *)header); blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
plen = forward->stash_len; plen = forward->stash_len;
if (find_pseudoheader(header, plen, NULL, &pheader, NULL)) if (find_pseudoheader(header, plen, NULL, &pheader, &is_sign) && !is_sign)
PUTSHORT((forward->flags & FREC_TEST_PKTSZ) ? SAFE_PKTSZ : forward->sentto->edns_pktsz, pheader); PUTSHORT(SAFE_PKTSZ, pheader);
if (forward->sentto->addr.sa.sa_family == AF_INET) 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"); log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, "retry", (struct all_addr *)&forward->sentto->addr.in.sin_addr, "dnssec");
...@@ -394,7 +394,14 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, ...@@ -394,7 +394,14 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
forward->log_id = daemon->log_id; forward->log_id = daemon->log_id;
if (option_bool(OPT_ADD_MAC)) 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)) if (option_bool(OPT_CLIENT_SUBNET))
{ {
...@@ -402,24 +409,25 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, ...@@ -402,24 +409,25 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
if (new != plen) if (new != plen)
{ {
plen = new; plen = new;
forward->flags |= FREC_HAS_SUBNET; forward->flags |= FREC_HAS_SUBNET | FREC_ADDED_PHEADER;
} }
} }
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID)) 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, /* For debugging, set Checking Disabled, otherwise, have the upstream check too,
this allows it to select auth servers when one is returning bad data. */ this allows it to select auth servers when one is returning bad data. */
if (option_bool(OPT_DNSSEC_DEBUG)) if (option_bool(OPT_DNSSEC_DEBUG))
header->hb4 |= HB4_CD; header->hb4 |= HB4_CD;
if (new_plen != plen)
forward->flags |= FREC_ADDED_PHEADER;
plen = new_plen;
} }
#endif #endif
...@@ -470,8 +478,21 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, ...@@ -470,8 +478,21 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
#endif #endif
} }
if (find_pseudoheader(header, plen, NULL, &pheader, NULL)) #ifdef HAVE_DNSSEC
PUTSHORT((forward->flags & FREC_TEST_PKTSZ) ? SAFE_PKTSZ : start->edns_pktsz, pheader); 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, if (retry_send(sendto(fd, (char *)header, plen, 0,
&start->addr.sa, &start->addr.sa,
...@@ -563,30 +584,34 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server ...@@ -563,30 +584,34 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
} }
#endif #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))) 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)) if (check_subnet && !check_source(header, plen, pheader, query_source))
{ {
my_syslog(LOG_WARNING, _("discarding DNS reply: subnet option mismatch")); my_syslog(LOG_WARNING, _("discarding DNS reply: subnet option mismatch"));
return 0; return 0;
} }
if (!is_sign)
{
if (added_pheader) if (added_pheader)
{ {
pheader = 0; /* client didn't send EDNS0, we added one, strip it off before returning answer. */
header->arcount = htons(0); 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);
}
} }
} }
...@@ -655,6 +680,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server ...@@ -655,6 +680,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
} }
if (option_bool(OPT_DNSSEC_VALID)) if (option_bool(OPT_DNSSEC_VALID))
{
header->hb4 &= ~HB4_AD; header->hb4 &= ~HB4_AD;
if (!(header->hb4 & HB4_CD) && ad_reqd && cache_secure) if (!(header->hb4 & HB4_CD) && ad_reqd && cache_secure)
...@@ -663,6 +689,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server ...@@ -663,6 +689,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
/* If the requestor didn't set the DO bit, don't return DNSSEC info. */ /* If the requestor didn't set the DO bit, don't return DNSSEC info. */
if (!do_bit) if (!do_bit)
n = rrfilter(header, n, 1); n = rrfilter(header, n, 1);
}
#endif #endif
/* do this after extract_addresses. Ensure NODATA reply and remove /* do this after extract_addresses. Ensure NODATA reply and remove
...@@ -761,8 +788,14 @@ void reply_query(int fd, int family, time_t now) ...@@ -761,8 +788,14 @@ void reply_query(int fd, int family, time_t now)
if ((nn = resize_packet(header, (size_t)n, pheader, plen))) if ((nn = resize_packet(header, (size_t)n, pheader, plen)))
{ {
header->hb3 &= ~(HB3_QR | HB3_AA | HB3_TC); header->hb3 &= ~(HB3_QR | HB3_AA | HB3_TC);
header->hb4 &= ~(HB4_RA | HB4_RCODE); header->hb4 &= ~(HB4_RA | HB4_RCODE | HB4_CD | HB4_AD);
forward_query(-1, NULL, NULL, 0, header, nn, now, forward, 0, 0); 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; return;
} }
} }
...@@ -1007,12 +1040,13 @@ void receive_query(struct listener *listen, time_t now) ...@@ -1007,12 +1040,13 @@ void receive_query(struct listener *listen, time_t now)
{ {
struct dns_header *header = (struct dns_header *)daemon->packet; struct dns_header *header = (struct dns_header *)daemon->packet;
union mysockaddr source_addr; 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 all_addr dst_addr;
struct in_addr netmask, dst_addr_4; struct in_addr netmask, dst_addr_4;
size_t m; size_t m;
ssize_t n; 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 #ifdef HAVE_AUTH
int local_auth = 0; int local_auth = 0;
#endif #endif
...@@ -1279,10 +1313,30 @@ void receive_query(struct listener *listen, time_t now) ...@@ -1279,10 +1313,30 @@ void receive_query(struct listener *listen, time_t now)
#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 #ifdef HAVE_AUTH
if (auth_dns) 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) if (m >= 1)
{ {
send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND), send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND),
...@@ -1293,9 +1347,13 @@ void receive_query(struct listener *listen, time_t now) ...@@ -1293,9 +1347,13 @@ void receive_query(struct listener *listen, time_t now)
else else
#endif #endif
{ {
int ad_reqd, do_bit; int ad_reqd = do_bit;
m = answer_request(header, ((char *) header) + daemon->packet_buff_sz, (size_t)n, /* RFC 6840 5.7 */
dst_addr_4, netmask, now, &ad_reqd, &do_bit); 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) if (m >= 1)
{ {
...@@ -1397,7 +1455,7 @@ unsigned char *tcp_request(int confd, time_t now, ...@@ -1397,7 +1455,7 @@ unsigned char *tcp_request(int confd, time_t now,
#ifdef HAVE_AUTH #ifdef HAVE_AUTH
int local_auth = 0; int local_auth = 0;
#endif #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; int check_subnet, no_cache_dnssec = 0, cache_secure = 0, bogusanswer = 0;
size_t m; size_t m;
unsigned short qtype; unsigned short qtype;
...@@ -1414,6 +1472,7 @@ unsigned char *tcp_request(int confd, time_t now, ...@@ -1414,6 +1472,7 @@ unsigned char *tcp_request(int confd, time_t now,
union mysockaddr peer_addr; union mysockaddr peer_addr;
socklen_t peer_len = sizeof(union mysockaddr); socklen_t peer_len = sizeof(union mysockaddr);
int query_count = 0; int query_count = 0;
unsigned char *pheader;
if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) == -1) if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) == -1)
return packet; return packet;
...@@ -1508,15 +1567,35 @@ unsigned char *tcp_request(int confd, time_t now, ...@@ -1508,15 +1567,35 @@ unsigned char *tcp_request(int confd, time_t now,
else else
dst_addr_4.s_addr = 0; 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 #ifdef HAVE_AUTH
if (auth_dns) 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 else
#endif #endif
{ {
int ad_reqd = do_bit;
/* RFC 6840 5.7 */
if (header->hb4 & HB4_AD)
ad_reqd = 1;
/* m > 0 if answered from cache */ /* m > 0 if answered from cache */
m = answer_request(header, ((char *) header) + 65536, (size_t)size, m = answer_request(header, ((char *) header) + 65536, (size_t)size,
dst_addr_4, netmask, now, &ad_question, &do_bit); dst_addr_4, netmask, now, ad_reqd, do_bit, have_pseudoheader);
/* Do this by steam now we're not in the select() loop */ /* Do this by steam now we're not in the select() loop */
check_log_writer(1); check_log_writer(1);
...@@ -1615,6 +1694,7 @@ unsigned char *tcp_request(int confd, time_t now, ...@@ -1615,6 +1694,7 @@ unsigned char *tcp_request(int confd, time_t now,
} }
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
added_pheader = 0;
if (option_bool(OPT_DNSSEC_VALID)) if (option_bool(OPT_DNSSEC_VALID))
{ {
size_t new_size = add_do_bit(header, size, ((char *) header) + 65536); size_t new_size = add_do_bit(header, size, ((char *) header) + 65536);
...@@ -1719,7 +1799,7 @@ unsigned char *tcp_request(int confd, time_t now, ...@@ -1719,7 +1799,7 @@ unsigned char *tcp_request(int confd, time_t now,
m = process_reply(header, now, last_server, (unsigned int)m, m = process_reply(header, now, last_server, (unsigned int)m,
option_bool(OPT_NO_REBIND) && !norebind, no_cache_dnssec, cache_secure, bogusanswer, 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; break;
} }
......
...@@ -288,7 +288,8 @@ int iface_enumerate(int family, void *parm, int (*callback)()) ...@@ -288,7 +288,8 @@ int iface_enumerate(int family, void *parm, int (*callback)())
rta = RTA_NEXT(rta, len1); rta = RTA_NEXT(rta, len1);
} }
if (inaddr && mac && callback_ok) if (!(neigh->ndm_state & (NUD_NOARP | NUD_INCOMPLETE | NUD_FAILED)) &&
inaddr && mac && callback_ok)
if (!((*callback)(neigh->ndm_family, inaddr, mac, maclen, parm))) if (!((*callback)(neigh->ndm_family, inaddr, mac, maclen, parm)))
callback_ok = 0; callback_ok = 0;
} }
......
...@@ -489,8 +489,8 @@ struct macparm { ...@@ -489,8 +489,8 @@ struct macparm {
union mysockaddr *l3; union mysockaddr *l3;
}; };
static size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit, 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) unsigned short udp_sz, int optno, unsigned char *opt, size_t optlen, int set_do)
{ {
unsigned char *lenp, *datap, *p; unsigned char *lenp, *datap, *p;
int rdlen, is_sign; int rdlen, is_sign;
...@@ -508,7 +508,7 @@ static size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned ...@@ -508,7 +508,7 @@ static size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned
return plen; return plen;
*p++ = 0; /* empty name */ *p++ = 0; /* empty name */
PUTSHORT(T_OPT, p); 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(0, p); /* extended RCODE and version */
PUTSHORT(set_do ? 0x8000 : 0, p); /* DO flag */ PUTSHORT(set_do ? 0x8000 : 0, p); /* DO flag */
lenp = p; lenp = p;
...@@ -594,7 +594,7 @@ static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *p ...@@ -594,7 +594,7 @@ static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *p
if (!match) if (!match)
return 1; /* continue */ 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 */ return 0; /* done */
} }
...@@ -603,12 +603,6 @@ size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysock ...@@ -603,12 +603,6 @@ size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysock
{ {
struct macparm parm; 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.header = header;
parm.limit = (unsigned char *)limit; parm.limit = (unsigned char *)limit;
parm.plen = plen; parm.plen = plen;
...@@ -699,13 +693,13 @@ size_t add_source_addr(struct dns_header *header, size_t plen, char *limit, unio ...@@ -699,13 +693,13 @@ size_t add_source_addr(struct dns_header *header, size_t plen, char *limit, unio
struct subnet_opt opt; struct subnet_opt opt;
len = calc_subnet_opt(&opt, source); 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 #ifdef HAVE_DNSSEC
size_t add_do_bit(struct dns_header *header, size_t plen, char *limit) 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 #endif
...@@ -1525,16 +1519,16 @@ static unsigned long crec_ttl(struct crec *crecp, time_t now) ...@@ -1525,16 +1519,16 @@ static unsigned long crec_ttl(struct crec *crecp, time_t now)
/* return zero if we can't answer from cache, or packet size if we can */ /* 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, size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
struct in_addr local_addr, struct in_addr local_netmask, 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; char *name = daemon->namebuff;
unsigned char *p, *ansp, *pheader; unsigned char *p, *ansp;
unsigned int qtype, qclass; unsigned int qtype, qclass;
struct all_addr addr; struct all_addr addr;
int nameoffset; int nameoffset;
unsigned short flag; unsigned short flag;
int q, ans, anscount = 0, addncount = 0; int q, ans, anscount = 0, addncount = 0;
int dryrun = 0, sec_reqd = 0, have_pseudoheader = 0; int dryrun = 0;
struct crec *crecp; struct crec *crecp;
int nxdomain = 0, auth = 1, trunc = 0, sec_data = 1; int nxdomain = 0, auth = 1, trunc = 0, sec_data = 1;
struct mx_srv_record *rec; struct mx_srv_record *rec;
...@@ -1550,36 +1544,12 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, ...@@ -1550,36 +1544,12 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (header->hb4 & HB4_CD) if (header->hb4 & HB4_CD)
sec_data = 0; 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 /* 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 partial replies, so we have to do a dry run to see if we can answer
the query. */ the query. */
if (ntohs(header->arcount) != 0) if (ntohs(header->arcount) != 0)
{
dryrun = 1; 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;
}
}
}
for (rec = daemon->mxnames; rec; rec = rec->next) for (rec = daemon->mxnames; rec; rec = rec->next)
rec->offset = 0; rec->offset = 0;
...@@ -1603,11 +1573,6 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, ...@@ -1603,11 +1573,6 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
GETSHORT(qtype, p); GETSHORT(qtype, p);
GETSHORT(qclass, 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 */ ans = 0; /* have we answered this question */
if (qtype == T_TXT || qtype == T_ANY) if (qtype == T_TXT || qtype == T_ANY)
...@@ -1739,7 +1704,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, ...@@ -1739,7 +1704,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
the zone is unsigned, which implies that we're doing the zone is unsigned, which implies that we're doing
validation. */ validation. */
if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) ||
!sec_reqd || !do_bit ||
(option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK))) (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK)))
{ {
do do
...@@ -1927,7 +1892,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, ...@@ -1927,7 +1892,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
} }
/* If the client asked for DNSSEC don't use cached data. */ /* 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 do
{ {
/* don't answer wildcard queries with data not from /etc/hosts /* don't answer wildcard queries with data not from /etc/hosts
...@@ -1960,10 +1925,6 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, ...@@ -1960,10 +1925,6 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
} }
if (crecp->flags & F_NEG) 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; ans = 1;
auth = 0; auth = 0;
...@@ -1972,7 +1933,6 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, ...@@ -1972,7 +1933,6 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (!dryrun) if (!dryrun)
log_query(crecp->flags, name, NULL, NULL); log_query(crecp->flags, name, NULL, NULL);
} }
}
else else
{ {
/* If we are returning local answers depending on network, /* If we are returning local answers depending on network,
...@@ -2209,10 +2169,11 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, ...@@ -2209,10 +2169,11 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
len = ansp - (unsigned char *)header; len = ansp - (unsigned char *)header;
/* Advertise our packet size limit in our reply */
if (have_pseudoheader) 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; header->hb4 |= HB4_AD;
else else
header->hb4 &= ~HB4_AD; header->hb4 &= ~HB4_AD;
......
...@@ -243,7 +243,7 @@ size_t rrfilter(struct dns_header *header, size_t plen, int mode) ...@@ -243,7 +243,7 @@ size_t rrfilter(struct dns_header *header, size_t plen, int mode)
for (p = rrs[0], i = 1; i < rr_found; i += 2) for (p = rrs[0], i = 1; i < rr_found; i += 2)
{ {
unsigned char *start = rrs[i]; unsigned char *start = rrs[i];
unsigned char *end = (i != rr_found - 1) ? rrs[i+1] : ((unsigned char *)(header+1)) + plen; unsigned char *end = (i != rr_found - 1) ? rrs[i+1] : ((unsigned char *)header) + plen;
memmove(p, start, end-start); memmove(p, start, end-start);
p += end-start; p += end-start;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment