commit 132dc8630e04d9ecbbacd31665f3bedce8a8e9ea
parent 8522d689c01293f8f4bad37ac1a27fffd7c9063a
Author: Christopher K. Davis <ckd@ckdhr.com>
Date: Thu, 24 Dec 2020 09:40:19 +0100
Handle DNS packets up to max EDNS response size.
Original: qmail-103.patch
Sometimes (rarely) the answer to a DNS query is larger than 512 bytes.
This is an important limit: it's the biggest response (unpatched) qmail
can understand. This threshold was originally based upon the UDP DNS
protocol definition (RFC 1025, section 4.2.1). It is still relatively
unusual for DNS responses to be bigger than that limit, even though they
are allowed to be. However, it does happen from time to time,
particularly on domains with a lot of alternatives (if each MX record is
approximately 50 bytes, you need 11 of them to cross the limit). This
patch allows DNS packets to be as big as the maximum EDNS response size:
64kb (though RFC 6891 strongly recommends capping responses at 4kb).
(Description from http://www.memoryhole.net/qmail/#oversize-dns)
Diffstat:
M | dns.c | | | 29 | +++++++++++++++++++++++------ |
1 file changed, 23 insertions(+), 6 deletions(-)
diff --git a/dns.c b/dns.c
@@ -23,10 +23,12 @@ extern int res_search();
static unsigned short getshort(c) unsigned char *c;
{ unsigned short u; u = c[0]; return (u << 8) + c[1]; }
-static union { HEADER hdr; unsigned char buf[PACKETSZ]; } response;
+static struct { unsigned char *buf; } response;
+static int responsebuflen = 0;
static int responselen;
static unsigned char *responseend;
static unsigned char *responsepos;
+static u_long saveresoptions;
static int numanswers;
static char name[MAXDNAME];
@@ -47,18 +49,33 @@ int type;
errno = 0;
if (!stralloc_copy(&glue,domain)) return DNS_MEM;
if (!stralloc_0(&glue)) return DNS_MEM;
- responselen = lookup(glue.s,C_IN,type,response.buf,sizeof(response));
+ if (!responsebuflen)
+ if (response.buf = (unsigned char *)alloc(PACKETSZ+1))
+ responsebuflen = PACKETSZ+1;
+ else return DNS_MEM;
+
+ responselen = lookup(glue.s,C_IN,type,response.buf,responsebuflen);
+ if ((responselen >= responsebuflen) ||
+ (responselen > 0 && (((HEADER *)response.buf)->tc)))
+ {
+ if (responsebuflen < 65536)
+ if (alloc_re(&response.buf, responsebuflen, 65536))
+ responsebuflen = 65536;
+ else return DNS_MEM;
+ saveresoptions = _res.options;
+ _res.options |= RES_USEVC;
+ responselen = lookup(glue.s,C_IN,type,response.buf,responsebuflen);
+ _res.options = saveresoptions;
+ }
if (responselen <= 0)
{
if (errno == ECONNREFUSED) return DNS_SOFT;
if (h_errno == TRY_AGAIN) return DNS_SOFT;
return DNS_HARD;
}
- if (responselen >= sizeof(response))
- responselen = sizeof(response);
responseend = response.buf + responselen;
responsepos = response.buf + sizeof(HEADER);
- n = ntohs(response.hdr.qdcount);
+ n = ntohs(((HEADER *)response.buf)->qdcount);
while (n-- > 0)
{
i = dn_expand(response.buf,responseend,responsepos,name,MAXDNAME);
@@ -68,7 +85,7 @@ int type;
if (i < QFIXEDSZ) return DNS_SOFT;
responsepos += QFIXEDSZ;
}
- numanswers = ntohs(response.hdr.ancount);
+ numanswers = ntohs(((HEADER *)response.buf)->ancount);
return 0;
}