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 */