[exim-cvs] Support TTL from SOA for NXDOMAIN & NODATA cache …

Top Page
Delete this message
Reply to this message
Author: Exim Git Commits Mailing List
Date:  
To: exim-cvs
Subject: [exim-cvs] Support TTL from SOA for NXDOMAIN & NODATA cache entries. Bug 1395
Gitweb: https://git.exim.org/exim.git/commitdiff/7d8d08c484958a90f5d5744894b9bc2f723bee4e
Commit:     7d8d08c484958a90f5d5744894b9bc2f723bee4e
Parent:     2944124ccb62cbf64e44bc8e0894fb30307514da
Author:     Jeremy Harris <jgh146exb@???>
AuthorDate: Sun Sep 1 19:44:31 2019 +0100
Committer:  Jeremy Harris <jgh146exb@???>
CommitDate: Sun Sep 1 19:54:04 2019 +0100


    Support TTL from SOA for NXDOMAIN & NODATA cache entries.  Bug 1395
---
 doc/doc-txt/ChangeLog            |  15 ++--
 src/src/dns.c                    | 173 ++++++++++++++++++++++++++++++++-------
 src/src/os.c                     |   2 +-
 src/src/search.c                 |   6 +-
 src/src/structs.h                |   6 +-
 test/confs/0183                  |  16 ++++
 test/dnszones-src/db.example.com |   3 +
 test/scripts/0000-Basic/0183     |   5 ++
 test/src/fakens.c                |  23 +++---
 test/stderr/0002                 |   1 +
 test/stderr/0094                 |   2 +
 test/stderr/0183                 | 120 +++++++++++++++++++++++++++
 test/stderr/0277                 |   8 ++
 test/stderr/0278                 |   3 +
 test/stderr/0361                 |   3 +
 test/stderr/0381                 |   2 +
 test/stderr/0419                 |   2 +
 test/stderr/0463                 |   6 ++
 test/stderr/0469                 |   1 +
 test/stderr/0545                 |   2 +
 test/stderr/1006                 |   4 +
 test/stderr/2201                 |   1 +
 test/stderr/2202                 |   4 +
 test/stderr/4802                 |   2 +
 test/stderr/4803                 |   2 +
 test/stdout/0183                 |   2 +
 26 files changed, 364 insertions(+), 50 deletions(-)


diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog
index 158871b..1ce151f 100644
--- a/doc/doc-txt/ChangeLog
+++ b/doc/doc-txt/ChangeLog
@@ -17,9 +17,6 @@ JH/02 OpenSSL: Suppress the sending of (stateful) TLS1.3 session tickets.

 JH/03 Debug output for ACL now gives the config file name and line number for
       each verb.
-HS/01 Fix handling of very log lines in -H files. If a -<key> <value>
-      line caused the extension of big_buffer, the following lines where
-      ignored.


JH/04 The default received_header_text now uses the RFC 8314 tls cipher clause.

@@ -73,10 +70,10 @@ HS/01 Bug 2390: Use message_id for tempfile creation to avoid races in a
       systems which restrict the file name length to lower values.
       (It was "hdr.$pid".)


-HS/01 Bug 2390: Use message_id for tempfile creation to avoid races in a
+HS/02 Bug 2390: Use message_id for tempfile creation to avoid races in a
       shared (NFS) environment.


-HS/02 Bug 2392: exigrep does case sensitive *option* processing (as it
+HS/03 Bug 2392: exigrep does case sensitive *option* processing (as it
       did for all versions <4.90). Notably -M, -m, --invert, -I may be
       affected.


@@ -176,6 +173,14 @@ JH/36 Have the general router option retry_use_local_part default to true when
 JH/37 Appendfile: when evaluating quota use (non-quota_size_regex) take the file
       link count into consideration.


+HS/04 Fix handling of very log lines in -H files. If a -<key> <value> line
+      caused the extension of big_buffer, the following lines were ignored.
+
+JH/38 Bug 1395: Teach the DNS negative-cache about TTL value from the SOA in
+      accordance with RFC 2308.  Previously there was no expiry, so a longlived
+      receive process (eg. due to ACL delays) versus a short SOA value could
+      surprise.
+


 Exim version 4.92
 -----------------
diff --git a/src/src/dns.c b/src/src/dns.c
index e384597..b309207 100644
--- a/src/src/dns.c
+++ b/src/src/dns.c
@@ -435,6 +435,7 @@ dnss->aptr += dnss->srr.size;            /* Advance to next RR */
 /* 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. */


+TRACE debug_printf("%s: return %s\n", __FUNCTION__, dns_text_type(dnss->srr.type));
return &dnss->srr;

 null_return:
@@ -593,6 +594,10 @@ static void
 dns_fail_tag(uschar * buf, const uschar * name, int dns_type)
 {
 res_state resp = os_get_dns_resolver_res();
+
+/*XX buf needs to be 255 +1 + (max(typetext) == 5) +1 + max(chars_for_long-max) +1
+We truncate the name here for safety... could use a dynamic string. */
+
 sprintf(CS buf, "%.255s-%s-%lx", name, dns_text_type(dns_type),
   (unsigned long) resp->options);
 }
@@ -606,21 +611,140 @@ caching.
 Arguments:
   name       the domain name
   type       the lookup type
+  expiry     time TTL expires, or zero for unlimited
   rc         the return code


 Returns:     the return code
 */


+/*XXX the derivation of this value needs explaining */
+#define DNS_FAILTAG_MAX 290
+
+static int
+dns_fail_return(const uschar * name, int type, time_t expiry, int rc)
+{
+uschar node_name[DNS_FAILTAG_MAX];
+tree_node * previous, * new;
+expiring_data * e;
+
+dns_fail_tag(node_name, name, type);
+if ((previous = tree_search(tree_dns_fails, node_name)))
+  e = previous->data.ptr;
+else
+  {
+  new = store_get_perm(
+    sizeof(tree_node) + DNS_FAILTAG_MAX + sizeof(expiring_data), is_tainted(name));
+
+  dns_fail_tag(new->name, name, type);
+  e = (expiring_data *)((char *)new + sizeof(tree_node) + DNS_FAILTAG_MAX);
+  new->data.ptr = e;
+  (void)tree_insertnode(&tree_dns_fails, new);
+  }
+
+DEBUG(D_dns) debug_printf(" %s neg-cache entry for %s, ttl %d\n",
+  previous ? "update" : "writing",
+  node_name, expiry ? (int)(expiry - time(NULL)) : -1);
+e->expiry = expiry;
+e->data.val = rc;
+return rc;
+}
+
+
+/* Return the cached result of a known-bad lookup, or -1.
+*/
 static int
-dns_return(const uschar * name, int type, int rc)
+dns_fail_cache_hit(const uschar * name, int type)
 {
-tree_node *node = store_get_perm(sizeof(tree_node) + 290, TRUE);
-dns_fail_tag(node->name, name, type);
-node->data.val = rc;
-(void)tree_insertnode(&tree_dns_fails, node);
+uschar node_name[DNS_FAILTAG_MAX];
+tree_node * previous;
+expiring_data * e;
+int val, rc;
+
+dns_fail_tag(node_name, name, type);
+if (!(previous = tree_search(tree_dns_fails, node_name)))
+  return -1;
+
+e = previous->data.ptr;
+val = e->data.val;
+rc = e->expiry && e->expiry <= time(NULL) ? -1 : val;
+
+DEBUG(D_dns) debug_printf("DNS lookup of %.255s-%s: %scached value %s%s\n",
+  name, dns_text_type(type),
+  rc == -1 ? "" : "using ",
+    val == DNS_NOMATCH ? "DNS_NOMATCH" :
+    val == DNS_NODATA ? "DNS_NODATA" :
+    val == DNS_AGAIN ? "DNS_AGAIN" :
+    val == DNS_FAIL ? "DNS_FAIL" : "??",
+  rc == -1 ? " past valid time" : "");
+
 return rc;
 }


+
+
+/* Return the TTL suitable for an NXDOMAIN result, which is given
+in the SOA.  We hope that one was returned in the lookup, and do not
+bother doing a separate lookup; if not found return a forever TTL.
+*/
+
+static time_t
+dns_expire_from_soa(dns_answer * dnsa)
+{
+const HEADER * h = (const HEADER *)dnsa->answer;
+dns_scan dnss;
+
+/* This is really gross. The successful return value from res_search() is
+the packet length, which is stored in dnsa->answerlen. If we get a
+negative DNS reply then res_search() returns -1, which causes the bounds
+checks for name decompression to fail when it is treated as a packet
+length, which in turn causes the authority search to fail. The correct
+packet length has been lost inside libresolv, so we have to guess a
+replacement value. (The only way to fix this properly would be to
+re-implement res_search() and res_query() so that they don't muddle their
+success and packet length return values.) For added safety we only reset
+the packet length if the packet header looks plausible. */
+
+if (  h->qr == 1 && h->opcode == QUERY && h->tc == 0
+   && (h->rcode == NOERROR || h->rcode == NXDOMAIN)
+   && (ntohs(h->qdcount) == 1 || f.running_in_test_harness)
+   && ntohs(h->ancount) == 0
+   && ntohs(h->nscount) >= 1)
+      dnsa->answerlen = sizeof(dnsa->answer);
+
+for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_AUTHORITY);
+     rr; rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)
+    ) if (rr->type == T_SOA)
+  {
+  const uschar * p = rr->data;
+  uschar discard_buf[256];
+  int len;
+  unsigned long ttl;
+
+  /* Skip the mname & rname strings */
+
+  if ((len = dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen,
+      p, (DN_EXPAND_ARG4_TYPE)discard_buf, 256)) < 0)
+    break;
+  p += len;
+  if ((len = dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen,
+      p, (DN_EXPAND_ARG4_TYPE)discard_buf, 256)) < 0)
+    break;
+  p += len;
+
+  /* Skip the SOA serial, refresh, retry & expire.  Grab the TTL */
+
+  if (p > dnsa->answer + dnsa->answerlen - 5 * NS_INT32SZ)
+    break;
+  p += 4 * NS_INT32SZ;
+  GETLONG(ttl, p);
+
+  return time(NULL) + ttl;
+  }
+DEBUG(D_dns) debug_printf("DNS: no SOA record found for neg-TTL\n");
+return 0;
+}
+
+
 /*************************************************
 *              Do basic DNS lookup               *
 *************************************************/
@@ -651,32 +775,21 @@ Returns:    DNS_SUCCEED   successful lookup
 */


int
-dns_basic_lookup(dns_answer *dnsa, const uschar *name, int type)
+dns_basic_lookup(dns_answer * dnsa, const uschar * name, int type)
{
+int rc;
#ifndef STAND_ALONE
-int rc = -1;
-const uschar *save_domain;
+const uschar * save_domain;
#endif

-tree_node *previous;
-uschar node_name[290];
-
/* DNS lookup failures of any kind are cached in a tree. This is mainly so that
a timeout on one domain doesn't happen time and time again for messages that
have many addresses in the same domain. We rely on the resolver and name server
-caching for successful lookups. */
+caching for successful lookups.
+*/

-dns_fail_tag(node_name, name, type);
-if ((previous = tree_search(tree_dns_fails, node_name)))
-  {
-  DEBUG(D_dns) debug_printf("DNS lookup of %.255s-%s: using cached value %s\n",
-    name, dns_text_type(type),
-      previous->data.val == DNS_NOMATCH ? "DNS_NOMATCH" :
-      previous->data.val == DNS_NODATA ? "DNS_NODATA" :
-      previous->data.val == DNS_AGAIN ? "DNS_AGAIN" :
-      previous->data.val == DNS_FAIL ? "DNS_FAIL" : "??");
-  return previous->data.val;
-  }
+if ((rc = dns_fail_cache_hit(name, type)) > 0)
+  return rc;


 #ifdef SUPPORT_I18N
 /* Convert all names to a-label form before doing lookup */
@@ -779,7 +892,7 @@ if (dnsa->answerlen < 0) switch (h_errno)
   case HOST_NOT_FOUND:
     DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave HOST_NOT_FOUND\n"
       "returning DNS_NOMATCH\n", name, dns_text_type(type));
-    return dns_return(name, type, DNS_NOMATCH);
+    return dns_fail_return(name, type, dns_expire_from_soa(dnsa), DNS_NOMATCH);


   case TRY_AGAIN:
     DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave TRY_AGAIN\n",
@@ -795,30 +908,30 @@ if (dnsa->answerlen < 0) switch (h_errno)
     if (rc != OK)
       {
       DEBUG(D_dns) debug_printf("returning DNS_AGAIN\n");
-      return dns_return(name, type, DNS_AGAIN);
+      return dns_fail_return(name, type, 0, DNS_AGAIN);
       }
     DEBUG(D_dns) debug_printf("%s is in dns_again_means_nonexist: returning "
       "DNS_NOMATCH\n", name);
-    return dns_return(name, type, DNS_NOMATCH);
+    return dns_fail_return(name, type, dns_expire_from_soa(dnsa), DNS_NOMATCH);


 #else   /* For stand-alone tests */
-    return dns_return(name, type, DNS_AGAIN);
+    return dns_fail_return(name, type, 0, DNS_AGAIN);
 #endif


   case NO_RECOVERY:
     DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave NO_RECOVERY\n"
       "returning DNS_FAIL\n", name, dns_text_type(type));
-    return dns_return(name, type, DNS_FAIL);
+    return dns_fail_return(name, type, 0, DNS_FAIL);


   case NO_DATA:
     DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave NO_DATA\n"
       "returning DNS_NODATA\n", name, dns_text_type(type));
-    return dns_return(name, type, DNS_NODATA);
+    return dns_fail_return(name, type, dns_expire_from_soa(dnsa), DNS_NODATA);


   default:
     DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave unknown DNS error %d\n"
       "returning DNS_FAIL\n", name, dns_text_type(type), h_errno);
-    return dns_return(name, type, DNS_FAIL);
+    return dns_fail_return(name, type, 0, DNS_FAIL);
   }


DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) succeeded\n",
diff --git a/src/src/os.c b/src/src/os.c
index c44bd3e..ae9c604 100644
--- a/src/src/os.c
+++ b/src/src/os.c
@@ -831,7 +831,7 @@ return type.
res_state
os_get_dns_resolver_res(void)
{
- return &_res;
+return &_res;
}

 #endif /* OS_GET_DNS_RESOLVER_RES */
diff --git a/src/src/search.c b/src/src/search.c
index df10f77..9d1a10a 100644
--- a/src/src/search.c
+++ b/src/src/search.c
@@ -492,7 +492,7 @@ if (  (t = tree_search(c->item_cache, keystring))
    && (!(e = t->data.ptr)->expiry || e->expiry > time(NULL))
    )
   { /* Data was in the cache already; set the pointer from the tree node */
-  data = e->ptr;
+  data = e->data.ptr;
   DEBUG(D_lookup) debug_printf_indent("cached data used for lookup of %s%s%s\n",
     keystring,
     filename ? US"\n  in " : US"", filename ? filename : US"");
@@ -532,13 +532,13 @@ else
     if (t)    /* Previous, out-of-date cache entry.  Update with the */
       {     /* new result and forget the old one */
       e->expiry = do_cache == UINT_MAX ? 0 : time(NULL)+do_cache;
-      e->ptr = data;
+      e->data.ptr = data;
       }
     else
       {
       e = store_get(sizeof(expiring_data) + sizeof(tree_node) + len, is_tainted(keystring));
       e->expiry = do_cache == UINT_MAX ? 0 : time(NULL)+do_cache;
-      e->ptr = data;
+      e->data.ptr = data;
       t = (tree_node *)(e+1);
       memcpy(t->name, keystring, len);
       t->data.ptr = e;
diff --git a/src/src/structs.h b/src/src/structs.h
index bc6edc5..338dccb 100644
--- a/src/src/structs.h
+++ b/src/src/structs.h
@@ -728,7 +728,11 @@ for the lookups cache */


 typedef struct expiring_data {
   time_t expiry;          /* if nonzero, data invalid after this time */
-  void   *ptr;              /* pointer to data */
+  union
+    {
+    void  *ptr;                   /* pointer to data */
+    int val;                      /* or integer data */
+    } data;
 } expiring_data;


/* Structure for holding the handle and the cached last lookup for searches.
diff --git a/test/confs/0183 b/test/confs/0183
index 6699237..8cd4c82 100644
--- a/test/confs/0183
+++ b/test/confs/0183
@@ -10,6 +10,15 @@ dns_ipv4_lookup = *
queue_run_in_order


+# ----- ACL -----
+
+begin acl
+
+delay:
+  accept
+    delay = 3s
+    message = y
+
 # ----- Routers -----


begin routers
@@ -31,6 +40,13 @@ useryz:

 lookuphost:
   driver = dnslookup
+  local_parts = !userd
+  transport = smtp
+  no_more
+
+delay:
+  driver = dnslookup
+  condition =    ${acl {delay}}
   transport = smtp
   no_more


diff --git a/test/dnszones-src/db.example.com b/test/dnszones-src/db.example.com
index b9aca04..683772f 100644
--- a/test/dnszones-src/db.example.com
+++ b/test/dnszones-src/db.example.com
@@ -16,6 +16,9 @@
; the use of V4NET and V6NET. These networks should be such that no real
; host ever uses them.

+; really short neg-cache interval, for testing NXDOMAIN caching
+example.com.     SOA     exim.test.ex. hostmaster.exim.test.ex 1430683638 1200 120 604800 2
+
 example.com.     NS      exim.example.com.


; The real example.com has an SPF record; duplicate that here
diff --git a/test/scripts/0000-Basic/0183 b/test/scripts/0000-Basic/0183
index 7dd6ff5..cf7600e 100644
--- a/test/scripts/0000-Basic/0183
+++ b/test/scripts/0000-Basic/0183
@@ -11,3 +11,8 @@ exim -d -bt userx@??? abcd@??? abcd@??? user
1
exim -d -bt srv@??? srv@???
****
+#
+# Set up a negative-cache entry with a short ttl, check it expires
+2
+exim -d -bt userx@??? userd@???
+****
diff --git a/test/src/fakens.c b/test/src/fakens.c
index 583b012..6d8a99d 100644
--- a/test/src/fakens.c
+++ b/test/src/fakens.c
@@ -59,7 +59,7 @@ as such then the response will have the "AD" bit set.

Any DNS record line can be prefixed with "NXDOMAIN ";
The record will be ignored (but the prefix set still applied);
-This lets us return a DNSSEC NXDOMAIN.
+This lets us return a DNSSEC NXDOMAIN (=> HOST_NOT_FOUND).

Any DNS record line can be prefixed with "AA "
if all the records found by a lookup are marked
@@ -763,22 +763,23 @@ if (zonefile == NULL)

(void)sprintf(CS buffer, "%s/dnszones/%s", argv[1], zonefile);

-/* Initialize the start of the response packet. We don't have to fake up
-everything, because we know that Exim will look only at the answer and
-additional section parts. */
+/* Initialize the start of the response packet. */

memset(packet, 0, 12);
pk += 12;

/* Open the zone file. */

-f = fopen(CS buffer, "r");
-if (f == NULL)
+if (!(f = fopen(CS buffer, "r")))
{
fprintf(stderr, "fakens: failed to open %s: %s\n", buffer, strerror(errno));
return NO_RECOVERY;
}

+header->qr = 1;     /* query */
+header->opcode = QUERY; /* standard query */
+header->tc = 0;     /* no trucation */
+
 /* Find the records we want, and add them to the result. */


count = 0;
@@ -789,12 +790,14 @@ header->ancount = htons(count);
/* If the AA bit should be set (as indicated by the AA prefix in the zone file),
we are expected to return some records in the authoritative section. Bind9: If
there is data in the answer section, the authoritative section contains the NS
-records, otherwise it contains the SOA record. Currently we mimic this
-behaviour for the first case (there is some answer record).
+records, otherwise it contains the SOA record. Mimic that.
*/

-if (aa)
-  find_records(f, zone, zone[0] == '.' ? zone+1 : zone, US"NS", 2, &pk, &count, NULL, NULL);
+if (strcmp(qtype, "SOA") != 0 && strcmp(qtype, "NS") != 0)
+  if (count)
+    find_records(f, zone, zone[0] == '.' ? zone+1 : zone, US"NS", 2, &pk, &count, NULL, NULL);
+  else
+    find_records(f, zone, zone[0] == '.' ? zone+1 : zone, US"SOA", 3, &pk, &count, NULL, NULL);
 header->nscount = htons(count - ntohs(header->ancount));


/* There is no need to return any additional records because Exim no longer
diff --git a/test/stderr/0002 b/test/stderr/0002
index 8f85e71..f0b7776 100644
--- a/test/stderr/0002
+++ b/test/stderr/0002
@@ -298,6 +298,7 @@ looking up host name for V4NET.0.0.1
DNS lookup of 1.0.0.V4NET.in-addr.arpa (PTR) using fakens
DNS lookup of 1.0.0.V4NET.in-addr.arpa (PTR) succeeded
IP address lookup yielded "ten-1.test.ex"
+ writing neg-cache entry for ten-1.test.ex-AAAA-41, ttl 3600
DNS lookup of ten-1.test.ex (A) using fakens
DNS lookup of ten-1.test.ex (A) succeeded
ten-1.test.ex V4NET.0.0.1 mx=-1 sort=xx
diff --git a/test/stderr/0094 b/test/stderr/0094
index 26cf916..b59d608 100644
--- a/test/stderr/0094
+++ b/test/stderr/0094
@@ -83,12 +83,14 @@ DNS lookup of 90.99.99.V4NET.in-addr.arpa (PTR) using fakens
DNS lookup of 90.99.99.V4NET.in-addr.arpa (PTR) succeeded
IP address lookup yielded "oneback.test.ex"
alias "host1.masq.test.ex"
+ writing neg-cache entry for oneback.test.ex-AAAA-41, ttl 3600
DNS lookup of oneback.test.ex (A) using fakens
DNS lookup of oneback.test.ex (A) succeeded
oneback.test.ex V4NET.99.99.90 mx=-1 sort=xx
checking addresses for oneback.test.ex
Forward DNS security status: unverified
V4NET.99.99.90 OK
+ writing neg-cache entry for host1.masq.test.ex-AAAA-41, ttl 3600
DNS lookup of host1.masq.test.ex (A) using fakens
DNS lookup of host1.masq.test.ex (A) succeeded
host1.masq.test.ex V4NET.90.90.90 mx=-1 sort=xx
diff --git a/test/stderr/0183 b/test/stderr/0183
index 0aaba1d..24fcc50 100644
--- a/test/stderr/0183
+++ b/test/stderr/0183
@@ -25,6 +25,8 @@ userx in "usery:userz"? no (end of list)
useryz router skipped: local_parts mismatch
--------> lookuphost router <--------
local_part=userx domain=test.again.dns
+checking local_parts
+userx in "!userd"? yes (end of list)
calling lookuphost router
lookuphost router called for userx@???
domain = test.again.dns
@@ -32,6 +34,7 @@ DNS lookup of test.again.dns (MX) using fakens
DNS lookup of test.again.dns (MX) gave TRY_AGAIN
test.again.dns in dns_again_means_nonexist? no (option unset)
returning DNS_AGAIN
+ writing neg-cache entry for test.again.dns-MX-c1, ttl -1
lookuphost router: defer for userx@???
message: host lookup did not complete
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

@@ -52,6 +55,8 @@ abcd in "usery:userz"? no (end of list)
useryz router skipped: local_parts mismatch
--------> lookuphost router <--------
local_part=abcd domain=test.again.dns
+checking local_parts
+abcd in "!userd"? yes (end of list)
calling lookuphost router
lookuphost router called for abcd@???
domain = test.again.dns
@@ -76,12 +81,15 @@ abcd in "usery:userz"? no (end of list)
useryz router skipped: local_parts mismatch
--------> lookuphost router <--------
local_part=abcd domain=ten-1.test.ex
+checking local_parts
+abcd in "!userd"? yes (end of list)
calling lookuphost router
lookuphost router called for abcd@???
domain = ten-1.test.ex
DNS lookup of ten-1.test.ex (MX) using fakens
DNS lookup of ten-1.test.ex (MX) gave NO_DATA
returning DNS_NODATA
+ writing neg-cache entry for ten-1.test.ex-MX-c1, ttl 3600
DNS lookup of ten-1.test.ex (A) using fakens
DNS lookup of ten-1.test.ex (A) succeeded
fully qualified name = ten-1.test.ex
@@ -124,6 +132,7 @@ DNS lookup of test.again.dns (A) using fakens
DNS lookup of test.again.dns (A) gave TRY_AGAIN
test.again.dns in dns_again_means_nonexist? no (option unset)
returning DNS_AGAIN
+ writing neg-cache entry for test.again.dns-A-41, ttl -1
useryz router: defer for usery@???
message: host lookup for test.again.dns did not complete (DNS timeout?)
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

@@ -171,6 +180,8 @@ xyz in "usery:userz"? no (end of list)
useryz router skipped: local_parts mismatch
--------> lookuphost router <--------
local_part=xyz domain=ten-1.test.ex
+checking local_parts
+xyz in "!userd"? yes (end of list)
calling lookuphost router
lookuphost router called for xyz@???
domain = ten-1.test.ex
@@ -216,12 +227,15 @@ userx in "usery:userz"? no (end of list)
useryz router skipped: local_parts mismatch
--------> lookuphost router <--------
local_part=userx domain=test.fail.dns
+checking local_parts
+userx in "!userd"? yes (end of list)
calling lookuphost router
lookuphost router called for userx@???
domain = test.fail.dns
DNS lookup of test.fail.dns (MX) using fakens
DNS lookup of test.fail.dns (MX) gave NO_RECOVERY
returning DNS_FAIL
+ writing neg-cache entry for test.fail.dns-MX-c1, ttl -1
lookuphost router: defer for userx@???
message: host lookup did not complete
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

@@ -242,6 +256,8 @@ abcd in "usery:userz"? no (end of list)
useryz router skipped: local_parts mismatch
--------> lookuphost router <--------
local_part=abcd domain=test.fail.dns
+checking local_parts
+abcd in "!userd"? yes (end of list)
calling lookuphost router
lookuphost router called for abcd@???
domain = test.fail.dns
@@ -266,12 +282,15 @@ abcd in "usery:userz"? no (end of list)
useryz router skipped: local_parts mismatch
--------> lookuphost router <--------
local_part=abcd domain=ten-1.test.ex
+checking local_parts
+abcd in "!userd"? yes (end of list)
calling lookuphost router
lookuphost router called for abcd@???
domain = ten-1.test.ex
DNS lookup of ten-1.test.ex (MX) using fakens
DNS lookup of ten-1.test.ex (MX) gave NO_DATA
returning DNS_NODATA
+ writing neg-cache entry for ten-1.test.ex-MX-c1, ttl 3600
DNS lookup of ten-1.test.ex (A) using fakens
DNS lookup of ten-1.test.ex (A) succeeded
fully qualified name = ten-1.test.ex
@@ -313,6 +332,7 @@ doing DNS lookup
DNS lookup of test.fail.dns (A) using fakens
DNS lookup of test.fail.dns (A) gave NO_RECOVERY
returning DNS_FAIL
+ writing neg-cache entry for test.fail.dns-A-41, ttl -1
useryz router: defer for usery@???
message: host lookup for test.fail.dns did not complete (DNS timeout?)
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

@@ -360,6 +380,8 @@ xyz in "usery:userz"? no (end of list)
useryz router skipped: local_parts mismatch
--------> lookuphost router <--------
local_part=xyz domain=ten-1.test.ex
+checking local_parts
+xyz in "!userd"? yes (end of list)
calling lookuphost router
lookuphost router called for xyz@???
domain = ten-1.test.ex
@@ -405,12 +427,15 @@ userx in "usery:userz"? no (end of list)
useryz router skipped: local_parts mismatch
--------> lookuphost router <--------
local_part=userx domain=nonexist.test.ex
+checking local_parts
+userx in "!userd"? yes (end of list)
calling lookuphost router
lookuphost router called for userx@???
domain = nonexist.test.ex
DNS lookup of nonexist.test.ex (MX) using fakens
DNS lookup of nonexist.test.ex (MX) gave HOST_NOT_FOUND
returning DNS_NOMATCH
+ writing neg-cache entry for nonexist.test.ex-MX-c1, ttl 3600
lookuphost router declined for userx@???
"more" is false: skipping remaining routers
no more routers
@@ -432,6 +457,8 @@ abcd in "usery:userz"? no (end of list)
useryz router skipped: local_parts mismatch
--------> lookuphost router <--------
local_part=abcd domain=nonexist.test.ex
+checking local_parts
+abcd in "!userd"? yes (end of list)
calling lookuphost router
lookuphost router called for abcd@???
domain = nonexist.test.ex
@@ -457,12 +484,15 @@ abcd in "usery:userz"? no (end of list)
useryz router skipped: local_parts mismatch
--------> lookuphost router <--------
local_part=abcd domain=ten-1.test.ex
+checking local_parts
+abcd in "!userd"? yes (end of list)
calling lookuphost router
lookuphost router called for abcd@???
domain = ten-1.test.ex
DNS lookup of ten-1.test.ex (MX) using fakens
DNS lookup of ten-1.test.ex (MX) gave NO_DATA
returning DNS_NODATA
+ writing neg-cache entry for ten-1.test.ex-MX-c1, ttl 3600
DNS lookup of ten-1.test.ex (A) using fakens
DNS lookup of ten-1.test.ex (A) succeeded
fully qualified name = ten-1.test.ex
@@ -504,6 +534,7 @@ doing DNS lookup
DNS lookup of nonexist.test.ex (A) using fakens
DNS lookup of nonexist.test.ex (A) gave HOST_NOT_FOUND
returning DNS_NOMATCH
+ writing neg-cache entry for nonexist.test.ex-A-41, ttl 3600
useryz router: defer for usery@???
message: lookup of host "nonexist.test.ex" failed in useryz router
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

@@ -551,6 +582,8 @@ xyz in "usery:userz"? no (end of list)
useryz router skipped: local_parts mismatch
--------> lookuphost router <--------
local_part=xyz domain=ten-1.test.ex
+checking local_parts
+xyz in "!userd"? yes (end of list)
calling lookuphost router
lookuphost router called for xyz@???
domain = ten-1.test.ex
@@ -595,6 +628,7 @@ DNS lookup of _smtp._tcp.test.again.dns (SRV) using fakens
DNS lookup of _smtp._tcp.test.again.dns (SRV) gave TRY_AGAIN
_smtp._tcp.test.again.dns in dns_again_means_nonexist? no (option unset)
returning DNS_AGAIN
+ writing neg-cache entry for _smtp._tcp.test.again.dns-SRV-c1, ttl -1
test.again.dns in "test.fail.dns"? no (end of list)
srv router: defer for srv@???
message: host lookup did not complete
@@ -614,17 +648,103 @@ srv router called for srv@???
DNS lookup of _smtp._tcp.test.fail.dns (SRV) using fakens
DNS lookup of _smtp._tcp.test.fail.dns (SRV) gave NO_RECOVERY
returning DNS_FAIL
+ writing neg-cache entry for _smtp._tcp.test.fail.dns-SRV-c1, ttl -1
test.fail.dns in "test.fail.dns"? yes (matched "test.fail.dns")
DNS_FAIL treated as DNS_NODATA (domain in srv_fail_domains)
DNS lookup of test.fail.dns (MX) using fakens
DNS lookup of test.fail.dns (MX) gave NO_RECOVERY
returning DNS_FAIL
+ writing neg-cache entry for test.fail.dns-MX-c1, ttl -1
test.fail.dns in "test.fail.dns"? yes (matched "test.fail.dns")
DNS_FAIL treated as DNS_NODATA (domain in mx_fail_domains)
DNS lookup of test.fail.dns (A) using fakens
DNS lookup of test.fail.dns (A) gave NO_RECOVERY
returning DNS_FAIL
+ writing neg-cache entry for test.fail.dns-A-c1, ttl -1
srv router: defer for srv@???
message: host lookup did not complete
search_tidyup called
>>>>>>>>>>>>>>>> Exim pid=pppp (main) terminating with rc=1 >>>>>>>>>>>>>>>>

+Exim version x.yz ....
+changed uid/gid: forcing real = effective
+  uid=uuuu gid=CALLER_GID pid=pppp
+configuration file is TESTSUITE/test-config
+admin user
+dropping to exim gid; retaining priv uid
+originator: uid=CALLER_UID gid=CALLER_GID login=CALLER name=CALLER_NAME
+sender address = CALLER@???
+Address testing: uid=uuuu gid=EXIM_GID euid=uuuu egid=EXIM_GID
+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
+Testing userx@???
+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
+Considering userx@???
+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
+routing userx@???
+--------> srv router <--------
+local_part=userx domain=nonexist.example.com
+checking local_parts
+userx in "^srv"? no (end of list)
+srv router skipped: local_parts mismatch
+--------> useryz router <--------
+local_part=userx domain=nonexist.example.com
+checking local_parts
+userx in "usery:userz"? no (end of list)
+useryz router skipped: local_parts mismatch
+--------> lookuphost router <--------
+local_part=userx domain=nonexist.example.com
+checking local_parts
+userx in "!userd"? yes (end of list)
+calling lookuphost router
+lookuphost router called for userx@???
+  domain = nonexist.example.com
+DNS lookup of nonexist.example.com (MX) using fakens
+DNS lookup of nonexist.example.com (MX) gave HOST_NOT_FOUND
+returning DNS_NOMATCH
+ writing neg-cache entry for nonexist.example.com-MX-c1, ttl 2
+lookuphost router declined for userx@???
+"more" is false: skipping remaining routers
+no more routers
+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
+Testing userd@???
+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
+Considering userd@???
+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
+routing userd@???
+--------> srv router <--------
+local_part=userd domain=nonexist.example.com
+checking local_parts
+userd in "^srv"? no (end of list)
+srv router skipped: local_parts mismatch
+--------> useryz router <--------
+local_part=userd domain=nonexist.example.com
+checking local_parts
+userd in "usery:userz"? no (end of list)
+useryz router skipped: local_parts mismatch
+--------> lookuphost router <--------
+local_part=userd domain=nonexist.example.com
+checking local_parts
+userd in "!userd"? no (matched "!userd")
+lookuphost router skipped: local_parts mismatch
+--------> delay router <--------
+local_part=userd domain=nonexist.example.com
+checking "condition" "${acl {delay}}"...
+  using ACL "delay"
+  processing "accept" (TESTSUITE/test-config 18)
+  check delay = 3s
+  delay modifier requests 3-second delay
+    message: y
+  accept: condition test succeeded in ACL "delay"
+  end of ACL "delay": ACCEPT
+calling delay router
+delay router called for userd@???
+  domain = nonexist.example.com
+DNS lookup of nonexist.example.com-MX: cached value DNS_NOMATCH past valid time
+DNS lookup of nonexist.example.com (MX) using fakens
+DNS lookup of nonexist.example.com (MX) gave HOST_NOT_FOUND
+returning DNS_NOMATCH
+ update neg-cache entry for nonexist.example.com-MX-c1, ttl 2
+delay router declined for userd@???
+"more" is false: skipping remaining routers
+no more routers
+search_tidyup called
+>>>>>>>>>>>>>>>> Exim pid=pppp (main) terminating with rc=2 >>>>>>>>>>>>>>>>
diff --git a/test/stderr/0277 b/test/stderr/0277
index c174db8..3d9490f 100644
--- a/test/stderr/0277
+++ b/test/stderr/0277
@@ -18,6 +18,8 @@ looking up host name for V4NET.2.3.4
 DNS lookup of 4.3.2.V4NET.in-addr.arpa (PTR) using fakens
 DNS lookup of 4.3.2.V4NET.in-addr.arpa (PTR) gave HOST_NOT_FOUND
 returning DNS_NOMATCH
+DNS: no SOA record found for neg-TTL
+ writing neg-cache entry for 4.3.2.V4NET.in-addr.arpa-PTR-41, ttl -1
 LOG: host_lookup_failed MAIN
   no host name found for IP address V4NET.2.3.4
 sender_fullhost = [V4NET.2.3.4]
@@ -94,6 +96,8 @@ looking up host name for V4NET.10.11.12
 DNS lookup of 12.11.10.V4NET.in-addr.arpa (PTR) using fakens
 DNS lookup of 12.11.10.V4NET.in-addr.arpa (PTR) gave HOST_NOT_FOUND
 returning DNS_NOMATCH
+DNS: no SOA record found for neg-TTL
+ writing neg-cache entry for 12.11.10.V4NET.in-addr.arpa-PTR-41, ttl -1
 LOG: host_lookup_failed MAIN
   no host name found for IP address V4NET.10.11.12
 sender_fullhost = [V4NET.10.11.12]
@@ -137,6 +141,8 @@ looking up host name for V4NET.1.1.1
 DNS lookup of 1.1.1.V4NET.in-addr.arpa (PTR) using fakens
 DNS lookup of 1.1.1.V4NET.in-addr.arpa (PTR) gave HOST_NOT_FOUND
 returning DNS_NOMATCH
+DNS: no SOA record found for neg-TTL
+ writing neg-cache entry for 1.1.1.V4NET.in-addr.arpa-PTR-41, ttl -1
 LOG: host_lookup_failed MAIN
   no host name found for IP address V4NET.1.1.1
 sender_fullhost = [V4NET.1.1.1]
@@ -180,6 +186,8 @@ looking up host name for V4NET.2.2.2
 DNS lookup of 2.2.2.V4NET.in-addr.arpa (PTR) using fakens
 DNS lookup of 2.2.2.V4NET.in-addr.arpa (PTR) gave HOST_NOT_FOUND
 returning DNS_NOMATCH
+DNS: no SOA record found for neg-TTL
+ writing neg-cache entry for 2.2.2.V4NET.in-addr.arpa-PTR-41, ttl -1
 LOG: host_lookup_failed MAIN
   no host name found for IP address V4NET.2.2.2
 sender_fullhost = [V4NET.2.2.2]
diff --git a/test/stderr/0278 b/test/stderr/0278
index aafb854..6e2fa35 100644
--- a/test/stderr/0278
+++ b/test/stderr/0278
@@ -291,9 +291,12 @@ r2 router called for unknown@???
 DNS lookup of test.ex (MX) using fakens
 DNS lookup of test.ex (MX) gave NO_DATA
 returning DNS_NODATA
+ writing neg-cache entry for test.ex-MX-c1, ttl 3600
+ writing neg-cache entry for test.ex-AAAA-c1, ttl 3600
 DNS lookup of test.ex (A) using fakens
 DNS lookup of test.ex (A) gave NO_DATA
 returning DNS_NODATA
+ writing neg-cache entry for test.ex-A-c1, ttl 3600
 r2 router declined for unknown@???
 --------> r3 router <--------
 local_part=unknown domain=test.ex
diff --git a/test/stderr/0361 b/test/stderr/0361
index 9b74af4..8846bea 100644
--- a/test/stderr/0361
+++ b/test/stderr/0361
@@ -99,10 +99,13 @@ r1 router called for kilos@???
 DNS lookup of recurse.test.ex (MX) using fakens
 DNS lookup of recurse.test.ex (MX) gave HOST_NOT_FOUND
 returning DNS_NOMATCH
+ writing neg-cache entry for recurse.test.ex-MX-c1, ttl 3600
 r1 router widened recurse.test.ex to recurse.test.ex.test.ex
 DNS lookup of recurse.test.ex.test.ex (MX) using fakens
 DNS lookup of recurse.test.ex.test.ex (MX) gave NO_DATA
 returning DNS_NODATA
+ writing neg-cache entry for recurse.test.ex.test.ex-MX-c1, ttl 3600
+ writing neg-cache entry for recurse.test.ex.test.ex-AAAA-c1, ttl 3600
 DNS lookup of recurse.test.ex.test.ex (A) using fakens
 DNS lookup of recurse.test.ex.test.ex (A) succeeded
 fully qualified name = recurse.test.ex.test.ex
diff --git a/test/stderr/0381 b/test/stderr/0381
index 42b52d0..62a0137 100644
--- a/test/stderr/0381
+++ b/test/stderr/0381
@@ -41,12 +41,14 @@ DNS lookup of 97.99.99.V4NET.in-addr.arpa (PTR) using fakens
 DNS lookup of 97.99.99.V4NET.in-addr.arpa (PTR) succeeded
 IP address lookup yielded "x.gov.uk.test.ex"
   alias "x.co.uk.test.ex"
+ writing neg-cache entry for x.gov.uk.test.ex-AAAA-41, ttl 3600
 DNS lookup of x.gov.uk.test.ex (A) using fakens
 DNS lookup of x.gov.uk.test.ex (A) succeeded
 x.gov.uk.test.ex V4NET.99.99.97 mx=-1 sort=xx 
 checking addresses for x.gov.uk.test.ex
 Forward DNS security status: unverified
   V4NET.99.99.97 OK
+ writing neg-cache entry for x.co.uk.test.ex-AAAA-41, ttl 3600
 DNS lookup of x.co.uk.test.ex (A) using fakens
 DNS lookup of x.co.uk.test.ex (A) succeeded
 x.co.uk.test.ex V4NET.99.99.97 mx=-1 sort=xx 
diff --git a/test/stderr/0419 b/test/stderr/0419
index 8aba8f1..636015b 100644
--- a/test/stderr/0419
+++ b/test/stderr/0419
@@ -24,8 +24,10 @@ dnslookup router called for k@???
   domain = mxt13.test.ex
 DNS lookup of mxt13.test.ex (MX) using fakens
 DNS lookup of mxt13.test.ex (MX) succeeded
+ writing neg-cache entry for other1.test.ex-AAAA-41, ttl 3600
 DNS lookup of other1.test.ex (A) using fakens
 DNS lookup of other1.test.ex (A) succeeded
+ writing neg-cache entry for other2.test.ex-AAAA-41, ttl 3600
 DNS lookup of other2.test.ex (A) using fakens
 DNS lookup of other2.test.ex (A) succeeded
 other1.test.ex in "!mxt13.test.ex : !other1.test.ex : *.test.ex"? no (matched "!other1.test.ex")
diff --git a/test/stderr/0463 b/test/stderr/0463
index 13b77a8..694d9df 100644
--- a/test/stderr/0463
+++ b/test/stderr/0463
@@ -19,6 +19,7 @@ checking domains
 DNS lookup of ten-1 (MX) using fakens
 DNS lookup of ten-1 (MX) gave NO_DATA
 returning DNS_NODATA
+ writing neg-cache entry for ten-1-MX-41, ttl 3600
 Address records are not being sought
 ten-1 in "!@mx_any"? yes (end of list)
 calling all router
@@ -27,6 +28,8 @@ all router called for x@ten-1
 DNS lookup of ten-1 (MX) using fakens
 DNS lookup of ten-1 (MX) gave NO_DATA
 returning DNS_NODATA
+ writing neg-cache entry for ten-1-MX-c1, ttl 3600
+ writing neg-cache entry for ten-1-AAAA-c1, ttl 3600
 DNS lookup of ten-1 (A) using fakens
 DNS lookup of ten-1 (A) succeeded
 fully qualified name = ten-1.test.ex
@@ -44,6 +47,7 @@ checking domains
 DNS lookup of ten-1.test.ex (MX) using fakens
 DNS lookup of ten-1.test.ex (MX) gave NO_DATA
 returning DNS_NODATA
+ writing neg-cache entry for ten-1.test.ex-MX-41, ttl 3600
 Address records are not being sought
 ten-1.test.ex in "!@mx_any"? yes (end of list)
 calling all router
@@ -52,6 +56,8 @@ all router called for x@???
 DNS lookup of ten-1.test.ex (MX) using fakens
 DNS lookup of ten-1.test.ex (MX) gave NO_DATA
 returning DNS_NODATA
+ writing neg-cache entry for ten-1.test.ex-MX-c1, ttl 3600
+ writing neg-cache entry for ten-1.test.ex-AAAA-c1, ttl 3600
 DNS lookup of ten-1.test.ex (A) using fakens
 DNS lookup of ten-1.test.ex (A) succeeded
 fully qualified name = ten-1.test.ex
diff --git a/test/stderr/0469 b/test/stderr/0469
index 919f65a..9dae8fc 100644
--- a/test/stderr/0469
+++ b/test/stderr/0469
@@ -25,6 +25,7 @@ fakens returned PASS_ON
 passing dontqualify on to res_search()
 DNS lookup of dontqualify (A) gave HOST_NOT_FOUND
 returning DNS_NOMATCH
+ writing neg-cache entry for dontqualify-A-41, ttl 86400
 fully qualified name = mxt1c.test.ex
 host_find_bydns yield = HOST_FIND_FAILED (0); returned hosts:
   dontqualify <null> MX=1 *
diff --git a/test/stderr/0545 b/test/stderr/0545
index a147fd9..50658a6 100644
--- a/test/stderr/0545
+++ b/test/stderr/0545
@@ -24,6 +24,7 @@ CNAME found: change to eximtesthost.test.ex
 DNS lookup of eximtesthost.test.ex (MX) using fakens
 DNS lookup of eximtesthost.test.ex (MX) gave NO_DATA
 returning DNS_NODATA
+ writing neg-cache entry for eximtesthost.test.ex-MX-c1, ttl 3600
 DNS lookup of alias-eximtesthost (A) using fakens
 DNS lookup of alias-eximtesthost (A) succeeded
 CNAME found: change to eximtesthost.test.ex
@@ -94,6 +95,7 @@ CNAME found: change to eximtesthost.test.ex
 DNS lookup of eximtesthost.test.ex (MX) using fakens
 DNS lookup of eximtesthost.test.ex (MX) gave NO_DATA
 returning DNS_NODATA
+ writing neg-cache entry for eximtesthost.test.ex-MX-c1, ttl 3600
 DNS lookup of alias-eximtesthost.test.ex (A) using fakens
 DNS lookup of alias-eximtesthost.test.ex (A) succeeded
 CNAME found: change to eximtesthost.test.ex
diff --git a/test/stderr/1006 b/test/stderr/1006
index 19cf904..8d8771e 100644
--- a/test/stderr/1006
+++ b/test/stderr/1006
@@ -13,10 +13,12 @@ DNS lookup of 46.test.ex (A) succeeded
 DNS lookup of v6.test.ex (MX) using fakens
 DNS lookup of v6.test.ex (MX) gave NO_DATA
 returning DNS_NODATA
+ writing neg-cache entry for v6.test.ex-MX-c1, ttl 3600
 DNS lookup of v6.test.ex (AAAA) succeeded
 DNS lookup of v6.test.ex (A) using fakens
 DNS lookup of v6.test.ex (A) gave NO_DATA
 returning DNS_NODATA
+ writing neg-cache entry for v6.test.ex-A-c1, ttl 3600

>>>>>>>>>>>>>>>> Exim pid=pppp (main) terminating with rc=0 >>>>>>>>>>>>>>>>

Exim version x.yz ....
configuration file is TESTSUITE/test-config
@@ -31,9 +33,11 @@ DNS lookup of 46.test.ex (A) succeeded
DNS lookup of v6.test.ex (MX) using fakens
DNS lookup of v6.test.ex (MX) gave NO_DATA
returning DNS_NODATA
+ writing neg-cache entry for v6.test.ex-MX-c1, ttl 3600
DNS lookup of v6.test.ex (A) using fakens
DNS lookup of v6.test.ex (A) gave NO_DATA
returning DNS_NODATA
+ writing neg-cache entry for v6.test.ex-A-c1, ttl 3600
>>>>>>>>>>>>>>>> Exim pid=pppp (main) terminating with rc=2 >>>>>>>>>>>>>>>>


******** SERVER ********
diff --git a/test/stderr/2201 b/test/stderr/2201
index c7410cd..7e8d8a8 100644
--- a/test/stderr/2201
+++ b/test/stderr/2201
@@ -99,6 +99,7 @@ dnsdb key: unknown
DNS lookup of unknown (TXT) using fakens
DNS lookup of unknown (TXT) gave HOST_NOT_FOUND
returning DNS_NOMATCH
+ writing neg-cache entry for unknown-TXT-41, ttl 3600
lookup failed
unknown in "dnsdb;unknown"? no (end of list)
r1 router skipped: local_parts mismatch
diff --git a/test/stderr/2202 b/test/stderr/2202
index 66debc3..9543fb1 100644
--- a/test/stderr/2202
+++ b/test/stderr/2202
@@ -42,6 +42,8 @@ DNS lookup of cioce.test.again.dns (MX) using fakens
DNS lookup of cioce.test.again.dns (MX) gave TRY_AGAIN
cioce.test.again.dns in dns_again_means_nonexist? yes (matched "*")
cioce.test.again.dns is in dns_again_means_nonexist: returning DNS_NOMATCH
+DNS: no SOA record found for neg-TTL
+ writing neg-cache entry for cioce.test.again.dns-MX-41, ttl -1
lookup failed
sender host name required, to match against *.cioce.test.again.dns
looking up host name for ip4.ip4.ip4.ip4
@@ -62,6 +64,8 @@ DNS lookup of cioce.test.again.dns (A) using fakens
DNS lookup of cioce.test.again.dns (A) gave TRY_AGAIN
cioce.test.again.dns in dns_again_means_nonexist? yes (matched "*")
cioce.test.again.dns is in dns_again_means_nonexist: returning DNS_NOMATCH
+DNS: no SOA record found for neg-TTL
+ writing neg-cache entry for cioce.test.again.dns-A-c1, ttl -1
get[host|ipnode]byname[2] returned 1 (HOST_NOT_FOUND)
no IP address found for host cioce.test.again.dns (during SMTP connection from the.local.host.name [ip4.ip4.ip4.ip4])
LOG: host_lookup_failed MAIN
diff --git a/test/stderr/4802 b/test/stderr/4802
index 9575f88..8f25c05 100644
--- a/test/stderr/4802
+++ b/test/stderr/4802
@@ -4,6 +4,7 @@ admin user
dropping to exim gid; retaining priv uid
DNS lookup of mx-sec-a-aa.test.ex (MX) using fakens
DNS lookup of mx-sec-a-aa.test.ex (MX) succeeded
+ writing neg-cache entry for a-aa.test.ex-AAAA-800041, ttl 3600
DNS lookup of a-aa.test.ex (A) using fakens
DNS lookup of a-aa.test.ex (A) succeeded
DNS lookup of a-aa.test.ex (A/AAAA) requested AD, but got AA
@@ -15,6 +16,7 @@ dropping to exim gid; retaining priv uid
DNS lookup of mx-aa-a-sec.test.ex (MX) using fakens
DNS lookup of mx-aa-a-sec.test.ex (MX) succeeded
DNS lookup of mx-aa-a-sec.test.ex (MX) requested AD, but got AA
+ writing neg-cache entry for a-sec.test.ex-AAAA-800041, ttl 3600
DNS lookup of a-sec.test.ex (A) using fakens
DNS lookup of a-sec.test.ex (A) succeeded
>>>>>>>>>>>>>>>> Exim pid=pppp (main) terminating with rc=0 >>>>>>>>>>>>>>>>

diff --git a/test/stderr/4803 b/test/stderr/4803
index a7aa7bf..dd4d99f 100644
--- a/test/stderr/4803
+++ b/test/stderr/4803
@@ -4,6 +4,7 @@ admin user
dropping to exim gid; retaining priv uid
DNS lookup of mx-sec-a-aa.test.ex (MX) using fakens
DNS lookup of mx-sec-a-aa.test.ex (MX) succeeded
+ writing neg-cache entry for a-aa.test.ex-AAAA-800041, ttl 3600
DNS lookup of a-aa.test.ex (A) using fakens
DNS lookup of a-aa.test.ex (A) succeeded
DNS faked the AD bit (got AA and matched with dns_trust_aa (test.ex in *))
@@ -19,6 +20,7 @@ DNS lookup of mx-aa-a-sec.test.ex (MX) succeeded
DNS faked the AD bit (got AA and matched with dns_trust_aa (test.ex in *))
DNS faked the AD bit (got AA and matched with dns_trust_aa (test.ex in *))
DNS faked the AD bit (got AA and matched with dns_trust_aa (test.ex in *))
+ writing neg-cache entry for a-sec.test.ex-AAAA-800041, ttl 3600
DNS lookup of a-sec.test.ex (A) using fakens
DNS lookup of a-sec.test.ex (A) succeeded
>>>>>>>>>>>>>>>> Exim pid=pppp (main) terminating with rc=0 >>>>>>>>>>>>>>>>

diff --git a/test/stdout/0183 b/test/stdout/0183
index 66b21e5..9d8e53f 100644
--- a/test/stdout/0183
+++ b/test/stdout/0183
@@ -30,3 +30,5 @@ xyz@???
host ten-1.test.ex [V4NET.0.0.1]
srv@??? cannot be resolved at this time: host lookup did not complete
srv@??? cannot be resolved at this time: host lookup did not complete
+userx@??? is undeliverable: Unrouteable address
+userd@??? is undeliverable: Unrouteable address