Merge branch 'exim-4.96+security' into master+security
[exim.git] / src / src / dns.c
index 4e01d8661271948ad1b75b2378d7eb9554a28598..db566f2e86b54a0840d9a229e3aca3a262994beb 100644 (file)
@@ -305,7 +305,7 @@ Return: TRUE for a bad result
 static BOOL
 dnss_inc_aptr(const dns_answer * dnsa, dns_scan * dnss, unsigned delta)
 {
-return (dnss->aptr += delta) >= dnsa->answer + dnsa->answerlen;
+return (dnss->aptr += delta) > dnsa->answer + dnsa->answerlen;
 }
 
 /*************************************************
@@ -389,7 +389,7 @@ if (reset != RESET_NEXT)
       TRACE trace = "A-hdr";
       if (dnss_inc_aptr(dnsa, dnss, namelen+8)) goto null_return;
       GETSHORT(dnss->srr.size, dnss->aptr); /* size of data portion */
-      /* skip over it */
+      /* skip over it, checking for a bogus size */
       TRACE trace = "A-skip";
       if (dnss_inc_aptr(dnsa, dnss, dnss->srr.size)) goto null_return;
       }
@@ -429,10 +429,9 @@ GETLONG(dnss->srr.ttl, dnss->aptr);                /* TTL */
 GETSHORT(dnss->srr.size, dnss->aptr);          /* Size of data portion */
 dnss->srr.data = dnss->aptr;                   /* The record's data follows */
 
-/* Unchecked increment ok here since no further access on this iteration;
-will be checked on next at "R-name". */
-
-dnss->aptr += dnss->srr.size;                  /* Advance to next RR */
+/* skip over it, checking for a bogus size */
+if (dnss_inc_aptr(dnsa, dnss, dnss->srr.size))
+  goto null_return;
 
 /* Return a pointer to the dns_record structure within the dns_answer. This is
 for convenience so that the scans can use nice-looking for loops. */
@@ -802,6 +801,7 @@ dns_basic_lookup(dns_answer * dnsa, const uschar * name, int type)
 int rc;
 #ifndef STAND_ALONE
 const uschar * save_domain;
+static BOOL try_again_recursion = FALSE;
 #endif
 
 /* DNS lookup failures of any kind are cached in a tree. This is mainly so that
@@ -906,11 +906,31 @@ if (dnsa->answerlen < 0) switch (h_errno)
 
     /* Cut this out for various test programs */
 #ifndef STAND_ALONE
-    save_domain = deliver_domain;
-    deliver_domain = string_copy(name);  /* set $domain */
-    rc = match_isinlist(name, CUSS &dns_again_means_nonexist, 0,
-      &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL);
-    deliver_domain = save_domain;
+    /* Permitting dns_again_means nonexist for TLSA lookups breaks the
+    doewngrade resistance of dane, so avoid for those. */
+
+    if (type == T_TLSA)
+      rc = FAIL;
+    else
+      {
+      if (try_again_recursion)
+       {
+       log_write(0, LOG_MAIN|LOG_PANIC,
+         "dns_again_means_nonexist recursion seen for %s"
+         " (assuming nonexist)", name);
+       return dns_fail_return(name, type, dns_expire_from_soa(dnsa, type),
+                             DNS_NOMATCH);
+       }
+
+      try_again_recursion = TRUE;
+      save_domain = deliver_domain;
+      deliver_domain = string_copy(name);  /* set $domain */
+      rc = match_isinlist(name, CUSS &dns_again_means_nonexist, 0,
+       &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL);
+      deliver_domain = save_domain;
+      try_again_recursion = FALSE;
+      }
+
     if (rc != OK)
       {
       DEBUG(D_dns) debug_printf("returning DNS_AGAIN\n");