From: Matthias Fischer Date: Sat, 9 May 2015 19:21:34 +0000 (+0200) Subject: dnsmasq: import latest upstream patches X-Git-Tag: v2.17-core92~10^2~40^2~3 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=7cbd533265e97b3f7d288f1d287e6a5699ebace8;p=ipfire-2.x.git dnsmasq: import latest upstream patches --- diff --git a/lfs/dnsmasq b/lfs/dnsmasq index b98e662173..7e6e849eb4 100644 --- a/lfs/dnsmasq +++ b/lfs/dnsmasq @@ -1,7 +1,7 @@ ############################################################################### # # # IPFire.org - A linux based firewall # -# Copyright (C) 2007 Michael Tremer & Christian Schmidt # +# Copyright (C) 2015 Michael Tremer & Christian Schmidt # # # # This program is free software: you can redistribute it and/or modify # # it under the terms of the GNU General Public License as published by # @@ -160,6 +160,8 @@ $(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects)) cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0085-Fix-argument-order-botch-which-broke-DNSSEC-for-TCP-.patch cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0086-Don-t-remove-RRSIG-RR-from-answers-to-ANY-queries-wh.patch cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0087-Constify-some-DHCP-lease-management-functions.patch + cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0088-Handle-UDP-packet-loss-when-fragmentation-of-large-packets-is-broken.patch + cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0089-Check-IPv4-mapped-IPv6-addresses-with--stop-rebind.patch cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq-Add-support-to-read-ISC-DHCP-lease-file.patch cd $(DIR_APP) && sed -i src/config.h \ -e 's|/\* #define HAVE_IDN \*/|#define HAVE_IDN|g' \ diff --git a/src/patches/dnsmasq/0088-Handle-UDP-packet-loss-when-fragmentation-of-large-packets-is-broken.patch b/src/patches/dnsmasq/0088-Handle-UDP-packet-loss-when-fragmentation-of-large-packets-is-broken.patch new file mode 100644 index 0000000000..0b12cb8037 --- /dev/null +++ b/src/patches/dnsmasq/0088-Handle-UDP-packet-loss-when-fragmentation-of-large-packets-is-broken.patch @@ -0,0 +1,331 @@ +From a77cec8d58231d71cbc26615f0c0f0292c09ef54 Mon Sep 17 00:00:00 2001 +From: Simon Kelley +Date: Fri, 8 May 2015 16:25:38 +0100 +Subject: [PATCH] Handle UDP packet loss when fragmentation of large packets + is broken. + +--- + CHANGELOG | 6 ++++++ + src/config.h | 1 + + src/dnsmasq.h | 5 +++-- + src/dnssec.c | 11 +++++++++-- + src/forward.c | 37 +++++++++++++++++++++++++++++-------- + src/network.c | 1 + + src/option.c | 18 +++++++++++------- + src/rfc1035.c | 22 ++++++---------------- + 8 files changed, 66 insertions(+), 35 deletions(-) + +diff --git a/CHANGELOG b/CHANGELOG +index af2b22c..d8fc57a 100644 +--- a/CHANGELOG ++++ b/CHANGELOG +@@ -109,6 +109,12 @@ version 2.73 + by quiet-dhcp6. Thanks to J. Pablo Abonia for + spotting the problem. + ++ Try and handle net connections with broken fragmentation ++ that lose large UDP packets. If a server times out, ++ reduce the maximum UDP packet size field in the EDNS0 ++ header to 1280 bytes. If it then answers, make that ++ change permanent. ++ + + version 2.72 + Add ra-advrouter mode, for RFC-3775 mobile IPv6 support. +diff --git a/src/config.h b/src/config.h +index 8def6f2..f75fe9d 100644 +--- a/src/config.h ++++ b/src/config.h +@@ -19,6 +19,7 @@ + #define CHILD_LIFETIME 150 /* secs 'till terminated (RFC1035 suggests > 120s) */ + #define TCP_MAX_QUERIES 100 /* Maximum number of queries per incoming TCP connection */ + #define EDNS_PKTSZ 4096 /* default max EDNS.0 UDP packet from RFC5625 */ ++#define SAFE_PKTSZ 1280 /* "go anywhere" UDP packet size */ + #define KEYBLOCK_LEN 40 /* choose to mininise fragmentation when storing DNSSEC keys */ + #define DNSSEC_WORK 50 /* Max number of queries to validate one question */ + #define TIMEOUT 10 /* drop UDP queries after TIMEOUT seconds */ +diff --git a/src/dnsmasq.h b/src/dnsmasq.h +index 824a860..ab16f79 100644 +--- a/src/dnsmasq.h ++++ b/src/dnsmasq.h +@@ -504,7 +504,7 @@ struct server { + char interface[IF_NAMESIZE+1]; + struct serverfd *sfd; + char *domain; /* set if this server only handles a domain. */ +- int flags, tcpfd; ++ int flags, tcpfd, edns_pktsz; + unsigned int queries, failed_queries; + #ifdef HAVE_LOOP + u32 uid; +@@ -594,6 +594,7 @@ struct hostsfile { + #define FREC_DO_QUESTION 64 + #define FREC_ADDED_PHEADER 128 + #define FREC_CHECK_NOSIGN 256 ++#define FREC_TEST_PKTSZ 512 + + #ifdef HAVE_DNSSEC + #define HASH_SIZE 20 /* SHA-1 digest size */ +@@ -1148,7 +1149,7 @@ int in_zone(struct auth_zone *zone, char *name, char **cut); + #endif + + /* dnssec.c */ +-size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, int class, int type, union mysockaddr *addr); ++size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, int class, int type, union mysockaddr *addr, int edns_pktsz); + int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t n, char *name, char *keyname, int class); + int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class); + int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class, int *neganswer, int *nons); +diff --git a/src/dnssec.c b/src/dnssec.c +index a9e1215..e91d7c2 100644 +--- a/src/dnssec.c ++++ b/src/dnssec.c +@@ -2162,10 +2162,12 @@ int dnskey_keytag(int alg, int flags, unsigned char *key, int keylen) + } + } + +-size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, int class, int type, union mysockaddr *addr) ++size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, int class, ++ int type, union mysockaddr *addr, int edns_pktsz) + { + unsigned char *p; + char *types = querystr("dnssec-query", type); ++ size_t ret; + + if (addr->sa.sa_family == AF_INET) + log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, name, (struct all_addr *)&addr->in.sin_addr, types); +@@ -2194,7 +2196,12 @@ size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, i + PUTSHORT(type, p); + PUTSHORT(class, p); + +- return 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)) ++ PUTSHORT(edns_pktsz, p); ++ ++ return ret; + } + + /* Go through a domain name, find "pointers" and fix them up based on how many bytes +diff --git a/src/forward.c b/src/forward.c +index a8e403c..592243f 100644 +--- a/src/forward.c ++++ b/src/forward.c +@@ -253,6 +253,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, + void *hash = &crc; + #endif + unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL); ++ unsigned char *pheader; + + (void)do_bit; + +@@ -261,19 +262,32 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, + forward = NULL; + else if (forward || (hash && (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, hash)))) + { ++ /* If we didn't get an answer advertising a maximal packet in EDNS, ++ fall back to 1280, which should work everywhere on IPv6. ++ If that generates an answer, it will become the new default ++ for this server */ ++ forward->flags |= FREC_TEST_PKTSZ; ++ + #ifdef HAVE_DNSSEC + /* If we've already got an answer to this query, but we're awaiting keys for validation, + there's no point retrying the query, retry the key query instead...... */ + if (forward->blocking_query) + { + int fd; +- ++ ++ forward->flags &= ~FREC_TEST_PKTSZ; ++ + while (forward->blocking_query) + forward = forward->blocking_query; ++ ++ 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 (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"); + #ifdef HAVE_IPV6 +@@ -417,7 +431,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, + plen = new_plen; + } + #endif +- ++ + while (1) + { + /* only send to servers dealing with our domain. +@@ -464,6 +478,9 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, + } + #endif + } ++ ++ if (find_pseudoheader(header, plen, NULL, &pheader, NULL)) ++ PUTSHORT((forward->flags & FREC_TEST_PKTSZ) ? SAFE_PKTSZ : start->edns_pktsz, pheader); + + if (retry_send(sendto(fd, (char *)header, plen, 0, + &start->addr.sa, +@@ -760,7 +777,6 @@ void reply_query(int fd, int family, time_t now) + } + + server = forward->sentto; +- + if ((forward->sentto->flags & SERV_TYPE) == 0) + { + if (RCODE(header) == REFUSED) +@@ -781,7 +797,12 @@ void reply_query(int fd, int family, time_t now) + if (!option_bool(OPT_ALL_SERVERS)) + daemon->last_server = server; + } +- ++ ++ /* We tried resending to this server with a smaller maximum size and got an answer. ++ Make that permanent. */ ++ if (server && (forward->flags & FREC_TEST_PKTSZ)) ++ server->edns_pktsz = SAFE_PKTSZ; ++ + /* If the answer is an error, keep the forward record in place in case + we get a good reply from another server. Kill it when we've + had replies from all to avoid filling the forwarding table when +@@ -890,7 +911,7 @@ void reply_query(int fd, int family, time_t now) + { + new->flags |= FREC_DNSKEY_QUERY; + nn = dnssec_generate_query(header, ((char *) header) + daemon->packet_buff_sz, +- daemon->keyname, forward->class, T_DNSKEY, &server->addr); ++ daemon->keyname, forward->class, T_DNSKEY, &server->addr, server->edns_pktsz); + } + else + { +@@ -899,7 +920,7 @@ void reply_query(int fd, int family, time_t now) + else + new->flags |= FREC_DS_QUERY; + nn = dnssec_generate_query(header,((char *) header) + daemon->packet_buff_sz, +- daemon->keyname, forward->class, T_DS, &server->addr); ++ daemon->keyname, forward->class, T_DS, &server->addr, server->edns_pktsz); + } + if ((hash = hash_questions(header, nn, daemon->namebuff))) + memcpy(new->hash, hash, HASH_SIZE); +@@ -1526,7 +1547,7 @@ static int tcp_check_for_unsigned_zone(time_t now, struct dns_header *header, s + + /* Can't find it in the cache, have to send a query */ + +- m = dnssec_generate_query(header, ((char *) header) + 65536, name_start, class, T_DS, &server->addr); ++ m = dnssec_generate_query(header, ((char *) header) + 65536, name_start, class, T_DS, &server->addr, server->edns_pktsz); + + *length = htons(m); + +@@ -1638,7 +1659,7 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si + + another_tcp_key: + m = dnssec_generate_query(new_header, ((char *) new_header) + 65536, keyname, class, +- new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS, &server->addr); ++ new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS, &server->addr, server->edns_pktsz); + + *length = htons(m); + +diff --git a/src/network.c b/src/network.c +index 992f023..a1d90c8 100644 +--- a/src/network.c ++++ b/src/network.c +@@ -1396,6 +1396,7 @@ void add_update_server(int flags, + serv->domain = domain_str; + serv->next = next; + serv->queries = serv->failed_queries = 0; ++ serv->edns_pktsz = daemon->edns_pktsz; + #ifdef HAVE_LOOP + serv->uid = rand32(); + #endif +diff --git a/src/option.c b/src/option.c +index f91cfbb..c7add88 100644 +--- a/src/option.c ++++ b/src/option.c +@@ -4498,15 +4498,19 @@ void read_opts(int argc, char **argv, char *compile_opts) + { + struct server *tmp; + for (tmp = daemon->servers; tmp; tmp = tmp->next) +- if (!(tmp->flags & SERV_HAS_SOURCE)) +- { +- if (tmp->source_addr.sa.sa_family == AF_INET) +- tmp->source_addr.in.sin_port = htons(daemon->query_port); ++ { ++ tmp->edns_pktsz = daemon->edns_pktsz; ++ ++ if (!(tmp->flags & SERV_HAS_SOURCE)) ++ { ++ if (tmp->source_addr.sa.sa_family == AF_INET) ++ tmp->source_addr.in.sin_port = htons(daemon->query_port); + #ifdef HAVE_IPV6 +- else if (tmp->source_addr.sa.sa_family == AF_INET6) +- tmp->source_addr.in6.sin6_port = htons(daemon->query_port); ++ else if (tmp->source_addr.sa.sa_family == AF_INET6) ++ tmp->source_addr.in6.sin6_port = htons(daemon->query_port); + #endif +- } ++ } ++ } + } + + if (daemon->if_addrs) +diff --git a/src/rfc1035.c b/src/rfc1035.c +index 5828055..8b1709d 100644 +--- a/src/rfc1035.c ++++ b/src/rfc1035.c +@@ -552,7 +552,7 @@ static size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned + return plen; + *p++ = 0; /* empty name */ + PUTSHORT(T_OPT, p); +- PUTSHORT(daemon->edns_pktsz, p); /* max packet length */ ++ PUTSHORT(SAFE_PKTSZ, p); /* max packet length, this will be overwritten */ + PUTSHORT(0, p); /* extended RCODE and version */ + PUTSHORT(set_do ? 0x8000 : 0, p); /* DO flag */ + lenp = p; +@@ -1537,7 +1537,6 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, + unsigned short flag; + int q, ans, anscount = 0, addncount = 0; + int dryrun = 0, sec_reqd = 0, have_pseudoheader = 0; +- int is_sign; + struct crec *crecp; + int nxdomain = 0, auth = 1, trunc = 0, sec_data = 1; + struct mx_srv_record *rec; +@@ -1557,28 +1556,19 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, + forward rather than answering from the cache, which doesn't include + security information, unless we're in DNSSEC validation mode. */ + +- if (find_pseudoheader(header, qlen, NULL, &pheader, &is_sign)) ++ if (find_pseudoheader(header, qlen, NULL, &pheader, NULL)) + { +- unsigned short udpsz, flags; +- unsigned char *psave = pheader; +- ++ unsigned short flags; ++ + have_pseudoheader = 1; + +- GETSHORT(udpsz, pheader); +- pheader += 2; /* ext_rcode */ ++ pheader += 4; /* udp size, ext_rcode */ + GETSHORT(flags, pheader); + + if ((sec_reqd = flags & 0x8000)) + *do_bit = 1;/* do bit */ +- *ad_reqd = 1; +- +- /* If our client is advertising a larger UDP packet size +- than we allow, trim it so that we don't get an overlarge +- response from upstream */ +- +- if (!is_sign && (udpsz > daemon->edns_pktsz)) +- PUTSHORT(daemon->edns_pktsz, psave); + ++ *ad_reqd = 1; + dryrun = 1; + } + +-- +1.7.10.4 diff --git a/src/patches/dnsmasq/0089-Check-IPv4-mapped-IPv6-addresses-with--stop-rebind.patch b/src/patches/dnsmasq/0089-Check-IPv4-mapped-IPv6-addresses-with--stop-rebind.patch new file mode 100644 index 0000000000..33463b6929 --- /dev/null +++ b/src/patches/dnsmasq/0089-Check-IPv4-mapped-IPv6-addresses-with--stop-rebind.patch @@ -0,0 +1,58 @@ +From b059c96dc69dfe3055c5b32b078a05c53b11ebb3 Mon Sep 17 00:00:00 2001 +From: Simon Kelley +Date: Fri, 8 May 2015 20:25:51 +0100 +Subject: [PATCH] Check IPv4-mapped IPv6 addresses with --stop-rebind. + +--- + CHANGELOG | 3 +++ + src/rfc1035.c | 21 +++++++++++++++++---- + 2 files changed, 20 insertions(+), 4 deletions(-) + +diff --git a/CHANGELOG b/CHANGELOG +index d8fc57a..94a521f 100644 +--- a/CHANGELOG ++++ b/CHANGELOG +@@ -115,6 +115,9 @@ version 2.73 + header to 1280 bytes. If it then answers, make that + change permanent. + ++ Check IPv4-mapped IPv6 addresses when --stop-rebind ++ is active. Thanks to Jordan Milne for spotting this. ++ + + version 2.72 + Add ra-advrouter mode, for RFC-3775 mobile IPv6 support. +diff --git a/src/rfc1035.c b/src/rfc1035.c +index 8b1709d..5e3f566 100644 +--- a/src/rfc1035.c ++++ b/src/rfc1035.c +@@ -1117,10 +1117,23 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t + memcpy(&addr, p1, addrlen); + + /* check for returned address in private space */ +- if (check_rebind && +- (flags & F_IPV4) && +- private_net(addr.addr.addr4, !option_bool(OPT_LOCAL_REBIND))) +- return 1; ++ if (check_rebind) ++ { ++ if ((flags & F_IPV4) && ++ private_net(addr.addr.addr4, !option_bool(OPT_LOCAL_REBIND))) ++ return 1; ++ ++#ifdef HAVE_IPV6 ++ if ((flags & F_IPV6) && ++ IN6_IS_ADDR_V4MAPPED(&addr.addr.addr6)) ++ { ++ struct in_addr v4; ++ v4.s_addr = ((const uint32_t *) (&addr.addr.addr6))[3]; ++ if (private_net(v4, !option_bool(OPT_LOCAL_REBIND))) ++ return 1; ++ } ++#endif ++ } + + #ifdef HAVE_IPSET + if (ipsets && (flags & (F_IPV4 | F_IPV6))) +-- +1.7.10.4