]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Bug 2785: DNS needs to set EDNS options advertising Squid capabilities
authorAmos Jeffries <amosjeffries@squid-cache.org>
Sun, 17 Oct 2010 03:26:02 +0000 (21:26 -0600)
committerAmos Jeffries <amosjeffries@squid-cache.org>
Sun, 17 Oct 2010 03:26:02 +0000 (21:26 -0600)
... allowing Squid to advertise a larger UDP reply size than 512 bytes.

Internally Squid has a buffer allocated on demand so there is no
practicable limit on individual packets. Network topology and external
software places stricter boundaries on what works and what does not.

Squid does not parse the additional section of replies so for now the
full auto-negotiation EDNS allows is not used. Instead a configuration
option is provided for admin to configure a desirable packet size in
bytes. EDNS defaults to "none" (disabled) until tested in a wider
environment.

Testing so far has brought to light problems with EDNS adverts on A and
IPv4-PTR queries. So support is limited to AAAA and IPv6-PTR queries only.
EDNS compliant resolvers have the option of caching the info between
requests for a short while so this will hopefully leak over to improve
IPv4 responses as well.

13 files changed:
include/rfc1035.h
include/rfc2671.h [new file with mode: 0644]
include/rfc3596.h
lib/Makefile.am
lib/rfc1035.c
lib/rfc2671.c [new file with mode: 0644]
lib/rfc3596.c
lib/tests/testRFC1035.cc
src/cache_cf.cc
src/cf.data.depend
src/cf.data.pre
src/dns_internal.cc
src/structs.h

index e00f462fc17c9f1ed0aa4e9a1111a36f884cec89..2728cbfc2aeca47a18eb9c9c035f14fd396c9eb0 100644 (file)
@@ -57,6 +57,7 @@
  */
 #define RFC1035_MAXHOSTNAMESZ RFC2181_MAXHOSTNAMELEN
 
+#define RFC1035_DEFAULT_PACKET_SZ 512
 
 typedef struct _rfc1035_rr rfc1035_rr;
 struct _rfc1035_rr {
@@ -97,12 +98,14 @@ SQUIDCEXTERN ssize_t rfc1035BuildAQuery(const char *hostname,
                                         char *buf,
                                         size_t sz,
                                         unsigned short qid,
-                                        rfc1035_query * query);
+                                        rfc1035_query * query,
+                                        ssize_t edns_sz);
 SQUIDCEXTERN ssize_t rfc1035BuildPTRQuery(const struct in_addr,
         char *buf,
         size_t sz,
         unsigned short qid,
-        rfc1035_query * query);
+        rfc1035_query * query,
+                                        ssize_t edns_sz);
 SQUIDCEXTERN void rfc1035SetQueryID(char *, unsigned short qid);
 SQUIDCEXTERN int rfc1035MessageUnpack(const char *buf,
                                       size_t sz,
@@ -131,5 +134,6 @@ SQUIDCEXTERN int rfc1035QuestionPack(char *buf,
                                      const char *name,
                                      const unsigned short type,
                                      const unsigned short _class);
+SQUIDCEXTERN int rfc1035RRPack(char *buf, size_t sz, const rfc1035_rr * RR);
 
 #endif /* SQUID_RFC1035_H */
diff --git a/include/rfc2671.h b/include/rfc2671.h
new file mode 100644 (file)
index 0000000..de25eef
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * $Id$
+ *
+ * AUTHOR: Amos Jeffries
+ *
+ * SQUID Web Proxy Cache          http://www.squid-cache.org/
+ * ----------------------------------------------------------
+ *
+ *  Squid is the result of efforts by numerous individuals from
+ *  the Internet community; see the CONTRIBUTORS file for full
+ *  details.   Many organizations have provided support for Squid's
+ *  development; see the SPONSORS file for full details.  Squid is
+ *  Copyrighted (C) 2001 by the Regents of the University of
+ *  California; see the COPYRIGHT file for full details.  Squid
+ *  incorporates software developed and/or copyrighted by other
+ *  sources; see the CREDITS file for full details.
+ *
+ *  This code is copyright (C) 2007 by Treehouse Networks Ltd of
+ *  New Zealand. It is published and Lisenced as an extension of
+ *  squid under the same conditions as the main squid application.
+ *
+ *  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
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ */
+
+#ifndef SQUID_RFC2671_H
+#define SQUID_RFC2671_H
+
+#include "config.h"
+
+/* RFC2671 section 7 defines new RR type OPT as 41 */
+#define RFC1035_TYPE_OPT 41
+
+SQUIDCEXTERN int rfc2671RROptPack(char *buf, size_t sz, ssize_t edns_sz);
+
+
+#endif /* SQUID_RFC3596_H */
index dab50acdf985374f98442d5b4e9aff9986c18326..912db74f5b813b1a5db67834f36bbb9f2c3110ed 100644 (file)
@@ -47,25 +47,29 @@ SQUIDCEXTERN ssize_t rfc3596BuildAQuery(const char *hostname,
                                         char *buf,
                                         size_t sz,
                                         unsigned short qid,
-                                        rfc1035_query * query);
+                                        rfc1035_query * query,
+                                        ssize_t edns_sz);
 
 SQUIDCEXTERN ssize_t rfc3596BuildAAAAQuery(const char *hostname,
         char *buf,
         size_t sz,
         unsigned short qid,
-        rfc1035_query * query);
+        rfc1035_query * query,
+                                        ssize_t edns_sz);
 
 SQUIDCEXTERN ssize_t rfc3596BuildPTRQuery4(const struct in_addr,
         char *buf,
         size_t sz,
         unsigned short qid,
-        rfc1035_query * query);
+        rfc1035_query * query,
+                                        ssize_t edns_sz);
 
 SQUIDCEXTERN ssize_t rfc3596BuildPTRQuery6(const struct in6_addr,
         char *buf,
         size_t sz,
         unsigned short qid,
-        rfc1035_query * query);
+        rfc1035_query * query,
+                                        ssize_t edns_sz);
 
 /* RFC3596 library implements RFC1035 generic host interface */
 SQUIDCEXTERN ssize_t rfc3596BuildHostQuery(const char *hostname,
@@ -73,7 +77,8 @@ SQUIDCEXTERN ssize_t rfc3596BuildHostQuery(const char *hostname,
         size_t sz,
         unsigned short qid,
         rfc1035_query * query,
-        int qtype);
+        int qtype,
+                                        ssize_t edns_sz);
 
 /* RFC3596 section 2.1 defines new RR type AAAA as 28 */
 #define RFC1035_TYPE_AAAA 28
index fbe878a89edfcb90c7c2a4dddaca38fd2a488dff..eb3dca3b4a124ffad9574131edcea91cea547e7f 100644 (file)
@@ -68,6 +68,7 @@ libmiscutil_a_SOURCES = \
        rfc1123.c \
        rfc1738.c \
        rfc2617.c \
+       rfc2671.c \
        rfc3596.c \
        $(SNPRINTFSOURCE) \
        Splay.cc \
index cc84fa49818feb0af4ff5c5a3ed107f716543b85..55901189ddf63b21b4e1b5c4a32a4550cc03907a 100644 (file)
@@ -67,6 +67,7 @@
 #endif
 
 #include "rfc1035.h"
+#include "rfc2671.h"
 
 #define RFC1035_MAXLABELSZ 63
 #define rfc1035_unpack_error 15
@@ -344,6 +345,50 @@ rfc1035NameUnpack(const char *buf, size_t sz, unsigned int *off, unsigned short
     return 0;
 }
 
+/*
+ * rfc1035RRPack()
+ *
+ * Packs a RFC1035 Resource Record into a message buffer from 'RR'.
+ * The caller must allocate and free RR->rdata and RR->name!
+ *
+ * Updates the new message buffer.
+ *
+ * Returns the number of bytes added to the buffer or 0 for error.
+ */
+int
+rfc1035RRPack(char *buf, const size_t sz, const rfc1035_rr * RR)
+{
+    unsigned int off;
+    uint16_t s;
+    uint32_t i;
+
+    off = rfc1035NamePack(buf, sz, RR->name);
+
+    /*
+     * Make sure the remaining message has enough octets for the
+     * rest of the RR fields.
+     */
+    if ((off + sizeof(s)*3 + sizeof(i) + RR->rdlength) > sz) {
+        return 0;
+    }
+    s = htons(RR->type);
+    memcpy(buf + off, &s, sizeof(s));
+    off += sizeof(s);
+    s = htons(RR->_class);
+    memcpy(buf + off, &s, sizeof(s));
+    off += sizeof(s);
+    i = htonl(RR->ttl);
+    memcpy(buf + off, &i, sizeof(i));
+    off += sizeof(i);
+    s = htons(RR->rdlength);
+    memcpy(buf + off, &s, sizeof(s));
+    off += sizeof(s);
+    memcpy(buf + off, &(RR->rdata), RR->rdlength);
+    off += RR->rdlength;
+    assert(off <= sz);
+    return off;
+}
+
 /*
  * rfc1035RRUnpack()
  *
@@ -647,7 +692,7 @@ rfc1035MessageUnpack(const char *buf,
  * Returns the size of the query
  */
 ssize_t
-rfc1035BuildAQuery(const char *hostname, char *buf, size_t sz, unsigned short qid, rfc1035_query * query)
+rfc1035BuildAQuery(const char *hostname, char *buf, size_t sz, unsigned short qid, rfc1035_query * query, ssize_t edns_sz)
 {
     static rfc1035_message h;
     size_t offset = 0;
@@ -657,12 +702,15 @@ rfc1035BuildAQuery(const char *hostname, char *buf, size_t sz, unsigned short qi
     h.rd = 1;
     h.opcode = 0;              /* QUERY */
     h.qdcount = (unsigned int) 1;
+    h.arcount = (edns_sz > 0 ? 1 : 0);
     offset += rfc1035HeaderPack(buf + offset, sz - offset, &h);
     offset += rfc1035QuestionPack(buf + offset,
                                   sz - offset,
                                   hostname,
                                   RFC1035_TYPE_A,
                                   RFC1035_CLASS_IN);
+    if (edns_sz > 0)
+        offset += rfc2671RROptPack(buf + offset, sz - offset, edns_sz);
     if (query) {
         query->qtype = RFC1035_TYPE_A;
         query->qclass = RFC1035_CLASS_IN;
@@ -683,7 +731,7 @@ rfc1035BuildAQuery(const char *hostname, char *buf, size_t sz, unsigned short qi
  * Returns the size of the query
  */
 ssize_t
-rfc1035BuildPTRQuery(const struct in_addr addr, char *buf, size_t sz, unsigned short qid, rfc1035_query * query)
+rfc1035BuildPTRQuery(const struct in_addr addr, char *buf, size_t sz, unsigned short qid, rfc1035_query * query, ssize_t edns_sz)
 {
     static rfc1035_message h;
     size_t offset = 0;
@@ -701,12 +749,15 @@ rfc1035BuildPTRQuery(const struct in_addr addr, char *buf, size_t sz, unsigned s
     h.rd = 1;
     h.opcode = 0;              /* QUERY */
     h.qdcount = (unsigned int) 1;
+    h.arcount = (edns_sz > 0 ? 1 : 0);
     offset += rfc1035HeaderPack(buf + offset, sz - offset, &h);
     offset += rfc1035QuestionPack(buf + offset,
                                   sz - offset,
                                   rev,
                                   RFC1035_TYPE_PTR,
                                   RFC1035_CLASS_IN);
+    if (edns_sz > 0)
+        offset += rfc2671RROptPack(buf + offset, sz - offset, edns_sz);
     if (query) {
         query->qtype = RFC1035_TYPE_PTR;
         query->qclass = RFC1035_CLASS_IN;
@@ -733,10 +784,10 @@ rfc1035SetQueryID(char *buf, unsigned short qid)
 int
 main(int argc, char *argv[])
 {
-    char input[512];
-    char buf[512];
-    char rbuf[512];
-    size_t sz = 512;
+    char input[SQUID_DNS_BUFSZ];
+    char buf[SQUID_DNS_BUFSZ];
+    char rbuf[SQUID_DNS_BUFSZ];
+    size_t sz = SQUID_DNS_BUFSZ;
     unsigned short sid;
     int s;
     int rl;
@@ -756,11 +807,11 @@ main(int argc, char *argv[])
     S.sin_family = AF_INET;
     S.sin_port = htons(atoi(argv[2]));
     S.sin_addr.s_addr = inet_addr(argv[1]);
-    while (fgets(input, 512, stdin)) {
+    while (fgets(input, RFC1035_DEFAULT_PACKET_SZ, stdin)) {
         struct in_addr junk;
         strtok(input, "\r\n");
-        memset(buf, '\0', 512);
-        sz = 512;
+        memset(buf, '\0', RFC1035_DEFAULT_PACKET_SZ);
+        sz = RFC1035_DEFAULT_PACKET_SZ;
         if (inet_pton(AF_INET, input, &junk)) {
             sid = rfc1035BuildPTRQuery(junk, buf, &sz);
         } else {
@@ -780,8 +831,8 @@ main(int argc, char *argv[])
             printf("TIMEOUT\n");
             continue;
         }
-        memset(rbuf, '\0', 512);
-        rl = recv(s, rbuf, 512, 0);
+        memset(rbuf, '\0', RFC1035_DEFAULT_PACKET_SZ);
+        rl = recv(s, rbuf, RFC1035_DEFAULT_PACKET_SZ, 0);
         {
             unsigned short rid = 0;
             int i;
diff --git a/lib/rfc2671.c b/lib/rfc2671.c
new file mode 100644 (file)
index 0000000..10f6eb1
--- /dev/null
@@ -0,0 +1,20 @@
+#include "config.h"
+#include "rfc2671.h"
+#include "rfc1035.h"
+
+int
+rfc2671RROptPack(char *buf, size_t sz, ssize_t edns_sz)
+{
+    // set the OPT record correctly. base it on a macro size of the Squid DNS read buffer
+    static rfc1035_rr opt;
+
+    // EDNS OPT record says only what our DNS buffer size is so far.
+    snprintf(opt.name, RFC1035_MAXHOSTNAMESZ, ".");
+    opt.type = RFC1035_TYPE_OPT;
+    opt._class = min(edns_sz, SQUID_UDP_SO_RCVBUF-1);
+    opt.ttl = 0; // relevant?
+    opt.rdata = NULL;
+    opt.rdlength = 0;
+
+    return rfc1035RRPack(buf, sz, &opt);
+}
index c99b6ec6ac4067fca1539107ab9a6a765bb49847..d9d202fa608a0c2e8544744c3c97b6e3f1b8c99f 100644 (file)
@@ -79,6 +79,7 @@
 #endif
 
 #include "rfc3596.h"
+#include "rfc2671.h"
 
 #ifndef SQUID_RFC1035_H
 #error RFC3596 Library depends on RFC1035
@@ -93,7 +94,7 @@
  * Returns the size of the query
  */
 ssize_t
-rfc3596BuildHostQuery(const char *hostname, char *buf, size_t sz, unsigned short qid, rfc1035_query * query, int qtype)
+rfc3596BuildHostQuery(const char *hostname, char *buf, size_t sz, unsigned short qid, rfc1035_query * query, int qtype, ssize_t edns_sz)
 {
     static rfc1035_message h;
     size_t offset = 0;
@@ -103,12 +104,15 @@ rfc3596BuildHostQuery(const char *hostname, char *buf, size_t sz, unsigned short
     h.rd = 1;
     h.opcode = 0;               /* QUERY */
     h.qdcount = (unsigned int) 1;
+    h.arcount = (edns_sz > 0 ? 1 : 0);
     offset += rfc1035HeaderPack(buf + offset, sz - offset, &h);
     offset += rfc1035QuestionPack(buf + offset,
                                   sz - offset,
                                   hostname,
                                   qtype,
                                   RFC1035_CLASS_IN);
+    if (edns_sz > 0)
+        offset += rfc2671RROptPack(buf + offset, sz - offset, edns_sz);
 
     if (query) {
         query->qtype = qtype;
@@ -129,9 +133,9 @@ rfc3596BuildHostQuery(const char *hostname, char *buf, size_t sz, unsigned short
  * \return the size of the query
  */
 ssize_t
-rfc3596BuildAQuery(const char *hostname, char *buf, size_t sz, unsigned short qid, rfc1035_query * query)
+rfc3596BuildAQuery(const char *hostname, char *buf, size_t sz, unsigned short qid, rfc1035_query * query, ssize_t edns_sz)
 {
-    return rfc3596BuildHostQuery(hostname, buf, sz, qid, query, RFC1035_TYPE_A);
+    return rfc3596BuildHostQuery(hostname, buf, sz, qid, query, RFC1035_TYPE_A, edns_sz);
 }
 
 /**
@@ -143,9 +147,9 @@ rfc3596BuildAQuery(const char *hostname, char *buf, size_t sz, unsigned short qi
  * \return the size of the query
  */
 ssize_t
-rfc3596BuildAAAAQuery(const char *hostname, char *buf, size_t sz, unsigned short qid, rfc1035_query * query)
+rfc3596BuildAAAAQuery(const char *hostname, char *buf, size_t sz, unsigned short qid, rfc1035_query * query, ssize_t edns_sz)
 {
-    return rfc3596BuildHostQuery(hostname, buf, sz, qid, query, RFC1035_TYPE_AAAA);
+    return rfc3596BuildHostQuery(hostname, buf, sz, qid, query, RFC1035_TYPE_AAAA, edns_sz);
 }
 
 
@@ -158,7 +162,7 @@ rfc3596BuildAAAAQuery(const char *hostname, char *buf, size_t sz, unsigned short
  * \return the size of the query
  */
 ssize_t
-rfc3596BuildPTRQuery4(const struct in_addr addr, char *buf, size_t sz, unsigned short qid, rfc1035_query * query)
+rfc3596BuildPTRQuery4(const struct in_addr addr, char *buf, size_t sz, unsigned short qid, rfc1035_query * query, ssize_t edns_sz)
 {
     static char rev[RFC1035_MAXHOSTNAMESZ];
     unsigned int i;
@@ -170,11 +174,11 @@ rfc3596BuildPTRQuery4(const struct in_addr addr, char *buf, size_t sz, unsigned
              (i >> 16) & 255,
              (i >> 24) & 255);
 
-    return rfc3596BuildHostQuery(rev, buf, sz, qid, query, RFC1035_TYPE_PTR);
+    return rfc3596BuildHostQuery(rev, buf, sz, qid, query, RFC1035_TYPE_PTR, edns_sz);
 }
 
 ssize_t
-rfc3596BuildPTRQuery6(const struct in6_addr addr, char *buf, size_t sz, unsigned short qid, rfc1035_query * query)
+rfc3596BuildPTRQuery6(const struct in6_addr addr, char *buf, size_t sz, unsigned short qid, rfc1035_query * query, ssize_t edns_sz)
 {
     static char rev[RFC1035_MAXHOSTNAMESZ];
     const uint8_t* r = addr.s6_addr;
@@ -189,7 +193,7 @@ rfc3596BuildPTRQuery6(const struct in6_addr addr, char *buf, size_t sz, unsigned
 
     snprintf(p,10,"ip6.arpa.");
 
-    return rfc3596BuildHostQuery(rev, buf, sz, qid, query, RFC1035_TYPE_PTR);
+    return rfc3596BuildHostQuery(rev, buf, sz, qid, query, RFC1035_TYPE_PTR, edns_sz);
 }
 
 
@@ -205,13 +209,15 @@ rfc3596BuildPTRQuery6(const struct in6_addr addr, char *buf, size_t sz, unsigned
 int
 main(int argc, char *argv[])
 {
-    char input[512];
-    char buf[512];
-    char rbuf[512];
-    size_t sz = 512;
+#define PACKET_BUFSZ           1024
+    char input[PACKET_BUFSZ];
+    char buf[PACKET_BUFSZ];
+    char rbuf[PACKET_BUFSZ];
+    size_t sz = PACKET_BUFSZ;
     unsigned short sid, sidb;
     int s;
     int rl;
+    ssize_t edns_max = -1;
 
     struct sockaddr* S;
     int var = 1;
@@ -229,8 +235,11 @@ main(int argc, char *argv[])
             prefer = AF_INET;
         else if (argv[var][1] == '6')
             prefer = AF_INET6;
+        else if (argv[var][1] == 'E')
+            edns_max = atoi(argv[var++]);
         else {
-            fprintf(stderr, "usage: %s [-6|-4] ip port\n", argv[0]);
+            fprintf(stderr, "usage: %s [-6|-4] [-E packet-size] ip port\n", argv[0]);
+            fprintf(stderr, "  EDNS packets my be up to %d\n", PACKET_BUFSZ);
             return 1;
         }
 
@@ -254,7 +263,7 @@ main(int argc, char *argv[])
         ((struct sockaddr_in6 *)S)->sin6_family = AF_INET6;
         ((struct sockaddr_in6 *)S)->sin6_port = htons(atoi(argv[var+1]));
 
-        if ( ! inet_pton(AF_INET6, argv[var], &((struct sockaddr_in6 *)S)->sin6_addr.s_addr) )
+        if ( ! inet_pton(AF_INET6, argv[var], &((struct sockaddr_in6 *)S)->sin6_addr.s_addr) ) {
             perror("listen address");
         return 1;
     }
@@ -275,25 +284,25 @@ else
 }
 }
 
-while (fgets(input, 512, stdin))
+while (fgets(input, PACKET_BUFSZ, stdin))
 {
 
     struct in6_addr junk6;
 
     struct in_addr junk4;
     strtok(input, "\r\n");
-    memset(buf, '\0', 512);
-    sz = 512;
+    memset(buf, '\0', PACKET_BUFSZ);
+    sz = PACKET_BUFSZ;
 
     if (inet_pton(AF_INET6, input, &junk6)) {
-        sid = rfc1035BuildPTRQuery6(junk6, buf, &sz);
+        sid = rfc1035BuildPTRQuery6(junk6, buf, &sz, edns_max);
         sidb=0;
     } else if (inet_pton(AF_INET, input, &junk4)) {
-        sid = rfc1035BuildPTRQuery4(junk4, buf, &sz);
+        sid = rfc1035BuildPTRQuery4(junk4, buf, &sz, edns_max);
         sidb=0;
     } else {
-        sid = rfc1035BuildAAAAQuery(input, buf, &sz);
-        sidb = rfc1035BuildAQuery(input, buf, &sz);
+        sid = rfc1035BuildAAAAQuery(input, buf, &sz, edns_max);
+        sidb = rfc1035BuildAQuery(input, buf, &sz, edns_max);
     }
 
     sendto(s, buf, sz, 0, S, sizeof(*S));
@@ -314,8 +323,8 @@ while (fgets(input, 512, stdin))
         continue;
     }
 
-    memset(rbuf, '\0', 512);
-    rl = recv(s, rbuf, 512, 0);
+    memset(rbuf, '\0', PACKET_BUFSZ);
+    rl = recv(s, rbuf, PACKET_BUFSZ, 0);
     {
         unsigned short rid = 0;
         int i;
index 9af9df97c73701a794705c5399fc7d93d4de9221..85920bb2049b8fbca9dc1085b040243d5151007c 100644 (file)
@@ -9,6 +9,7 @@
 
 /* Being a C library code it is best bodily included and tested with C++ type-safe techniques. */
 #include "lib/rfc1035.c"
+#include "lib/rfc2671.c"
 
 CPPUNIT_TEST_SUITE_REGISTRATION( testRFC1035 );
 
index 87ffbf5cb2cf24207b4261695c31215791ac7d0d..7e91a7f5aa14bd2212f7e762fc0da8daad754e96 100644 (file)
@@ -145,6 +145,9 @@ static void default_all(void);
 static void defaults_if_none(void);
 static int parse_line(char *);
 static void parseBytesLine(size_t * bptr, const char *units);
+#if !USE_DNSSERVERS
+static void parseBytesLineSigned(ssize_t * bptr, const char *units);
+#endif
 static size_t parseBytesUnits(const char *unit);
 static void free_all(void);
 void requirePathnameExists(const char *name, const char *path);
@@ -1046,6 +1049,52 @@ parseBytesLine(size_t * bptr, const char *units)
         self_destruct();
 }
 
+#if !USE_DNSSERVERS
+static void
+parseBytesLineSigned(ssize_t * bptr, const char *units)
+{
+    char *token;
+    double d;
+    int m;
+    int u;
+
+    if ((u = parseBytesUnits(units)) == 0) {
+        self_destruct();
+        return;
+    }
+
+    if ((token = strtok(NULL, w_space)) == NULL) {
+        self_destruct();
+        return;
+    }
+
+    if (strcmp(token, "none") == 0 || token[0] == '-' /* -N */) {
+        *bptr = -1;
+        return;
+    }
+
+    d = xatof(token);
+
+    m = u;                     /* default to 'units' if none specified */
+
+    if (0.0 == d)
+        (void) 0;
+    else if ((token = strtok(NULL, w_space)) == NULL)
+        debugs(3, 0, "WARNING: No units on '" <<
+               config_input_line << "', assuming " <<
+               d << " " <<  units  );
+    else if ((m = parseBytesUnits(token)) == 0) {
+        self_destruct();
+        return;
+    }
+
+    *bptr = static_cast<size_t>(m * d / u);
+
+    if (static_cast<double>(*bptr) * 2 != m * d / u * 2)
+        self_destruct();
+}
+#endif
+
 static size_t
 parseBytesUnits(const char *unit)
 {
@@ -2824,6 +2873,14 @@ dump_b_size_t(StoreEntry * entry, const char *name, size_t var)
     storeAppendPrintf(entry, "%s %d %s\n", name, (int) var, B_BYTES_STR);
 }
 
+#if !USE_DNSSERVERS
+static void
+dump_b_ssize_t(StoreEntry * entry, const char *name, ssize_t var)
+{
+    storeAppendPrintf(entry, "%s %d %s\n", name, (int) var, B_BYTES_STR);
+}
+#endif
+
 #if UNUSED_CODE
 static void
 dump_kb_size_t(StoreEntry * entry, const char *name, size_t var)
@@ -2860,6 +2917,14 @@ parse_b_size_t(size_t * var)
     parseBytesLine(var, B_BYTES_STR);
 }
 
+#if !USE_DNSSERVERS
+static void
+parse_b_ssize_t(ssize_t * var)
+{
+    parseBytesLineSigned(var, B_BYTES_STR);
+}
+#endif
+
 #if UNUSED_CODE
 static void
 parse_kb_size_t(size_t * var)
@@ -2886,6 +2951,14 @@ free_size_t(size_t * var)
     *var = 0;
 }
 
+#if !USE_DNSSERVERS
+static void
+free_ssize_t(ssize_t * var)
+{
+    *var = 0;
+}
+#endif
+
 static void
 free_b_int64_t(int64_t * var)
 {
@@ -2893,6 +2966,7 @@ free_b_int64_t(int64_t * var)
 }
 
 #define free_b_size_t free_size_t
+#define free_b_ssize_t free_ssize_t
 #define free_kb_size_t free_size_t
 #define free_mb_size_t free_size_t
 #define free_gb_size_t free_size_t
index 951ec0329755e3df74f40ad1d21a7a6e58de770b..3b0e6912dc0d269cc9172616d0baf29e0f2ed079 100644 (file)
@@ -10,6 +10,7 @@ address
 authparam
 b_int64_t
 b_size_t
+b_ssize_t
 cachedir               cache_replacement_policy
 cachemgrpasswd
 ConfigAclTos
index b06bca8a4f3b52365e0e2aef0b00178e8a601101..2c850cb01b89b68dd632af7316a6ff079d9150f8 100644 (file)
@@ -6602,6 +6602,34 @@ DOC_START
        are assumed to be unavailable.
 DOC_END
 
+NAME: dns_packet_max
+TYPE: b_ssize_t
+DEFAULT: none
+LOC: Config.dns.packet_max
+IFDEF: !USE_DNSSERVERS
+DOC_START
+       Maximum number of bytes packet size to advertise via EDNS.
+       Set to "none" to disable EDNS large packet support.
+       
+       For legacy reasons DNS UDP replies will default to 512 bytes which
+       is too small for many responses. EDNS provides a means for Squid to
+       negotiate receiving larger responses back immediately without having
+       to failover with repeat requests. Responses larger than this limit
+       will retain the old behaviour of failover to TCP DNS.
+       
+       Squid has no real fixed limit internally, but allowing packet sizes
+       over 1500 bytes requires network jumbogram support and is usually not
+       necessary.
+       
+       WARNING: The RFC also indicates that some older resolvers will reply
+       with failure of the whole request if the extension is added. Some
+       resolvers have already been identified which will reply with mangled
+       EDNS response on occasion. Usually in response to many-KB jumbogram
+       sizes being advertised by Squid.
+       Squid will currently treat these both as an unable-to-resolve domain
+       even if it would be resolvable without EDNS.
+DOC_END
+
 NAME: dns_defnames
 COMMENT: on|off
 TYPE: onoff
index e338ccfeaaffd4ba719f4de20786c236993c89cc..3ca63105efba5b759698a9e8cba0cf4886520a7d 100644 (file)
 #endif
 
 #define IDNS_MAX_TRIES 20
-#define MAX_RCODE 6
+#define MAX_RCODE 17
 #define MAX_ATTEMPT 3
 static int RcodeMatrix[MAX_RCODE][MAX_ATTEMPT];
+// NP: see http://www.iana.org/assignments/dns-parameters
+static const char *Rcodes[] = {
+ /* RFC 1035 */
+         "Success",
+         "Packet Format Error",
+         "DNS Server Failure",
+         "Non-Existent Domain",
+         "Not Implemented",
+         "Query Refused",
+ /* RFC 2136 */
+         "Name Exists when it should not",
+         "RR Set Exists when it should not",
+         "RR Set that should exist does not",
+         "Server Not Authoritative for zone",
+         "Name not contained in zone",
+ /* unassigned */
+         "","","","","",
+ /* RFC 2671 */
+         "Bad OPT Version or TSIG Signature Failure"
+ };
 
 typedef struct _idns_query idns_query;
 
@@ -140,6 +160,9 @@ struct _ns {
     Ip::Address S;
     int nqueries;
     int nreplies;
+#if WHEN_EDNS_RESPONSES_ARE_PARSED
+    int last_seen_edns;
+#endif
     nsvc *vc;
 };
 
@@ -162,6 +185,34 @@ static dlink_list lru_list;
 static int event_queued = 0;
 static hash_table *idns_lookup_hash = NULL;
 
+/*
+ * Notes on EDNS:
+ *
+ * IPv4:
+ *   EDNS as specified may be sent as an additional record for any request.
+ *   early testing has revealed that it works on common devices, but cannot
+ *   be reliably used on any A or PTR requet done for IPv4 addresses.
+ *
+ * As such the IPv4 packets are still hard-coded not to contain EDNS (0)
+ *
+ * Squid design:
+ *   Squid is optimized to generate one packet and re-send it to all NS
+ *   due to this we cannot customize the EDNS size per NS.
+ *
+ * As such we take the configuration option value as fixed.
+ *
+ * FUTURE TODO:
+ *   This may not be worth doing, but if/when additional-records are parsed
+ *   we will be able to recover the OPT value specific to any one NS and
+ *   cache it. Effectively automating the tuning of EDNS advertised to the
+ *   size our active NS are capable.
+ * Default would need to start with 512 bytes RFC1035 says every NS must accept.
+ * Responses from the configured NS may cause this to be raised or turned off.
+ */
+#if WHEN_EDNS_RESPONSES_ARE_PARSED
+static int max_shared_edns = RFC1035_DEFAULT_PACKET_SZ;
+#endif
+
 static OBJH idnsStats;
 static void idnsAddNameserver(const char *buf);
 static void idnsAddPathComponent(const char *buf);
@@ -182,7 +233,7 @@ static void idnsDoSendQueryVC(nsvc *vc);
 
 static int idnsFromKnownNameserver(Ip::Address const &from);
 static idns_query *idnsFindQuery(unsigned short id);
-static void idnsGrokReply(const char *buf, size_t sz);
+static void idnsGrokReply(const char *buf, size_t sz, int from_ns);
 static PF idnsRead;
 static EVH idnsCheckQueue;
 static void idnsTickleQueue(void);
@@ -230,6 +281,10 @@ idnsAddNameserver(const char *buf)
     assert(nns < nns_alloc);
     A.SetPort(NS_DEFAULTPORT);
     nameservers[nns].S = A;
+#if WHEN_EDNS_RESPONSES_ARE_PARSED
+    nameservers[nns].last_seen_edns = RFC1035_DEFAULT_PACKET_SZ;
+    // TODO generate a test packet to probe this NS from EDNS size and ability.
+#endif
     debugs(78, 3, "idnsAddNameserver: Added nameserver #" << nns << " (" << A << ")");
     nns++;
 }
@@ -609,6 +664,11 @@ idnsStats(StoreEntry * sentry)
                           tvSubDsec(q->sent_t, current_time));
     }
 
+    if (Config.dns.packet_max > 0)
+        storeAppendPrintf(sentry, "DNS jumbo-grams: %d Bytes\n", Config.dns.packet_max);
+    else
+        storeAppendPrintf(sentry, "DNS jumbo-grams: not working\n");
+
     storeAppendPrintf(sentry, "\nNameservers:\n");
     storeAppendPrintf(sentry, "IP ADDRESS                                     # QUERIES # REPLIES\n");
     storeAppendPrintf(sentry, "---------------------------------------------- --------- ---------\n");
@@ -626,15 +686,18 @@ idnsStats(StoreEntry * sentry)
     for (i = 0; i < MAX_ATTEMPT; i++)
         storeAppendPrintf(sentry, " ATTEMPT%d", i + 1);
 
-    storeAppendPrintf(sentry, "\n");
+    storeAppendPrintf(sentry, " PROBLEM\n");
 
     for (j = 0; j < MAX_RCODE; j++) {
+        if (j > 10 && j < 16)
+            continue; // unassigned by IANA.
+
         storeAppendPrintf(sentry, "%5d", j);
 
         for (i = 0; i < MAX_ATTEMPT; i++)
             storeAppendPrintf(sentry, " %8d", RcodeMatrix[j][i]);
 
-        storeAppendPrintf(sentry, "\n");
+        storeAppendPrintf(sentry, " : %s\n",Rcodes[j]);
     }
 
     if (npc) {
@@ -953,7 +1016,7 @@ idnsDropMessage(rfc1035_message *message, idns_query *q)
 }
 
 static void
-idnsGrokReply(const char *buf, size_t sz)
+idnsGrokReply(const char *buf, size_t sz, int from_ns)
 {
     int n;
     rfc1035_message *message = NULL;
@@ -982,6 +1045,30 @@ idnsGrokReply(const char *buf, size_t sz)
         return;
     }
 
+#if WHEN_EDNS_RESPONSES_ARE_PARSED
+// TODO: actually gr the message right here.
+//     pull out the DNS meta data we need (A records, AAAA records and EDNS OPT) and store in q
+//     this is overall better than force-feeding A response with AAAA an section later anyway.
+//     AND allows us to merge AN+AR sections from both responses (one day)
+
+    if (q->edns_seen >= 0) {
+        if (max_shared_edns == nameservers[from_ns].last_seen_edns && max_shared_edns < q->edns_seen) {
+            nameservers[from_ns].last_seen_edns = q->edns_seen;
+            // the altered NS was limiting the whole group.
+            max_shared_edns = q->edns_seen;
+            // may be limited by one of the others still
+            for (int i = 0; i < nns; i++)
+                max_shared_edns = min(max_shared_edns, nameservers[i].last_seen_edns);
+        } else {
+            nameservers[from_ns].last_seen_edns = q->edns_seen;
+            // maybe reduce the global limit downwards to accomodate this NS
+            max_shared_edns = min(max_shared_edns, q->edns_seen);
+        }
+        if (max_shared_edns < RFC1035_DEFAULT_PACKET_SZ)
+            max_shared_edns = -1;
+    }
+#endif
+
     if (message->tc) {
         debugs(78, 3, HERE << "Resolver requested TC (" << q->query.name << ")");
         dlinkDelete(&q->lru, &lru_list);
@@ -1040,10 +1127,11 @@ idnsGrokReply(const char *buf, size_t sz)
             rfc1035SetQueryID(q->buf, q->id);
             if (Ip::EnableIpv6 && q->query.qtype == RFC1035_TYPE_AAAA) {
                 debugs(78, 3, "idnsGrokReply: Trying AAAA Query for " << q->name);
-                q->sz = rfc3596BuildAAAAQuery(q->name, q->buf, sizeof(q->buf), q->id, &q->query);
+                q->sz = rfc3596BuildAAAAQuery(q->name, q->buf, sizeof(q->buf), q->id, &q->query, Config.dns.packet_max);
             } else {
                 debugs(78, 3, "idnsGrokReply: Trying A Query for " << q->name);
-                q->sz = rfc3596BuildAQuery(q->name, q->buf, sizeof(q->buf), q->id, &q->query);
+                // see EDNS notes at top of file why this sends 0
+                q->sz = rfc3596BuildAQuery(q->name, q->buf, sizeof(q->buf), q->id, &q->query, 0);
             }
             idnsCacheQuery(q);
             idnsSendQuery(q);
@@ -1081,7 +1169,8 @@ idnsGrokReply(const char *buf, size_t sz)
         q->start_t = current_time;
         q->id = idnsQueryID();
         rfc1035SetQueryID(q->buf, q->id);
-        q->sz = rfc3596BuildAQuery(q->name, q->buf, sizeof(q->buf), q->id, &q->query);
+        // see EDNS notes at top of file why this sends 0
+        q->sz = rfc3596BuildAQuery(q->name, q->buf, sizeof(q->buf), q->id, &q->query, 0);
         q->need_A = false;
         idnsCacheQuery(q);
         idnsSendQuery(q);
@@ -1205,7 +1294,7 @@ idnsRead(int fd, void *data)
             continue;
         }
 
-        idnsGrokReply(rbuf, len);
+        idnsGrokReply(rbuf, len, ns);
     }
 }
 
@@ -1286,7 +1375,7 @@ idnsReadVC(int fd, char *buf, size_t len, comm_err_t flag, int xerrno, void *dat
            (int) vc->msg->contentSize() << " bytes via tcp from " <<
            nameservers[vc->ns].S << ".");
 
-    idnsGrokReply(vc->msg->buf, vc->msg->contentSize());
+    idnsGrokReply(vc->msg->buf, vc->msg->contentSize(), vc->ns);
     vc->msg->clean();
     comm_read(fd, (char *)&vc->msglen, 2 , idnsReadVCHeader, vc);
 }
@@ -1439,6 +1528,13 @@ idnsInit(void)
         init++;
     }
 
+#if WHEN_EDNS_RESPONSES_ARE_PARSED
+    if (Config.onoff.ignore_unknown_nameservers && max_shared_edns > 0) {
+        debugs(0, DBG_IMPORTANT, "ERROR: cannot negotiate EDNS with unknown nameservers. Disabling");
+        max_shared_edns = -1; // disable if we might receive random replies.
+    }
+#endif
+
     idnsRegisterWithCacheManager();
 }
 
@@ -1535,10 +1631,11 @@ idnsALookup(const char *name, IDNSCB * callback, void *data)
     }
 
     if (Ip::EnableIpv6) {
-        q->sz = rfc3596BuildAAAAQuery(q->name, q->buf, sizeof(q->buf), q->id, &q->query);
+        q->sz = rfc3596BuildAAAAQuery(q->name, q->buf, sizeof(q->buf), q->id, &q->query, Config.dns.packet_max);
         q->need_A = true;
     } else {
-        q->sz = rfc3596BuildAQuery(q->name, q->buf, sizeof(q->buf), q->id, &q->query);
+        // see EDNS notes at top of file why this sends 0
+        q->sz = rfc3596BuildAQuery(q->name, q->buf, sizeof(q->buf), q->id, &q->query, 0);
         q->need_A = false;
     }
 
@@ -1579,11 +1676,12 @@ idnsPTRLookup(const Ip::Address &addr, IDNSCB * callback, void *data)
     if (Ip::EnableIpv6 && addr.IsIPv6()) {
         struct in6_addr addr6;
         addr.GetInAddr(addr6);
-        q->sz = rfc3596BuildPTRQuery6(addr6, q->buf, sizeof(q->buf), q->id, &q->query);
+        q->sz = rfc3596BuildPTRQuery6(addr6, q->buf, sizeof(q->buf), q->id, &q->query, Config.dns.packet_max);
     } else {
         struct in_addr addr4;
         addr.GetInAddr(addr4);
-        q->sz = rfc3596BuildPTRQuery4(addr4, q->buf, sizeof(q->buf), q->id, &q->query);
+        // see EDNS notes at top of file why this sends 0
+        q->sz = rfc3596BuildPTRQuery4(addr4, q->buf, sizeof(q->buf), q->id, &q->query, 0);
     }
 
     /* PTR does not do inbound A/AAAA */
index 106b8e185ce2c54b7687dc74c5524bc61e4c7aba..b0e51a2d7eaf9ead2ff2f243f029824ab0c4937b 100644 (file)
@@ -623,6 +623,10 @@ struct SquidConfig {
 #endif
 
     int client_ip_max_connections;
+
+    struct {
+        ssize_t packet_max; ///< maximum size EDNS advertised for DNS replies.
+    } dns;
 };
 
 SQUIDCEXTERN SquidConfig Config;