]> git.ipfire.org Git - ipfire-2.x.git/commitdiff
dnsmasq: import latest upstream patches
authorMatthias Fischer <fischerm@ipfire.org>
Sat, 9 May 2015 19:21:34 +0000 (21:21 +0200)
committerMatthias Fischer <fischerm@ipfire.org>
Sat, 9 May 2015 19:21:34 +0000 (21:21 +0200)
lfs/dnsmasq
src/patches/dnsmasq/0088-Handle-UDP-packet-loss-when-fragmentation-of-large-packets-is-broken.patch [new file with mode: 0644]
src/patches/dnsmasq/0089-Check-IPv4-mapped-IPv6-addresses-with--stop-rebind.patch [new file with mode: 0644]

index b98e6621734c2c4fd943aa4ae96ead3ae0284cae..7e6e849eb49953c7164461be3e87e4e8d1142411 100644 (file)
@@ -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 (file)
index 0000000..0b12cb8
--- /dev/null
@@ -0,0 +1,331 @@
+From a77cec8d58231d71cbc26615f0c0f0292c09ef54 Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+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 (file)
index 0000000..33463b6
--- /dev/null
@@ -0,0 +1,58 @@
+From b059c96dc69dfe3055c5b32b078a05c53b11ebb3 Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+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