Handle extending EDNS0 OPT RR.

This commit is contained in:
Simon Kelley
2015-12-21 16:23:47 +00:00
parent 1d03016bbc
commit 5bb88f0963
4 changed files with 81 additions and 56 deletions

View File

@@ -1117,8 +1117,6 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
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);
unsigned char *find_pseudoheader(struct dns_header *header, size_t plen,
size_t *len, unsigned char **p, int *is_sign);
int check_for_local_domain(char *name, time_t now); 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,
@@ -1514,6 +1512,8 @@ u16 *rrfilter_desc(int type);
int expand_workspace(unsigned char ***wkspc, int *szp, int new); int expand_workspace(unsigned char ***wkspc, int *szp, int new);
/* edns0.c */ /* edns0.c */
unsigned char *find_pseudoheader(struct dns_header *header, size_t plen,
size_t *len, unsigned char **p, int *is_sign, int *is_last);
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,
unsigned short udp_sz, 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);
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);

View File

@@ -2206,7 +2206,7 @@ size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, i
ret = add_do_bit(header, p - (unsigned char *)header, end); ret = add_do_bit(header, p - (unsigned char *)header, end);
if (find_pseudoheader(header, ret, NULL, &p, NULL)) if (find_pseudoheader(header, ret, NULL, &p, NULL, NULL))
PUTSHORT(edns_pktsz, p); PUTSHORT(edns_pktsz, p);
return ret; return ret;

View File

@@ -16,12 +16,12 @@
#include "dnsmasq.h" #include "dnsmasq.h"
unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t *len, unsigned char **p, int *is_sign) unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t *len, unsigned char **p, int *is_sign, int *is_last)
{ {
/* See if packet has an RFC2671 pseudoheader, and if so return a pointer to it. /* See if packet has an RFC2671 pseudoheader, and if so return a pointer to it.
also return length of pseudoheader in *len and pointer to the UDP size in *p also return length of pseudoheader in *len and pointer to the UDP size in *p
Finally, check to see if a packet is signed. If it is we cannot change a single bit before Finally, check to see if a packet is signed. If it is we cannot change a single bit before
forwarding. We look for SIG and TSIG in the addition section, and TKEY queries (for GSS-TSIG) */ forwarding. We look for TSIG in the addition section, and TKEY queries (for GSS-TSIG) */
int i, arcount = ntohs(header->arcount); int i, arcount = ntohs(header->arcount);
unsigned char *ansp = (unsigned char *)(header+1); unsigned char *ansp = (unsigned char *)(header+1);
@@ -76,8 +76,13 @@ unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t
{ {
if (len) if (len)
*len = ansp - start; *len = ansp - start;
if (p) if (p)
*p = save; *p = save;
if (is_last)
*is_last = (i == arcount-1);
ret = start; ret = start;
} }
else if (is_sign && else if (is_sign &&
@@ -100,50 +105,31 @@ struct macparm {
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,
unsigned short udp_sz, 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, *udp_len, *buff = NULL;
int rdlen, is_sign; int rdlen = 0, is_sign, is_last;
unsigned short flags = set_do ? 0x8000 : 0, rcode = 0;
if (!(p = find_pseudoheader(header, plen, NULL, NULL, &is_sign))) p = find_pseudoheader(header, plen, NULL, &udp_len, &is_sign, &is_last);
{
if (is_sign)
return plen;
/* We are adding the pseudoheader */ if (is_sign)
if (!(p = skip_questions(header, plen)) || return plen;
!(p = skip_section(p,
ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount), if (p)
header, plen)))
return plen;
*p++ = 0; /* empty name */
PUTSHORT(T_OPT, p);
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;
PUTSHORT(0, p); /* RDLEN */
rdlen = 0;
if (((ssize_t)optlen) > (limit - (p + 4)))
return plen; /* Too big */
header->arcount = htons(ntohs(header->arcount) + 1);
datap = p;
}
else
{ {
/* Existing header */
int i; int i;
unsigned short code, len, flags; unsigned short code, len;
/* Must be at the end, if exists */ p = udp_len;
if (ntohs(header->arcount) != 1 || GETSHORT(udp_sz, p);
is_sign || GETSHORT(rcode, p);
(!(p = skip_name(p, header, plen, 10))))
return plen;
p += 6; /* skip UDP length and RCODE */
GETSHORT(flags, p); GETSHORT(flags, p);
if (set_do) if (set_do)
{ {
p -=2; p -=2;
PUTSHORT(flags | 0x8000, p); flags |= 0x8000;
PUTSHORT(flags, p);
} }
lenp = p; lenp = p;
@@ -166,21 +152,60 @@ size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *l
p += len; p += len;
} }
if (((ssize_t)optlen) > (limit - (p + 4))) /* If we're going to extend the RR, it has to be the last RR in the packet */
return plen; /* Too big */ if (!is_last)
{
/* First, take a copy of the options. */
if (rdlen != 0 && (buff = whine_malloc(rdlen)))
memcpy(buff, datap, rdlen);
/* now, delete OPT RR */
plen = rrfilter(header, plen, 0);
/* Now, force addition of a new one */
p = NULL;
}
} }
if (!p)
{
/* We are (re)adding the pseudoheader */
if (!(p = skip_questions(header, plen)) ||
!(p = skip_section(p,
ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount),
header, plen)))
return plen;
*p++ = 0; /* empty name */
PUTSHORT(T_OPT, p);
PUTSHORT(udp_sz, p); /* max packet length, 512 if not given in EDNS0 header */
PUTSHORT(rcode, p); /* extended RCODE and version */
PUTSHORT(flags, p); /* DO flag */
lenp = p;
PUTSHORT(rdlen, p); /* RDLEN */
datap = p;
/* Copy back any options */
if (buff)
{
memcpy(p, buff, rdlen);
free(buff);
p += rdlen;
}
header->arcount = htons(ntohs(header->arcount) + 1);
}
if (((ssize_t)optlen) > (limit - (p + 4)))
return plen; /* Too big */
/* Add new option */
if (optno != 0) if (optno != 0)
{ {
PUTSHORT(optno, p); PUTSHORT(optno, p);
PUTSHORT(optlen, p); PUTSHORT(optlen, p);
memcpy(p, opt, optlen); memcpy(p, opt, optlen);
p += optlen; p += optlen;
PUTSHORT(p - datap, lenp);
} }
PUTSHORT(p - datap, lenp);
return p - (unsigned char *)header; return p - (unsigned char *)header;
} }
static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv) static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv)

View File

@@ -276,7 +276,7 @@ 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, &is_sign) && !is_sign) if (find_pseudoheader(header, plen, NULL, &pheader, &is_sign, NULL) && !is_sign)
PUTSHORT(SAFE_PKTSZ, pheader); PUTSHORT(SAFE_PKTSZ, pheader);
if (forward->sentto->addr.sa.sa_family == AF_INET) if (forward->sentto->addr.sa.sa_family == AF_INET)
@@ -479,7 +479,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
} }
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && !do_bit) if (option_bool(OPT_DNSSEC_VALID) && (forward->flags & FREC_ADDED_PHEADER))
{ {
/* Difficult one here. If our client didn't send EDNS0, we will have set the UDP /* 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. packet size to 512. But that won't provide space for the RRSIGS in many cases.
@@ -489,7 +489,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
the truncated bit? */ the truncated bit? */
unsigned char *pheader; unsigned char *pheader;
int is_sign; int is_sign;
if (find_pseudoheader(header, plen, NULL, &pheader, &is_sign)) if (find_pseudoheader(header, plen, NULL, &pheader, &is_sign, NULL) && !is_sign)
PUTSHORT(start->edns_pktsz, pheader); PUTSHORT(start->edns_pktsz, pheader);
} }
#endif #endif
@@ -584,7 +584,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
} }
#endif #endif
if ((pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign))) if ((pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign, NULL)))
{ {
if (check_subnet && !check_source(header, plen, pheader, query_source)) if (check_subnet && !check_source(header, plen, pheader, query_source))
{ {
@@ -779,7 +779,7 @@ void reply_query(int fd, int family, time_t now)
int is_sign; int is_sign;
/* recreate query from reply */ /* recreate query from reply */
pheader = find_pseudoheader(header, (size_t)n, &plen, NULL, &is_sign); pheader = find_pseudoheader(header, (size_t)n, &plen, NULL, &is_sign, NULL);
if (!is_sign) if (!is_sign)
{ {
header->ancount = htons(0); header->ancount = htons(0);
@@ -1313,7 +1313,7 @@ void receive_query(struct listener *listen, time_t now)
#endif #endif
} }
if (find_pseudoheader(header, (size_t)n, NULL, &pheader, NULL)) if (find_pseudoheader(header, (size_t)n, NULL, &pheader, NULL, NULL))
{ {
unsigned short flags; unsigned short flags;
@@ -1569,7 +1569,7 @@ unsigned char *tcp_request(int confd, time_t now,
do_bit = 0; do_bit = 0;
if (find_pseudoheader(header, (size_t)size, NULL, &pheader, NULL)) if (find_pseudoheader(header, (size_t)size, NULL, &pheader, NULL, NULL))
{ {
unsigned short flags; unsigned short flags;
@@ -1578,7 +1578,7 @@ unsigned char *tcp_request(int confd, time_t now,
GETSHORT(flags, pheader); GETSHORT(flags, pheader);
if (flags & 0x8000) if (flags & 0x8000)
do_bit = 1;/* do bit */ do_bit = 1; /* do bit */
} }
#ifdef HAVE_AUTH #ifdef HAVE_AUTH