diff --git a/src/dnsmasq.h b/src/dnsmasq.h index a41c8cc..9828819 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -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, struct bogus_addr *addr, time_t now); 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); unsigned int questions_crc(struct dns_header *header, size_t plen, char *buff); 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); /* 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, 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); diff --git a/src/dnssec.c b/src/dnssec.c index 486e422..e0b7f39 100644 --- a/src/dnssec.c +++ b/src/dnssec.c @@ -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); - if (find_pseudoheader(header, ret, NULL, &p, NULL)) + if (find_pseudoheader(header, ret, NULL, &p, NULL, NULL)) PUTSHORT(edns_pktsz, p); return ret; diff --git a/src/edns0.c b/src/edns0.c index f348b01..d1a11e7 100644 --- a/src/edns0.c +++ b/src/edns0.c @@ -16,12 +16,12 @@ #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. 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 - 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); 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) *len = ansp - start; + if (p) *p = save; + + if (is_last) + *is_last = (i == arcount-1); + ret = start; } else if (is_sign && @@ -100,50 +105,31 @@ struct macparm { 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; - - if (!(p = find_pseudoheader(header, plen, NULL, NULL, &is_sign))) - { - if (is_sign) - return plen; + unsigned char *lenp, *datap, *p, *udp_len, *buff = NULL; + int rdlen = 0, is_sign, is_last; + unsigned short flags = set_do ? 0x8000 : 0, rcode = 0; - /* We are 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(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 + p = find_pseudoheader(header, plen, NULL, &udp_len, &is_sign, &is_last); + + if (is_sign) + return plen; + + if (p) { + /* Existing header */ int i; - unsigned short code, len, flags; - - /* Must be at the end, if exists */ - if (ntohs(header->arcount) != 1 || - is_sign || - (!(p = skip_name(p, header, plen, 10)))) - return plen; - - p += 6; /* skip UDP length and RCODE */ + unsigned short code, len; + + p = udp_len; + GETSHORT(udp_sz, p); + GETSHORT(rcode, p); GETSHORT(flags, p); + if (set_do) { p -=2; - PUTSHORT(flags | 0x8000, p); + flags |= 0x8000; + PUTSHORT(flags, p); } lenp = p; @@ -165,22 +151,61 @@ size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *l return plen; p += len; } - - if (((ssize_t)optlen) > (limit - (p + 4))) - return plen; /* Too big */ + + /* If we're going to extend the RR, it has to be the last RR in the packet */ + 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) { PUTSHORT(optno, p); PUTSHORT(optlen, p); memcpy(p, opt, optlen); p += optlen; + PUTSHORT(p - datap, lenp); } - - PUTSHORT(p - datap, lenp); return p - (unsigned char *)header; - } static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv) diff --git a/src/forward.c b/src/forward.c index 041353c..2ca3c86 100644 --- a/src/forward.c +++ b/src/forward.c @@ -276,7 +276,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, blockdata_retrieve(forward->stash, forward->stash_len, (void *)header); 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); 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 - 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 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? */ unsigned char *pheader; 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); } #endif @@ -584,7 +584,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server } #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)) { @@ -779,7 +779,7 @@ void reply_query(int fd, int family, time_t now) int is_sign; /* 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) { header->ancount = htons(0); @@ -1313,7 +1313,7 @@ void receive_query(struct listener *listen, time_t now) #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; @@ -1569,7 +1569,7 @@ unsigned char *tcp_request(int confd, time_t now, 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; @@ -1578,7 +1578,7 @@ unsigned char *tcp_request(int confd, time_t now, GETSHORT(flags, pheader); if (flags & 0x8000) - do_bit = 1;/* do bit */ + do_bit = 1; /* do bit */ } #ifdef HAVE_AUTH