=== modified file 'include/rfc1035.h'
--- include/rfc1035.h	2010-05-09 14:21:38 +0000
+++ include/rfc1035.h	2010-09-22 04:47:11 +0000
@@ -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 @@
                                         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 @@
                                      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 */

=== added file 'include/rfc2671.h'
--- include/rfc2671.h	1970-01-01 00:00:00 +0000
+++ include/rfc2671.h	2010-09-20 09:42:41 +0000
@@ -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 */

=== modified file 'include/rfc3596.h'
--- include/rfc3596.h	2010-05-09 14:21:38 +0000
+++ include/rfc3596.h	2010-09-20 09:38:17 +0000
@@ -47,25 +47,29 @@
                                         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 @@
         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

=== modified file 'include/xstrto.h'
--- include/xstrto.h	2010-10-06 03:50:45 +0000
+++ include/xstrto.h	2010-10-07 09:49:12 +0000
@@ -3,7 +3,7 @@
 
 #include "config.h"
 
-#ifdef HAVE_STDBOOL_H
+#if HAVE_STDBOOL_H
 #include <stdbool.h>
 #endif
 

=== modified file 'lib/Makefile.am'
--- lib/Makefile.am	2010-10-06 03:50:45 +0000
+++ lib/Makefile.am	2010-10-06 06:08:23 +0000
@@ -64,6 +64,7 @@
 	rfc1123.c \
 	rfc1738.c \
 	rfc2617.c \
+	rfc2671.c \
 	rfc3596.c \
 	$(SNPRINTFSOURCE) \
 	Splay.cc \

=== modified file 'lib/rfc1035.c'
--- lib/rfc1035.c	2010-08-23 13:15:48 +0000
+++ lib/rfc1035.c	2010-10-07 11:18:12 +0000
@@ -67,6 +67,7 @@
 #endif
 
 #include "rfc1035.h"
+#include "rfc2671.h"
 
 #define RFC1035_MAXLABELSZ 63
 #define rfc1035_unpack_error 15
@@ -345,6 +346,50 @@
 }
 
 /*
+ * 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()
  *
  * Unpacks a RFC1035 Resource Record into 'RR' from a message buffer.
@@ -647,7 +692,7 @@
  * 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 @@
     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 @@
  * 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 @@
     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 @@
 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 @@
     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 @@
             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;

=== added file 'lib/rfc2671.c'
--- lib/rfc2671.c	1970-01-01 00:00:00 +0000
+++ lib/rfc2671.c	2010-09-21 14:29:24 +0000
@@ -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);
+}

=== modified file 'lib/rfc3596.c'
--- lib/rfc3596.c	2010-05-09 14:21:38 +0000
+++ lib/rfc3596.c	2010-09-20 13:21:27 +0000
@@ -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 @@
     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 @@
  * \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 @@
  * \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 @@
  * \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 @@
              (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 @@
 
     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 @@
 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 @@
             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 @@
         ((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 @@
 }
 }
 
-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 @@
         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;

=== modified file 'src/cache_cf.cc'
--- src/cache_cf.cc	2010-10-06 13:03:11 +0000
+++ src/cache_cf.cc	2010-10-07 11:21:41 +0000
@@ -145,6 +145,7 @@
 static void defaults_if_none(void);
 static int parse_line(char *);
 static void parseBytesLine(size_t * bptr, const char *units);
+static void parseBytesLineSigned(ssize_t * bptr, const char *units);
 static size_t parseBytesUnits(const char *unit);
 static void free_all(void);
 void requirePathnameExists(const char *name, const char *path);
@@ -1046,6 +1047,50 @@
         self_destruct();
 }
 
+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();
+}
+
 static size_t
 parseBytesUnits(const char *unit)
 {
@@ -2812,6 +2857,12 @@
     storeAppendPrintf(entry, "%s %d %s\n", name, (int) var, B_BYTES_STR);
 }
 
+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);
+}
+
 #if UNUSED_CODE
 static void
 dump_kb_size_t(StoreEntry * entry, const char *name, size_t var)
@@ -2848,6 +2899,12 @@
     parseBytesLine(var, B_BYTES_STR);
 }
 
+static void
+parse_b_ssize_t(ssize_t * var)
+{
+    parseBytesLineSigned(var, B_BYTES_STR);
+}
+
 #if UNUSED_CODE
 static void
 parse_kb_size_t(size_t * var)
@@ -2875,12 +2932,19 @@
 }
 
 static void
+free_ssize_t(ssize_t * var)
+{
+    *var = 0;
+}
+
+static void
 free_b_int64_t(int64_t * var)
 {
     *var = 0;
 }
 
 #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

=== modified file 'src/cf.data.depend'
--- src/cf.data.depend	2010-10-06 03:50:45 +0000
+++ src/cf.data.depend	2010-10-06 06:08:23 +0000
@@ -10,6 +10,7 @@
 authparam
 b_int64_t
 b_size_t
+b_ssize_t
 cachedir		cache_replacement_policy
 cachemgrpasswd
 ConfigAclTos

=== modified file 'src/cf.data.pre'
--- src/cf.data.pre	2010-10-06 03:50:45 +0000
+++ src/cf.data.pre	2010-10-06 10:10:02 +0000
@@ -6598,6 +6598,34 @@
 	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

=== modified file 'src/dns_internal.cc'
--- src/dns_internal.cc	2010-09-10 22:01:36 +0000
+++ src/dns_internal.cc	2010-10-07 11:55:31 +0000
@@ -85,9 +85,29 @@
 #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 @@
     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 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 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 @@
     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 @@
                           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 @@
     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 @@
 }
 
 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 @@
         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 @@
             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 @@
         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 @@
             continue;
         }
 
-        idnsGrokReply(rbuf, len);
+        idnsGrokReply(rbuf, len, ns);
     }
 }
 
@@ -1286,7 +1375,7 @@
            (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 @@
         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 @@
     }
 
     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 @@
     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 */

=== modified file 'src/structs.h'
--- src/structs.h	2010-10-06 03:50:45 +0000
+++ src/structs.h	2010-10-06 06:08:23 +0000
@@ -623,6 +623,10 @@
 #endif
 
     int client_ip_max_connections;
+
+    struct {
+        ssize_t packet_max; ///< maximum size EDNS advertised for DNS replies.
+    } dns;
 };
 
 SQUIDCEXTERN SquidConfig Config;


