diff --git a/src/dns-protocol.h b/src/dns-protocol.h index 2777be9..565ac90 100644 --- a/src/dns-protocol.h +++ b/src/dns-protocol.h @@ -142,15 +142,15 @@ struct dns_header { #define RCODE(x) ((x)->hb4 & HB4_RCODE) #define SET_RCODE(x, code) (x)->hb4 = ((x)->hb4 & ~HB4_RCODE) | code -#define GETSHORT(s, cp) { \ +#define GETSHORT(s, cp) do { \ unsigned char *t_cp = (unsigned char *)(cp); \ (s) = ((u16)t_cp[0] << 8) \ | ((u16)t_cp[1]) \ ; \ (cp) += 2; \ -} + } while(0) -#define GETLONG(l, cp) { \ +#define GETLONG(l, cp) do { \ unsigned char *t_cp = (unsigned char *)(cp); \ (l) = ((u32)t_cp[0] << 24) \ | ((u32)t_cp[1] << 16) \ @@ -158,17 +158,17 @@ struct dns_header { | ((u32)t_cp[3]) \ ; \ (cp) += 4; \ -} + } while (0) -#define PUTSHORT(s, cp) { \ +#define PUTSHORT(s, cp) do { \ u16 t_s = (u16)(s); \ unsigned char *t_cp = (unsigned char *)(cp); \ *t_cp++ = t_s >> 8; \ *t_cp = t_s; \ (cp) += 2; \ -} + } while(0) -#define PUTLONG(l, cp) { \ +#define PUTLONG(l, cp) do { \ u32 t_l = (u32)(l); \ unsigned char *t_cp = (unsigned char *)(cp); \ *t_cp++ = t_l >> 24; \ @@ -176,7 +176,7 @@ struct dns_header { *t_cp++ = t_l >> 8; \ *t_cp = t_l; \ (cp) += 4; \ -} + } while (0) #define CHECK_LEN(header, pp, plen, len) \ ((size_t)((pp) - (unsigned char *)(header) + (len)) <= (plen)) diff --git a/src/dnsmasq.h b/src/dnsmasq.h index 379e8a4..f79222e 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -779,7 +779,7 @@ struct frec { union all_addr dest; unsigned int iface, log_id; int fd; - unsigned short orig_id; + unsigned short orig_id, udp_pkt_size; struct frec_src *next; } frec_src; struct server *sentto; /* NULL means free */ @@ -1395,8 +1395,7 @@ void report_addresses(struct dns_header *header, size_t len, u32 mark); #endif 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, int have_pseudoheader, - int *stale, int *filtered); + time_t now, int ad_reqd, int do_bit, int *stale, int *filtered); int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name, time_t now); int check_for_ignored_address(struct dns_header *header, size_t qlen); @@ -1419,7 +1418,7 @@ int in_zone(struct auth_zone *zone, char *name, char **cut); /* dnssec.c */ #ifdef HAVE_DNSSEC -size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char *name, int class, int id, int type, int edns_pktsz); +size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char *name, int class, int id, int type); int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class, int *validate_count); int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, diff --git a/src/dnssec.c b/src/dnssec.c index c791480..027bb37 100644 --- a/src/dnssec.c +++ b/src/dnssec.c @@ -2204,11 +2204,10 @@ int dnskey_keytag(int alg, int flags, unsigned char *key, int keylen) } size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char *name, - int class, int id, int type, int edns_pktsz) + int class, int id, int type) { unsigned char *p; - size_t ret; - + header->qdcount = htons(1); header->ancount = htons(0); header->nscount = htons(0); @@ -2228,12 +2227,7 @@ size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char PUTSHORT(type, p); PUTSHORT(class, p); - ret = add_do_bit(header, p - (unsigned char *)header, end); - - if (find_pseudoheader(header, ret, NULL, &p, NULL, NULL)) - PUTSHORT(edns_pktsz, p); - - return ret; + return add_do_bit(header, p - (unsigned char *)header, end); } int errflags_to_ede(int status) diff --git a/src/edns0.c b/src/edns0.c index 598478f..89ae2a7 100644 --- a/src/edns0.c +++ b/src/edns0.c @@ -96,7 +96,12 @@ unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t } -/* replace == 2 ->delete existing option only. */ +/* replace == 0 ->don't replace existing option + replace == 1 ->replace existing or add option + replace == 2 ->relpace existing option only. + + udp_sz == 0 -> leave unchanged in existing EDNS0 or set to deamon->edns_pksz in a new one. +*/ 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, int replace) { @@ -114,9 +119,14 @@ size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *l /* Existing header */ int i; unsigned short code, len; - + p = udp_len; - GETSHORT(udp_sz, p); + + if (udp_sz == 0) + GETSHORT(udp_sz, p); + else + PUTSHORT(udp_sz, p); + GETSHORT(rcode, p); GETSHORT(flags, p); @@ -197,12 +207,15 @@ size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *l free(buff); return plen; /* bad packet */ } - + + if (udp_sz == 0) + udp_sz = daemon->edns_pktsz; + *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 */ + PUTSHORT(rcode, p); /* extended RCODE and version */ + PUTSHORT(flags, p); /* DO flag */ lenp = p; PUTSHORT(rdlen, p); /* RDLEN */ datap = p; @@ -245,7 +258,7 @@ size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *l size_t add_do_bit(struct dns_header *header, size_t plen, unsigned char *limit) { - return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, 0, NULL, 0, 1, 0); + return add_pseudoheader(header, plen, (unsigned char *)limit, 0, 0, NULL, 0, 1, 0); } static unsigned char char64(unsigned char c) @@ -290,7 +303,7 @@ static size_t add_dns_client(struct dns_header *header, size_t plen, unsigned ch replace = 2; if (replace != 0 || maclen == 6) - plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMDEVICEID, (unsigned char *)encode, strlen(encode), 0, replace); + plen = add_pseudoheader(header, plen, limit, 0, EDNS0_OPTION_NOMDEVICEID, (unsigned char *)encode, strlen(encode), 0, replace); return plen; } @@ -315,7 +328,7 @@ static size_t add_mac(struct dns_header *header, size_t plen, unsigned char *lim replace = 2; if (replace != 0 || maclen != 0) - plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_MAC, mac, maclen, 0, replace); + plen = add_pseudoheader(header, plen, limit, 0, EDNS0_OPTION_MAC, mac, maclen, 0, replace); return plen; } @@ -415,7 +428,7 @@ static size_t add_source_addr(struct dns_header *header, size_t plen, unsigned c else return plen; - return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0, replace); + return add_pseudoheader(header, plen, (unsigned char *)limit, 0, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0, replace); } int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer) @@ -515,7 +528,7 @@ static size_t add_umbrella_opt(struct dns_header *header, size_t plen, unsigned PUTLONG(daemon->umbrella_asset, u); } - return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, EDNS0_OPTION_UMBRELLA, (unsigned char *)&opt, u - (u8 *)&opt, 0, 1); + return add_pseudoheader(header, plen, (unsigned char *)limit, 0, EDNS0_OPTION_UMBRELLA, (unsigned char *)&opt, u - (u8 *)&opt, 0, 1); } /* Set *check_subnet if we add a client subnet option, which needs to checked @@ -530,7 +543,7 @@ size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *l plen = add_dns_client(header, plen, limit, source, now, cacheable); if (daemon->dns_client_id) - plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMCPEID, + plen = add_pseudoheader(header, plen, limit, 0, EDNS0_OPTION_NOMCPEID, (unsigned char *)daemon->dns_client_id, strlen(daemon->dns_client_id), 0, 1); if (option_bool(OPT_UMBRELLA)) diff --git a/src/forward.c b/src/forward.c index 4536555..d7f4cfa 100644 --- a/src/forward.c +++ b/src/forward.c @@ -165,7 +165,7 @@ static int domain_no_rebind(char *domain) static int forward_query(int udpfd, union mysockaddr *udpaddr, union all_addr *dst_addr, unsigned int dst_iface, - struct dns_header *header, size_t plen, char *limit, time_t now, + struct dns_header *header, size_t plen, size_t replylimit, time_t now, struct frec *forward, int ad_reqd, int do_bit, int fast_retry) { unsigned int flags = 0; @@ -176,8 +176,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, int old_src = 0, old_reply = 0; int first, last, start = 0; int cacheable, forwarded = 0; - size_t edns0_len; - unsigned char *pheader, *oph; + unsigned char *oph; int ede = EDE_UNSET; (void)do_bit; unsigned short rrtype; @@ -263,6 +262,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, src->log_id = daemon->log_id; src->iface = dst_iface; src->fd = udpfd; + src->udp_pkt_size = (unsigned short)replylimit; /* closely spaced identical queries cannot be a try and a retry, so it's safe to wait for the reply from the first without @@ -313,8 +313,10 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, if (!(forward = get_new_frec(now, master, 0))) goto reply; /* table full - flags == 0, return REFUSED */ - - plen = add_edns0_config(header, plen, ((unsigned char *)header) + PACKETSZ, &forward->frec_src.source, now, &cacheable); + + forward->flags = fwd_flags; + + plen = add_edns0_config(header, plen, ((unsigned char *)header) + daemon->edns_pktsz, &forward->frec_src.source, now, &cacheable); if (!cacheable) forward->flags |= FREC_NO_CACHE; @@ -322,7 +324,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, #ifdef HAVE_DNSSEC if (option_bool(OPT_DNSSEC_VALID) && (master->flags & SERV_DO_DNSSEC)) { - plen = add_do_bit(header, plen, ((unsigned char *) header) + PACKETSZ); + plen = add_do_bit(header, plen, ((unsigned char *) header) + daemon->edns_pktsz); /* For debugging, set Checking Disabled, otherwise, have the upstream check too, this allows it to select auth servers when one is returning bad data. */ @@ -331,17 +333,10 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, } #endif - if (find_pseudoheader(header, plen, &edns0_len, &pheader, NULL, NULL)) - { - /* If there wasn't a PH before, and there is now, we added it. */ - if (!oph) - forward->flags |= FREC_ADDED_PHEADER; - - /* Reduce udp size on retransmits. */ - if (forward->flags & FREC_TEST_PKTSZ) - PUTSHORT(SAFE_PKTSZ, pheader); - } - + /* If there wasn't a PH before, and there is now, we added it. */ + if (!oph && find_pseudoheader(header, plen, NULL, NULL, NULL, NULL)) + forward->flags |= FREC_ADDED_PHEADER; + /* Do these before saving query. */ forward->frec_src.orig_id = ntohs(header->id); forward->new_id = get_id(); @@ -361,8 +356,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, forward->frec_src.iface = dst_iface; forward->frec_src.next = NULL; forward->frec_src.fd = udpfd; + forward->frec_src.udp_pkt_size = (unsigned short)replylimit; forward->forwardall = 0; - forward->flags = fwd_flags; if (domain_no_rebind(daemon->namebuff)) forward->flags |= FREC_NOREBIND; if (header->hb4 & HB4_CD) @@ -405,20 +400,13 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, if (forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY)) { - int is_sign; - unsigned char *pheader; - /* log_id should match previous DNSSEC query. */ daemon->log_display_id = forward->frec_src.log_id; blockdata_retrieve(forward->stash, forward->stash_len, (void *)header); plen = forward->stash_len; /* get query for logging. */ - extract_request(header, plen, daemon->namebuff, NULL); - - /* Use go-anywhere size limit, as it's a retry. */ - if (find_pseudoheader(header, plen, NULL, &pheader, &is_sign, NULL) && !is_sign) - PUTSHORT(SAFE_PKTSZ, pheader); + gotname = extract_request(header, plen, daemon->namebuff, NULL); /* Find suitable servers: should never fail. */ if (!filter_servers(forward->sentto->arrayposn, F_DNSSECOK, &first, &last)) @@ -498,22 +486,9 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, if (option_bool(OPT_CONNTRACK)) set_outgoing_mark(forward, fd); #endif - -#ifdef HAVE_DNSSEC - 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. - 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. We check returned size after stripping and set - the truncated bit if it's still too big. */ - unsigned char *pheader; - int is_sign; - if (find_pseudoheader(header, plen, NULL, &pheader, &is_sign, NULL) && !is_sign) - PUTSHORT(srv->edns_pktsz, pheader); - } -#endif + + /* send EDNS0 packet size to the maximum size we believe the link allows at this time. */ + plen = add_pseudoheader(header, plen, (unsigned char *)(header + daemon->edns_pktsz), srv->edns_pktsz, 0, NULL, 0, 0, 0); if (retry_send(sendto(fd, (char *)header, plen, 0, &srv->addr.sa, @@ -531,17 +506,16 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, daemon->packet_len = plen; daemon->fd_save = fd; - if (!(forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY))) - { - if (!gotname) - strcpy(daemon->namebuff, "query"); - log_query_mysockaddr(F_SERVER | F_FORWARD, daemon->namebuff, - &srv->addr, NULL, 0); - } + if (!gotname) + strcpy(daemon->namebuff, "query"); + + if (!(forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY))) + log_query_mysockaddr(F_SERVER | F_FORWARD, daemon->namebuff, + &srv->addr, NULL, 0); #ifdef HAVE_DNSSEC - else - log_query_mysockaddr(F_NOEXTRA | F_DNSSEC | F_SERVER, daemon->namebuff, &srv->addr, - (forward->flags & FREC_DNSKEY_QUERY) ? "dnssec-retry[DNSKEY]" : "dnssec-retry[DS]", 0); + else + log_query_mysockaddr(F_NOEXTRA | F_DNSSEC | F_SERVER, daemon->namebuff, &srv->addr, + (forward->flags & FREC_DNSKEY_QUERY) ? "dnssec-retry[DNSKEY]" : "dnssec-retry[DS]", 0); #endif srv->queries++; @@ -571,7 +545,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, reply: if (udpfd != -1) { - if (!(plen = make_local_answer(flags, gotname, plen, header, daemon->namebuff, limit, first, last, ede))) + if (!(plen = make_local_answer(flags, gotname, plen, header, daemon->namebuff, (char *)(header + replylimit), first, last, ede))) return 0; if (oph) @@ -579,9 +553,9 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, u16 swap = htons((u16)ede); if (ede != EDE_UNSET) - plen = add_pseudoheader(header, plen, (unsigned char *)limit, daemon->edns_pktsz, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0); + plen = add_pseudoheader(header, plen, (unsigned char *)(header + replylimit), 0, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0); else - plen = add_pseudoheader(header, plen, (unsigned char *)limit, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0); + plen = add_pseudoheader(header, plen, (unsigned char *)(header + replylimit), 0, 0, NULL, 0, do_bit, 0); } #if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS) @@ -626,8 +600,6 @@ int fast_retry(time_t now) to_run = f->forward_delay - t; else { - unsigned char *udpsz; - unsigned short udp_size = PACKETSZ; /* default if no EDNS0 */ struct dns_header *header = (struct dns_header *)daemon->packet; /* packet buffer overwritten */ @@ -635,16 +607,12 @@ int fast_retry(time_t now) blockdata_retrieve(f->stash, f->stash_len, (void *)header); - /* UDP size already set in saved query. */ - if (find_pseudoheader(header, f->stash_len, NULL, &udpsz, NULL, NULL)) - GETSHORT(udp_size, udpsz); - daemon->log_display_id = f->frec_src.log_id; daemon->log_source_addr = NULL; - forward_query(-1, NULL, NULL, 0, header, f->stash_len, ((char *) header) + udp_size, now, f, + forward_query(-1, NULL, NULL, 0, header, f->stash_len, 0, now, f, f->flags & FREC_AD_QUESTION, f->flags & FREC_DO_QUESTION, 1); - + to_run = f->forward_delay = 2 * f->forward_delay; } @@ -703,7 +671,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server { /* Get extended RCODE. */ rcode |= sizep[2] << 4; - + if (option_bool(OPT_CLIENT_SUBNET) && !check_source(header, plen, pheader, query_source)) { my_syslog(LOG_WARNING, _("discarding DNS reply: subnet option mismatch")); @@ -720,17 +688,9 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server } 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; - GETSHORT(udpsz, sizep); - if (udpsz > daemon->edns_pktsz) - { - sizep -= 2; - PUTSHORT(daemon->edns_pktsz, sizep); - } - + /* Advertise our max UDP packet to the client. */ + PUTSHORT(daemon->edns_pktsz, sizep); + #ifdef HAVE_DNSSEC /* If the client didn't set the do bit, but we did, reset it. */ if (option_bool(OPT_DNSSEC_VALID) && !do_bit) @@ -874,7 +834,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server if (pheader && ede != EDE_UNSET) { u16 swap = htons((u16)ede); - n = add_pseudoheader(header, n, limit, daemon->edns_pktsz, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 1); + n = add_pseudoheader(header, n, limit, 0, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 1); } if (RCODE(header) == NXDOMAIN) @@ -1017,6 +977,7 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header, struct randfd_list *rfds = NULL; struct frec *new = NULL; struct blockdata *newstash = NULL; + unsigned char *p; /* Make sure we don't expire and free the orig frec during the allocation of a new one: third arg of get_new_frec() does that. */ @@ -1024,7 +985,7 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header, (server = daemon->serverarray[serverind]) && (nn = dnssec_generate_query(header, ((unsigned char *) header) + server->edns_pktsz, daemon->keyname, forward->class, get_id(), - STAT_ISEQUAL(status, STAT_NEED_KEY) ? T_DNSKEY : T_DS, server->edns_pktsz)) && + STAT_ISEQUAL(status, STAT_NEED_KEY) ? T_DNSKEY : T_DS)) && (fd = allocate_rfd(&rfds, server)) != -1 && (newstash = blockdata_alloc((char *)header, nn)) && (new = get_new_frec(now, server, 1))) @@ -1067,6 +1028,10 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header, if (option_bool(OPT_CONNTRACK)) set_outgoing_mark(orig, fd); #endif + + /* Maximum packet size depends on the server */ + if (find_pseudoheader(header, nn, NULL, &p, NULL, NULL)) + PUTSHORT(server->edns_pktsz, p); server_send(server, fd, header, nn, 0); server->queries++; @@ -1230,9 +1195,6 @@ void reply_query(int fd, time_t now) if ((RCODE(header) == REFUSED || RCODE(header) == SERVFAIL) && forward->forwardall == 0) /* for broken servers, attempt to send to another one. */ { - unsigned char *udpsz; - unsigned short udp_size = PACKETSZ; /* default if no EDNS0 */ - #ifdef HAVE_DNSSEC /* The query MAY have got a good answer, and be awaiting the results of further queries, in which case @@ -1244,11 +1206,7 @@ void reply_query(int fd, time_t now) /* Get the saved query back. */ blockdata_retrieve(forward->stash, forward->stash_len, (void *)header); - /* UDP size already set in saved query. */ - if (find_pseudoheader(header, (size_t)forward->stash_len, NULL, &udpsz, NULL, NULL)) - GETSHORT(udp_size, udpsz); - - forward_query(-1, NULL, NULL, 0, header, forward->stash_len, ((char *) header) + udp_size, now, forward, + forward_query(-1, NULL, NULL, 0, header, forward->stash_len, 0, now, forward, forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION, 0); return; } @@ -1264,9 +1222,9 @@ void reply_query(int fd, time_t now) /* We tried resending to this server with a smaller maximum size and got an answer. Make that permanent. To avoid reduxing the packet size for a single dropped packet, - only do this when we get a truncated answer, or one larger than the safe size. */ + only do this when we get a truncated answer, or one that fits the safe size. */ if (server->edns_pktsz > SAFE_PKTSZ && (forward->flags & FREC_TEST_PKTSZ) && - ((header->hb3 & HB3_TC) || n >= SAFE_PKTSZ)) + ((header->hb3 & HB3_TC) || n <= SAFE_PKTSZ)) { server->edns_pktsz = SAFE_PKTSZ; server->pktsz_reduced = now; @@ -1335,7 +1293,7 @@ void return_reply(time_t now, struct frec *forward, struct dns_header *header, s union all_addr a; a.log.ede = ede = errflags_to_ede(status); - + if (STAT_ISEQUAL(status, STAT_ABANDONED)) { result = "ABANDONED"; @@ -1358,10 +1316,10 @@ void return_reply(time_t now, struct frec *forward, struct dns_header *header, s log_query(F_SECSTAT, domain, &a, result, 0); } } - + if ((daemon->limit[LIMIT_CRYPTO] - forward->validate_counter) > (int)daemon->metrics[METRIC_CRYPTO_HWM]) daemon->metrics[METRIC_CRYPTO_HWM] = daemon->limit[LIMIT_CRYPTO] - forward->validate_counter; - + if ((daemon->limit[LIMIT_WORK] - forward->work_counter) > (int)daemon->metrics[METRIC_WORK_HWM]) daemon->metrics[METRIC_WORK_HWM] = daemon->limit[LIMIT_WORK] - forward->work_counter; #endif @@ -1386,23 +1344,9 @@ void return_reply(time_t now, struct frec *forward, struct dns_header *header, s ((unsigned char *)header) + daemon->edns_pktsz, ede))) { struct frec_src *src; + int do_trunc; - header->id = htons(forward->frec_src.orig_id); -#ifdef HAVE_DNSSEC - /* We added an EDNSO header for the purpose of getting DNSSEC RRs, and set the value of the UDP payload size - greater than the no-EDNS0-implied 512 to have space for the RRSIGS. If, having stripped them and the EDNS0 - header, the answer is still bigger than 512, truncate it and mark it so. The client then retries with TCP. */ - if (option_bool(OPT_DNSSEC_VALID) && (forward->flags & FREC_ADDED_PHEADER) && (nn > PACKETSZ)) - { - header->ancount = htons(0); - header->nscount = htons(0); - header->arcount = htons(0); - header->hb3 |= HB3_TC; - nn = resize_packet(header, nn, NULL, 0); - } -#endif - - for (src = &forward->frec_src; src; src = src->next) + for (do_trunc = 0, src = &forward->frec_src; src; src = src->next) { header->id = htons(src->orig_id); @@ -1418,8 +1362,18 @@ void return_reply(time_t now, struct frec *forward, struct dns_header *header, s if (src->fd != -1) { - send_from(src->fd, option_bool(OPT_NOWILD) || option_bool (OPT_CLEVERBIND), daemon->packet, nn, - &src->source, &src->dest, src->iface); + /* Only send packets that fit what the requestor allows. + We'll send a truncated packet to others below. */ + if (nn <= src->udp_pkt_size) + { + send_from(src->fd, option_bool(OPT_NOWILD) || option_bool (OPT_CLEVERBIND), daemon->packet, nn, + &src->source, &src->dest, src->iface); +#ifdef HAVE_DUMPFILE + dump_packet_udp(DUMP_REPLY, daemon->packet, (size_t)nn, NULL, &src->source, src->fd); +#endif + } + else + do_trunc = 1; if (option_bool(OPT_EXTRALOG) && src != &forward->frec_src) { @@ -1427,17 +1381,42 @@ void return_reply(time_t now, struct frec *forward, struct dns_header *header, s daemon->log_source_addr = &src->source; log_query(F_UPSTREAM, "query", NULL, "duplicate", 0); } - -#ifdef HAVE_DUMPFILE - dump_packet_udp(DUMP_REPLY, daemon->packet, (size_t)nn, NULL, &src->source, src->fd); -#endif } } - } + + /* The packet is too big for one or more requestors, send them a truncated answer. */ + if (do_trunc) + { + size_t hlen, new; + unsigned char *pheader = find_pseudoheader(header, nn, &hlen, NULL, NULL, NULL); + + header->ancount = htons(0); + header->nscount = htons(0); + header->arcount = htons(0); + header->hb3 |= HB3_TC; + new = resize_packet(header, nn, pheader, hlen); + daemon->log_display_id = forward->frec_src.log_id; + daemon->log_source_addr = &forward->frec_src.source; + log_query(F_UPSTREAM, "query", NULL, "truncated", 0); + + for (src = &forward->frec_src; src; src = src->next) + if (src->fd != -1 && nn > src->udp_pkt_size) + { + header->id = htons(src->orig_id); + send_from(src->fd, option_bool(OPT_NOWILD) || option_bool (OPT_CLEVERBIND), daemon->packet, new, + &src->source, &src->dest, src->iface); + +#ifdef HAVE_DUMPFILE + dump_packet_udp(DUMP_REPLY, daemon->packet, (size_t)nn, NULL, &src->source, src->fd); +#endif + } + } + } + free_frec(forward); /* cancel */ } - + #ifdef HAVE_CONNTRACK static int is_query_allowed_for_mark(u32 mark, const char *name) @@ -1481,7 +1460,7 @@ static size_t answer_disallowed(struct dns_header *header, size_t qlen, u32 mark return p - (unsigned char *)header; } #endif - + void receive_query(struct listener *listen, time_t now) { struct dns_header *header = (struct dns_header *)daemon->packet; @@ -1784,9 +1763,6 @@ void receive_query(struct listener *listen, time_t now) udp_size = daemon->edns_pktsz; else if (udp_size < PACKETSZ) udp_size = PACKETSZ; /* Sanity check - can't reduce below default. RFC 6891 6.2.3 */ - - pheader -= 6; /* ext_class */ - PUTSHORT(udp_size, pheader); /* Bounding forwarded queries to maximum configured */ } #ifdef HAVE_CONNTRACK @@ -1806,7 +1782,7 @@ void receive_query(struct listener *listen, time_t now) m = answer_disallowed(header, (size_t)n, (u32)mark, is_single_query ? daemon->namebuff : NULL); if (have_pseudoheader && m != 0) - m = add_pseudoheader(header, m, ((unsigned char *) header) + udp_size, daemon->edns_pktsz, + m = add_pseudoheader(header, m, ((unsigned char *) header) + deamon->edns_pktsz, 0, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0); if (m >= 1) @@ -1853,7 +1829,7 @@ void receive_query(struct listener *listen, time_t now) 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, &stale, &filtered); + dst_addr_4, netmask, now, ad_reqd, do_bit, &stale, &filtered); if (m >= 1) { @@ -1870,9 +1846,12 @@ void receive_query(struct listener *listen, time_t now) { u16 swap = htons(ede); - m = add_pseudoheader(header, m, ((unsigned char *) header) + udp_size, daemon->edns_pktsz, + m = add_pseudoheader(header, m, ((unsigned char *) header) + daemon->edns_pktsz, 0, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0); } + else + m = add_pseudoheader(header, m, ((unsigned char *) header) + daemon->edns_pktsz, 0, + 0, NULL, 0, do_bit, 0); } #ifdef HAVE_DUMPFILE @@ -1911,7 +1890,7 @@ void receive_query(struct listener *listen, time_t now) blockdata_retrieve(saved_question, (size_t)n, (void *)header); blockdata_free(saved_question); if (forward_query(fd, &source_addr, &dst_addr, if_index, header, (size_t)n, - ((char *) header) + udp_size, now, NULL, ad_reqd, do_bit, 0)) + udp_size, now, NULL, ad_reqd, do_bit, 0)) daemon->metrics[METRIC_DNS_QUERIES_FORWARDED]++; else daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]++; @@ -1921,7 +1900,7 @@ void receive_query(struct listener *listen, time_t now) } } } - + /* Send query in packet, qsize to a server determined by first,last,start and get the reply. return reply size. */ static ssize_t tcp_talk(int first, int last, int start, unsigned char *packet, size_t qsize, @@ -2200,7 +2179,7 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si } m = dnssec_generate_query(new_header, ((unsigned char *) new_header) + 65536, keyname, class, 0, - STAT_ISEQUAL(new_status, STAT_NEED_KEY) ? T_DNSKEY : T_DS, server->edns_pktsz); + STAT_ISEQUAL(new_status, STAT_NEED_KEY) ? T_DNSKEY : T_DS); if ((start = dnssec_server(server, keyname, &first, &last)) == -1) { @@ -2427,7 +2406,7 @@ unsigned char *tcp_request(int confd, time_t now, m = answer_disallowed(header, size, (u32)mark, is_single_query ? daemon->namebuff : NULL); if (have_pseudoheader && m != 0) - m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz, + m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, 0, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0); } #endif @@ -2455,7 +2434,7 @@ unsigned char *tcp_request(int confd, time_t now, /* 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, &stale, &filtered); + dst_addr_4, netmask, now, ad_reqd, do_bit, &stale, &filtered); } /* Do this by steam now we're not in the select() loop */ check_log_writer(1); @@ -2596,34 +2575,27 @@ unsigned char *tcp_request(int confd, time_t now, if (!(m = make_local_answer(flags, gotname, size, header, daemon->namebuff, ((char *) header) + 65536, first, last, ede))) break; - - if (have_pseudoheader) - { - u16 swap = htons((u16)ede); - - if (ede != EDE_UNSET) - m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0); - else - m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0); - } } - else if (have_pseudoheader) + else { ede = EDE_UNSET; - + if (filtered) ede = EDE_FILTERED; else if (stale) ede = EDE_STALE; + } + + if (have_pseudoheader) + { + u16 swap = htons((u16)ede); if (ede != EDE_UNSET) - { - u16 swap = htons((u16)ede); - - m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0); - } + m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, 0, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0); + else + m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, 0, 0, NULL, 0, do_bit, 0); } - + check_log_writer(1); *length = htons(m); diff --git a/src/rfc1035.c b/src/rfc1035.c index 387d894..478e6d0 100644 --- a/src/rfc1035.c +++ b/src/rfc1035.c @@ -1316,7 +1316,7 @@ static int check_bad_address(struct dns_header *header, size_t qlen, struct bogu GETSHORT(qtype, p); GETSHORT(qclass, p); GETLONG(ttl, p); - GETSHORT(rdlen, p) + GETSHORT(rdlen, p); if (ttlp) *ttlp = ttl; @@ -1565,8 +1565,7 @@ static int cache_validated(const struct crec *crecp) /* 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, int have_pseudoheader, - int *stale, int *filtered) + time_t now, int ad_reqd, int do_bit, int *stale, int *filtered) { char *name = daemon->namebuff; unsigned char *p, *ansp; @@ -2342,10 +2341,6 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, len = ansp - (unsigned char *)header; - /* Advertise our packet size limit in our reply */ - if (have_pseudoheader) - len = add_pseudoheader(header, len, (unsigned char *)limit, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0); - if (ad_reqd && sec_data) header->hb4 |= HB4_AD; else