Merge "Lay the groundwork for enabling EDNS0 in queries."
diff --git a/libc/dns/net/getaddrinfo.c b/libc/dns/net/getaddrinfo.c
index db683bb..47b3308 100644
--- a/libc/dns/net/getaddrinfo.c
+++ b/libc/dns/net/getaddrinfo.c
@@ -2171,8 +2171,12 @@
int class, type;
u_char *answer;
int anslen;
+ u_int oflags;
hp = (HEADER *)(void *)t->answer;
+ oflags = res->_flags;
+
+again:
hp->rcode = NOERROR; /* default */
/* make it easier... */
@@ -2188,7 +2192,8 @@
n = res_nmkquery(res, QUERY, name, class, type, NULL, 0, NULL,
buf, sizeof(buf));
#ifdef RES_USE_EDNS0
- if (n > 0 && (res->options & RES_USE_EDNS0) != 0)
+ if (n > 0 && (res->_flags & RES_F_EDNS0ERR) == 0 &&
+ (res->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0)
n = res_nopt(res, n, buf, sizeof(buf), anslen);
#endif
if (n <= 0) {
@@ -2213,6 +2218,18 @@
if (n < 0 || hp->rcode != NOERROR || ntohs(hp->ancount) == 0) {
rcode = hp->rcode; /* record most recent error */
+#ifdef RES_USE_EDNS0
+ /* if the query choked with EDNS0, retry without EDNS0 */
+ if ((res->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0 &&
+ ((oflags ^ res->_flags) & RES_F_EDNS0ERR) != 0) {
+ res->_flags |= RES_F_EDNS0ERR;
+#ifdef DEBUG
+ if (res->options & RES_DEBUG)
+ printf(";; res_nquery: retry without EDNS0\n");
+#endif
+ goto again;
+ }
+#endif
#ifdef DEBUG
if (res->options & RES_DEBUG)
printf(";; rcode = %u, ancount=%u\n", hp->rcode,
diff --git a/libc/dns/resolv/res_cache.c b/libc/dns/resolv/res_cache.c
index 9c65570..dda8694 100644
--- a/libc/dns/resolv/res_cache.c
+++ b/libc/dns/resolv/res_cache.c
@@ -591,12 +591,12 @@
/* QR must be set to 0, opcode must be 0 and AA must be 0 */
/* RA, Z, and RCODE must be 0 */
- if ((p[2] & 0xFC) != 0 || p[3] != 0) {
+ if ((p[2] & 0xFC) != 0 || (p[3] & 0xCF) != 0) {
XLOG("query packet flags unsupported");
return 0;
}
- /* Note that we ignore the TC and RD bits here for the
+ /* Note that we ignore the TC, RD, CD, and AD bits here for the
* following reasons:
*
* - there is no point for a query packet sent to a server
@@ -606,11 +606,11 @@
* _resolv_cache_add. We should not freak out if this
* is the case.
*
- * - we consider that the result from a RD=0 or a RD=1
- * query might be different, hence that the RD bit
+ * - we consider that the result from a query might depend on
+ * the RD, AD, and CD bits, so these bits
* should be used to differentiate cached result.
*
- * this implies that RD is checked when hashing or
+ * this implies that these bits are checked when hashing or
* comparing query packets, but not TC
*/
@@ -620,7 +620,7 @@
dnCount = (p[8] << 8) | p[9];
arCount = (p[10]<< 8) | p[11];
- if (anCount != 0 || dnCount != 0 || arCount != 0) {
+ if (anCount != 0 || dnCount != 0 || arCount > 1) {
XLOG("query packet contains non-query records");
return 0;
}
@@ -817,12 +817,26 @@
}
static unsigned
+_dnsPacket_hashRR( DnsPacket* packet, unsigned hash )
+{
+ int rdlength;
+ hash = _dnsPacket_hashQR(packet, hash);
+ hash = _dnsPacket_hashBytes(packet, 4, hash); /* TTL */
+ rdlength = _dnsPacket_readInt16(packet);
+ hash = _dnsPacket_hashBytes(packet, rdlength, hash); /* RDATA */
+ return hash;
+}
+
+static unsigned
_dnsPacket_hashQuery( DnsPacket* packet )
{
unsigned hash = FNV_BASIS;
- int count;
+ int count, arcount;
_dnsPacket_rewind(packet);
+ /* ignore the ID */
+ _dnsPacket_skip(packet, 2);
+
/* we ignore the TC bit for reasons explained in
* _dnsPacket_checkQuery().
*
@@ -832,19 +846,29 @@
*/
hash = hash*FNV_MULT ^ (packet->base[2] & 1);
- /* assume: other flags are 0 */
- _dnsPacket_skip(packet, 4);
+ /* mark the first header byte as processed */
+ _dnsPacket_skip(packet, 1);
+
+ /* process the second header byte */
+ hash = _dnsPacket_hashBytes(packet, 1, hash);
/* read QDCOUNT */
count = _dnsPacket_readInt16(packet);
- /* assume: ANcount, NScount, ARcount are 0 */
- _dnsPacket_skip(packet, 6);
+ /* assume: ANcount and NScount are 0 */
+ _dnsPacket_skip(packet, 4);
+
+ /* read ARCOUNT */
+ arcount = _dnsPacket_readInt16(packet);
/* hash QDCOUNT QRs */
for ( ; count > 0; count-- )
hash = _dnsPacket_hashQR(packet, hash);
+ /* hash ARCOUNT RRs */
+ for ( ; arcount > 0; arcount-- )
+ hash = _dnsPacket_hashRR(packet, hash);
+
return hash;
}
@@ -929,9 +953,28 @@
}
static int
+_dnsPacket_isEqualRR( DnsPacket* pack1, DnsPacket* pack2 )
+{
+ int rdlength1, rdlength2;
+ /* compare query + TTL */
+ if ( !_dnsPacket_isEqualQR(pack1, pack2) ||
+ !_dnsPacket_isEqualBytes(pack1, pack2, 4) )
+ return 0;
+
+ /* compare RDATA */
+ rdlength1 = _dnsPacket_readInt16(pack1);
+ rdlength2 = _dnsPacket_readInt16(pack2);
+ if ( rdlength1 != rdlength2 ||
+ !_dnsPacket_isEqualBytes(pack1, pack2, rdlength1) )
+ return 0;
+
+ return 1;
+}
+
+static int
_dnsPacket_isEqualQuery( DnsPacket* pack1, DnsPacket* pack2 )
{
- int count1, count2;
+ int count1, count2, arcount1, arcount2;
/* compare the headers, ignore most fields */
_dnsPacket_rewind(pack1);
@@ -943,7 +986,12 @@
return 0;
}
- /* assume: other flags are all 0 */
+ if (pack1->base[3] != pack2->base[3]) {
+ XLOG("different CD or AD");
+ return 0;
+ }
+
+ /* mark ID and header bytes as compared */
_dnsPacket_skip(pack1, 4);
_dnsPacket_skip(pack2, 4);
@@ -955,9 +1003,17 @@
return 0;
}
- /* assume: ANcount, NScount and ARcount are all 0 */
- _dnsPacket_skip(pack1, 6);
- _dnsPacket_skip(pack2, 6);
+ /* assume: ANcount and NScount are 0 */
+ _dnsPacket_skip(pack1, 4);
+ _dnsPacket_skip(pack2, 4);
+
+ /* compare ARCOUNT */
+ arcount1 = _dnsPacket_readInt16(pack1);
+ arcount2 = _dnsPacket_readInt16(pack2);
+ if (arcount1 != arcount2 || arcount1 < 0) {
+ XLOG("different ARCOUNT");
+ return 0;
+ }
/* compare the QDCOUNT QRs */
for ( ; count1 > 0; count1-- ) {
@@ -966,6 +1022,14 @@
return 0;
}
}
+
+ /* compare the ARCOUNT RRs */
+ for ( ; arcount1 > 0; arcount1-- ) {
+ if (!_dnsPacket_isEqualRR(pack1, pack2)) {
+ XLOG("different additional RR");
+ return 0;
+ }
+ }
return 1;
}
diff --git a/libc/dns/resolv/res_mkquery.c b/libc/dns/resolv/res_mkquery.c
index 6fb1957..c73d588 100644
--- a/libc/dns/resolv/res_mkquery.c
+++ b/libc/dns/resolv/res_mkquery.c
@@ -145,6 +145,7 @@
hp->id = htons(res_randomid());
hp->opcode = op;
hp->rd = (statp->options & RES_RECURSE) != 0U;
+ hp->ad = (statp->options & RES_USE_DNSSEC) != 0U;
hp->rcode = NOERROR;
cp = buf + HFIXEDSZ;
ep = buf + buflen;
@@ -253,7 +254,9 @@
ns_put16(T_OPT, cp); /* TYPE */
cp += INT16SZ;
- ns_put16(anslen & 0xffff, cp); /* CLASS = UDP payload size */
+ if (anslen > 0xffff)
+ anslen = 0xffff;
+ ns_put16(anslen, cp); /* CLASS = UDP payload size */
cp += INT16SZ;
*cp++ = NOERROR; /* extended RCODE */
*cp++ = 0; /* EDNS version */